【问题标题】:FPS-like camera movement with basic matrix transformations (WebGL)具有基本矩阵变换 (WebGL) 的类似 FPS 的相机移动
【发布时间】:2017-12-16 20:14:46
【问题描述】:

我在 WebGL 中有一个简单的场景,我将每个变换(用于相机和模型)存储在单个模型/视图矩阵中,并通过旋转和移动所述矩阵来设置它们。

我想要的是,能够旋转相机,当我“向前”移动到相机指向的地方时。

到目前为止,我已将this 代码修改为:

    mat4.identity(mvMatrix);    
    mat4.rotateX(mvMatrix, degToRad(elev), mvMatrix);   
    mat4.rotateY(mvMatrix, degToRad(ang), mvMatrix);   
    mat4.rotateZ(mvMatrix, degToRad(-roll), mvMatrix);  
    mat4.translate(mvMatrix, [-px, -py, -pz], mvMatrix);

因为它并没有按原样工作,但它有点工作,直到你进行极端旋转(超过 90 度)。

这不是我正在做的事情的破坏者,但我想知道。这是我能得到的最好的结果,而无需像这样计算相机方向吗?

【问题讨论】:

    标签: camera rotation webgl homogenous-transformation


    【解决方案1】:

    WebGL 摄像头通常指向 -Z 轴,因此要沿摄像头面向的方向移动,只需将摄像头的 Z 轴(元素 8、9、10)添加到摄像头的位置乘以某个速度。

    const m4 = twgl.m4;
    const v3 = twgl.v3;
    const gl = document.querySelector("canvas").getContext("webgl");
    const vs = `
    uniform mat4 u_worldViewProjection;
    uniform mat4 u_worldInverseTranspose;
    
    attribute vec4 position;
    attribute vec3 normal;
    
    varying vec3 v_normal;
    
    void main() {
      gl_Position = u_worldViewProjection * position;
      v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;
    }
    `;
    const fs = `
    precision mediump float;
    
    varying vec3 v_normal;
    uniform vec3 u_lightDir;
    uniform vec4 u_color;
    
    void main() {
      vec3 norm = normalize(v_normal);
      float light = dot(u_lightDir, norm) * .5 + .5;
      gl_FragColor = vec4(u_color.rgb * light, u_color.a);
    }
    `;
    
    const progInfo = twgl.createProgramInfo(gl, [vs, fs]);
    const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);
    
    const projection = m4.identity();
    const camera = m4.identity();
    const view = m4.identity();
    const viewProjection = m4.identity();
    const world = m4.identity();
    const worldViewProjection = m4.identity();
    const worldInverse = m4.identity();
    const worldInverseTranspose = m4.identity();
    
    const fov = degToRad(90);
    const zNear = 0.1;
    const zFar = 100;
    
    const lightDir = v3.normalize([1, 2, 3]);
    
    const keys = {};
    
    let px = 0;
    let py = 0;
    let pz = 0;
    let elev = 0;
    let ang = 0;
    let roll = 0;
    const speed = 1;
    const turnSpeed = 90;
    
    let then = 0;
    function render(now) {
      now *= 0.001;  // seconds;
      const deltaTime = now - then;
      then = now;
      
      twgl.resizeCanvasToDisplaySize(gl.canvas);
      gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
      
      gl.enable(gl.DEPTH_TEST);
      gl.enable(gl.CULL_FACE);
      
      gl.useProgram(progInfo.program);
      
      const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
      m4.perspective(fov, aspect, zNear, zFar, projection);
    
      m4.identity(camera);    
      m4.translate(camera, [px, py, pz], camera);
      m4.rotateX(camera, degToRad(elev), camera);   
      m4.rotateY(camera, degToRad(-ang), camera);   
      m4.rotateZ(camera, degToRad(roll), camera);
      
      m4.inverse(camera, view);
    
      m4.multiply(projection, view, viewProjection);
      
      for (let z = -1; z <= 1; ++z) {
        for (let y = -1; y <= 1; ++y) {
          for (let x = -1; x <= 1; ++x) {
            if (x === 0 && y === 0 && z === 0) {
              continue;
            }
            
            m4.identity(world);
            m4.translate(world, [x * 3, y * 3, z * 3], world);
            
            m4.multiply(viewProjection, world, worldViewProjection);
            m4.inverse(world, worldInverse);
            m4.transpose(worldInverse, worldInverseTranspose);
            
            twgl.setBuffersAndAttributes(gl, progInfo, bufferInfo);
            twgl.setUniforms(progInfo, {
              u_worldViewProjection: worldViewProjection,
              u_worldInverseTranspose: worldInverseTranspose,
              u_color: [(x + 2) / 3, (y + 2) / 3, (z + 2) / 3, 1],
              u_lightDir: lightDir,
            });
            twgl.drawBufferInfo(gl, bufferInfo);
          }
        }
      }
      
      if (keys['87'] || keys['83']) {
        const direction = keys['87'] ? 1 : -1;
        px -= camera[ 8] * deltaTime * speed * direction;
        py -= camera[ 9] * deltaTime * speed * direction;
        pz -= camera[10] * deltaTime * speed * direction;
      }
      
      if (keys['65'] || keys['68']) {
        const direction = keys['65'] ? 1 : -1;
        ang += deltaTime * turnSpeed * direction;
      }
    
      if (keys['81'] || keys['69']) {
        const direction = keys['81'] ? 1 : -1;
        roll += deltaTime * turnSpeed * direction;
      }
    
      if (keys['38'] || keys['40']) {
        const direction = keys['38'] ? 1 : -1;
        elev += deltaTime * turnSpeed * direction;
      }
    
      requestAnimationFrame(render);
    }
    requestAnimationFrame(render);
    
    window.addEventListener('keydown', (e) => {
      keys[e.keyCode] = true;
      e.preventDefault();
    });
    window.addEventListener('keyup', (e) => {
      keys[e.keyCode] = false;
      e.preventDefault();
    });
    
    function degToRad(d) {
      return d * Math.PI / 180;
    }
    body { margin: 0; }
    canvas { width: 100vw; height: 100vh; display: block; }
    pre { position: absolute; left: 1em; top: 0; }
    <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
    <canvas></canvas>
    <pre>
    A = left
    D = right
    W = forward
    S = down
    Q = roll left
    E = roll right
    UP = look up
    DN = look down
    </pre>

    【讨论】:

    • 嘿gman,非常感谢,这好多了。我还有一个问题,只是想看看我是否理解正确,因为我之前尝试过这样的事情。
    • 当我们旋转相机时,我们实际上是在围绕相机旋转场景,所以当我们向前移动时,我们想要沿着默认的 Z 轴 ([0, 0, 1]) 移动。通过将相机矩阵的 Z 分量添加到平移中,您所做的是“取消”旋转。对吗?
    • 相机令人困惑。对我来说,相机是我放置在这个世界上的东西。如果您在 Unity、Maya 或 Blender 中,您可以在场景中看到一个相机图标。该相机存在于场景本身中。可以计算其世界矩阵。相机向下看它的 -Z 轴。从相机的世界矩阵中,您可以通过取相机矩阵的逆矩阵来创建视图矩阵。视图矩阵相对于相机移动所有其他东西,以便相机的位置最终位于原点。见this article
    猜你喜欢
    • 2013-12-26
    • 2017-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-22
    • 1970-01-01
    • 1970-01-01
    • 2012-10-15
    相关资源
    最近更新 更多