球体的光线追踪纹理实现

9

我正在尝试在我的光线追踪器中为球体实现纹理。 我设法让一些东西运作,但我不确定它是否正确。 下面是获取纹理坐标的代码。 目前,纹理是随机生成的并在运行时生成。

virtual void GetTextureCoord(Vect hitPoint, int hres, int vres, int& x, int& y) {
    float theta = acos(hitPoint.getVectY());
    float phi   = atan2(hitPoint.getVectX(), hitPoint.getVectZ());

    if (phi < 0.0) {
        phi += TWO_PI;
    }

    float u = phi * INV_TWO_PI;
    float v = 1 - theta * INV_PI;

    y = (int) ((hres - 1) * u);
    x = (int) ((vres - 1) * v);
}

这就是现在的球形外观: enter image description here 我必须对击中点的坐标进行归一化处理,才能使球看起来像那样。否则,它们会看起来像这样: enter image description here 将击中点平移到世界原点(就好像球心在那里)而不是进行归一化处理,得到了以下结果: enter image description here 顺便说一下,我正在使用256x256分辨率的纹理。

“hitPoint” 是否总是计算为球体在原点(然后转换到其他位置),还是您直接支持非原点居中的球体进行交集测试?使用余弦和正切计算 phi 和 theta 可以得到与以原点为中心的球体相对应的值,因此,如果球体实际上不是这样,则纹理坐标映射可能不是您预期的。 - Wyzard
顺便提一下,uv是纹理坐标,计算纹理坐标的函数通常只返回它们。将它们映射到整数纹素索引并不特定于球体,并且应该在其他地方完成。 - Wyzard
我曾经在实时阴影算法中动态生成纹理时遇到过类似的问题。我的错误在于计算纹理坐标使用了世界空间,而实际上应该考虑使用本地空间。hitPoint是在世界/视图空间还是本地空间? - codingadventures
我认为我在直接支持交集测试中的非原点中心球体。有没有什么解决方法? - ionutt93
击中点位于世界空间。 - ionutt93
我认为我找到了解决办法,我将球体的击中点移动到球体在世界原点的位置。我会发布新图片。 - ionutt93
1个回答

4
“normalizing”这个术语在你发布的代码中没有被规范化,所以不清楚你的意思。但你提到你的击中点在世界空间中。
此外,你没有说明你要实现什么纹理映射,但我猜想你希望你的U和V纹理坐标表示球面表面上的纬度和经度。
你的第一个问题是,将笛卡尔坐标转换为球面坐标需要球体在笛卡尔空间中以原点为中心,但在世界空间中这并不正确。如果击中点在世界空间中,你必须减去球体的世界空间中心点来获得本地坐标系中的有效击中点。(你已经解决了这一部分,并更新了问题与新图像。)
你的第二个问题是,计算theta的方式要求球体半径为1,即使将球体中心移动到原点后仍然不正确。记住三角学:acos的参数是三角形边与其斜边的比值,并且始终在范围(-1,+1)内。在这种情况下,你的Y坐标是边,球体半径是斜边。因此,在调用acos时必须除以球体半径。如果浮点舍入误差使值略微超出范围,则还应将该值夹紧在(-1,+1)范围内。
(原则上,您还必须将X和Z坐标除以半径,但您仅使用它们进行反正切运算,并将它们都除以半径不会改变它们的商,因此不会改变phi。)

现在,您的球体交点和纹理坐标函数是在世界空间中运行的,但您可能会发现稍后实现变换矩阵很有用,这样可以将物体从一个坐标空间转换到另一个坐标空间。然后,您可以将球体函数更改为在本地坐标空间中运行,其中心是原点,半径为1,并给每个对象关联一个变换矩阵,将本地坐标空间映射到世界坐标空间。这将简化您的光线/球体交点代码,并让您从 GetTextureCoord 中删除原点减法和半径除法(因为它们始终分别为(0,0,0)和1)。

要使射线与对象相交,您需要使用对象的变换矩阵将射线转换为对象的本地坐标空间,在那里进行相交(并计算纹理坐标),然后将结果(例如击中点和表面法线)转换回世界空间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接