球体的光线追踪纹理实现

Ray tracing texture implementation for spheres

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

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);
}

这是球体现在的样子:

我必须对命中点的坐标进行归一化以使球体看起来像那样。否则它们看起来像:

正常化命中点坐标是正确的方法,还是我的代码中有其他问题?谢谢!

我尝试将其转换为世界原点(就像球体中心在那里一样),而不是对生命值进行归一化,并获得了以下结果:

顺便说一句,我使用的是 256x256 分辨率的纹理。

不清楚你所说的 "normalizing" 生命值是什么意思,因为在你发布的代码中没有任何东西可以规范化它,但你提到你的生命值在世界 space.

此外,您没有说明您要实现的纹理映射,但我假设您希望 U 和 V 纹理坐标代表球体表面的纬度和经度。

您的第一个问题是,将笛卡尔坐标转换为球坐标需要球体在笛卡尔 space 中以原点为中心,这在世界 space 中并非如此。如果击中点在世界 space 中,则必须减去球体的世界-space 中心点以获得局部坐标中的 有效 击中点。 (您已经想出了这部分并用新图片更新了问题。)

您的第二个问题是您计算 theta 的方式要求球体的半径为 1,即使您将球体的中心移动到原点后,这也不正确。记住你的三角函数:acos 的参数是三角形边与其斜边的 比率 ,并且始终在 (-1, +1) 范围内。在这种情况下,您的 Y 坐标是边,球体的半径是斜边。所以调用acos时必须除以球体的半径。将值限制在 (-1, +1) 范围内也是一个好主意,以防浮点舍入误差将其稍微超出范围。

(原则上你还必须将 X 和 Z 坐标除以半径,但你只是将它们用于反正切,并且将它们都除以半径不会改变它们的商和因此不会改变 phi.)


现在您的球体交集和纹理坐标函数正在世界 space 中运行,但您稍后可能会发现实现 transformation matrices 很有用,它可以让您从一个坐标转换事物space 给另一个。然后你可以改变你的球体函数在局部坐标 space 中运行,其中中心是原点,半径是 1,并给每个对象一个关联的变换矩阵,将局部坐标 space 映射到世界坐标 space。这将简化您的 ray/sphere 交集代码,并让您从 GetTextureCoord 中删除原点减法和半径除法(因为它们总是分别为 (0, 0, 0) 和 1)。

要使光线与物体相交,您需要使用物体的变换矩阵将光线变换为物体的局部坐标 space,在那里进行相交(并计算纹理坐标),然后变换结果(例如命中点和表面法线)返回世界 space.