尝试在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>
答案 0 :(得分:1)
setLookAt通常用于将3个向量(或9个单独的值)作为参数。第一个参数是眼睛/相机/你的位置。第二个参数是沿着您想要查看的方向的位置。第三个参数是向上轴。这用于设置摄像机的方向。这是必要的,因为存在无限多个方向,所有方向都共享相同的方向。想象一个飞机,飞机的方向告诉你它的走向,但是飞机的方向表明它是正常飞行还是颠倒。
setLookAt通常返回一个视图矩阵(或其反转),然后传递给GPU。
因此,您可能希望使用模型的位置而不是三个0作为外观位置?此外,请注意,您的移动实现当前会以绝对坐标移动您,而不是基于摄像机的当前方向。 +/- 1.0到eye_x实际上可能不会让你在相机空间中向左移动。
这里,作为参考,一个简单的伪FPSCamera实现,应该让你开始。需要注意的主要事项是摄像机应通过向前,向上和侧向矢量跟踪其方向。这些是世界坐标,它们被标准化为一个长度。
/** @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);
};