光线追踪球体的纹理实现

时间:2015-04-17 12:32:37

标签: c++ raytracing

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

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分辨率纹理。

1 个答案:

答案 0 :(得分:4)

目前还不清楚你对命中点的“规范化”是什么意思,因为在你发布的代码中没有任何标准化它,但是你提到你的生命点在世界空间。

另外,你没有说明你想要实现的纹理贴图,但我想你希望你的U和V纹理坐标代表球体表面的纬度和经度。

你的第一个问题是将笛卡尔坐标转换为球坐标要求球体在笛卡尔空间中的原点居中,这在世界空间中是不正确的。如果生命点位于世界空间中,则必须减去球体的世界空间中心点,以获得本地坐标中的有效生命值。 (您已经计算出这部分并用新图像更新了问题。)

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

(原则上你还必须将X和Z坐标除以半径,但是你只使用反正切,并将它们除以半径不会改变它们的商,从而赢得了不要改变phi。)


现在你的球体交叉和纹理坐标函数在世界空间中运行,但是你可能会发现它稍后用于实现transformation matrices,它可以让你将事物从一个坐标空间转换到另一个坐标空间。然后,您可以更改球体函数以在中心为原点且半径为1的局部坐标空间中操作,并为每个对象提供关联的变换矩阵,该矩阵将局部坐标空间映射到世界坐标空间。这将简化光线/球体交叉代码,并允许您从GetTextureCoord中删除原点减法和半径除法(因为它们总是分别为(0,0,0)和1)。

要使光线与对象相交,您可以使用对象的变换矩阵将光线转换为对象的局部坐标空间,在那里进行交叉(并计算纹理坐标),然后转换结果(例如,生命点)和表面正常)回到世界空间。