如何解决相机沿 X 轴旋转的问题?

5
我有一个OpenGL光线追踪器,可以加载OBJ文件并以光线追踪的方式进行可视化。我的应用使用Assimp加载OBJ文件,然后通过使用着色器存储缓冲区将所有三角形面(包括顶点及其索引)发送到片段着色器中。然后,片段着色器通过跟踪光线路径来计算像素的颜色。光线从相机位置(posCamera)发出并穿过虚拟画布的像素。基本结构是将结果呈现到这个四边形形状的画布上。
目前,我正在处理相机问题。相机始终朝向0,0,0点(viewPoint),并可以通过以下函数围绕y轴旋转:
void setCamera(float param) {
    connect = posCamera - viewPoint;

    posCamera = glm::vec3(connect.x * cos(param) + connect.z * sin(param), connect.y,
                          -connect.x * sin(param) + connect.z * cos(param)) + viewPoint;

    canvasX = cross(upVector, connect) * getLength(connect) * tanf(fieldOfview / 2);
    canvasY = cross(connect, canvasX) * getLength(connect) * tanf(fieldOfview / 2);

    setCam(posCamera,viewPoint, canvasY, fieldOfview);

我只想将场景绕x轴旋转。为了解决这个问题,我创建了一个新的函数:

void rotateAroundX(float param) {
    connect = posCamera - viewPoint;
    posCamera = glm::vec3(posCamera.x,
                          connect.y * cos(param) + connect.z * sin(param),
                          -connect.y * sin(param) + connect.z * cos(param))+viewPoint;
    canvasX = cross(upVector, connect) * getLength(connect) * tanf(fieldOfview / 2);
    canvasY = cross(connect, canvasX) * getLength(connect) * tanf(fieldOfview / 2);

    setCam(posCamera,viewPoint, canvasY, fieldOfview);
}

它可以工作,但不幸的是与比例有关的一些失真问题。更糟糕的是,有时相机只会在某个点周围旋转。这是一个涉及到该问题的视频:链接。您可以看到水平旋转效果良好。

以下是我的顶点着色器:

#version 460 core
layout(location = 0) in vec2 normQuadCoord;

uniform vec3 viewPoint;
uniform vec3 canvasX, canvasY;
out vec3 pixel;

void main()
{
    pixel = canvasX * normQuadCoord.x + canvasY * normQuadCoord.y + viewPoint;
    gl_Position = vec4(normQuadCoord, 0, 1);
}

以下是完整源代码的链接:source

除了相机旋转之外,我不知道是否可能使用这种“渲染到四边形”的概念来实现自由飞行的相机。非常感谢您的任何帮助!


更新1

我尝试了Rabbid76的建议并修改了源代码。我还进行了一些重构。下面是更新矩形画布尺寸的方法:

void Init::updateCanvasSizes() {
    connect = camera.getPosCamera() - camera.getViewPoint();

    float aspect = (float)SCR_W_H.first/(float)SCR_W_H.second;
    float length = tanf(camera.getFieldOfview() / 2);

    canvasX = glm::normalize(glm::cross(camera.upVector, connect)) / length / aspect;
    canvasY = glm::normalize(glm::cross(connect, canvasX)) / length;

}

该方法,处理相机绕X轴的旋转已被修改为:

void Init::rotateCamAroundX(float param) {
    camera.posCamera = glm::vec3(camera.posCamera.x,
               (camera.posCamera.y - camera.viewPoint.y) * cos(param) + (camera.posCamera.z - camera.viewPoint.z) * sin(param) + camera.viewPoint.y,
               -(camera.posCamera.y - camera.viewPoint.y) * sin(param) + (camera.posCamera.z - camera.viewPoint.z) * cos(param) + camera.viewPoint.z);

    updateCanvasSizes();
}

视野已从 45 度修改为 45 * (float)M_PI / 180 ,因此模型的比例变得更好了一些。不幸的是,在相机绕 X 轴旋转时,仍然可能出现场景拉伸和扭曲。当相机达到一定角度时,它还会回头看 camera.viewPoint。我不知道如何“关闭”它。

结果可以在这个视频中看到:链接。首先可以看到绕 Y 轴的旋转,然后是绕 X 轴的旋转。


在我开始构建程序的示例中,并没有提到比率,但是创建者提到光轴必须垂直于窗口,以避免比率问题。 - Fox1942
我已经尝试过了。正如您在我的问题中所看到的,我基于水平旋转的逻辑修改了垂直旋转方法。但不幸的是,它给我带来了视频中的结果。 - Fox1942
我将尝试规范化。 - Fox1942
我在你的问题中看不到任何“方面”。请查看答案。 - Rabbid76
1个回答

2

您需要尊重视口的纵横比。

在计算光线方向的点时,canvasXcanvasY会考虑到纵横比。

p = pixel = canvasX * normQuadCoord.x + canvasY * normQuadCoord.y + viewPoint;

如果视口是矩形的,则向量的大小必须不同。canvasX是从中心点到视口右侧的向量,canvasY是从中心点到视口顶部的向量。为了补偿视图的矩形性并实现正确的投影,向量长度的比率必须等于倒数的宽高比:

 width     | canvasY |
-------- = -----------
 height    | canvasX |

因此,canvasX的长度必须按照宽高比例的倒数进行缩放。宽高比是视图的宽度和高度之商((float)SCR_WIDTH/(float)SCR_HEIGHT):
float aspect = (float)SCR_WIDTH/(float)SCR_HEIGHT;
float length = tanf(fieldOfview / 2);

canvasX = glm::normalize(glm::cross(upVector, connect)) / length / aspect;
canvasY = glm::normalize(glm::cross(connect, canvasX)) / length;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接