自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一起學(xué) WebGL:可視空間之正射投影

開(kāi)發(fā) 前端
長(zhǎng)方體可視空間是一種常用的可視空間,它由正射投影(Orthographic Projection) 產(chǎn)生。此外還有更常用的 透視投影。

嗨,大家好呀,我是你們的前端西瓜哥啊。上一節(jié)我們學(xué)習(xí)了 視圖矩陣,通過(guò)它我們可以像一個(gè)自由的攝像機(jī)一樣,可以在三維世界的任意位置觀察目標(biāo)模型,但也遇到了一些問(wèn)題。

其中一個(gè)問(wèn)題就是,在超過(guò)某個(gè)臨界值時(shí)。三角形會(huì)出現(xiàn)殘缺的現(xiàn)象,甚至直接不見(jiàn)了,見(jiàn)下圖。這是為什么呢?

圖片

對(duì)應(yīng)源碼:

https://codesandbox.io/s/j86roh?file=/index.js。

可視空間

上面殘缺的情況會(huì)在 z 變得比較大時(shí)出現(xiàn)。所謂視圖矩陣,其實(shí)利用的是一種相對(duì)關(guān)系。

我們沒(méi)有移動(dòng),坐標(biāo)系的原點(diǎn)還是在畫(huà)布的中心位置,但視圖矩陣可以計(jì)算出模型新的位置,給一個(gè)更小的 z,其實(shí)就是這個(gè)模型的所有頂點(diǎn)在遠(yuǎn)離我們。

但是,部分像素超出了某個(gè)范圍,就被 WebGL 直接忽略掉了。

這個(gè)范圍是多少呢?是 [-1, 1] 區(qū)間(大于等于 -1,小于等于 1),x、y、z 維都是這個(gè)范圍。超出這個(gè)范圍的像素,一律不畫(huà)。

利用這個(gè)性質(zhì),我們可以將一些不需要出現(xiàn)在畫(huà)面中的物體刪除。比如一些遙遠(yuǎn)的遠(yuǎn)景,或是跑到視口外的模型,不繪制它們能提高繪制效率。

正視投影

長(zhǎng)方體可視空間是一種常用的可視空間,它由 正射投影(Orthographic Projection) 產(chǎn)生。此外還有更常用的 透視投影,我們后面再聊。

圖片

正視投影的作用是:將一個(gè)指定尺寸的盒子,映射壓縮成 [-1, 1] 范圍的盒子,使其中的像素點(diǎn)全部可以繪制出來(lái),之外的像素點(diǎn)則被拋棄。

正射投影的矩陣公式為:

圖片

其中 r、l 等值,對(duì)應(yīng) left、right、top、bottom、near、far 的縮寫(xiě)。

JavaScript 代碼實(shí)現(xiàn)為:

function createOrthoMatrix(left, right, bottom, top, near, far) {
  const width = right - left;
  const height = top - bottom;
  const depth = far - near;

  // prettier-ignore
  return new Float32Array([
    2 / width, 0, 0, 0,
    0, 2 / height, 0, 0,
    0, 0, -2 / depth, 0,
    -(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1
  ]);
}

這里不得不感謝 Github Colipot 提供的支持了。

實(shí)驗(yàn)

我們來(lái)寫(xiě)個(gè) demo,來(lái)體驗(yàn)一下透視矩陣的效果。

為了不引入過(guò)多的復(fù)雜度,我們只繪制兩個(gè)三角形,且不引入視圖矩陣。然后可以通過(guò)上下方向鍵改變可視空間的 near 和 far 值。此時(shí)我們就能看到三角形消失的現(xiàn)象,因?yàn)樗艹鑫覀兊目梢暱臻g了。

兩個(gè)三角形都平行于 xy 屏幕,只是 z 不同,分別為 0.2 和 -0.2。紅色在上邊,黃色在下邊。

兩個(gè)三角形的緩沖區(qū)數(shù)據(jù)為:

const verticesColors = new Float32Array([
  // 底下的黃色三角形
  0, 0.5, -0.1, 1, 1, 0,  // 點(diǎn) 1 的位置和顏色信息
  -0.5, -0.5, -0.1, 1, 1, 0,  // 點(diǎn) 2
  0.5, -0.5, -0.1, 1, 1, 0,  // 點(diǎn) 3
  // 上邊的紅色三角形
  -0.5, 0.25, 0.1, 1, 0, 0, 
  0.5, 0.25, 0.1, 1, 0, 0,
  0, -0.75, 0.1, 1, 0, 0,  
]);

