【问题标题】:How to use Matrix4.setLookAt to move camera through scene如何使用 Matrix4.setLookAt 在场景中移动相机
【发布时间】:2015-11-18 04:51:01
【问题描述】:

尝试在 webGL 中制作第一人称相机控制器。我正在尝试使用 Matrix4.setLookAt() 函数来执行此操作,但不确定如何计算(坦率地说有点不确定我需要修改哪些参数以及何时)如何移动。我目前如何实现左右看最初似乎工作正常,但是一旦 g_eyeX 值的值接近 1.0 或 -1.0,它就会开始将相机从场景中的立方体移开(在负 x 方向上) .我找不到太多关于如何利用此功能在场景中移动“相机”的文档,因为其中大部分都引用了 three.js(我正在尝试了解它是如何工作的并且不想使用图书馆)。任何人都可以提供一些帮助或指出我正确的方向吗?将不胜感激

下面列出了我的代码,这里是 setLookAt 函数作为参数的内容 Matrix4.setLookAt(eyeX, eyeY, eyeZ, atX, atY, atZ, upX, upY, upZ)

'eyeX, Y, Z' - 指定眼点的位置 'atX, atY, atZ' - 指定观察点的位置 'upX, upY, upZ' - 指定场景中的向上方向

JS:

 // sceneWalker.js
    // modified from RotatingTriangle.js (c) 2012 matsuda
    // uses a non-indexed cube - 2 triangles per side - 36 vertices
    // Vertex shader program
    var VSHADER_SOURCE =
      'attribute vec4 a_Position;\n' +
      'uniform mat4 u_ViewMatrix;\n' +
      'uniform mat4 u_ModelMatrix;\n' +
      'uniform mat4 u_ProjMatrix;\n' +
      'void main() {\n' +
      '  gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;\n' +
      '}\n';

    // Fragment shader program
    var FSHADER_SOURCE =
      'void main() {\n' +
      '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
      '}\n';

    // Rotation angle (degrees/second)
    var ANGLE_STEP = 0.0;
    var MOVE_AMOUNT = 0.0;
    var g_eyeX = 0.0, g_eyeY = 0.0, g_eyeZ = 0.25; // Eye position
    var g_curX = 0.0, g_curZ = -3.0;

    function main() {
      // Retrieve <canvas> element
      var canvas = document.getElementById('webgl');

      // Get the rendering context for WebGL
      var gl = getWebGLContext(canvas);
      if (!gl) {
        console.log('Failed to get the rendering context for WebGL');
        return;
      }

      // Initialize shaders
      if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
        console.log('Failed to intialize shaders.');
        return;
      }

      // Write the positions of vertices to a vertex shader
      var n = initVertexBuffers(gl);
      if (n < 0) {
        console.log('Failed to set the positions of the vertices');
        return;
      }

      // Specify the color for clearing <canvas>
      gl.clearColor(0.0, 0.0, 0.0, 1.0);

      // Get storage location of u_ViewMatrix
      var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
      if (!u_ViewMatrix) { 
        console.log('Failed to get the storage location of u_ViewMatrix');
        return;
      }

      var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
      if (!u_ModelMatrix) { 
        console.log('Failed to get the storage location of u_ModelMatrix');
        return;
      }

      var u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
        if (!u_ModelMatrix) { 
            console.log('Failed to get the storage location of u_ProjMatrix');
            return;
        }   
      // Current rotation angle
      var currentAngle = 0.0;
      // Model matrix
      var modelMatrix = new Matrix4();
      var viewMatrix = new Matrix4();
      var projMatrix = new Matrix4();
      modelMatrix.setTranslate(0, 0, 100);
      viewMatrix.setLookAt(g_eyeX, g_eyeY, g_eyeZ, 0, 0, 0, 0, 1, 0);
      projMatrix.setPerspective(45, (canvas.width)/(canvas.height), 0.1, 10000000);
      gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
      gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
      gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);

      document.onkeydown = function(ev){ keydown(ev, gl, n, u_ViewMatrix, viewMatrix); };
      // Start drawing
      var tick = function() {
        //currentAngle = animate(currentAngle);  // Update the rotation angle
        draw(gl, n, currentAngle, modelMatrix, viewMatrix, u_ModelMatrix, u_ViewMatrix);   // Draw the triangle
        requestAnimationFrame(tick, canvas); // Request that the browser calls tick
      };
      tick();
    }

    function keydown(ev, gl, n, u_ViewMatrix, viewMatrix) {
    console.log(ev.keyCode);
        if(ev.keyCode == 39) { // The right arrow key was pressed
          g_eyeX -= 0.01;
          console.log(g_eyeX);
        } else 
        if (ev.keyCode == 37) { // The left arrow key was pressed
          g_eyeX += 0.01;
          console.log(g_eyeX);
        }
        if(ev.keyCode == 38){
            g_eyeY += 0.01;
        }
        if(ev.keyCode == 40){
            g_eyeY -= 0.01;
        }
        if(ev.keyCode == 68){
            g_curX -= 0.01;
        }
        if(ev.keyCode == 65){
            g_curX += 0.01;
        }
        if(ev.keyCode == 87){
            g_curZ += 0.01;
        }
        if(ev.keyCode == 83){
            g_curZ -= 0.01;
        }
        else { return; }   
    }


    function initVertexBuffers(gl) {
      var vertices = new Float32Array ([
        -0.5, 0.5, 0.5,
        -0.5, -0.5, 0.5,
        0.5, -0.5, 0.5,
        -0.5, 0.5, 0.5,
        0.5, -0.5, 0.5,
        0.5, 0.5, 0.5,
        0.5, 0.5, 0.5,
        0.5, -0.5, 0.5,
        0.5, -0.5, -0.5,
        0.5, 0.5, 0.5,
        0.5, -0.5, -0.5,
        0.5, 0.5, -0.5,
        0.5, -0.5, 0.5,
        -0.5, -0.5, 0.5,
        -0.5, -0.5, -0.5,
        0.5, -0.5, 0.5,
        -0.5, -0.5, -0.5,
        0.5, -0.5, -0.5,
        0.5, 0.5, -0.5,
        -0.5, 0.5, -0.5,
        -0.5, 0.5, 0.5,
        0.5, 0.5, -0.5,
        -0.5, 0.5, 0.5,
        0.5, 0.5, 0.5,
        -0.5, -0.5, -0.5,
        -0.5, 0.5, -0.5,
        0.5, 0.5, -0.5,
        -0.5, -0.5, -0.5,
        0.5, 0.5, -0.5,
        0.5, -0.5, -0.5,
        -0.5, 0.5, -0.5,
        -0.5, -0.5, -0.5,
        -0.5, -0.5, 0.5,
        -0.5, 0.5, -0.5,
        -0.5, -0.5, 0.5,
        -0.5, 0.5, 0.5
      ]);
      var n = 36;   // The number of vertices

      // Create a buffer object
      var vertexBuffer = gl.createBuffer();
      if (!vertexBuffer) {
        console.log('Failed to create the buffer object');
        return -1;
      }

      // Bind the buffer object to target
      gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
      // Write date into the buffer object
      gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

      // Assign the buffer object to a_Position variable
      var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
      if(a_Position < 0) {
        console.log('Failed to get the storage location of a_Position');
        return -1;
      }
      gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0);

      // Enable the assignment to a_Position variable
      gl.enableVertexAttribArray(a_Position);

      return n;
    }

    function draw(gl, n, currentAngle, modelMatrix, viewMatrix, u_ModelMatrix, u_ViewMatrix) {
      // Set the rotation matrix
      modelMatrix.setRotate(currentAngle, 1, 1, 1); 
      modelMatrix.setTranslate(g_curX, 0, g_curZ);

      viewMatrix.setLookAt(g_eyeX, g_eyeY, g_eyeZ, 0, 0, 0, 0, 1, 0);
      // Pass the rotation matrix to the vertex shader
      gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
      gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

      // Clear <canvas>
      gl.clear(gl.COLOR_BUFFER_BIT);

      // Draw the rectangle
      gl.drawArrays(gl.TRIANGLES, 0, n);
    }

    // Last time that this function was called
    var g_last = Date.now();
    function animate(angle) {
      // Calculate the elapsed time
      var now = Date.now();
      var elapsed = now - g_last;
      g_last = now;
      // Update the current rotation angle (adjusted by the elapsed time)
      var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
      return newAngle %= 360;
    }

