1. ShaderX5 2.6
Normal Mapping without
Precomputed Tangents
Christian Schuler, Phenomic
ShaderStudy 2013.03.18 황규석
http://cafe.naver.com/shader
2. Introduction
TBN Matrix(Tangent Frames)를 Pixel Shader에서 실시간으로
계산하는 방법
장점
- Vertex attributes에 Tangent Frames를 넣어 줄 필요가 없다
• Band width, Memory, Complexity 감소
• 그래픽 디자이너가 툴에서 이것을 신경 쓸 필요가 없다
단점
- Pixel Shader에서의 부하, Optimization 필수
Cotangent Frames
- More optimization
5. Tangents to a surface
Position 벡터(p1, p2)와 Texture 좌표 벡터(u1, u2, v1, v2)를 이용해 Tangent
벡터(T, B) 계산
6. // A code snippet to perform normal perturbation using a tangent-space normal map
float3 perturb_normal(float3 T, float3 B, float3 N, float2 texcoord)
{
// Build a tangent frame as float3x3 for convenience
float3x3 tangent_frame = float3x3(T, B, N);
// Read the perturbed normal from the normal map
float3 perturbed_normal = tex2D(normalmap, texcoord);
// Sign-expand (for a normal map in unsigned RGB format)
perturbed_normal = 2 * perturbed_normal – 1;
// And transform the perturbed normal out of tangent space
// (into whatever space T, B and N were originally expressed in, usually world).
return normalize( mul( perturbed_normal, tangent_frame));
}
Tangent space Normal mapping
7. // A function that takes an interpolated surface normal N, a position vector p,
// and texture coordinates u and v and returns a complete tangent frame that is
// ready to be used in lighting calculations (46 pixel shader instructions)
float3x3 compute_tangent_frame(float3 N, float3 p, float2 uv)
{
// Get edge vectors of the pixel triangle
float3 dp1 = ddx(p); // 가로 방향의 옆 픽셀과의 Position 변화량
float3 dp2 = ddy(p); // 세로 방향의 옆 픽셀과의 Position 변화량
float2 duv1 = ddx(uv); // 가로 방향의 옆 픽셀과의 UV값 변화량
float2 duv2 = ddy(uv); // 세로 방향의 옆 픽셀과의 UV값 변화량
// Solve the linear system
float3x3 M = float3x3(dp1, dp2, cross(dp1, dp2));
float3x3 inverseM = invert_3x3(M);
float3 T = mul(inverseM, float3(duv1.x, duv2.x, 0));
float3 B = mul(inverseM, float3(duv1.y, duv2.y, 0));
// Construct tangent frame
// (* see discussion regarding the square patch assumption)
float maxLength = max(length(T), length(B));
return float3x3 (T / maxLength, B / maxLength, N);
}
float3x3 invert_3x3(float3x3 M)
{
float det = dot( cross(M[0], M[1]), M[2] );
float3x3 T = transpose(M);
return float3x3( cross(T[1], T[2]), cross(T[2], T[0]), cross(T[0], T[1]) ) / det;
}
Moving to the Pixel Sahder
8.
9. // Optimization 1:
// normalize T and B and spare the determinant.
// (31 pixel shader instructions)
float3x3 compute_tangent_frame_01(float3 N, float3 p, float2 uv)
{
// Get edge vectors of the pixel triangle
float3 dp1 = ddx(p);
float3 dp2 = ddy(p);
float2 duv1 = ddx(uv);
float2 duv2 = ddy(uv);
// Solve the linear system
float3x3 M = float3x3(dp1, dp2, cross(dp1, dp2));
float3x3 inverseM = invert_3x3_nodet(M);
float3 T = mul(inverseM, float3(duv1.x, duv2.x, 0));
float3 B = mul(inverseM, float3(duv1.y, duv2.y, 0));
// Construct tangent frame
return float3x3(normalize(T), normalize(B), N);
}
float3x3 invert_3x3_nodet(float3x3 M)
{
float3x3 T = transpose(M);
return float3x3( cross(T[1], T[2]), cross(T[2], T[0]), cross(T[0], T[1]) );
}
Optimization 1
10. // Optimization 2:
// exploits the zero components found in the solution vectors and collapses
// a hidden double transpose
// (17 pixel shader instructions)
float3x3 compute_tangent_frame_02(float3 N, float3 p, float2 uv)
{
// Get edge vectors of the pixel triangle
float3 dp1 = ddx(p);
float3 dp2 = ddy(p);
float2 duv1 = ddx(uv);
float2 duv2 = ddy(uv);
// Solve the linear system
float3x3 M = float3x3(dp1, dp2, cross(dp1, dp2));
float3x3 inversetransposeM = float2x3( cross(M[1], M[2]), cross(M[2], M[0]) ); //inversetranspose = original
float3 T = mul(float2(duv1.x, duv2.x), inversetransposeM);
float3 B = mul(float2(duv1.y, duv2.y), inversetransposeM);
// Construct tangent frame
return float3x3(normalize(T), normalize(B), N);
}
Optimization 2
11. // Optimization 3:
// assume M is orthogonal and exploits the fact that we have made an inverse transpose explicit.
// (14 pixel shader instructions)
float3x3 compute_tangent_frame_03(float3 N, float3 p, float2 uv)
{
// Get edge vectors of the pixel triangle
float3 dp1 = ddx(p);
float3 dp2 = ddy(p);
float2 duv1 = ddx(uv);
float2 duv2 = ddy(uv);
// Solve the linear system
// (not much solving is left going here)
float2x3 M = float2x3(dp1, dp2);
float3 T = mul(float2(duv1.x, duv2.x), M);
float3 B = mul(float2(duv1.y, duv2.y), M);
// Construct tangent frame
return float3x3(normalize(T), normalize(B), N);
}
Optimization 3
12.
13. Conclusion
TBN matrix 계산을 Pixel shader에서 낮은 비용으로 하는 방법 제
공함
Vertex 속성에서 Tangent frames를 제거할 수 있지만 픽셀셰이더
에서 부하가 증가
텍스처 좌표 공간에서의 procedural 처리도 가능한 방법
추가적으로 Cotangent frames를 이용한 방법을 블로그에 소개하
고 있음