首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用Matrix4.setLookAt在场景中移动摄像头

如何使用Matrix4.setLookAt在场景中移动摄像头
EN

Stack Overflow用户
提问于 2015-11-18 01:45:34
回答 1查看 2K关注 0票数 3

尝试在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:

代码语言:javascript
复制
 // 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:

代码语言:javascript
复制
<!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>
EN

回答 1

Stack Overflow用户

发布于 2015-11-18 02:43:33

setLookAt通常实现为接受3个向量(或9个单独的值)作为其参数。第一个参数是eye/camera/you的位置。第二个参数是沿您想要查看的方向的位置。第三个参数是向上轴。设置摄像头的朝向。这是必要的,因为有无限多的方向都共享相同的方向。想一想一个平面,平面的方向告诉你它的方向,而平面的方向表明它是正常飞行还是颠倒飞行。

setLookAt通常返回一个视图矩阵(或其逆),然后将其传递给图形处理器。

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

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

代码语言:javascript
复制
/** @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);
};
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33763637

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档