HMTL:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Cube</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>

    <script src="../lib/webgl-utils.js"></script>
    <script src="../lib/webgl-debug.js"></script>
    <script src="../lib/cuon-utils.js"></script>
    <script src="../lib/cuon-matrix.js"></script>
    <script src="sceneWalker.js"></script>
  </body>
</html>

【问题讨论】:

  • this answer might be helpful。它使用lookAt
  • 只是一个细节(除了答案中提到的那些): > modelMatrix.setRotate(currentAngle, 1, 1, 1); > modelMatrix.setTranslate(g_curX, 0, g_curZ);上面的第二个语句覆盖了第一个。

标签: javascript webgl


【解决方案1】:

setLookAt 通常被实现为将 3 个向量(或 9 个单独的值)作为其参数。第一个参数是眼睛/相机/你的位置。第二个参数是沿您要查看的方向的位置。第三个参数是上轴。这用于设置相机的方向。这是必需的,因为有无限多的方向都共享相同的方向。想象一架飞机,飞机的方向告诉你它的方向,但飞机的方向表明它是正常飞行还是倒挂。

setLookAt 通常会返回一个视图矩阵(或其逆矩阵),然后您将其传递给 GPU。

因此,您可能希望使用模型的位置,而不是作为外观位置的三个 0?此外,请注意,您的移动实现当前以绝对坐标移动您,而不是基于相机的当前方向。 +/- 1.0 到 eye_x 可能实际上不会让您在相机空间中向左移动。