效果如下:

圖片

這些點(diǎn)都在 -1 到 1 的范圍內(nèi)。所以能全部渲染出來(lái)。

頂點(diǎn)著色器要加一個(gè)保存正射投影矩陣的 mat4 矩陣類(lèi)型變量,因?yàn)椴恍枰淖?,所以使?uniform 關(guān)鍵字。

const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_OrthoMatrix; // 正射投影矩陣
varying vec4 v_Color;
void main() {
 gl_Position = u_OrthoMatrix * a_Position;
 v_Color = a_Color;
}
`;

然后綁定鍵盤(pán)按下事件:

window.addEventListener('keydown', (event) => {
  switch (event.key) {
    case 'ArrowUp':
      near += 0.1;
      far += 0.1;
      break;
    case 'ArrowDown':
      near -= 0.1;
      far -= 0.1;
      break;
  }
  near = Math.round(near * 10) / 10; // 消除浮點(diǎn)數(shù)誤差
  far = Math.round(far * 10) / 10;

  render();
});

渲染函數(shù)為:

function render() {
  gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, SIZE * 6, 0);
  gl.enableVertexAttribArray(a_Position);

  gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, SIZE * 6, SIZE * 3);
  gl.enableVertexAttribArray(a_Color);

  /****** 正射投影 ******/
  // prettier-ignore
  const orthoMatrix = createOrthoMatrix(
    // 左右上下近遠(yuǎn)
    -1, 1, -1, 1, near, far
  )

  gl.uniformMatrix4fv(u_OrthoMatrix, false, orthoMatrix);
  /*** 繪制 ***/
  // 清空畫(huà)布,并指定顏色
  gl.clearColor(0, 0, 0, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  // 繪制三角形
  gl.drawArrays(gl.TRIANGLES, 0, 6);
}

不斷地按下方向鍵,near 和 far 越來(lái)越小,即可視空間離我們?cè)絹?lái)越遠(yuǎn)。

可以看到,當(dāng) near 從 0.1 變成 0 后,紅色三角形不見(jiàn)了,黃色的還在。因?yàn)槲覀円婚_(kāi)始給紅色三角形設(shè)置的 z 為 0.1,已經(jīng)不在可視范圍內(nèi)了。

同理,當(dāng) far 越來(lái)越大,從 -0.1 變成 0 后,黃色三角形則不見(jiàn)了。

圖片

源碼實(shí)現(xiàn)

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");
const infoDiv = document.createElement("div");
document.body.appendChild(infoDiv);

const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_OrthoMatrix; // 正射投影矩陣
varying vec4 v_Color;
void main() {
 gl_Position = u_OrthoMatrix * a_Position;
 v_Color = a_Color;
}
`;

const fragmentShaderSrc = `
precision highp float;
varying vec4 v_Color;
void main() {
  gl_FragColor = v_Color;
}
`;

/**** 渲染器生成處理 ****/
// 創(chuàng)建頂點(diǎn)渲染器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
// 創(chuàng)建片元渲染器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
// 程序?qū)ο?const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;

// prettier-ignore
const verticesColors = new Float32Array([
  // 底下的黃色三角形
  0, 0.5, -0.1, 1, 1, 0,  // 點(diǎn) 1 的位置和顏色信息
  -0.5, -0.5, -0.1, 1, 1, 0,  // 點(diǎn) 2
  0.5, -0.5, -0.1, 1, 1, 0,  // 點(diǎn) 3
  // 上邊的紅色三角形
  -0.5, 0.25, 0.1, 1, 0, 0, 
  0.5, 0.25, 0.1, 1, 0, 0,
  0, -0.75, 0.1, 1, 0, 0,  
]);
// 每個(gè)數(shù)組元素的字節(jié)數(shù)
const SIZE = verticesColors.BYTES_PER_ELEMENT;

const orthoMatrix = createOrthoMatrix(-1, 1, -1, 1, 1.05, -1);

// 創(chuàng)建緩存對(duì)象
const vertexColorBuffer = gl.createBuffer();
// 綁定緩存對(duì)象到上下文
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
// 向緩存區(qū)寫(xiě)入數(shù)據(jù)
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

// 獲取 a_Position 變量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
const a_Color = gl.getAttribLocation(gl.program, "a_Color");
const u_OrthoMatrix = gl.getUniformLocation(gl.program, "u_OrthoMatrix");

let near = 1;
let far = -1;

