使用旋转组件在 3D space 中投射光线

Casting Rays in 3D space with rotation components

我目前正在研究基于 CPU 的简单光线追踪器,以将几个三角形渲染为一个项目。除了生成实际光线外,我对它的每个方面都满意。我不希望将世界坐标投影到屏幕 space,但实际上是根据相机在 3D 坐标中的位置创建光线。

现在,我有一个相当不错的算法,它允许我为屏幕上的每个像素生成一条射线,以围绕 Y 轴进行任何旋转,并且它也尝试合并 X 轴,从而提供一些向上和向下看的能力,但是当用户向上或向下看时,图像会变形。

这是我到目前为止的计算结果:

Ray ray = new Ray(Camera.position, 
                new Vector3(
                        Math.sin(Camera.rotation.y+(x*2/Main.renderSize.width)/2) * Math.cos(Camera.rotation.x+(y*2/Main.renderSize.height)/2),
                        Math.sin(Camera.rotation.x+(-y*2/Main.renderSize.height)/2),
                        Math.cos(Camera.rotation.y+(x*2/Main.renderSize.width)/2) * Math.cos(Camera.rotation.x-(y*2/Main.renderSize.height)/2)
                        ));

当相机不面向任何向上或向下的方向时,这为我提供了良好的观看投影。

相机向前时的投影图像:

.

相机稍微向上看时的投影图像

.

如果有人能帮助我解决算法问题或指出新算法或好的来源,我将不胜感激。速度是必需的,因为它是实时的。谢谢。

除了光线方向错误之外,可能还会发生更多事情。因此,这里要更清楚地说明我的评论的更详细含义:

所以假设所有的东西都在相机 space 坐标系中。所以屏幕在平面 z=0 上,中间像素是 (0,0,0)。焦点位于 focus=(0,0,-f),其中 f 是投影的焦距。现在你需要为每个像素投射光线(或更多),所以它的位置和方向是:

pos = (x,y,0);
dir = pos-focus = (x,y,f);
// most likely you should also normalize it so
dir = dir / |dir|;

当您的 x,y 坐标不是世界单位而是像素时,您应该相应地重新缩放它们。所以让屏幕分辨率 xs,ys 并且我们希望在 xFOVx = 60.0deg 中有视野,那么你需要稍微改变一下:

// pixel size
sz = f*tan(0.5*FOVx)/(0.5*xs);
// x,y screen position [pixels] -> xx,yy [world units]
xx=(x-(0.5*xs))*sz;
yy=(y-(0.5*ys))*sz;
// ray
pos = (xx,yy,0);
dir = pos-focus = (xx,yy,f);
dir = dir / |dir|;

现在进来。让 M 成为代表您屏幕的变换矩阵。原点设置为屏幕中间,x,y 轴向量对应于屏幕轴。您还需要 M0 矩阵,它是 M 的副本,但原点设置为 (0,0,0)

因为你想在世界全局坐标系中制作所有东西 GCS 然后从屏幕 space 转换到世界做:

world_position  = M *screen_position;
world_direction = M0*screen_direction;

但是如果在上面 link 中使用不同的矩阵元素和操作数顺序约定,这可能会略有不同。

所以现在世界 GCS 中的光线将是:

// pixel size
sz = f*tan(0.5*FOVx)/(0.5*xs);
// x,y screen position [pixels] -> xx,yy [world units]
xx=(x-(0.5*xs))*sz;
yy=(y-(0.5*ys))*sz;
// ray
pos = (xx,yy,0);
dir = pos-focus = (xx,yy,f);
// convert to world GCS
pos = M *pos;
dir = M0*dir;
// normalize
dir = dir / |dir|;

通常你可以从 M 作为单位矩阵开始,然后乘以击键的旋转和平移来处理视图移动和旋转。在每次渲染之前,您可以计算 M0=M,然后将保持原点的 3 个元素设置为零。此外,如果您的引擎使用 4D 向量(使用 x,y,z,w),那么您也可以使用 M 而不是 M0,您只需将转换向量的 w 设置为 w=0用于方向和 w=1 用于位置。