首页 文章

歪斜:一个简单的基于CPU的体素raycaster / raytracer中的旋转相机

提问于
浏览
1

我正在尝试编写一个简单的体素raycaster作为学习练习 . 这是纯粹基于CPU的,直到我弄清楚事情是如何工作的 - 现在,OpenGL只是(ab)用来尽可能频繁地将生成的位图blit到屏幕上 .

现在我已经达到了透视投影相机可以穿越世界的程度,我可以渲染(主要是减去一些需要调查的文物)透视 - 正确的“世界”三维视图,这基本上是空的但是包含斯坦福兔子的体素立方体 .

所以我有一个相机,我可以上下移动,左右扫视和“向前/向后走” - 所有轴对齐到目前为止,没有相机旋转 . 这就是我的问题所在 .

截图:(1) raycasting voxels while... ...(2) the camera remains... ...(3) strictly axis-aligned.

现在我已经有几天试图让轮换工作了 . 从理论上讲,矩阵和3D旋转背后的基本逻辑和理论对我来说非常清楚 . 然而,当相机旋转时,我只能实现“2.5渲染”......鱼眼,有点像谷歌街景:即使我有体积世界表示,似乎 - 无论我尝试什么 - 就像我首先从“前视图”创建渲染,然后根据相机旋转旋转该平面渲染 . 毋庸置疑,我现在意识到旋转光线并不是特别必要且容易出错 .

尽管如此,在我最近的设置中,使用最简化的光线投射光线位置和方向算法,我的旋转仍然会产生相同的鱼眼平面渲染旋转样式:

camera "rotated to the right by 39 degrees" - 请注意屏幕#2中蓝色阴影的左侧如何在此旋转中不可见,但现在"it really should"!

当然我现在意识到这一点:在一个简单的轴对齐 - 无旋转设置中,就像我在开始时一样,光线只是以小步长正向z方向,向左或向右和顶部发散 . 或仅取决于像素位置和投影矩阵的底部 . 当我“向右或向左旋转相机” - 即我围绕Y轴旋转时 - 这些步骤应该通过适当的旋转矩阵进行简单转换,对吧?因此,对于前向遍历,Z步骤在凸轮旋转越多时越小,在X步骤中“增加” . 然而,对于基于像素位置的水平垂直发散,需要将x步的增加分数“加”到z步骤 . 不知怎的,我试验过的很多矩阵,以及我用无矩阵硬编码的详细sin / cos计算的实验都没有真正得到这个部分 .

这是我的基本per-ray预遍历算法 - Go中的语法,但将其视为伪代码:

  • fxfy :像素位置x和y

  • rayPos :vec3表示世界空间中的射线起始位置(如下所示)

  • rayDir :vec3用于在光线遍历期间在每个步骤中添加到rayPos的xyz步骤

  • rayStep :临时vec3

  • camPos :vec3用于世界空间中的摄像机位置

  • camRad :vec3用于以弧度为单位的相机旋转

  • pmat :典型的透视投影矩阵

算法/伪代码:

// 1: rayPos is for now "this pixel, as a vector on the view plane in 3d, at The Origin"
rayPos.X, rayPos.Y, rayPos.Z = ((fx / width) - 0.5), ((fy / height) - 0.5), 0

// 2: rotate around Y axis depending on cam rotation. No prob since view plane still at Origin 0,0,0
rayPos.MultMat(num.NewDmat4RotationY(camRad.Y))

// 3: a temp vec3. planeDist is -0.15 or some such -- fov-based dist of view plane from eye and also the non-normalized, "in axis-aligned world" traversal step size "forward into the screen"
rayStep.X, rayStep.Y, rayStep.Z = 0, 0, planeDist

// 4: rotate this too -- 0,zstep should become some meaningful xzstep,xzstep
rayStep.MultMat(num.NewDmat4RotationY(CamRad.Y))

// set up direction vector from still-origin-based-ray-position-off-rotated-view-plane plus rotated-zstep-vector
rayDir.X, rayDir.Y, rayDir.Z = -rayPos.X - me.rayStep.X, -rayPos.Y, rayPos.Z + rayStep.Z

// perspective projection
rayDir.Normalize()
rayDir.MultMat(pmat)

// before traversal, the ray starting position has to be transformed from origin-relative to campos-relative
rayPos.Add(camPos)

我正在跳过遍历和采样部分 - 根据屏幕#1到#3,那些“基本上大部分都是正确的”(虽然不是很漂亮) - 当轴对齐/不旋转时 .

1 回答

  • 4

    如果您将系统描绘为针孔相机而不是其他任何东西,那就容易多了 . 不是从表示图像的矩形表面拍摄光线,而是将光线从一个点,通过将成为图像平面的矩形射入场景 . 所有主要光线应该具有相同的原点,只有略微不同的方向 . 使用基本三角形确定方向,通过该三角形,您希望它们通过图像平面中的像素 . 为了得到最简单的例子,让我们想象你的点在摄像机上,你的图像平面是沿z轴的一个单位,两个单位高和宽 . 这样,左上角的像素想要从(0,0,0)到(-1,-1,1) . 标准化(-1,-1,1)以获得方向 . (您实际上不需要将方向标准化以进行光线交叉,但如果您决定不这样做,请记住在尝试计算光线行进的距离或类似之前,您的方向是非标准化的 . )每隔一个像素,计算它想要经过的平面上的点通过将平面的大小除以每个方向上的像素数,您已经在做的方式 .

    然后,这是最重要的事情, don't 尝试做一个透视投影 . 通过使所有光线的方向相同,并且起始位置在整个图像平面上变化,从而实现了这一点 .

    如果你这样做,你将有一个很好的起点 . 然后,您可以再次尝试添加相机旋转,方法是在迭代它以计算光线方向之前围绕原点旋转图像平面,或者直接旋转光线方向 . 直接旋转方向没有错!当你记住方向就是你的光线从原点开始经过的位置时,很容易看到旋转方向,并旋转它经过的点,做同样的事情 .

相关问题