// 初次渲染
render();
window.addEventListener("keydown", (event) => {
  switch (event.key) {
    case "ArrowUp":
      near += 0.1;
      far += 0.1;
      break;
    case "ArrowDown":
      near -= 0.1;
      far -= 0.1;
      break;
  }
  near = Math.round(near * 10) / 10; // 消除浮點(diǎn)數(shù)誤差
  far = Math.round(far * 10) / 10;

  render();
});

function render() {
  infoDiv.innerHTML = `Near: ${near}, Far: ${far}`;
  gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, SIZE * 6, 0);
  gl.enableVertexAttribArray(a_Position);

  gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, SIZE * 6, SIZE * 3);
  gl.enableVertexAttribArray(a_Color);

  /****** 正射投影 ******/
  // prettier-ignore
  const orthoMatrix = createOrthoMatrix(
    // 左右上下近遠(yuǎn)
    -1, 1, -1, 1, near, far
  )

  gl.uniformMatrix4fv(u_OrthoMatrix, false, orthoMatrix);
  /*** 繪制 ***/
  // 清空畫(huà)布,并指定顏色
  gl.clearColor(0, 0, 0, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  // 繪制三角形
  gl.drawArrays(gl.TRIANGLES, 0, 6);
}

/********* 構(gòu)造正射投影 *********/
function createOrthoMatrix(left, right, bottom, top, near, far) {
  const width = right - left;
  const height = top - bottom;
  const depth = far - near;

  // prettier-ignore
  return new Float32Array([
    2 / width, 0, 0, 0,
    0, 2 / height, 0, 0,
    0, 0, -2 / depth, 0,
    -(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1
  ]);
}

線(xiàn)上體驗(yàn) demo:

https://codesandbox.io/s/f1q0ct?file=/index.js。

回到開(kāi)頭

回到開(kāi)頭,我們?cè)谑褂靡晥D矩陣的時(shí)候,修改了模型的頂點(diǎn)坐標(biāo),這個(gè)坐標(biāo)容易跑到可視空間外,這就是上面三角形出現(xiàn)殘缺的問(wèn)題。

我們來(lái)看看到底發(fā)生了什么事。

從表現(xiàn)上看,根據(jù)前面我們學(xué)到的知識(shí),應(yīng)該是左下角的點(diǎn)超出了 -1 到 1 的范圍。

我們確認(rèn)一下,用計(jì)算出來(lái)的視圖矩陣乘以左下角點(diǎn) (-0.2, -0.2, 0, 1):

圖片

矩陣運(yùn)算示意圖用 Matrix Calculator 網(wǎng)頁(yè)工具生成。

我們只要加個(gè)正視投影矩陣,并將 near 設(shè)置為大于 1.04946 的值即可繪制一個(gè)完整的三角形啦。

const orthoMatrix = createOrthoMatrix(
  -1, 1, -1, 1, 1.05, -1 
)

這個(gè) near 再小一丟丟就會(huì)導(dǎo)致殘缺,讀者可以去 demo 中修改代碼驗(yàn)證。

線(xiàn)上體驗(yàn) demo:

https://codesandbox.io/s/6i9n2y。

責(zé)任編輯:姜華 來(lái)源: 前端西瓜哥
相關(guān)推薦

2023-04-26 07:42:16

WebGL圖元的類(lèi)型

2023-05-04 08:48:42

WebGL復(fù)合矩陣

2023-06-26 15:14:19

WebGL紋理對(duì)象學(xué)習(xí)

2023-04-12 07:46:24

JavaScriptWebGL

2023-03-29 07:31:09

WebGL坐標(biāo)系

2023-05-31 20:10:03

WebGL繪制立方體

2023-05-16 07:44:03

紋理映射WebGL

2023-04-13 07:45:15

WebGL片元著色器

2023-04-11 07:48:32

WebGLCanvas

2023-05-08 07:29:48

WebGL視圖矩陣

2023-04-27 08:27:29

WebGL變形矩陣

2023-04-17 09:01:01

WebGL繪制三角形

2022-11-29 16:35:02

Tetris鴻蒙

2022-12-02 14:20:09

Tetris鴻蒙

2022-11-14 17:01:34

游戲開(kāi)發(fā)畫(huà)布功能

2023-03-30 09:32:27

2022-08-19 19:02:20

開(kāi)源鴻蒙操作系統(tǒng)

2023-05-06 07:23:57

2023-02-28 07:28:50

Spritepixijs

2023-11-13 22:27:53

Mapping數(shù)據(jù)庫(kù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)