我有一个用于控制相机的相机类,其主要功能是:
void PNDCAMERA::renderMatrix()
{
float dttime=getElapsedSeconds();
GetCursorPos(&cmc.p_cursorPos);
ScreenToClient(hWnd, &cmc.p_cursorPos);
double d_horangle=((double)cmc.p_cursorPos.x-(double)cmc.p_origin.x)/(double)screenWidth*PI;
double d_verangle=((double)cmc.p_cursorPos.y-(double)cmc.p_origin.y)/(double)screenHeight*PI;
cmc.horizontalAngle=d_horangle+cmc.d_horangle_prev;
cmc.verticalAngle=d_verangle+cmc.d_verangle_prev;
if(cmc.verticalAngle>PI/2) cmc.verticalAngle=PI/2;
if(cmc.verticalAngle<-PI/2) cmc.verticalAngle=-PI/2;
changevAngle(cmc.verticalAngle);
changehAngle(cmc.horizontalAngle);
rightVector=glm::vec3(sin(horizontalAngle - PI/2.0f),0,cos(horizontalAngle - PI/2.0f));
directionVector=glm::vec3(cos(verticalAngle) * sin(horizontalAngle), sin(verticalAngle), cos(verticalAngle) * cos(horizontalAngle));
upVector=glm::vec3(glm::cross(rightVector,directionVector));
glm::normalize(upVector);
glm::normalize(directionVector);
glm::normalize(rightVector);
if(moveForw==true)
{
cameraPosition=cameraPosition+directionVector*(float)C_SPEED*dttime;
}
if(moveBack==true)
{
cameraPosition=cameraPosition-directionVector*(float)C_SPEED*dttime;
}
if(moveRight==true)
{
cameraPosition=cameraPosition+rightVector*(float)C_SPEED*dttime;
}
if(moveLeft==true)
{
cameraPosition=cameraPosition-rightVector*(float)C_SPEED*dttime;
}
glViewport(0,0,screenWidth,screenHeight);
glScissor(0,0,screenWidth,screenHeight);
projection_matrix=glm::perspective(60.0f, float(screenWidth) / float(screenHeight), 1.0f, 40000.0f);
view_matrix = glm::lookAt(
cameraPosition,
cameraPosition+directionVector,
upVector);
gShader->bindShader();
gShader->sendUniform4x4("model_matrix",glm::value_ptr(model_matrix));
gShader->sendUniform4x4("view_matrix",glm::value_ptr(view_matrix));
gShader->sendUniform4x4("projection_matrix",glm::value_ptr(projection_matrix));
gShader->sendUniform("camera_position",cameraPosition.x,cameraPosition.y,cameraPosition.z);
gShader->sendUniform("screen_size",(GLfloat)screenWidth,(GLfloat)screenHeight);
};
它运行平稳,我可以用鼠标在X和Y方向上控制 Angular ,但不能围绕Z轴(Y是世界空间中的“向上”)。
在我的渲染方法中,我通过一个VAO调用来渲染terrain网格。网格本身是以四边形为中心(高地),其他网格是L形网格,以2的幂进行缩放。它总是在相机前重新放置,缩放到世界空间中,并通过高度图移动。
rcampos.x = round((camera_position.x)/(pow(2,6)*gridscale))*(pow(2,6)*gridscale);
rcampos.y = 0;
rcampos.z = round((camera_position.z)/(pow(2,6)*gridscale))*(pow(2,6)*gridscale);
vPos = vec3(uv.x,0,uv.y)*pow(2,LOD)*gridscale + rcampos;
vPos.y = texture(hmap,vPos.xz/horizontal_scale).r*vertical_scale;
问题:
相机从原点开始,即
(0,0,0)
。当我将其远离该点移动时,它将导致沿X轴的旋转不连续。感觉鼠标光标与屏幕空间中的网格对齐,并且仅将网格点处的位置记录为光标移动。我还记录了相机的位置,当它变得非常明显时,它在X或Z方向上距原点大约1,000,000。我注意到,这个“滞后”随着距离(从原点开始)而线性增加。
即使我使用一个没有位移的平面,也没有平面可以重叠,这时也有一点Z角战斗(或类似的效果)。 (我使用镶嵌细分着色器并渲染补丁。)补丁上会出现黑点。可能是由雾引起的:
float fc = (view_matrix*vec4(Pos,1)).z/(view_matrix*vec4(Pos,1)).w;
float fResult = exp(-pow(0.00005f*fc, 2.0));
fResult = clamp(fResult, 0.0, 1.0);
gl_FragColor = vec4(mix(vec4(0.0,0.0,0.0,0),vec4(n,1),fResult));
另一个奇怪的行为是Z轴的旋转很小,这也随着距离而增加,但是我不使用这种旋转。
可变格式:
顶点为
unsigned short
格式,索引为unsigned int
格式。cmc
结构是带有double
变量的camera / cursor结构。PI
和C_SPEED
是#define
常数。附加信息:
网格是使用上述
ushort
数组创建的,间距为1。在着色器中,我用一个常数对其进行缩放,然后使用细分来实现最佳性能和最大查看距离。在镶嵌评估着色器中计算顶点的最终位置。
mat4 MVP = projection_matrix*view_matrix*model_matrix;
如您所见,我使用glm库将矩阵发送到着色器。
+问:
浮点数(或任何其他格式)的长度怎么会导致这种“精度损失”,或者是什么引起问题。
view_matrix
可能是造成这种情况的原因,但是我仍然无法在运行时在屏幕上输出它。PS :我不知道这是否有帮助,但是关于“延迟开始位置”的 View 矩阵是
-0.49662 -0.49662 0.863129 0
0.00514956 0.994097 0.108373 0
-0.867953 0.0582648 -0.493217 0
1.62681e+006 16383.3 -290126 1
编辑
比较相机位置和 View 矩阵:
view matrix = 0.967928 0.967928 0.248814 0
-0.00387854 0.988207 0.153079 0
-0.251198 -0.149134 0.956378 0
-2.88212e+006 89517.1 -694945 1
position = 2.9657e+006, 6741.52, -46002
最佳答案
这是一篇很长的文章,所以我可能不会回答所有问题。
我认为这很可能是精度问题。让我们从相机旋转问题开始。我认为主要问题在这里
view_matrix = glm::lookAt(
cameraPosition,
cameraPosition+directionVector,
upVector);
如您所说,位置是一个很大的数字,例如2.9657e + 006-并查看glm在glm::lookAt中的作用:
GLM_FUNC_QUALIFIER detail::tmat4x4<T> lookAt
(
detail::tvec3<T> const & eye,
detail::tvec3<T> const & center,
detail::tvec3<T> const & up
)
{
detail::tvec3<T> f = normalize(center - eye);
detail::tvec3<T> u = normalize(up);
detail::tvec3<T> s = normalize(cross(f, u));
u = cross(s, f);
在您的情况下,眼和中心是这些大(非常相似)的数字,然后glm将其减去以计算f。这是不好的,因为如果您减去两个几乎相等的浮点数,则最高有效位将设置为零,这将使您留下无关紧要的(最错误的)数字。然后将其用于进一步的计算,这只会强调错误。查看此link了解更多详细信息。
Z战是类似的问题。 Z缓冲区不是线性的,由于视角分割,它在相机附近具有最佳分辨率。 Z缓冲区范围是根据您的近和远剪切平面值设置的。您始终希望在远值和近值之间使用最小的比率(通常,远/近不应大于30000)。 openGL wiki对此有很好的解释,建议您阅读:)
回到相机问题-首先,我会考虑您是否真的需要这么大的场景。我不这么认为,但是如果是这样,您可以尝试以不同的方式计算 View 矩阵,分别计算旋转和平移,这可能会对您的情况有所帮助。我通常使用相机的方式:
glm::vec3 cameraPos;
glm::vec3 cameraRot;
glm::vec3 cameraPosLag;
glm::vec3 cameraRotLag;
int ox, oy;
const float inertia = 0.08f; //mouse inertia
const float rotateSpeed = 0.2f; //mouse rotate speed (sensitivity)
const float walkSpeed = 0.25f; //walking speed (wasd)
void updateCameraViewMatrix() {
//camera inertia
cameraPosLag += (cameraPos - cameraPosLag) * inertia;
cameraRotLag += (cameraRot - cameraRotLag) * inertia;
// view transform
g_CameraViewMatrix = glm::rotate(glm::mat4(1.0f), cameraRotLag[0], glm::vec3(1.0, 0.0, 0.0));
g_CameraViewMatrix = glm::rotate(g_CameraViewMatrix, cameraRotLag[1], glm::vec3(0.0, 1.0, 0.0));
g_CameraViewMatrix = glm::translate(g_CameraViewMatrix, cameraPosLag);
}
void mousePositionChanged(int x, int y) {
float dx, dy;
dx = (float) (x - ox);
dy = (float) (y - oy);
ox = x;
oy = y;
if (mouseRotationEnabled) {
cameraRot[0] += dy * rotateSpeed;
cameraRot[1] += dx * rotateSpeed;
}
}
void keyboardAction(int key, int action) {
switch (key) {
case 'S':// backwards
cameraPos[0] -= g_CameraViewMatrix[0][2] * walkSpeed;
cameraPos[1] -= g_CameraViewMatrix[1][2] * walkSpeed;
cameraPos[2] -= g_CameraViewMatrix[2][2] * walkSpeed;
break;
...
}
}
这样,该位置不会影响您的旋转。我应该补充一点,就是我从NVIDIA CUDA示例v5.0(烟雾粒子)改编了这段代码,我真的很喜欢:)
希望其中至少有帮助。