gpt4 book ai didi

opengl-es - 在 WebGL 中创建 3D 自由相机 - 为什么这两种方法都不起作用?

转载 作者:行者123 更新时间:2023-12-04 18:13:47 24 4
gpt4 key购买 nike

编辑

OK, I've tried a camera using quaternions:


qyaw = [Math.cos(rot[0]/2), 0, Math.sin(rot[0]/2), 0];
qpitch = [Math.cos(rot[1]/2), 0, 0, Math.sin(rot[1]/2)];
rotQuat = quat4.multiply (qpitch, qyaw);
camRot = quat4.toMat4(rotQuat);
camMat = mat4.multiply(camMat,camRot);

and I get exactly the same problem. So I'm guessing it's not gimbal lock. I've tried changing the order I multiply my matrices, but it just goes camera matrix * model view matrix, then object matrix * model view. That's right isn't it?



我正在尝试在 webGL 中构建一个 3d 相机,它可以在世界上移动并围绕 x 和 y(右和上)轴旋转。

我遇到了一个熟悉的问题(可能是万向节锁?),一旦其中一个轴旋转,另一个轴的旋转就会被搞砸;例如,当您绕 Y 轴旋转 90 度时,绕 x 的旋转变成绕 z 的旋转。

我很欣赏这是一个常见问题,并且有大量指南可以避免这个问题,但据我所知,我已经实现了两种不同的解决方案,但仍然遇到相同的问题。坦率地说,这让我很头疼……

我正在使用的一种解决方案是(改编自 http://www.toymaker.info/Games/html/camera.html ):
function updateCam(){
yAx = [0,1,0];
xAx = [1,0,0];
zAx = [0,0,1];

mat4.identity(camMat);

xRotMat = mat4.create();
mat4.identity(xRotMat)
mat4.rotate(xRotMat,rot[0],xAx);
mat4.multiplyVec3(xRotMat,zAx);
mat4.multiplyVec3(xRotMat,yAx);


yRotMat = mat4.create();
mat4.identity(yRotMat)
mat4.rotate(yRotMat,rot[1],yAx);
mat4.multiplyVec3(yRotMat,zAx);
mat4.multiplyVec3(yRotMat,xAx);


zRotMat = mat4.create();
mat4.identity(zRotMat)
mat4.rotate(zRotMat,rot[2],zAx);
mat4.multiplyVec3(zRotMat,yAx);
mat4.multiplyVec3(zRotMat,xAx);


camMat[0] = xAx[0];
camMat[1] = yAx[0];
camMat[2] = zAx[0];
//camMat[3] =
camMat[4] = xAx[1]
camMat[5] = yAx[1];
camMat[6] = zAx[1];
//camMat[7] =
camMat[8] = xAx[2]
camMat[9] = yAx[2];
camMat[10]= zAx[2];
//camMat[11]=
camMat[12]= -1* vec3.dot(camPos, xAx);
camMat[13]= -1* vec3.dot(camPos, yAx);
camMat[14]= -1* vec3.dot(camPos, zAx);
//camMat[15]=

var movSpeed = 1.5 * forward;
var movVec= vec3.create(zAx);
vec3.scale(movVec, movSpeed);
vec3.add(camPos, movVec);
movVec= vec3.create(xAx);
movSpeed = 1.5 * strafe;
vec3.scale(movVec, movSpeed);
vec3.add(camPos, movVec);

}

我也尝试使用这种方法使用
mat4.rotate(camMat, rot[1], yAx);

而不是明确构建相机矩阵 - 结果相同。

我的第二个(实际上是第一个...)方法看起来像这样(rot 是一个包含当前围绕 x、y 和 z 旋转的数组(z 始终为零):
   function updateCam(){
mat4.identity(camRot);
mat4.identity(camMat);
camRot = fullRotate(rot);
mat4.set(camRot,camMat);
mat4.translate(camMat, camPos);
}

function fullRotate(angles){
var cosX = Math.cos(angles[0]);
var sinX = Math.sin(angles[0]);
var cosY = Math.cos(angles[1]);
var sinY = Math.sin(angles[1]);
var cosZ = Math.cos(angles[2]);
var sinZ = Math.sin(angles[2]);
rotMatrix = mat4.create([cosZ*cosY, -1*sinZ*cosX + cosZ*sinY*sinX, sinZ*sinX+cosZ*sinY*cosX, 0,
sinZ*cosY, cosZ*cosX + sinZ*sinY*sinX, -1*cosZ*sinX + sinZ*sinY*cosX, 0,
-1*sinY, cosY*sinX, cosY*cosX, 0,
0,0,0,1 ] );
mat4.transpose(rotMatrix);
return (rotMatrix);
}

实际绘制场景的代码(我已经去掉了大部分样板 gl 照明等,只留下了转换):
   function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 2000.0, pMatrix);

mat4.identity(mvMatrix);

for(var i=0; i<planets.length; i++){
if (planets[i].type =="sun"){
currentProgram = perVertexSunProgram;
} else {
currentProgram = perVertexNormalProgram;
}
alpha = planets[i].alphaFlag;

mat4.identity(planets[i].rotMat);

mvPushMatrix();
//all the following puts planets in orbit around a central sun, but it's not really relevant to my current problem
var rot = [0,rotCount*planets[i].orbitSpeed,0];

var planetMat;
planetMat = mat4.create(fullRotate(rot));

mat4.multiply(planets[i].rotMat, planetMat);

mat4.translate(planets[i].rotMat, planets[i].position);

if (planets[i].type == "moon"){
var rot = [0,rotCount*planets[i].moonOrbitSpeed,0];
moonMat = mat4.create(fullRotate(rot));
mat4.multiply(planets[i].rotMat, moonMat);
mat4.translate(planets[i].rotMat, planets[i].moonPosition);
mat4.multiply(planets[i].rotMat, mat4.inverse(moonMat));
}

mat4.multiply(planets[i].rotMat, mat4.inverse(planetMat));
mat4.rotate(planets[i].rotMat, rotCount*planets[i].spinSpd, [0, 1, 0]);


//this bit does the work - multiplying the model view by the camera matrix, then by the matrix of the object we want to render
mat4.multiply(mvMatrix, camMat);
mat4.multiply(mvMatrix, planets[i].rotMat);



gl.useProgram(currentProgram);

setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, planets[i].VertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
mvPopMatrix();
}
}

