亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么使用WebGL三維透視投影

發布時間:2021-10-29 15:29:48 來源:億速云 閱讀:162 作者:iii 欄目:web開發

本篇內容介紹了“怎么使用WebGL三維透視投影”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

透視投影? 通俗的講就是 近大遠小。

在上方的示例中,遠處的物體會變小,想要實現例子中近大遠小的效果, 簡單的做法就是將裁減空間中的 X 和 Y 值除以 Z 值。

你可以這么想:如果一個線段是 (10, 15) 到 (20,15), 它長度為十個單位,在當前的代碼中它就是 10 個像素長, 但是如果我們將它除以 Z ,且 Z 值 為 1

10 / 1 = 10

20 / 1 = 20

abs(10-20) = 10

它將是 10 個像素長,如果 Z 值為 2

10 / 2 = 5

20 / 2 = 10

abs(5 – 10) = 5

就是 5 像素了,當 Z 值為 3 時

10 / 3 = 3.333

20 / 3 = 6.666

abs(3.333 – 6.666) = 3.333

你可以看出隨著 Z 變大距離就變遠了,畫的也會小一點。 如果我們除以裁剪空間中的 Z ,值可能會變大,因為 Z 是一個較小的值(-1 到 +1)。但是我們可以提供一個 fudgeFactor 因子和 Z 相乘,這樣就可以調整縮放的程度。

讓我們來試試,首先修改頂點著色器,除以 Z 再乘以我們的 “fudgeFactor” 因子。

<script id="3d-vertex-shader" type="x-shader/x-vertex">
...
uniform float u_fudgeFactor;
...
void main() {
  // 將位置和矩陣相乘
  vec4 position = u_matrix * a_position;
  // 調整除數
  float zToDivideBy = 1.0 + position.z * u_fudgeFactor;
  // x 和 y 除以調整后的除數
  gl_Position = vec4(position.xy / zToDivideBy, position.zw);
}
</script>

注意,由于裁減空間中的 Z 值是 -1 到 +1 的,所以 +1 是為了讓 zToDivideBy 變成 0 到 +2 * fudgeFactor

