WebGL FPS camera movement along the local axis instead of the world axis with glMatrix.js

落花浮王杯 提交于 2021-01-29 15:21:40

问题


I'm trying to make a FPS camera in WebGL. When I move the camera from the initial position and then rotate, the camera moves along the start position instead of the new position. The translation and rotation are made with glMatrix.js library.

I have the variables:

var rotateY = 0;  // For rotating along the Y axis
var fronBackMovement = 0;   // Front and back movement
var sideMovement  = 0;  // Movement from side to side

Keyboard events:

// Movement
if( key.w == true ){
    fronBackMovement += 5;
}
if( key.a == true ){
    sideMovement += 5;
}
if( key.s == true ){
    fronBackMovement -= 5;
}
if( key.d == true ){
    sideMovement -= 5;
}

Mouse movement:

// Rotation
if( mouse.movementX != 0 ){
    rotateY += mouse.movementX;  // mouse.movementX is screen movement of the cursor
}

Translation and rotation:

camera.matrix = mat4.fromTranslation( mat4.create() ,vec3.fromValues(sideMovement,0,fronBackMovement) )
camera.matrix = mat4.rotateY(camera.matrix,camera.matrix,rotateY )

回答1:


The camera generally looks down the -Z axis so to move forward you just add the camera's Z axis to your position. If you don't want to move vertically then zero out the Y component and normalize. Finally multiply by your desired speed.

The camera's x-axis contains the side movement direction so you can do the same for that.

const cameraPosition = vec3.create();
const tempForwardDirection = vec3.create();
const tempSideDirection = vec3.create();
...


tempForwardDirection[0] = camera.matrix[8];
tempForwardDirection[1] = 0;  // camera.matrix[9];
tempForwardDirection[2] = camera.matrix[10];
vec3.normalize(tempForwardDirection, tempForwardDirection)

tempSideDirection[0] = camera.matrix[0];
tempSideDirection[1] = camera.matrix[1];
tempSideDirection[2] = camera.matrix[2];

vec3.scaleAndAdd(
    cameraPosition,
    cameraPosition,
    tempForwardDirection, 
    -fronBackMovement);
vec3.scaleAndAdd(
    cameraPosition,
    cameraPosition,
    tempSideDirection,
    sideMovement)

camera.matrix = mat4.fromTranslation(camera.matrix, cameraPosition);
camera.matrix = mat4.rotateY(camera.matrix,camera.matrix,rotateY);

let rotateY = 0;  // For rotating along the Y axis
let fronBackMovement = 0;   // Front and back movement
let sideMovement  = 0;  // Movement from side to side

const cameraPosition = vec3.create();
const tempForwardDirection = vec3.create();
const tempSideDirection = vec3.create();

const camera = {
  matrix: mat4.create(),
};
const mouse = {
  movementX: 0,
};

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 = mat4.create();
const view = mat4.create();
const viewProjection = mat4.create();
const world = mat4.create();
const worldViewProjection = mat4.create();
const worldInverse = mat4.create();
const worldInverseTranspose = mat4.create();

const fov = degToRad(90);
const zNear = 0.1;
const zFar = 100;

const lightDir = vec3.normalize(vec3.create(), [1, 2, 3]);

const key = {};

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;
  mat4.perspective(projection, fov, aspect, zNear, zFar);

  fronBackMovement = 0;
  sideMovement = 0;
  
  if( key.w == true ){
      fronBackMovement = 5 * deltaTime;
  }
  if( key.a == true ){
      sideMovement = -5 * deltaTime;
  }
  if( key.s == true ){
      fronBackMovement = -5 * deltaTime;
  }
  if( key.d == true ){
      sideMovement = 5 * deltaTime;
  }

  // Rotation
  if( mouse.movementX != 0 ){
      rotateY += mouse.movementX / 200;  // mouse.movementX is screen movement of the cursor
      mouse.movementX = 0;
  }

  tempForwardDirection[0] = camera.matrix[8];
  tempForwardDirection[1] = 0;  // camera.matrix[9];
  tempForwardDirection[2] = camera.matrix[10];
  vec3.normalize(tempForwardDirection, tempForwardDirection)

  tempSideDirection[0] = camera.matrix[0];
  tempSideDirection[1] = camera.matrix[1];
  tempSideDirection[2] = camera.matrix[2];

  vec3.scaleAndAdd(
      cameraPosition,
      cameraPosition,
      tempForwardDirection, 
      -fronBackMovement);
  vec3.scaleAndAdd(
      cameraPosition,
      cameraPosition,
      tempSideDirection,
      sideMovement)

  camera.matrix = mat4.fromTranslation(camera.matrix, cameraPosition);
  camera.matrix = mat4.rotateY(camera.matrix,camera.matrix,rotateY);


  mat4.invert(view, camera.matrix);

  mat4.multiply(viewProjection, projection, view);
  
  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;
        }
        
        mat4.identity(world);
        mat4.translate(world, world, [x * 3, y * 3, z * 3]);
        
        mat4.multiply(worldViewProjection, viewProjection, world);
        mat4.invert(worldInverse, world);
        mat4.transpose(worldInverseTranspose, worldInverse);
        
        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);
      }
    }
  }
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

window.addEventListener('keydown', (e) => {
  key[e.key] = true;
  e.preventDefault();
});
window.addEventListener('keyup', (e) => {
  key[e.key] = false;
  e.preventDefault();
});
window.addEventListener('mousemove', (e) => {
  mouse.movementX = e.movementX;
});


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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
<canvas></canvas>
<pre>
A = left
D = right
W = forward
S = down
</pre>



来源:https://stackoverflow.com/questions/62092581/webgl-fps-camera-movement-along-the-local-axis-instead-of-the-world-axis-with-gl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!