Phong Tessellation无法按形状因子使物体变形的问题求助
Phong曲面细分实现问题排查
我参照《PhongTessellation.pdf》文档实现了Phong曲面细分代码,但对Blender导出的二十面体设置形状因子为1时,物体未变形为球面,仍保持平面化结构。以下是顶点、外壳、域着色器的相关代码,恳请排查原因。
顶点着色器代码
VertexShaderOutput output; output.position = float4(input.position, 1.0f); output.wPos = mul(output.position, world); output.position = mul(output.position, mul(mul(world, view), projection)); output.normal = mul(float4(input.normal, 0.0f), world); return output;
外壳着色器代码
float CalcTessFactor(float3 v0WPos, float3 v1WPos) { float edgeLen = distance(v0WPos, v1WPos); float edgeCamDist = distance((v0WPos+v1WPos)/2, camPos.xyz); return clamp((edgeLen*576) / (edgeCamDist*50), 1.0f, 63.0f); } HullShaderConstDataOutput CalcHSPatchConstants(InputPatch<HullShaderInput, NUM_CONTROL_POINTS> ip) { HullShaderConstDataOutput output; if (ShouldPatchBeClipped(ip[0].position, ip[1].position, ip[2].position)) { output.EdgeTessFactor[0] = output.EdgeTessFactor[1] = output.EdgeTessFactor[2] = output.InsideTessFactor = 0; } else { [unroll] for (int i=0; i<NUM_CONTROL_POINTS; i++) {output.EdgeTessFactor[i] = CalcTessFactor(ip[(i+1)%3].wPos.xyz, ip[(i+2)%3].wPos.xyz);} output.InsideTessFactor = (output.EdgeTessFactor[0] + output.EdgeTessFactor[1] + output.EdgeTessFactor[2]) / 3; } return output; } [domain("tri")] [partitioning("fractional_odd")] [outputtopology("triangle_cw")] [outputcontrolpoints(3)] [patchconstantfunc("CalcHSPatchConstants")] HullShaderOutput main(InputPatch<HullShaderInput, NUM_CONTROL_POINTS> ip, uint i : SV_OutputControlPointID) { return ip[i]; }
域着色器代码
#define NUM_CONTROL_POINTS 3 #define BARYCENTRIC_INTERPOLATE(structVar) u*patch[0].structVar + v*patch[1].structVar + w*patch[2].structVar float3 OrtoProject(float3 q, float3 p, float3 n) { return q - dot((q - p), n) * n; } [domain("tri")] DomainShaderOutput main ( DomainShaderConstDataInput input, float3 barycentric : SV_DomainLocation, const OutputPatch<DomainShaderInput, NUM_CONTROL_POINTS> patch ) { DomainShaderOutput output; float u = barycentric.x, v = barycentric.y, w = barycentric.z; float3 Puv = BARYCENTRIC_INTERPOLATE(wPos.xyz); float shapeFactor = 1; output.wPos = float4((1 - shapeFactor)*Puv + shapeFactor * float3 ( u*OrtoProject(Puv, patch[0].wPos.xyz, patch[0].normal) + v*OrtoProject(Puv, patch[1].wPos.xyz, patch[1].normal) + w*OrtoProject(Puv, patch[2].wPos.xyz, patch[2].normal) ), 1.0f); output.position = mul(mul(output.wPos, view), projection); output.normal = BARYCENTRIC_INTERPOLATE(normal); }
问题排查分析
1. 矩阵乘法顺序错误(顶点着色器)
HLSL默认使用列向量,矩阵乘法顺序应为「矩阵 × 向量」,但你写反了:
// 错误写法:向量 × 矩阵 output.wPos = mul(output.position, world); // 正确写法:矩阵 × 向量 output.wPos = mul(world, float4(input.position, 1.0f));
裁剪空间位置计算同样存在顺序错误,正确写法:
float4x4 worldViewProj = mul(mul(world, view), projection); output.position = mul(worldViewProj, float4(input.position, 1.0f));
矩阵顺序错误会导致顶点世界坐标完全失真,后续Phong偏移计算自然无法生成球面。
2. Phong曲面细分公式实现错误(域着色器)
根据《PhongTessellation.pdf》,正确的Phong偏移是将细分点沿顶点法线方向拉伸,而非投影到切平面。你的OrtoProject函数逻辑完全相反,应替换为:
// 正确的Phong偏移:将Puv沿顶点法线方向移动到对应偏移平面 float3 PhongOffset(float3 Puv, float3 V, float3 N) { return V + dot(Puv - V, N) * N; }
域着色器中修改位置计算:
output.wPos = float4((1 - shapeFactor)*Puv + shapeFactor * ( u*PhongOffset(Puv, patch[0].wPos.xyz, patch[0].normal) + v*PhongOffset(Puv, patch[1].wPos.xyz, patch[1].normal) + w*PhongOffset(Puv, patch[2].wPos.xyz, patch[2].normal) ), 1.0f);
当shapeFactor=1时,该逻辑会将细分点拉向各顶点法线的交集,形成平滑球面。
3. 法线插值优化(可选)
启用Phong偏移后,直接使用重心插值法线会导致光照不准确,建议根据偏移后的位置重新计算法线,或使用基于顶点法线的加权插值。
内容的提问来源于stack exchange,提问作者Atryx10
相关产品推荐
相关产品推荐

