因此,我试图使用u,v坐标在我的软件渲染器中正确映射纹理,但似乎无法正常工作。我得到仿射纹理贴图工作。这是我可以使用它产生的结果:



从纹理的“倾斜”中可以看出它不太正确。如果我向网格中添加更多三角形,它将变得更加正确。据我了解,这些不是正确的纹理。但是这种观点分歧使我感到困惑。我读到我需要将u,v坐标除以w分量(透视除法)。我猜想这是与投影矩阵相乘后顶点的w分量。但是我做不到。因此,我对问题进行了更多研究。首先,这是我的一个三角形的顶点所发生的情况。

float4 pt1 = worldViewProj * mesh->vertices[mesh->indices[i]];
float2 uv1 = mesh->texCoords[mesh->indices[i]];
triangle.p1 = float3((pt1.x / pt1.w + 0.5f) * screen->GetScreenWidth(), (pt1.y / pt1.w + 0.5f) * screen->GetScreenHeight(), pt1.w);


我将其与wVP相乘,然后用x和y进行透视除法。 + 0.5f将其在均匀空间中居中,然后乘以屏幕尺寸以使其在屏幕空间中。然后,我将顶点的w分量填充为z值,以便将其发送到三角形绘图函数,并可以用于深度缓冲区之类。

我从与投影矩阵相乘后,顶点被放入w中。这是有道理的,因为在我的投影矩阵中m32 = 1。因此,在与投影矩阵相乘后w = z。我首先尝试将每个顶点的纹理坐标除以w。像这样:

 float2 uv1 = mesh->texCoords[mesh->indices[i]] / w;


然后在三角形绘图功能中,我使用重心中心坐标在这些uv坐标上进行插值。这没有用。由于w = z。当纹理远离相机时(即z是一个很大的值。应用我的世界平移z =〜700时),纹理坐标会缩小很多,并且在每个渲染中,我只获得纹理的(0,0)值。

但是我还是在三角形绘制函数中尝试了以下方法:

float2 texCoord = (texCoord1 * u + texCoord2 * v + texCoord3 * w) / z;


(这里u,v和w是重心坐标。每个texCoord是纹理坐标,位于该顶点)的区别在于,插值的z值在每个像素处被分割,而不是在顶点处被分割。无论哪种方式,我似乎都无法正常工作。同样,z值太大,最终我得到(0,0)。

所以我正在研究除以u和v的方法。显然我做错了。与投影矩阵相乘后,顶点的w分量真的吗?我的w值错了吗?投影矩阵会导致这种情况吗?我已经搜索了一段时间... x / w和y / w是[-1,1](同构),但是z不是[-1,1]。应该是吗?我对x和y的透视除法显然很有效。

这是我如何构建投影矩阵以防万一呢?:

float tanHalfFov = tanf(fov * 0.5f * 3.14f / 180.0f);
float s = 1.0f / (tanHalfFov * aspectRatio);
float zRange = farZ - nearZ;

// Set the projection matrix
proj.m00 = s;    proj.m01 = 0.0f;            proj.m02 = 0.0f;          proj.m03 = 0.0f;

proj.m10 = 0.0f; proj.m11 = aspectRatio * s; proj.m12 = 0.0f;          proj.m13 = 0.0f;

proj.m20 = 0.0f; proj.m21 = 0.0f;            proj.m22 = farZ / zRange; proj.m23 = farZ * nearZ / zRange;

proj.m30 = 0.0f; proj.m31 = 0.0f;            proj.m32 = 1.0f;          proj.m33 = 0.0f;


评论

投影矩阵将点转换为剪辑空间,该剪辑空间是一个以原点为中心的2x2x2立方体,而不是您所谓的“屏幕空间”。齐次坐标意味着除了空间坐标(x,y,可能还有z)之外,还有w。它与规范化无关。您永远不会除以Z-总是除以W。W的要点是您可以透视除法并保持深度(再次在剪辑空间中),并且很好地让您将平移添加到矩阵中,而不必执行单独的步骤。您正在混淆很多不同的概念。

#1 楼

您处在正确的轨道上,但您需要做的是计算u / w和v / w,以及​​每个顶点的1 / w,您可以在光栅化器的屏幕空间中线性插值。然后,对于每个像素,将插值的u / w和v / w坐标除以插值的1 / w,以获得该像素的透视正确的uv坐标。

所有顶点属性同样如此,例如如果您需要获得透视正确的颜色或每个像素的顶点位置,则可以对c / w和p / w进行插值,然后将这些值除以每个像素1 / w。即使您没有实现软件光栅化程序,这也可能非常有用,例如在屏幕空间反射实现的光线行进中。我见过有人在世界空间中为SSR进行射线行进,因为他们不知道如何正确地插值屏幕空间中的位置。

以前使用的旧软件光栅化器执行的操作是1 /( 1 / w)仅对每个X像素进行除法,并在其间进行线性插值,因为除法是执行每个像素的相对昂贵的操作。

评论


$ \ begingroup $
1 /(1 / w)是错别字还是我误解了?
$ \ endgroup $
– trichoplax
16-10-11在9:03

$ \ begingroup $
不是错字。您在屏幕上线性内插1 / w,并计算每个像素1 /(1 / w)。然后将线性插值的u / w和v / w与每个像素的1 /(1 / w)值相乘。只是将div转换为mul的简单优化,以避免每个像素进行多次除法。
$ \ endgroup $
–JarkkoL
16-10-11在12:41