在这里,作为参考,一个简单的伪 FPSCamera 实现应该可以帮助您入门。需要注意的主要事情是相机应该通过前向、向上和侧向矢量来跟踪其方向。它们位于世界坐标中,并且被标准化为长度为 1。

/** @constructor */
function FPSCamera(){
    this.pos = [0.0, 0.0, 0.0];

    this.dir = [0.0, 0.0, -1.0]; // or forward
    this.up = [0.0, 1.0, 0.0];
    this.side = [1.0, 0.0, 0.0]; // or right
}

FPSCamera.prototype.forward = function(dist){
    this.pos[0] += this.dir[0] * dist;
    this.pos[1] += this.dir[1] * dist;
    this.pos[2] += this.dir[2] * dist;
};
// do the same for other 2 directions, strife and fly

// looks to left/right
FPSCamera.prototype.yaw = function(radians){
    var orientationChange = ORIENTATION.fromAxisAngle(this.up, radians);
    this.dir = VEC3.rotatedByOrientation(this.dir, orientationChange);
    this.side = VEC3.cross(this.dir, this.up);
    this.side = VEC3.normalize(this.side);
};

// same for pitch... except the rotation axis is this.side and you need to ensure the pitch is within +/- 90 degrees

FPSCamera.prototype.getViewMatrix = function(){
    // matrix can be extracted from the 3 direction vectors, but lets use lookAt here;
    return MAT4.lookAt(this.pos, VEC3.plus(this.pos, this.dir), this.up);
};

【讨论】:

  • 我也很好奇这个问题的解决方案,因为我对 vanilla WebGL 还是很陌生。你能提供一个简单的例子吗?
  • 把 3D 的东西放到 jsfiddle 上有点烦人,因为有很多依赖项,因为我宁愿使用我的抽象而不是直接使用 webgl API。我想我要做的是建立一个简单的 FPS 相机实现,这应该可以帮助您入门。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-31
  • 1970-01-01
  • 2011-02-21
  • 1970-01-01
  • 2015-03-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多