我目前正在研究基于CPU的简单光线跟踪器,以将几个三角形渲染为项目。除了生成实际光线外,我对它的每个方面都没关系。我不希望将世界坐标投影到屏幕空间,但实际上根据摄像机在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)
));
当相机未朝向任何向上或向下方向时,这为我提供了良好的观看投影。
相机前进时的投影图像:
相机略微向上看时的投影图像
如果有人可以帮助我使用算法或指向一个新的或一个好的来源,我将不胜感激。速度是必要的,因为它是全部实时的。谢谢。
答案 0 :(得分:0)
可能会有更多的事情发生,然后只是错误的光线方向。所以在这里要更清楚的是我的评论更详细的含义:
因此,假设所有东西都在相机空间坐标系中。因此屏幕在平面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
,我们希望在x
轴FOVx = 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|;
现在进入transformation matrix。设M
为表示屏幕的变换矩阵。原点设置为屏幕中间,x,y
轴向量对应于屏幕轴。您还需要M0
矩阵,该矩阵是M
的副本,但原点设置为(0,0,0)
如果您想在世界全球坐标系 GCS 中制作所有内容,那么从屏幕空间转换为世界:
world_position = M *screen_position;
world_direction = M0*screen_direction;
但如果在上面的链接中使用了不同的矩阵元素和操作数顺序约定,这可能会略有不同。
所以现在世界 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个元素设置为零。