“在 WebGL 中如何繪制文本”是一個(gè)我們常見的問(wèn)題。那么第一件事就是我們要問(wèn)自己繪制文本的目的何在。現(xiàn)在有一個(gè)瀏覽器,瀏覽器用來(lái)顯示文本。所以你的第一個(gè)答案應(yīng)該是如何使用 HTML 來(lái)顯示文本。
讓我們從最簡(jiǎn)單的例子開始:你只是想在你的 WebGL 上繪制一些文本。我們可以稱之為一個(gè)文本覆蓋?;旧线@是停留在同一個(gè)位置的文本。
簡(jiǎn)單的方法是構(gòu)造一些 HTML 元素,使用 CSS 使它們重疊。
例如:先構(gòu)造一個(gè)容器,把畫布和一些 HTML 元素重疊放置在容器內(nèi)部。
<div class="container">
<canvas id="canvas" width="400" height="300"></canvas>
<div id="overlay">
<div>Time: <span id="time"></span></div>
<div>Angle: <span id="angle"></span></div>
</div>
</div>
接下來(lái)設(shè)置 CSS,以達(dá)到畫布和 HTML 重疊的目的。
.container {
position: relative;
}
#overlay {
position: absolute;
left: 10px;
top: 10px;
}
現(xiàn)在按照初始化和創(chuàng)建時(shí)間查找這些元素,或者查找你想要改變的區(qū)域。
// look up the elements we want to affect
var timeElement = document.getElementById("time");
var angleElement = document.getElementById("angle");
// Create text nodes to save some time for the browser.
var timeNode = document.createTextNode("");
var angleNode = document.createTextNode("");
// Add those text nodes where they need to go
timeElement.appendChild(timeNode);
angleElement.appendChild(angleNode);
最后在渲染時(shí)更新節(jié)點(diǎn)。
function drawScene() {
...
// convert rotation from radians to degrees
var angle = radToDeg(rotation[1]);
// only report 0 - 360
angle = angle % 360;
// set the nodes
angleNode.nodeValue = angle.toFixed(0); // no decimal place
timeNode.nodeValue = clock.toFixed(2); // 2 decimal places
這里有一個(gè)例子:
注意為了我想改變的部分,我是如何把 spans 置入特殊的 div 內(nèi)的。在這里我做一個(gè)假設(shè),這比只使用 div 而沒有 spans 速度要快,類似的有:
timeNode.value = "Time " + clock.toFixed(2);
另外,我們可以使用文本節(jié)點(diǎn),通過(guò)調(diào)用 node = document.createTextNode() 和 laternode.node = someMsg。我們也可以使用 someElement.innerHTML = someHTML。這將會(huì)更加靈活,雖然這樣可能會(huì)稍微慢一些,但是您卻可以插入任意的 HTML 字符串,因?yàn)槟忝看卧O(shè)置它,瀏覽器都不得不創(chuàng)建和銷毀節(jié)點(diǎn)。這對(duì)你來(lái)說(shuō)更加方便。
不采用疊加技術(shù)很重要的一點(diǎn)是,WebGL 在瀏覽器中運(yùn)行。要記得在適當(dāng)?shù)臅r(shí)候使用瀏覽器的特征。大量的 OpenGL 程序員習(xí)慣于從一開始就 100% 靠他們自己實(shí)現(xiàn)應(yīng)用的每一部分自己,因?yàn)?WebGL 要在一個(gè)已經(jīng)有很多特征的瀏覽器上運(yùn)行。使用它們有很多好處。例如使用 CSS 樣式,你可以很容易就覆蓋一個(gè)有趣的風(fēng)格。
這里有一個(gè)相同的例子,但是添加了一些風(fēng)格。背景是圓形的,字母的周圍有光暈。這兒有一個(gè)紅色的邊界。你可以使用 HTML 免費(fèi)得到所有。
我們要討論的下一件最常見的事情是文本相對(duì)于你渲染的東西的位置。我們也可以在 HTML 中做到這一點(diǎn)。
在本例中,我們將再次構(gòu)造一個(gè)畫布容器和另一個(gè)活動(dòng)的 HTML 容器。
<div class="container">
<canvas id="canvas" width="400" height="300"></canvas>
<div id="divcontainer"></div>
</div>
我們將設(shè)置 CSS
.container {
position: relative;
overflow: none;
}
#divcontainer {
position: absolute;
left: 0px;
top: 0px;
width: 400px;
height: 300px;
z-index: 10;
overflow: hidden;
}
.floating-div {
position: absolute;
}
相對(duì)于第一個(gè) 父類位置 position: relative 或者 position: absolute 風(fēng)格,position: absolute; 部分使 #divcontainer 放置于絕對(duì)位置。在本例中,畫布和 #divcontainer 都在容器內(nèi)。
left: 0px; top: 0px 使 #divcontainer 結(jié)合一切。z-index: 10 使它浮在畫布上。overflow: hidden 讓它的子類被剪除。
最后,floating-div 將使用我們創(chuàng)建的可移式 div。
現(xiàn)在我們需要查找 div 容器,創(chuàng)建一個(gè) div,將 div 附加到容器。
// look up the divcontainer
var divContainerElement = document.getElementById("divcontainer");
// make the div
var div = document.createElement("div");
// assign it a CSS class
div.className = "floating-div";
// make a text node for its content
var textNode = document.createTextNode("");
div.appendChild(textNode);
// add it to the divcontainer
divContainerElement.appendChild(div);
現(xiàn)在,我們可以通過(guò)設(shè)置它的風(fēng)格定位 div。
div.style.left = Math.floor(x) + "px";
div.style.top = Math.floor(y) + "px";
textNode.nodeValue = clock.toFixed(2);
下面是一個(gè)例子,我們只需要限制 div 的邊界。
下一步,我們想在 3D 場(chǎng)景中設(shè)計(jì)它相對(duì)于某些事物的位置。我們?cè)撊绾巫觯慨?dāng)我們透視投影覆蓋,我們?nèi)绾巫鰧?shí)際上就是我們請(qǐng)求 GPU 如何做。
通過(guò)這個(gè)例子我們學(xué)習(xí)了如何使用模型,如何復(fù)制它們,以及如何應(yīng)用一個(gè)投影模型將他們轉(zhuǎn)換成 clipspace。然后我們討論著色器的內(nèi)容,它在本地空間復(fù)制模型,并將其轉(zhuǎn)換成 clipspace。我們也可以在 JavaScript 中做所有的這些。然后我們可以增加 clipspace(-1 到 +1) 到像素和使用 div 位置。
gl.drawArrays(...);
// We just got through computing a matrix to draw our
// F in 3D.
// choose a point in the local space of the 'F'.
// X Y Z W
var point = [100, 0, 0, 1]; // this is the front top right corner
// compute a clipspace position
// using the matrix we computed for the F
var clipspace = matrixVectorMultiply(point, matrix);
// divide X and Y by W just like the GPU does.
clipspace[0] /= clipspace[3];
clipspace[1] /= clipspace[3];
// convert from clipspace to pixels
var pixelX = (clipspace[0] * 0.5 + 0.5) * gl.canvas.width;
var pixelY = (clipspace[1] * -0.5 + 0.5) * gl.canvas.height;
// position the div
div.style.left = Math.floor(pixelX) + "px";
div.style.top = Math.floor(pixelY) + "px";
textNode.nodeValue = clock.toFixed(2);
wahlah,我們 div 的左上角和 F 的右上角完全符合。
當(dāng)然,如果你想要更多的文本構(gòu)造更多的 div,如下:
你可以查看最后一個(gè)例子的源代碼來(lái)觀察細(xì)節(jié)。一個(gè)重要的點(diǎn)是我猜測(cè)從 DOM 創(chuàng)建、添加、刪除 HTML 元素是緩慢的,所以上面的示例用來(lái)創(chuàng)建它們,將它們保留在周圍。它將任何未使用的都隱藏起來(lái),而不是把他們從 DOM 刪除。你必須明確知道是否可以更快。這只是我選擇的方法。
希望這已經(jīng)清楚地說(shuō)明了如何使用 HTML 制造文本。接下來(lái),我們將介紹如何使用 Canvas2D 制造文本。
更多建議: