在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ WebGL 文本 紋理
WebGL 文本 HTML
WebGL 文本 Canvas 2D
WebGL 2D 圖像旋轉(zhuǎn)
WebGL 圖像處理(續(xù))
WebGL 2D 矩陣
WebGL 繪制多個(gè)東西
WebGL 圖像處理
WebGL 2D 圖像轉(zhuǎn)換
WebGL 3D 透視
WebGL 是如何工作的
WebGL 文本 紋理
WebGL 2D 圖像伸縮
WebGL 場(chǎng)景圖
WebGL 3D 攝像機(jī)
WebGL 文本 使用字符紋理
WebGL 正交 3D
WebGL 基本原理
WebGL - 更少的代碼,更多的樂趣
WebGL 著色器和 GLSL

WebGL 文本 紋理

在上一篇文章中我們學(xué)習(xí)了在 WebGL 場(chǎng)景中如何使用一個(gè) 2D 畫布繪制文本。這個(gè)技術(shù)可以工作且很容易做到,但它有一個(gè)限制,即文本不能被其他的 3D 對(duì)象遮蓋。要做到這一點(diǎn),我們實(shí)際上需要在 WebGL 中繪制文本。

最簡(jiǎn)單的方法是繪制帶有文本的紋理。例如你可以使用 photoshop 或其他繪畫程序,來(lái)繪制帶有文本的一些圖像。

http://wiki.jikexueyuan.com/project/webgl/images/my-awesme-text.png" alt="" />

然后我們構(gòu)造一些平面幾何并顯示它。這實(shí)際上是一些游戲中構(gòu)造所有的文本的方式。例如 Locoroco 只有大約 270 個(gè)字符串。它本地化成 17 種語(yǔ)言。我們有一個(gè)包含所有語(yǔ)言的 Excel 表和一個(gè)腳本,該腳本將啟動(dòng) Photoshop 并生成紋理,每個(gè)紋理都對(duì)應(yīng)一種語(yǔ)言里的一個(gè)消息。

當(dāng)然你也可以在運(yùn)行時(shí)生成紋理。因?yàn)樵跒g覽器中 WebGL 是依靠畫布 2d api 來(lái)幫助生成紋理的。

我們來(lái)看上一篇文章的例子,在其中添加一個(gè)函數(shù):用文本填補(bǔ)一個(gè) 2D 畫布。

var textCtx = document.createElement("canvas").getContext("2d");

// Puts text in center of canvas.
function makeTextCanvas(text, width, height) {
  textCtx.canvas.width  = width;
  textCtx.canvas.height = height;
  textCtx.font = "20px monospace";
  textCtx.textAlign = "center";
  textCtx.textBaseline = "middle";
  textCtx.fillStyle = "black";
  textCtx.clearRect(0, 0, textCtx.canvas.width, textCtx.canvas.height);
  textCtx.fillText(text, width / 2, height / 2);
  return textCtx.canvas;
}

現(xiàn)在我們需要在 WebGL 中繪制 2 個(gè)不同東西:“F”和文本,我想切換到使用一些前一篇文章中所描述的輔助函數(shù)。如果你還不清楚 programInfo,bufferInfo 等,你需要瀏覽那篇文章。

現(xiàn)在,讓我們創(chuàng)建一個(gè)“F”和四元組單元。

// Create data for 'F'
var fBufferInfo = primitives.create3DFBufferInfo(gl);
// Create a unit quad for the 'text'
var textBufferInfo = primitives.createPlaneBufferInfo(gl, 1, 1, 1, 1, makeXRotation(Math.PI / 2));

一個(gè)四元組單元是一個(gè) 1 單元大小的四元組(方形),中心在原點(diǎn)。createPlaneBufferInfo 在 xz 平面創(chuàng)建一個(gè)平面。我們通過(guò)一個(gè)矩陣旋轉(zhuǎn)它,就得到一個(gè) xy 平面四元組單元。

接下來(lái)創(chuàng)建 2 個(gè)著色器:

// setup GLSL programs
var fProgramInfo = createProgramInfo(gl, ["3d-vertex-shader", "3d-fragment-shader"]);
var textProgramInfo = createProgramInfo(gl, ["text-vertex-shader", "text-fragment-shader"]);

創(chuàng)建我們的文本紋理:

// create text texture.
var textCanvas = makeTextCanvas("Hello!", 100, 26);
var textWidth  = textCanvas.width;
var textHeight = textCanvas.height;
var textTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, textTex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textCanvas);
// make sure we can render it even if it's not a power of 2
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

為“F”和文本設(shè)置 uniforms:

var fUniforms = {
  u_matrix: makeIdentity(),
};

var textUniforms = {
  u_matrix: makeIdentity(),
  u_texture: textTex,
};

當(dāng)我們計(jì)算 F 的矩陣時(shí),保存 F 的矩陣視圖:

var matrix = makeIdentity();
matrix = matrixMultiply(matrix, preTranslationMatrix);
matrix = matrixMultiply(matrix, scaleMatrix);
matrix = matrixMultiply(matrix, rotationZMatrix);
matrix = matrixMultiply(matrix, rotationYMatrix);
matrix = matrixMultiply(matrix, rotationXMatrix);
matrix = matrixMultiply(matrix, translationMatrix);
matrix = matrixMultiply(matrix, viewMatrix);
var fViewMatrix = copyMatrix(matrix);  // remember the view matrix for the text
matrix = matrixMultiply(matrix, projectionMatrix);

像這樣繪制 F:

gl.useProgram(fProgramInfo.program);

setBuffersAndAttributes(gl, fProgramInfo.attribSetters, fBufferInfo);

copyMatrix(matrix, fUniforms.u_matrix);
setUniforms(fProgramInfo.uniformSetters, fUniforms);

// Draw the geometry.
gl.drawElements(gl.TRIANGLES, fBufferInfo.numElements, gl.UNSIGNED_SHORT, 0);

文本中我們只需要知道 F 的原點(diǎn)位置,我們還需要測(cè)量和單元四元組相匹配的紋理尺寸。最后,我們需要多種投影矩陣。

// scale the F to the size we need it.
// use just the view position of the 'F' for the text
var textMatrix = makeIdentity();
textMatrix = matrixMultiply(textMatrix, makeScale(textWidth, textHeight, 1));
textMatrix = matrixMultiply(
textMatrix,
makeTranslation(fViewMatrix[12], fViewMatrix[13], fViewMatrix[14]));
textMatrix = matrixMultiply(textMatrix, projectionMatrix);

然后渲染文本

// setup to draw the text.
gl.useProgram(textProgramInfo.program);

setBuffersAndAttributes(gl, textProgramInfo.attribSetters, textBufferInfo);

copyMatrix(textMatrix, textUniforms.u_matrix);
setUniforms(textProgramInfo.uniformSetters, textUniforms);

// Draw the text.
gl.drawElements(gl.TRIANGLES, textBufferInfo.numElements, gl.UNSIGNED_SHORT, 0);

即:

你會(huì)發(fā)現(xiàn)有時(shí)候我們文本的一部分遮蓋了我們 Fs 的一部分。這是因?yàn)槲覀兝L制一個(gè)四元組。畫布的默認(rèn)顏色是透明的黑色(0,0,0,0)和我們?cè)谒脑M中使用這種顏色繪制。我們也可以混合像素。

gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

根據(jù)混合函數(shù),將源像素(這個(gè)顏色取自片段著色器)和 目的像素(畫布顏色)結(jié)合在一起。在混合函數(shù)中,我們?yōu)樵聪袼卦O(shè)置:SRC_ALPHA,為目的像素設(shè)置:ONE_MINUS_SRC_ALPHA。

result = dest * (1 - src_alpha) + src * src_alpha

舉個(gè)例子,如果目的像素是綠色的 0,1,0,1 和源像素是紅色的 1,0,0,1,如下:

src = [1, 0, 0, 1]
dst = [0, 1, 0, 1]
src_alpha = src[3]  // this is 1
result = dst * (1 - src_alpha) + src * src_alpha

// which is the same as
result = dst * 0 + src * 1

// which is the same as
result = src

對(duì)于紋理的部分內(nèi)容,使用透明的黑色 0,0,0,0

src = [0, 0, 0, 0]
dst = [0, 1, 0, 1]
src_alpha = src[3]  // this is 0
result = dst * (1 - src_alpha) + src * src_alpha

// which is the same as
result = dst * 1 + src * 0

// which is the same as
result = dst

這是啟用了混合的結(jié)果。

你可以看到盡管它還不完美,但它已經(jīng)更好了。如果你仔細(xì)看,有時(shí)能看到這個(gè)問(wèn)題

http://wiki.jikexueyuan.com/project/webgl/images/text-zbuffer-issue.png" alt="" />

發(fā)生什么事情了?我們正在繪制一個(gè) F 然后是它的文本,然后下一個(gè) F 的重復(fù)文本。所以當(dāng)我們繪制文本時(shí),我們?nèi)匀恍枰粋€(gè)深度緩沖,即使混合了一些像素來(lái)保持背景顏色,深度緩沖仍然需要更新。當(dāng)我們繪制下一個(gè) F,如果 F 的部分是之前繪制文本的一些像素,他們就不會(huì)再繪制。

我們剛剛遇到的最困難的問(wèn)題之一,在 GPU 上渲染 3D。透明度也存在問(wèn)題。

針對(duì)幾乎所有透明呈現(xiàn)問(wèn)題,最常見的解決方案是先畫出所有不透明的東西,之后,按中心距的排序,繪制所有的透明的東西,中心距的排序是在深度緩沖測(cè)試開啟但深度緩沖更新關(guān)閉的情況下得出的。

讓我們先單獨(dú)繪制透明材料(文本)中不透明材料(Fs)的部分。首先,我們要聲明一些來(lái)記錄文本的位置。

var textPositions = [];

在循環(huán)中渲染記錄位置的 Fs

matrix = matrixMultiply(matrix, viewMatrix);
var fViewMatrix = copyMatrix(matrix);  // remember the view matrix for the text
textPositions.push([matrix[12], matrix[13], matrix[14]]);  // remember the position for the text

在我們繪制 “F”s之前,我們禁用混合并打開寫深度緩沖

gl.disable(gl.BLEND);
gl.depthMask(true);

繪制文本時(shí),我們將打開混合并關(guān)掉寫作深度緩沖

gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.depthMask(false);

然后在我們保存的所有位置繪制文本

textPositions.forEach(function(pos) {
  // draw the text
  // scale the F to the size we need it.
  // use just the position of the 'F' for the text
  var textMatrix = makeIdentity();
  textMatrix = matrixMultiply(textMatrix, makeScale(textWidth, textHeight, 1));
  textMatrix = matrixMultiply(textMatrix, makeTranslation(pos[0], pos[1], pos[2]));
  textMatrix = matrixMultiply(textMatrix, projectionMatrix);

  // setup to draw the text.
  gl.useProgram(textProgramInfo.program);

  setBuffersAndAttributes(gl, textProgramInfo.attribSetters, textBufferInfo);

  copyMatrix(textMatrix, textUniforms.u_matrix);
  setUniforms(textProgramInfo.uniformSetters, textUniforms);

  // Draw the text.
  gl.drawElements(gl.TRIANGLES, textBufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
});

現(xiàn)在啟動(dòng):

請(qǐng)注意我們沒有像我上面提到的那樣分類。在這種情況下,因?yàn)槲覀兝L制大部分是不透明文本,所以即使排序也沒有明顯差異,所以就省去了這一步驟,節(jié)省資源用于其他文章。

另一個(gè)問(wèn)題是文本的“F”總是交叉。實(shí)際上這個(gè)問(wèn)題沒有一個(gè)具體的解決方案。如果你正在構(gòu)造一個(gè) MMO,希望每個(gè)游戲者的文本總是出現(xiàn)在你試圖使文本出現(xiàn)的頂部。只需要將之轉(zhuǎn)化為一些單元 +Y,足以確保它總是位于游戲者之上。

你也可以使之向 cameara 移動(dòng)。在這里我們這樣做只是為了好玩。因?yàn)?“pos” 是在坐標(biāo)系中,意味著它是相對(duì)于眼(在坐標(biāo)系中即:0,0,0)。所以如果我們使之標(biāo)準(zhǔn)化,我們可以得到一個(gè)單位向量,這個(gè)向量的指向是從原點(diǎn)到某一點(diǎn),我們可以乘一定數(shù)值將文本特定數(shù)量的單位靠近或遠(yuǎn)離眼。

// because pos is in view space that means it's a vector from the eye to
// some position. So translate along that vector back toward the eye some distance
var fromEye = normalize(pos);
var amountToMoveTowardEye = 150;  // because the F is 150 units long
var viewX = pos[0] - fromEye[0] * amountToMoveTowardEye;
var viewY = pos[1] - fromEye[1] * amountToMoveTowardEye;
var viewZ = pos[2] - fromEye[2] * amountToMoveTowardEye;

var textMatrix = makeIdentity();
textMatrix = matrixMultiply(textMatrix, makeScale(textWidth, textHeight, 1));
textMatrix = matrixMultiply(textMatrix, makeTranslation(viewX, viewY, viewZ));
textMatrix = matrixMultiply(textMatrix, projectionMatrix);

即:

你還可能會(huì)注意到一個(gè)字母邊緣問(wèn)題。

http://wiki.jikexueyuan.com/project/webgl/images/text-gray-outline.png" alt="" />

這里的問(wèn)題是 Canvas2D api 只引入了自左乘 alpha 值。當(dāng)我們上傳內(nèi)容到試圖 unpremultiply 的紋理 WebGL,它就不能完全做到,這是因?yàn)樽宰蟪?alpha 會(huì)失真。

為了解決這個(gè)問(wèn)題,使 WebGL 不會(huì) unpremultiply:

gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);

這告訴 WebGL 支持自左乘 alpha 值到 gl.texImage2Dgl.texSubImage2D。如果數(shù)據(jù)傳遞給 gl.texImage2D 已經(jīng)自左乘,就像 canvas2d 數(shù)據(jù),那么 WebGL 就可以通過(guò)。

我們還需要改變混合函數(shù)

gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

老方法是源色乘以 alpha。這是 SRC_ALPHA 意味著什么。但是現(xiàn)在我們的紋理數(shù)據(jù)已經(jīng)被乘以其 alpha。這是 premultipled 意味著什么。所以我們不需要 GPU 做乘法。將其設(shè)置為 ONE 意味著乘以 1。

邊緣現(xiàn)在沒有了。

如果你想保持文本在一種固定大小,但仍然正確?那么,如果你還記得透視文章中透視矩陣以 -Z 調(diào)整我們的對(duì)象使其在距離上更小。所以,我們可以以 -Z 倍數(shù)調(diào)整以達(dá)到我們想要的規(guī)模作為補(bǔ)償。

...
// because pos is in view space that means it's a vector from the eye to
// some position. So translate along that vector back toward the eye some distance
var fromEye = normalize(pos);
var amountToMoveTowardEye = 150;  // because the F is 150 units long
var viewX = pos[0] - fromEye[0] * amountToMoveTowardEye;
var viewY = pos[1] - fromEye[1] * amountToMoveTowardEye;
var viewZ = pos[2] - fromEye[2] * amountToMoveTowardEye;
var desiredTextScale = -1 / gl.canvas.height;  // 1x1 pixels
var scale = viewZ * desiredTextScale;

var textMatrix = makeIdentity();
textMatrix = matrixMultiply(textMatrix, makeScale(textWidth * scale, textHeight * scale, 1));
textMatrix = matrixMultiply(textMatrix, makeTranslation(viewX, viewY, viewZ));
textMatrix = matrixMultiply(textMatrix, projectionMatrix);
...

如果你想在每個(gè) F 中繪制不同文本,你應(yīng)該為每個(gè) F 構(gòu)造一個(gè)新紋理,為每個(gè) F 更新文本模式。

// create text textures, one for each F
var textTextures = [
  "anna",   // 0
  "colin",  // 1
  "james",  // 2
  "danny",  // 3
  "kalin",  // 4
  "hiro",   // 5
  "eddie",  // 6
  "shu",// 7
  "brian",  // 8
  "tami",   // 9
  "rick",   // 10
  "gene",   // 11
  "natalie",// 12,
  "evan",   // 13,
  "sakura", // 14,
  "kai",// 15,
].map(function(name) {
  var textCanvas = makeTextCanvas(name, 100, 26);
  var textWidth  = textCanvas.width;
  var textHeight = textCanvas.height;
  var textTex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, textTex);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textCanvas);
  // make sure we can render it even if it's not a power of 2
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  return {
texture: textTex,
width: textWidth,
height: textHeight,
  };
});

然后在呈現(xiàn)時(shí)選擇一個(gè)紋理

textPositions.forEach(function(pos, ndx) {

  +// select a texture
  +var tex = textTextures[ndx];

  // scale the F to the size we need it.
  // use just the position of the 'F' for the text
  var textMatrix = makeIdentity();
  *textMatrix = matrixMultiply(textMatrix, makeScale(tex.width, tex.height, 1));

并在繪制前為紋理設(shè)置統(tǒng)一結(jié)構(gòu)

textUniforms.u_texture = tex.texture;

我們一直用黑色繪制到畫布上的文本。這比用白色呈現(xiàn)文本更有用。然后我們?cè)僭黾游谋镜念伾?,以便得到我們想要的任何顏色?/p>

首先我們改變文本材質(zhì),通過(guò)復(fù)合一個(gè)顏色

varying vec2 v_texcoord;

uniform sampler2D u_texture;
uniform vec4 u_color;

void main() {
   gl_FragColor = texture2D(u_texture, v_texcoord) * u_color;
}

當(dāng)我們繪制文本到畫布上時(shí)使用白色

textCtx.fillStyle = "white";

然后我們添加一些其他顏色

// colors, 1 for each F
var colors = [
  [0.0, 0.0, 0.0, 1], // 0
  [1.0, 0.0, 0.0, 1], // 1
  [0.0, 1.0, 0.0, 1], // 2
  [1.0, 1.0, 0.0, 1], // 3
  [0.0, 0.0, 1.0, 1], // 4
  [1.0, 0.0, 1.0, 1], // 5
  [0.0, 1.0, 1.0, 1], // 6
  [0.5, 0.5, 0.5, 1], // 7
  [0.5, 0.0, 0.0, 1], // 8
  [0.0, 0.0, 0.0, 1], // 9
  [0.5, 5.0, 0.0, 1], // 10
  [0.0, 5.0, 0.0, 1], // 11
  [0.5, 0.0, 5.0, 1], // 12,
  [0.0, 0.0, 5.0, 1], // 13,
  [0.5, 5.0, 5.0, 1], // 14,
  [0.0, 5.0, 5.0, 1], // 15,
];

在繪制時(shí)選擇一個(gè)顏色

// set color uniform
textUniforms.u_color = colors[ndx];

結(jié)果如下:

這個(gè)技術(shù)實(shí)際上是大多數(shù)瀏覽器使用 GPU 加速時(shí)的技術(shù)。他們用 HTML 的內(nèi)容和你應(yīng)用的各種風(fēng)格生成紋理,只要這些內(nèi)容沒有改變,他們就可以在滾動(dòng)時(shí)再次渲染紋理。當(dāng)然,如果你一直都在更新那么這技術(shù)可能會(huì)有點(diǎn)慢,因?yàn)橹匦律杉y理并更新它對(duì)于 GPU 來(lái)說(shuō)是一個(gè)相對(duì)緩慢的操作。

上一篇:WebGL 2D 圖像伸縮下一篇:WebGL 2D 矩陣