C++ 为什么我的光线跟踪器有这么多的边缘失真?

C++ 为什么我的光线跟踪器有这么多的边缘失真?,c++,raytracing,C++,Raytracing,我正在从头开始写一个光线跟踪器。示例是使用光线球体相交检测渲染两个球体。当球体接近屏幕中心时,它们看起来很好。但是,当我移动相机时,或者如果我调整球体的位置使其更靠近边缘,它们就会扭曲 这是光线投射代码: void Renderer::RenderThread(int start, int span) { // pCamera holds the position, rotation, and fov of the camera // pRenderTarget is th

我正在从头开始写一个光线跟踪器。示例是使用光线球体相交检测渲染两个球体。当球体接近屏幕中心时,它们看起来很好。但是,当我移动相机时,或者如果我调整球体的位置使其更靠近边缘,它们就会扭曲

这是光线投射代码:

void Renderer::RenderThread(int start, int span)
{

    // pCamera holds the position, rotation, and fov of the camera
    // pRenderTarget is the screen to render to

    // calculate the camera space to world space matrix
    Mat4 camSpaceMatrix = Mat4::Get3DTranslation(pCamera->position.x, pCamera->position.y, pCamera->position.z) *
        Mat4::GetRotation(pCamera->rotation.x, pCamera->rotation.y, pCamera->rotation.z);

    // use the cameras origin as the rays origin
    Vec3 origin(0, 0, 0);
    origin = (camSpaceMatrix * origin.Vec4()).Vec3();

    // this for loop loops over all the pixels on the screen
    for ( int p = start; p < start + span; ++p ) {

        // get the pixel coordinates on the screen
        int px = p % pRenderTarget->GetWidth();
        int py = p / pRenderTarget->GetWidth();

        // in ray tracing, ndc space is from [0, 1]
        Vec2 ndc((px + 0.75f) / pRenderTarget->GetWidth(), (py + 0.75f) / pRenderTarget->GetHeight());

        // in ray tracing, screen space is [-1, 1]
        Vec2 screen(2 * ndc.x - 1, 1 - 2 * ndc.y);

        // scale x by aspect ratio
        screen.x *= (float)pRenderTarget->GetWidth() / pRenderTarget->GetHeight();

        // scale screen by the field of view
        // fov is currently set to 90
        screen *= tan((pCamera->fov / 2) * (PI / 180));

        // screen point is the pixels point in camera space,
        // give a z value of -1
        Vec3 camSpace(screen.x, screen.y, -1);
        camSpace = (camSpaceMatrix * camSpace.Vec4()).Vec3();

        // the rays direction is its point on the cameras viewing plane
        // minus the cameras origin
        Vec3 dir = (camSpace - origin).Normalized();

        Ray ray = { origin, dir };

        // find where the ray intersects with the spheres
        // using ray-sphere intersection algorithm
        Vec4 color = TraceRay(ray);
        pRenderTarget->PutPixel(px, py, color);
    }

}
void呈现器::RenderThread(int start,int span)
{
//pCamera控制摄像机的位置、旋转和视野
//pRenderTarget是要渲染到的屏幕
//计算相机空间到世界空间矩阵
Mat4 camSpaceMatrix=Mat4::Get3DTranslation(pCamera->position.x,pCamera->position.y,pCamera->position.z)*
Mat4::GetRotation(pCamera->rotation.x,pCamera->rotation.y,pCamera->rotation.z);
//使用摄影机原点作为光线原点
Vec3原点(0,0,0);
原点=(camSpaceMatrix*origin.Vec4()).Vec3();
//这个for循环在屏幕上的所有像素上循环
对于(int p=start;pGetWidth();
int py=p/pRenderTarget->GetWidth();
//在光线跟踪中,ndc空间来自[0,1]
Vec2 ndc((px+0.75f)/pRenderTarget->GetWidth(),(py+0.75f)/pRenderTarget->GetHeight());
//在光线跟踪中,屏幕空间为[-1,1]
Vec2屏幕(2*ndc.x-1,1-2*ndc.y);
//按纵横比缩放x
screen.x*=(float)pRenderTarget->GetWidth()/pRenderTarget->GetHeight();
//按视野缩放屏幕
//fov当前设置为90
屏幕*=tan((pCamera->fov/2)*(PI/180));
//屏幕点是相机空间中的像素点,
//给z值-1
Vec3凸轮空间(屏幕x,屏幕y,-1);
camSpace=(camSpace矩阵*camSpace.Vec4()).Vec3();
//光线方向是其在摄影机观察平面上的点
//减去摄像机原点
Vec3 dir=(camSpace-origin).Normalized();
射线={origin,dir};
//找到光线与球体相交的位置
//利用光线球相交算法
Vec4颜色=TraceRay(光线);
pRenderTarget->PutPixel(像素x、像素y、颜色);
}
}

视场设置为90。我见过其他人有这个问题,但这是因为他们使用了一个非常高的视野值。我不认为90有什么问题。即使相机没有移动,此问题仍然存在。任何靠近屏幕边缘的对象都会出现扭曲。

如果有疑问,您可以随时查看其他渲染器正在执行的操作。我总是将我的结果和设置与Blender进行比较。例如,Blender 2.82的默认视野为39.6度

我也倾向于指出这是错误的:

vec2ndc((px+0.75f)/pRenderTarget->GetWidth(),(py+0.75f)/pRenderTarget->GetHeight());
如果要获得像素的中心,则它应为
0.5f

vec2ndc((px+0.5f)/pRenderTarget->GetWidth(),(py+0.5f)/pRenderTarget->GetHeight());
而且,这确实是一种吹毛求疵的事情,你的间隔是开放的间隔,而不是封闭的间隔(正如你在源评论中提到的)。图像平面坐标永远不会达到0或1,你的相机空间坐标永远不会完全是-1或1。最后,将图像平面坐标转换为像素坐标,其为左闭合间隔[0,宽度)和[0,高度)


祝你的光线跟踪器好运!

如果有疑问,你可以随时查看其他渲染器正在做什么。我总是将我的结果和设置与Blender进行比较。例如,Blender 2.82的默认视场为39.6度

我也倾向于指出这是错误的:

vec2ndc((px+0.75f)/pRenderTarget->GetWidth(),(py+0.75f)/pRenderTarget->GetHeight());
如果要获得像素的中心,则它应为
0.5f

vec2ndc((px+0.5f)/pRenderTarget->GetWidth(),(py+0.5f)/pRenderTarget->GetHeight());
而且,这确实是一种吹毛求疵的事情,您的时间间隔是开放的时间间隔,而不是封闭的时间间隔(正如您在源代码注释中提到的)图像平面坐标永远不会达到0或1,您的相机空间坐标永远不会完全为-1或1。最终,图像平面坐标将转换为像素坐标,其左闭合间隔为[0,宽度)和[0,高度)


祝你的光线跟踪器好运!

你确定这不仅仅是正常的透视扭曲吗?如果没有完整的场景供参考,正常的透视变换可能看起来很奇怪。将3D投影到2D总是会有一些扭曲。至于扭曲程度取决于视场。值越高,边缘的扭曲程度越大。如果只是或不是错误的透视失真,为什么它在90视场下显示得如此高?在我最近制作的光栅渲染器中,90看起来很棒。问题确实在40视场左右开始消失,但我不想使用如此低的视场。90视场实际上相当高。就像我说的,如果你添加风景或运动作为参考,它将dn看起来不那么明显。我不确定为什么光栅渲染器看起来更好。我怀疑这只是一种认知偏见。换句话说,你可能只是记住它比以前更好,你可能只是以前没有注意到它,或者上下文使它变得不那么明显。根据我的经验,光线跟踪变差时,它要么变得非常糟糕,要么变得不明显糟糕。编辑:你们能把光栅版本拿来比较吗?好的。我认为90是一种标准。你们确定这不仅仅是正常的透视扭曲吗?若并没有完整的场景作为参考,正常的透视变换可能看起来很奇怪。将3D投影到2D总是会有一些距离