然而,大多数变换都可以忽略,同样的效果可以看到,只是在世界坐标 0,0,0 处显示一个球体。

我认为我的两种方法 - 一次旋转一个轴,或者一次构建旋转矩阵避免了一个接一个旋转的问题。任何想法我哪里出错了?

PS - 我仍然非常开始学习 WebGL 和 3d 数学,所以要温柔地和我说话,就像几个月前还没有听说过矩阵的人一样......另外,我知道四元数是一个很好的选择3d 旋转的解决方案,这将是我的下一次尝试,但是,我想我需要了解为什么这两种方法首先不起作用......

最佳答案

为了澄清起见,请以这种方式考虑万向节锁定:您玩过地震/虚幻/使命召唤/任何第一人称射击游戏,对吗?你知道当你向前看并左右移动鼠标时,你的 View 会以一个很好的宽弧线摆动,但如果你直视上下并左右移动鼠标,你基本上只是围绕一个点紧紧地旋转?那就是云台锁。几乎所有 FPS 游戏都会使用它,因为它恰好模仿了我们在现实生活中会做的事情,因此大多数人通常不会认为这是一个问题。

然而,对于太空飞行模拟之类的东西,或者(更常见的)骨骼动画,这种效果是不可取的,因此我们使用四元数之类的东西来帮助我们解决它。您是否关心相机的万向节锁定取决于您想要达到的效果。

但是,我不认为您正在经历这种情况。这听起来像是你的矩阵乘法顺序搞砸了,结果你的 View 以一种你意想不到的方式旋转。我会尝试按照您执行 X/Y/Z 旋转的顺序进行播放,看看您是否可以找到一个顺序而不是给您所需的结果。

现在,我讨厌进行代码转储,但这可能对您有用,所以我们开始吧:这是我在大多数较新的 WebGL 项目中用于管理自由 float 相机的代码。它是万向节锁定的,但正如我之前提到的,在这种情况下它并不重要。基本上,它只是为您提供 FPS 风格的控件,您可以使用这些控件在场景中飞行。