還需要更新代碼以設置 fudgeFactor。

 ...
  var fudgeLocation = gl.getUniformLocation(program, "u_fudgeFactor");
  ...
  var fudgeFactor = 1;
  ...
  function drawScene() {
    ...
    // 設置 fudgeFactor
    gl.uniform1f(fudgeLocation, fudgeFactor);
    // 繪制幾何體
    var primitiveType = gl.TRIANGLES;
    var offset = 0;
    var count = 16 * 6;
    gl.drawArrays(primitiveType, offset, count);

運行下面的代碼:

"use strict";
function main() {
  // Get A WebGL context
  /** @type {HTMLCanvasElement} */
  var canvas = document.getElementById("canvas");
  var gl = canvas.getContext("webgl");
  if (!gl) {
    return;
  }
  // setup GLSL program
  var program = webglUtils.createProgramFromScripts(gl, ["3d-vertex-shader", "3d-fragment-shader"]);
  // look up where the vertex data needs to go.
  var positionLocation = gl.getAttribLocation(program, "a_position");
  var colorLocation = gl.getAttribLocation(program, "a_color");
  // lookup uniforms
  var matrixLocation = gl.getUniformLocation(program, "u_matrix");
  var fudgeLocation = gl.getUniformLocation(program, "u_fudgeFactor");
  // Create a buffer to put positions in
  var positionBuffer = gl.createBuffer();
  // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  // Put geometry data into buffer
  setGeometry(gl);
  // Create a buffer to put colors in
  var colorBuffer = gl.createBuffer();
  // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = colorBuffer)
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  // Put geometry data into buffer
  setColors(gl);
  function radToDeg(r) {
    return r * 180 / Math.PI;
  }
  function degToRad(d) {
    return d * Math.PI / 180;
  }
  var translation = [45, 150, 0];
  var rotation = [degToRad(40), degToRad(25), degToRad(325)];
  var scale = [1, 1, 1];
  var fudgeFactor = 1;
  drawScene();
  // Setup a ui.
  webglLessonsUI.setupSlider("#fudgeFactor", {value: fudgeFactor, slide: updateFudgeFactor, max: 2, step: 0.001, precision: 3 });
  webglLessonsUI.setupSlider("#x", {value: translation[0], slide: updatePosition(0), max: gl.canvas.width });
  webglLessonsUI.setupSlider("#y", {value: translation[1], slide: updatePosition(1), max: gl.canvas.height});
  webglLessonsUI.setupSlider("#z", {value: translation[2], slide: updatePosition(2), max: gl.canvas.height, min: -gl.canvas.height});
  webglLessonsUI.setupSlider("#angleX", {value: radToDeg(rotation[0]), slide: updateRotation(0), max: 360});
  webglLessonsUI.setupSlider("#angleY", {value: radToDeg(rotation[1]), slide: updateRotation(1), max: 360});
  webglLessonsUI.setupSlider("#angleZ", {value: radToDeg(rotation[2]), slide: updateRotation(2), max: 360});
  webglLessonsUI.setupSlider("#scaleX", {value: scale[0], slide: updateScale(0), min: -5, max: 5, step: 0.01, precision: 2});
  webglLessonsUI.setupSlider("#scaleY", {value: scale[1], slide: updateScale(1), min: -5, max: 5, step: 0.01, precision: 2});
  webglLessonsUI.setupSlider("#scaleZ", {value: scale[2], slide: updateScale(2), min: -5, max: 5, step: 0.01, precision: 2});
  function updateFudgeFactor(event, ui) {
    fudgeFactor = ui.value;
    drawScene();
  }
  function updatePosition(index) {
    return function(event, ui) {
      translation[index] = ui.value;
      drawScene();
    }
  }
  function updateRotation(index) {
    return function(event, ui) {
      var angleInDegrees = ui.value;
      var angleInRadians = angleInDegrees * Math.PI / 180;
      rotation[index] = angleInRadians;
      drawScene();
    }
  }
  function updateScale(index) {
    return function(event, ui) {
      scale[index] = ui.value;
      drawScene();
    }
  }
  // Draw the scene.
  function drawScene() {
    webglUtils.resizeCanvasToDisplaySize(gl.canvas);
    // Tell WebGL how to convert from clip space to pixels
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    // Clear the canvas AND the depth buffer.
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    // Turn on culling. By default backfacing triangles
    // will be culled.
    gl.enable(gl.CULL_FACE);
    // Enable the depth buffer
    gl.enable(gl.DEPTH_TEST);
    // Tell it to use our program (pair of shaders)
    gl.useProgram(program);
    // Turn on the position attribute
    gl.enableVertexAttribArray(positionLocation);
    // Bind the position buffer.
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    // Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
    var size = 3;          // 3 components per iteration
    var type = gl.FLOAT;   // the data is 32bit floats
    var normalize = false; // don't normalize the data
    var stride = 0;        // 0 = move forward size * sizeof(type) each iteration to get the next position
    var offset = 0;        // start at the beginning of the buffer
    gl.vertexAttribPointer(
        positionLocation, size, type, normalize, stride, offset)
    // Turn on the color attribute
    gl.enableVertexAttribArray(colorLocation);
    // Bind the color buffer.
    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
    // Tell the attribute how to get data out of colorBuffer (ARRAY_BUFFER)
    var size = 3;                 // 3 components per iteration
    var type = gl.UNSIGNED_BYTE;  // the data is 8bit unsigned values
    var normalize = true;         // normalize the data (convert from 0-255 to 0-1)
    var stride = 0;               // 0 = move forward size * sizeof(type) each iteration to get the next position
    var offset = 0;               // start at the beginning of the buffer
    gl.vertexAttribPointer(
        colorLocation, size, type, normalize, stride, offset)
    // Compute the matrices
    var matrix = m4.projection(gl.canvas.clientWidth, gl.canvas.clientHeight, 400);
    matrix = m4.translate(matrix, translation[0], translation[1], translation[2]);
    matrix = m4.xRotate(matrix, rotation[0]);
    matrix = m4.yRotate(matrix, rotation[1]);
    matrix = m4.zRotate(matrix, rotation[2]);
    matrix = m4.scale(matrix, scale[0], scale[1], scale[2]);
    // Set the matrix.
    gl.uniformMatrix4fv(matrixLocation, false, matrix);
    // Set the fudgeFactor
    gl.uniform1f(fudgeLocation, fudgeFactor);
    // Draw the geometry.
    var primitiveType = gl.TRIANGLES;
    var offset = 0;
    var count = 16 * 6;
    gl.drawArrays(primitiveType, offset, count);
  }
}
var m4 = {
  projection: function(width, height, depth) {
    // Note: This matrix flips the Y axis so 0 is at the top.
    return [
       2 / width, 0, 0, 0,
       0, -2 / height, 0, 0,
       0, 0, 2 / depth, 0,
      -1, 1, 0, 1,
    ];
  },
  multiply: function(a, b) {
    var a00 = a[0 * 4 + 0];
    var a01 = a[0 * 4 + 1];
    var a02 = a[0 * 4 + 2];
    var a03 = a[0 * 4 + 3];
    var a10 = a[1 * 4 + 0];
    var a11 = a[1 * 4 + 1];
    var a12 = a[1 * 4 + 2];
    var a13 = a[1 * 4 + 3];
    var a20 = a[2 * 4 + 0];
    var a21 = a[2 * 4 + 1];
    var a22 = a[2 * 4 + 2];
    var a23 = a[2 * 4 + 3];
    var a30 = a[3 * 4 + 0];
    var a31 = a[3 * 4 + 1];
    var a32 = a[3 * 4 + 2];
    var a33 = a[3 * 4 + 3];
    var b00 = b[0 * 4 + 0];
    var b01 = b[0 * 4 + 1];
    var b02 = b[0 * 4 + 2];
    var b03 = b[0 * 4 + 3];
    var b10 = b[1 * 4 + 0];
    var b11 = b[1 * 4 + 1];
    var b12 = b[1 * 4 + 2];
    var b13 = b[1 * 4 + 3];
    var b20 = b[2 * 4 + 0];
    var b21 = b[2 * 4 + 1];
    var b22 = b[2 * 4 + 2];
    var b23 = b[2 * 4 + 3];
    var b30 = b[3 * 4 + 0];
    var b31 = b[3 * 4 + 1];
    var b32 = b[3 * 4 + 2];
    var b33 = b[3 * 4 + 3];
    return [
      b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
      b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
      b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
      b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
      b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
      b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
      b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
      b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
      b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
      b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
      b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
      b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
      b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
      b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
      b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
      b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
    ];
  },
  translation: function(tx, ty, tz) {
    return [
       1,  0,  0,  0,
       0,  1,  0,  0,
       0,  0,  1,  0,
       tx, ty, tz, 1,
    ];
  },
  xRotation: function(angleInRadians) {
    var c = Math.cos(angleInRadians);
    var s = Math.sin(angleInRadians);
    return [
      1, 0, 0, 0,
      0, c, s, 0,
      0, -s, c, 0,
      0, 0, 0, 1,
    ];
  },
  yRotation: function(angleInRadians) {
    var c = Math.cos(angleInRadians);
    var s = Math.sin(angleInRadians);
    return [
      c, 0, -s, 0,
      0, 1, 0, 0,
      s, 0, c, 0,
      0, 0, 0, 1,
    ];
  },
  zRotation: function(angleInRadians) {
    var c = Math.cos(angleInRadians);
    var s = Math.sin(angleInRadians);
    return [
       c, s, 0, 0,
      -s, c, 0, 0,
       0, 0, 1, 0,
       0, 0, 0, 1,
    ];
  },
  scaling: function(sx, sy, sz) {
    return [
      sx, 0,  0,  0,
      0, sy,  0,  0,
      0,  0, sz,  0,
      0,  0,  0,  1,
    ];
  },
  translate: function(m, tx, ty, tz) {
    return m4.multiply(m, m4.translation(tx, ty, tz));
  },
  xRotate: function(m, angleInRadians) {
    return m4.multiply(m, m4.xRotation(angleInRadians));
  },
  yRotate: function(m, angleInRadians) {
    return m4.multiply(m, m4.yRotation(angleInRadians));
  },
  zRotate: function(m, angleInRadians) {
    return m4.multiply(m, m4.zRotation(angleInRadians));
  },
  scale: function(m, sx, sy, sz) {
    return m4.multiply(m, m4.scaling(sx, sy, sz));
  },
};
// Fill the buffer with the values that define a letter 'F'.
function setGeometry(gl) {
  gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array([
          // left column front
          0,   0,  0,
          0, 150,  0,
          30,   0,  0,
          0, 150,  0,
          30, 150,  0,
          30,   0,  0,
          // top rung front
          30,   0,  0,
          30,  30,  0,
          100,   0,  0,
          30,  30,  0,
          100,  30,  0,
          100,   0,  0,
          // middle rung front
          30,  60,  0,
          30,  90,  0,
          67,  60,  0,
          30,  90,  0,
          67,  90,  0,
          67,  60,  0,
          // left column back
            0,   0,  30,
           30,   0,  30,
            0, 150,  30,
            0, 150,  30,
           30,   0,  30,
           30, 150,  30,
          // top rung back
           30,   0,  30,
          100,   0,  30,
           30,  30,  30,
           30,  30,  30,
          100,   0,  30,
          100,  30,  30,
          // middle rung back
           30,  60,  30,
           67,  60,  30,
           30,  90,  30,
           30,  90,  30,
           67,  60,  30,
           67,  90,  30,
          // top
            0,   0,   0,
          100,   0,   0,
          100,   0,  30,
            0,   0,   0,
          100,   0,  30,
            0,   0,  30,
          // top rung right
          100,   0,   0,
          100,  30,   0,
          100,  30,  30,
          100,   0,   0,
          100,  30,  30,
          100,   0,  30,
          // under top rung
          30,   30,   0,
          30,   30,  30,
          100,  30,  30,
          30,   30,   0,
          100,  30,  30,
          100,  30,   0,
          // between top rung and middle
          30,   30,   0,
          30,   60,  30,
          30,   30,  30,
          30,   30,   0,
          30,   60,   0,
          30,   60,  30,
          // top of middle rung
          30,   60,   0,
          67,   60,  30,
          30,   60,  30,
          30,   60,   0,
          67,   60,   0,
          67,   60,  30,
          // right of middle rung
          67,   60,   0,
          67,   90,  30,
          67,   60,  30,
          67,   60,   0,
          67,   90,   0,
          67,   90,  30,
          // bottom of middle rung.
          30,   90,   0,
          30,   90,  30,
          67,   90,  30,
          30,   90,   0,
          67,   90,  30,
          67,   90,   0,
          // right of bottom
          30,   90,   0,
          30,  150,  30,
          30,   90,  30,
          30,   90,   0,
          30,  150,   0,
          30,  150,  30,
          // bottom
          0,   150,   0,
          0,   150,  30,
          30,  150,  30,
          0,   150,   0,
          30,  150,  30,
          30,  150,   0,
          // left side
          0,   0,   0,
          0,   0,  30,
          0, 150,  30,
          0,   0,   0,
          0, 150,  30,
          0, 150,   0]),
      gl.STATIC_DRAW);
}
// Fill the buffer with colors for the 'F'.
function setColors(gl) {
  gl.bufferData(
      gl.ARRAY_BUFFER,
      new Uint8Array([
          // left column front
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
          // top rung front
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
          // middle rung front
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
        200,  70, 120,
          // left column back
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
          // top rung back
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
          // middle rung back
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
        80, 70, 200,
          // top
        70, 200, 210,
        70, 200, 210,
        70, 200, 210,
        70, 200, 210,
        70, 200, 210,
        70, 200, 210,
          // top rung right
        200, 200, 70,
        200, 200, 70,
        200, 200, 70,
        200, 200, 70,
        200, 200, 70,
        200, 200, 70,
          // under top rung
        210, 100, 70,
        210, 100, 70,
        210, 100, 70,
        210, 100, 70,
        210, 100, 70,
        210, 100, 70,
          // between top rung and middle
        210, 160, 70,
        210, 160, 70,
        210, 160, 70,
        210, 160, 70,
        210, 160, 70,
        210, 160, 70,
          // top of middle rung
        70, 180, 210,
        70, 180, 210,
        70, 180, 210,
        70, 180, 210,
        70, 180, 210,
        70, 180, 210,
          // right of middle rung
        100, 70, 210,
        100, 70, 210,
        100, 70, 210,
        100, 70, 210,
        100, 70, 210,
        100, 70, 210,
          // bottom of middle rung.
        76, 210, 100,
        76, 210, 100,
        76, 210, 100,
        76, 210, 100,
        76, 210, 100,
        76, 210, 100,
          // right of bottom
        140, 210, 80,
        140, 210, 80,
        140, 210, 80,
        140, 210, 80,
        140, 210, 80,
        140, 210, 80,
          // bottom
        90, 130, 110,
        90, 130, 110,
        90, 130, 110,
        90, 130, 110,
        90, 130, 110,
        90, 130, 110,
          // left side
        160, 160, 220,
        160, 160, 220,
        160, 160, 220,
        160, 160, 220,
        160, 160, 220,
        160, 160, 220]),
      gl.STATIC_DRAW);
}
main();

事實上WebGL會將我們提供給 gl_Position 的 x,y,z,w 值自動除以 w 。

我們可以通過修改著色器來證明,用 zToDivideBy 代替 gl_Position.w

<script id="2d-vertex-shader" type="x-shader/x-vertex">
...
uniform float u_fudgeFactor;
...
void main() {
  // 將位置和矩陣相乘
  vec4 position = u_matrix * a_position;
  // 調整除數
  float zToDivideBy = 1.0 + position.z * u_fudgeFactor;
  // 將 x y z 除以 zToDivideBy
  gl_Position = vec4(position.xyz, zToDivideBy);
  // 傳遞顏色到給片斷著色器
  v_color = a_color;
}
</script>

為什么WebGL會自動除以 W ?因為使用矩陣的魔力,可以用把值從 z 傳值到 w 。

一個這樣的矩陣

1, 0, 0, 0,

0, 1, 0, 0,

0, 0, 1, 1,

0, 0, 0, 0,

將會把 z 的值復制給 w , 你可以把每列看作

x_out = x_in * 1 +

y_in * 0 +

z_in * 0 +

w_in * 0 ;

y_out = x_in * 0 +

y_in * 1 +

z_in * 0 +

w_in * 0 ;

z_out = x_in * 0 +

y_in * 0 +

