Sign in to follow this  
DrGUI

Bump mapping in skinned shaders

Recommended Posts

Hi I was trying to implement the unified bump mapping described in the paper below, whereby the normal map normal is multiplied by the inverse of the tangent space matrix, then by the world matrix. The trouble is, in skinned shaders the world matrix is not constant; I'm not convinced that reskinning every single pixel to get its world matrix is a good solution. http://www.gamasutra.com/features/20050729/lacroix_pfv.htm Really what I want to end up with is the world space normal so I can do lighting and reflection. Thank you!

Share this post


Link to post
Share on other sites
btw I found this way of offsetting the normal along the tangent and binormal, but I don't think that is proper bump mapping.
...but if it looks good...I give it a go...

Share this post


Link to post
Share on other sites
Hey

I think this (NVidia's BumpReflectHLSL.fx in their SDK) is what I'm looking for:

///////////////// vertex shader //////////////////

v2f BumpReflectVS(a2v IN,
uniform float4x4 WorldViewProj,
uniform float4x4 World,
uniform float4x4 ViewIT)
{
v2f OUT;
// Position in screen space.
OUT.Position = mul(IN.Position, WorldViewProj);
// pass texture coordinates for fetching the normal map
OUT.TexCoord.xyz = IN.TexCoord;
OUT.TexCoord.w = 1.0;
// compute the 4x4 tranform from tangent space to object space
float3x3 TangentToObjSpace;
// first rows are the tangent and binormal scaled by the bump scale
TangentToObjSpace[0] = float3(IN.Tangent.x, IN.Binormal.x, IN.Normal.x);
TangentToObjSpace[1] = float3(IN.Tangent.y, IN.Binormal.y, IN.Normal.y);
TangentToObjSpace[2] = float3(IN.Tangent.z, IN.Binormal.z, IN.Normal.z);
OUT.TexCoord1.x = dot(World[0].xyz, TangentToObjSpace[0]);
OUT.TexCoord1.y = dot(World[1].xyz, TangentToObjSpace[0]);
OUT.TexCoord1.z = dot(World[2].xyz, TangentToObjSpace[0]);
OUT.TexCoord2.x = dot(World[0].xyz, TangentToObjSpace[1]);
OUT.TexCoord2.y = dot(World[1].xyz, TangentToObjSpace[1]);
OUT.TexCoord2.z = dot(World[2].xyz, TangentToObjSpace[1]);
OUT.TexCoord3.x = dot(World[0].xyz, TangentToObjSpace[2]);
OUT.TexCoord3.y = dot(World[1].xyz, TangentToObjSpace[2]);
OUT.TexCoord3.z = dot(World[2].xyz, TangentToObjSpace[2]);
float4 worldPos = mul(IN.Position, World);
// compute the eye vector (going from shaded point to eye) in cube space
float4 eyeVector = worldPos - ViewIT[3]; // view inv. transpose contains eye position in world space in last row.
OUT.TexCoord1.w = eyeVector.x;
OUT.TexCoord2.w = eyeVector.y;
OUT.TexCoord3.w = eyeVector.z;
return OUT;
}

///////////////// pixel shader //////////////////

float4 BumpReflectPS(v2f IN,
uniform sampler2D NormalMap,
uniform samplerCUBE EnvironmentMap,
uniform float BumpScale) : COLOR
{
// fetch the bump normal from the normal map
float3 normal = tex2D(NormalMap, IN.TexCoord.xy).xyz * 2.0 - 1.0;
normal = normalize(float3(normal.x * BumpScale, normal.y * BumpScale, normal.z));
// transform the bump normal into cube space
// then use the transformed normal and eye vector to compute a reflection vector
// used to fetch the cube map
// (we multiply by 2 only to increase brightness)
float3 eyevec = float3(IN.TexCoord1.w, IN.TexCoord2.w, IN.TexCoord3.w);
float3 worldNorm;
worldNorm.x = dot(IN.TexCoord1.xyz,normal);
worldNorm.y = dot(IN.TexCoord2.xyz,normal);
worldNorm.z = dot(IN.TexCoord3.xyz,normal);
float3 lookup = reflect(eyevec, worldNorm);
return texCUBE(EnvironmentMap, lookup);
}

Share this post


Link to post
Share on other sites
I hardcoded the normal from the bump map to be (0,1,0) and rendered the normal as color, expecting to get my original normal. However, I didn't :(

Can some genius work out what's going wrong please; here's my shader code (simplified):

Pixel_VSOutput TnLPixelVS(const in a2v IN
PARAM_NUMBERBONES)
{
Pixel_VSOutput Out;

//Skin inputs
float4x3 worldMatrix;
SKIN_MATRIX(worldMatrix)


// compute the tranform from tangent space to object space
float3x3 TangentToObjSpace;
float3 binormal = cross(IN.Tan, IN.Nor);
TangentToObjSpace[0] = float3(IN.Tan.x, binormal.x, IN.Nor.x);
TangentToObjSpace[1] = float3(IN.Tan.y, binormal.y, IN.Nor.y);
TangentToObjSpace[2] = float3(IN.Tan.z, binormal.z, IN.Nor.z);
Out.TexCoord1.x = dot(worldMatrix[0].xyz, TangentToObjSpace[0]);
Out.TexCoord1.y = dot(worldMatrix[1].xyz, TangentToObjSpace[0]);
Out.TexCoord1.z = dot(worldMatrix[2].xyz, TangentToObjSpace[0]);
Out.TexCoord2.x = dot(worldMatrix[0].xyz, TangentToObjSpace[1]);
Out.TexCoord2.y = dot(worldMatrix[1].xyz, TangentToObjSpace[1]);
Out.TexCoord2.z = dot(worldMatrix[2].xyz, TangentToObjSpace[1]);
Out.TexCoord3.x = dot(worldMatrix[0].xyz, TangentToObjSpace[2]);
Out.TexCoord3.y = dot(worldMatrix[1].xyz, TangentToObjSpace[2]);
Out.TexCoord3.z = dot(worldMatrix[2].xyz, TangentToObjSpace[2]);

float3 worldPos = mul(float4(IN.Pos,1), worldMatrix);
// compute the eye vector (going from shaded point to eye) in world space
float3 eyeVector = Eye - worldPos;
Out.TexCoord1.w = eyeVector.x;
Out.TexCoord2.w = eyeVector.y;
Out.TexCoord3.w = eyeVector.z;


Out.Pos = mul(float4(worldPos, 1), ViewProjection);
Out.Tex0 = IN.Tex0;

return Out;
}

float4 TnLPixelPS(const in float2 tex0 : TEXCOORD0,
const in float4 tex1 : TEXCOORD1,
const in float4 tex2 : TEXCOORD2,
const in float4 tex3 : TEXCOORD3,
uniform bool includeEnvironmentHigh) : COLOR
{

//Get the bumped normal
//float3 bumpN = tex2D(BumpSampler, tex0).xyz * 2.0 - 1.0;
float3 bumpN = float3(0,1,0);
//bumpN = normalize(float3(bumpN.x * BumpScale, bumpN.y * BumpScale, bumpN.z));
//transform the bump normal into world space
float3 worldNorm;
worldNorm.x = dot(tex1.xyz, bumpN);
worldNorm.y = dot(tex2.xyz, bumpN);
worldNorm.z = dot(tex3.xyz, bumpN);
//get the packed VtoE vector out - snip, that's fine
worldNorm = normalize(worldNorm);

return float4(worldNorm, 1);
}




Thank you ever so much!

[Edited by - DrGUI on April 16, 2006 11:25:43 AM]

Share this post


Link to post
Share on other sites
This works:

//Uses interpolated vertexToEye vector as this does not change much over vertices
float4 TnLPixelPS(const in float2 tex0 : TEXCOORD0,
const in float3 nor : TEXCOORD1,
const in float3 tan : TEXCOORD2,
const in float3 vertexToEye : TEXCOORD3,
uniform bool includeEnvironmentHigh) : COLOR
{

//Normalize interpolated
float3 N = normalize(nor);
float3 T = normalize(tan);
float3 VtoE = normalize(vertexToEye);

//Get the bumped normal
float3 bumpN = tex2D(BumpSampler, tex0).xyz * 2.0 - 1.0;
bumpN = float3(bumpN.x * BumpScale, bumpN.y * BumpScale, bumpN.z);
//Construct binormal
float3 B = cross(T, N);

//transform the bump normal into world space
float3 worldNorm = normalize(T * bumpN.x + B * bumpN.y + N * bumpN.z);

float3 reflection = reflect(-VtoE, worldNorm);
float4 envReflection = GetEnvironmentReflection(reflection, includeEnvironmentHigh);

//Calculate the lighting
float4 diffuse;
float4 specular;
ComputeColorDS(worldNorm, VtoE, diffuse, specular);

//Apply the diffuse map
float4 color = lerp(float4(SampleBaseTexture(tex0).rgb, 1) * diffuse + specular, envReflection, Reflectance);
color.a = Visibility;
return color;
}



but it does use more instructions

Share this post


Link to post
Share on other sites
I constructed the matrix differently in the VS and yay it works!


Pixel_VSOutput TnLPixelVS(const in a2v IN
PARAM_NUMBERBONES)
{
Pixel_VSOutput Out;

//Skin inputs
float3 P;
float3 N;
float3 T;
SKIN_POINT_2VEC(float4(IN.Pos, 1), IN.Nor, IN.Tan, P, N, T)
N = normalize(N);
T = normalize(T);

float3 B = cross(T, N);
float3 VtoE = Eye - P; //normalize this in PS
Out.TexCoord1 = float4(T.x, B.x, N.x, VtoE.x);
Out.TexCoord2 = float4(T.y, B.y, N.y, VtoE.y);
Out.TexCoord3 = float4(T.z, B.z, N.z, VtoE.z);

Out.Pos = mul(float4(P, 1), ViewProjection);
Out.Tex0 = IN.Tex0;

return Out;
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this