/**
* A Flying Camera allows free motion around the scene using FPS style controls (WASD + mouselook)
* This type of camera is good for displaying large scenes
*/
var FlyingCamera = Object.create(Object, {
_angles: {
value: null
},

angles: {
get: function() {
return this._angles;
},
set: function(value) {
this._angles = value;
this._dirty = true;
}
},

_position: {
value: null
},

position: {
get: function() {
return this._position;
},
set: function(value) {
this._position = value;
this._dirty = true;
}
},

speed: {
value: 100
},

_dirty: {
value: true
},

_cameraMat: {
value: null
},

_pressedKeys: {
value: null
},

_viewMat: {
value: null
},

viewMat: {
get: function() {
if(this._dirty) {
var mv = this._viewMat;
mat4.identity(mv);
mat4.rotateX(mv, this.angles[0]-Math.PI/2.0);
mat4.rotateZ(mv, this.angles[1]);
mat4.rotateY(mv, this.angles[2]);
mat4.translate(mv, [-this.position[0], -this.position[1], - this.position[2]]);
this._dirty = false;
}

return this._viewMat;
}
},

init: {
value: function(canvas) {
this.angles = vec3.create();
this.position = vec3.create();
this.pressedKeys = new Array(128);

// Initialize the matricies
this.projectionMat = mat4.create();
this._viewMat = mat4.create();
this._cameraMat = mat4.create();

// Set up the appropriate event hooks
var moving = false;
var lastX, lastY;
var self = this;

window.addEventListener("keydown", function(event) {
self.pressedKeys[event.keyCode] = true;
}, false);

window.addEventListener("keyup", function(event) {
self.pressedKeys[event.keyCode] = false;
}, false);

canvas.addEventListener('mousedown', function(event) {
if(event.which == 1) {
moving = true;
}
lastX = event.pageX;
lastY = event.pageY;
}, false);

canvas.addEventListener('mousemove', function(event) {
if (moving) {
var xDelta = event.pageX - lastX;
var yDelta = event.pageY - lastY;
lastX = event.pageX;
lastY = event.pageY;

self.angles[1] += xDelta*0.025;
while (self.angles[1] < 0)
self.angles[1] += Math.PI*2;
while (self.angles[1] >= Math.PI*2)
self.angles[1] -= Math.PI*2;

self.angles[0] += yDelta*0.025;
while (self.angles[0] < -Math.PI*0.5)
self.angles[0] = -Math.PI*0.5;
while (self.angles[0] > Math.PI*0.5)
self.angles[0] = Math.PI*0.5;

self._dirty = true;
}
}, false);

canvas.addEventListener('mouseup', function(event) {
moving = false;
}, false);

return this;
}
},

update: {
value: function(frameTime) {
var dir = [0, 0, 0];

var speed = (this.speed / 1000) * frameTime;

// This is our first person movement code. It's not really pretty, but it works
if(this.pressedKeys['W'.charCodeAt(0)]) {
dir[1] += speed;
}
if(this.pressedKeys['S'.charCodeAt(0)]) {
dir[1] -= speed;
}
if(this.pressedKeys['A'.charCodeAt(0)]) {
dir[0] -= speed;
}
if(this.pressedKeys['D'.charCodeAt(0)]) {
dir[0] += speed;
}
if(this.pressedKeys[32]) { // Space, moves up
dir[2] += speed;
}
if(this.pressedKeys[17]) { // Ctrl, moves down
dir[2] -= speed;
}

if(dir[0] != 0 || dir[1] != 0 || dir[2] != 0) {
var cam = this._cameraMat;
mat4.identity(cam);
mat4.rotateX(cam, this.angles[0]);
mat4.rotateZ(cam, this.angles[1]);
mat4.inverse(cam);

mat4.multiplyVec3(cam, dir);

// Move the camera in the direction we are facing
vec3.add(this.position, dir);

this._dirty = true;
}
}
}
});

该相机假定 Z 是您的“向上”轴,这对您来说可能是也可能不是。它还使用 ECMAScript 5 样式对象,但这对于任何支持 WebGL 的浏览器来说都不是问题,它使用了我的 glMatrix 库,但看起来您已经在使用它了。基本用法非常简单:
// During your init code
var camera = Object.create(FlyingCamera).init(canvasElement);

// During your draw loop
camera.update(16); // 16ms per-frame == 60 FPS

// Bind a shader, etc, etc...
gl.uniformMatrix4fv(shaderUniformModelViewMat, false, camera.viewMat);

其他一切都在内部为您处理,包括键盘和鼠标控制。可能不完全符合您的需求,但希望您能从那里收集到您需要的东西。 (注意:这与我的 Quake 3 demo 中使用的相机基本相同,因此应该可以让您了解它的工作原理。)

好吧,这足以让我在一篇文章中胡言乱语了!祝你好运!

关于opengl-es - 在 WebGL 中创建 3D 自由相机 - 为什么这两种方法都不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7646124/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com