z_in * 1 +

w_in * 0 ;

w_out = x_in * 0 +

y_in * 0 +

z_in * 1 +

w_in * 0 ;

簡化后得到

x_out = x_in;

y_out = y_in;

z_out = z_in;

w_out = z_in;

如果 w 原來就是 1.0 就會加 1

1, 0, 0, 0,

0, 1, 0, 0,

0, 0, 1, 1,

0, 0, 0, 1,

他會將 W 的運算變為

w_out = x_in * 0 +

y_in * 0 +

z_in * 1 +

w_in * 1 ;

因為 w_in = 1.0 是已知的

w_out = z_in + 1;

最后可以將 fudgeFactor 像這樣放入矩陣中

1, 0, 0, 0,

0, 1, 0, 0,

0, 0, 1, fudgeFactor,

0, 0, 0, 1,

相當于

w_out = x_in * 0 +

y_in * 0 +

z_in * fudgeFactor +

w_in * 1 ;

簡化后為

w_out = z_in * fudgeFactor + 1;

我們來修改代碼,使用這個矩陣。

首先將頂點著色器還原,又變成簡單的樣子

<script id="2d-vertex-shader" type="x-shader/x-vertex">
uniform mat4 u_matrix;
void main() {
  // 位置和矩陣相乘
  gl_Position = u_matrix * a_position;
  ...
}
</script>

