HLSL height map to normal algorithm

Started by
4 comments, last by xytor 13 years, 2 months ago
There is something slightly wrong with my algorithm that converts a grayscale height map into a normal, per pixel. I wanted to stay away from going into tangent space, and go directly into world space (which is what the pixel shader in HLSL is in).
Anyway, given a normal, the height map is sampled in 5 places, the current pixel and the 4-connected pixels surrounding it. These values form an "offset vector" that is transformed into world space, added to the normal, and the normal is normalized and used for lighting calculations.


float me = tex2D(heightMapSampler,IN.tex).x;
float n = tex2D(heightMapSampler,float2(IN.tex.x,IN.tex.y+1.0/heightMapSizeY)).x;
float s = tex2D(heightMapSampler,float2(IN.tex.x,IN.tex.y-1.0/heightMapSizeY)).x;
float e = tex2D(heightMapSampler,float2(IN.tex.x+1.0/heightMapSizeX,IN.tex.y)).x;
float w = tex2D(heightMapSampler,float2(IN.tex.x-1.0/heightMapSizeX,IN.tex.y)).x;
float3 normalOffset = bumpHeightScale*float3((n-me)-(s-me),(e-me)-(w-me),0);
mul(normalOffset,World);
norm += normalOffset;
norm = normalize(norm);


This creates nice looking bump-mapped surfaces, but there is something slightly off about the lighting. Any ideas?
Advertisement
You can try this out to create a normal map on the cpu and skip the extra work on the gpu

D3DX10ComputeNormalMap()
Wisdom is knowing when to shut up, so try it.
--Game Development http://nolimitsdesigns.com: Reliable UDP library, Threading library, Math Library, UI Library. Take a look, its all free.

You can try this out to create a normal map on the cpu and skip the extra work on the gpu

D3DX10ComputeNormalMap()


No thanks, I can't use DX10. Plus, I have already done what that function does, but ran into problems since it was in tangent space.
The algorithm is very close, It's just very slightly wrong. Does anybody know why (or even understand it at all)?


float t = tex2D(heightMapSampler,float2(IN.tex.x,IN.tex.y+1.0/heightMapSizeY)).x;
float b = tex2D(heightMapSampler,float2(IN.tex.x,IN.tex.y-1.0/heightMapSizeY)).x;
float l = tex2D(heightMapSampler,float2(IN.tex.x+1.0/heightMapSizeX,IN.tex.y)).x;
float r = tex2D(heightMapSampler,float2(IN.tex.x-1.0/heightMapSizeX,IN.tex.y)).x;
vec3 tanZ(0.0f, (t-B), 1.0f);
vec3 tanX(1.0f, (r-l), 0.0f);
vec3 N;
N = Cross(tanZ, tanX);
N.normalize();


This is the code that I use
Wisdom is knowing when to shut up, so try it.
--Game Development http://nolimitsdesigns.com: Reliable UDP library, Threading library, Math Library, UI Library. Take a look, its all free.


float t = tex2D(heightMapSampler,float2(IN.tex.x,IN.tex.y+1.0/heightMapSizeY)).x;
float b = tex2D(heightMapSampler,float2(IN.tex.x,IN.tex.y-1.0/heightMapSizeY)).x;
float l = tex2D(heightMapSampler,float2(IN.tex.x+1.0/heightMapSizeX,IN.tex.y)).x;
float r = tex2D(heightMapSampler,float2(IN.tex.x-1.0/heightMapSizeX,IN.tex.y)).x;
vec3 tanZ(0.0f, (t-B), 1.0f);
vec3 tanX(1.0f, (r-l), 0.0f);
vec3 N;
N = Cross(tanZ, tanX);
N.normalize();


This is the code that I use


For what? Can you please explain what you're doing?
I solved the problem with this algorithm. It basically forms a basis with one axis being the normal, and the other axes are arbitrary, but perpendicular to it. Then, depending on the change in the 4-connected neighborhood of the height map, the normal is deformed in its own space.



float me = tex2D(heightMapSampler,IN.tex).x;
float n = tex2D(heightMapSampler,float2(IN.tex.x,IN.tex.y+1.0/heightMapSizeY)).x;
float s = tex2D(heightMapSampler,float2(IN.tex.x,IN.tex.y-1.0/heightMapSizeY)).x;
float e = tex2D(heightMapSampler,float2(IN.tex.x+1.0/heightMapSizeX,IN.tex.y)).x;
float w = tex2D(heightMapSampler,float2(IN.tex.x-1.0/heightMapSizeX,IN.tex.y)).x;

//find perpendicular vector to norm:
float3 temp = norm; //a temporary vector that is not parallel to norm
if(norm.x==1)
temp.y+=0.5;
else
temp.x+=0.5;

//form a basis with norm being one of the axes:
float3 perp1 = normalize(cross(norm,temp));
float3 perp2 = normalize(cross(norm,perp1));

//use the basis to move the normal in its own space by the offset
float3 normalOffset = -bumpHeightScale*(((n-me)-(s-me))*perp1 + ((e-me)-(w-me))*perp2);
norm += normalOffset;
norm = normalize(norm);

This topic is closed to new replies.

Advertisement