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

Fox*_*942 5 c++ opengl raytracing glsl glm-math

我有一个 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);
Run Code Online (Sandbox Code Playgroud)

我只想围绕 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);
}
Run Code Online (Sandbox Code Playgroud)

它正在工作,但不幸的是,该比率存在一些失真。更重要的是,有时摄像机只是围绕一个点循环。这是与该问题相关的视频:链接。您可以看到水平旋转效果很好。

这是我的顶点着色器:

#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);
}
Run Code Online (Sandbox Code Playgroud)

这是完整源代码的链接: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;

}
Run Code Online (Sandbox Code Playgroud)

处理相机绕 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();
}
Run Code Online (Sandbox Code Playgroud)

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

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

Rab*_*d76 2

您必须尊重视口的纵横比。

计算射线方向的点时canvasX会考虑纵横比。canvasY

p = pixel = canvasX * normQuadCoord.x + canvasY * normQuadCoord.y + viewPoint;
Run Code Online (Sandbox Code Playgroud)

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

 width     | canvasY |
-------- = -----------
 height    | canvasX |
Run Code Online (Sandbox Code Playgroud)

因此, 的长度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;
Run Code Online (Sandbox Code Playgroud)