接下來定義一個方法實現 Z → W 的矩陣

function makeZToWMatrix(fudgeFactor) {
  return [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, fudgeFactor,
    0, 0, 0, 1,
  ];
}

然后使用它:

...
    // 計算矩陣
    var matrix = makeZToWMatrix(fudgeFactor);
    matrix = m4.multiply(matrix, m4.projection(gl.canvas.clientWidth, gl.canvas.clientHeight, 400));
    matrix = m4.translate(matrix, translation[0], translation[1], translation[2]);
    matrix = m4.xRotate(matrix, rotation[0]);
    matrix = m4.yRotate(matrix, rotation[1]);
    matrix = m4.zRotate(matrix, rotation[2]);
    matrix = m4.scale(matrix, scale[0], scale[1], scale[2]);
    ...

這只是展示了除以 Z 值獲可以實現透視投影,以及在WebGL中簡單實現。

但還有一些問題需要解決,比如將 Z 值設置為 -100 左右的時候會遇到下面的情形

怎么使用WebGL三維透視投影

為什么會這樣?為什么 F 提前消失了?WebGL裁剪空間中的 X 和 Y 會被 +1 和 -1 裁剪, Z也一樣。我們看到的是 Z < -1 的情況。

我可以從數學方法深入探討并尋找解決辦法,但是你可以 聯想 二維中的的解決方法。我們需要獲取 Z 值,然后加上一些量, 縮放一些量,就可以將任意范圍映射到 -1 到 +1 的范圍內。

最有意思的是這件事可以在一個矩陣中完成,更方便的是, 我們可以定義一個 fieldOfView 代替 fudgeFactor , 計算出更合適的值。

這是創建矩陣的方法。

var m4 = {
  perspective: function(fieldOfViewInRadians, aspect, near, far) {
    var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians);
    var rangeInv = 1.0 / (near - far);
    return [
      f / aspect, 0, 0, 0,
      0, f, 0, 0,
      0, 0, (near + far) * rangeInv, -1,
      0, 0, near * far * rangeInv * 2, 0
    ];
  },
  ...

這個矩陣會為我們完成所有轉換。它可以調整單位以適應裁剪空間, 它可以自定義視場角,選擇 Z-裁剪面。假設有一個眼睛或者攝像機 在原點(0, 0, 0),根據 zNear 和 fieldOfView 可以將 zNear 對應到 Z = -1 ,在 zNear 平面上一半的 fieldOfView 長度 對應畫布中心到 Y = -1 或 Y = 1 的距離,X 的值通過乘以 aspect 獲取,最后通過設置 zFar 對應 Z = 1 ,控制縮放的程度。

這是矩陣的圖解。

怎么使用WebGL三維透視投影

正方體所在的有四個側面的椎體叫做“視錐”,矩陣將視錐中的空間轉換到裁剪空間中, zNear 決定了被正面切割的位置,zFar 決定被背面切割的位置。 將 zNear 設置為 23 就會看到正方體正面被切割, 將 zFar 設置為 24 就會看到正方體背面被切割。

還有一個問題,矩陣假定觀察位置為 0,0,0 并且看向 Z 軸負方向, Y 軸為上方向。這和我們目前為止做法不同, 為了解決這個問題我們需要將物體放到視圖范圍內。

我們在 (45, 150, 0) 繪制的 F,可以將它移動到 (-150, 0, -360)

使用 m4.projection 方法代替之前的投影方法,可以調用 m4.perspective

var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
var zNear = 1;
var zFar = 2000;
var matrix = m4.perspective(fieldOfViewRadians, aspect, zNear, zFar);
matrix = m4.translate(matrix, translation[0], translation[1], translation[2]);
matrix = m4.xRotate(matrix, rotation[0]);
matrix = m4.yRotate(matrix, rotation[1]);
matrix = m4.zRotate(matrix, rotation[2]);
matrix = m4.scale(matrix, scale[0], scale[1], scale[2]);

我們講了矩陣乘法,視角和自定義 Z 范圍。還有很多沒講完, 但這篇文章已經很長了,所以接下來繼續講相機。

為什么將 F 移動到那么遠的距離(Z = -360)?

在其他的例子中 F 都在 (45, 150, 0) ,但在最后一個例子中它被移動到了 (-150, 0, -360)。為什么它被移動到那么遠的地方?

原因是在最后一個例子中用 m4.projection 方法將 像素移動到裁減空間,我們的顯示范圍是 400×300 像素, “像素”在三維中無法解釋。所以新投影創建了一個視錐,它在 zNear 的距離時是 2 個單位高和 2 * aspect 個單位寬。由于 ‘F’ 的大小是 150 個單位, 在近平面的時候只能看到 2 個單位的高度, 所以我們將它移到足夠遠的地方才能看到完整的它。

同樣的將 ‘X’ 從 45 移動到 -150 。過去視圖表示的范圍是 0 到 400 個單位, 現在它表示的 -1 到 +1 個單位。

“怎么使用WebGL三維透視投影”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

泰宁县| 宽城| 清水河县| 白沙| 安龙县| 芒康县| 交口县| 巴青县| 济南市| 聂拉木县| 图们市| 包头市| 阳信县| 乐亭县| 太和县| 惠州市| 察雅县| 潮安县| 宜兰市| 于田县| 三穗县| 罗江县| 新田县| 黔东| 河间市| 焉耆| 西安市| 永胜县| 江陵县| 高邮市| 房山区| 梨树县| 临西县| 邛崃市| 海盐县| 舟曲县| 沂源县| 阿坝县| 英超| 上饶市| 湖北省|