Normal map from height map

Started by
6 comments, last by StarMire 9 years, 7 months ago
I am trying to create a normal map from a height map in HLSL. I followed this http://stackoverflow.com/a/5284527/451136 which is for GLSL. Here is how I translated GLSL to HLSL:
GLSL:

    uniform sampler2D unit_wave
    noperspective in vec2 tex_coord;
    const vec2 size = vec2(2.0,0.0);
    const ivec3 off = ivec3(-1,0,1);
    
        vec4 wave = texture(unit_wave, tex_coord);
        float s11 = wave.x;
        float s01 = textureOffset(unit_wave, tex_coord, off.xy).x;
        float s21 = textureOffset(unit_wave, tex_coord, off.zy).x;
        float s10 = textureOffset(unit_wave, tex_coord, off.yx).x;
        float s12 = textureOffset(unit_wave, tex_coord, off.yz).x;
        vec3 va = normalize(vec3(size.xy,s21-s01));
        vec3 vb = normalize(vec3(size.yx,s12-s10));
        vec4 bump = vec4( cross(va,vb), s11 );
HLSL:

    sampler2D image : register(s0);
    
    noperspective float2 TEXCOORD;
    static const float2 size = (2.0,0.0);
    static const int3 off = (-1,0,1);
    
    float4 main(float2 uv : TEXCOORD) : COLOR 
    { 
     
        float4 color = tex2D(image, uv);
        float s11 = color.x;
        float s01 = tex2D(image, uv + off.xy).x;
        float s21 = tex2D(image, uv + off.zy).x;
        float s10 = tex2D(image, uv + off.yx).x;
        float s12 = tex2D(image, uv + off.yz).x;
        float3 va = normalize((size.xy,s21-s01));
        float3 vb = normalize((size.yx,s12-s10));
        float4 bump = (cross(va,vb), s11);
     
     return bump; 
    }
The output is a black and white image, with the darker pixels being more transparent (since the alpha is the height).
How can I generate a normal map like this http://www.filterforge.com/filters/3051-normal.jpg from a height map?
Advertisement

[EDIT: Never mind what I posted earlier. Your code already computes what I suggested. What happens if you set the alpha component to "solid" instead of the height? Does that give you the result you want?]

If I set the alpha component to 1, I get a blank white image.

Maybe these

tex2D(image, uv + off.xy).x
should be more along the lines of

tex2D(image, uv + off.xy * float2(1.0/textureWidth, 1.0/textureHeight)).x
at least if you are using normalized texture coordinates.

Also you need to output bump.xyz * 0.5 + 0.5 to get the colors of that image.

Thanks. Here is the working HLSL code:


float Width : register(C0);

float Height : register(C1);

sampler2D image : register(s0);

noperspective float2 TEXCOORD;
static const float2 size = {2.0,0.0};
static const float3 off = {-1.0,0.0,1.0};
static const float2 nTex = {Width, Height};
float4 main(float2 uv : TEXCOORD) : COLOR 
{ 

    float4 color = tex2D(image, uv.xy);

    float2 offxy = {off.x/nTex.x , off.y/nTex.y};
    float2 offzy = {off.z/nTex.x , off.y/nTex.y};
    float2 offyx = {off.y/nTex.x , off.x/nTex.y};
    float2 offyz = {off.y/nTex.x , off.z/nTex.y};

    float s11 = color.x;
    float s01 = tex2D(image, uv.xy + offxy).x;
    float s21 = tex2D(image, uv.xy + offzy).x;
    float s10 = tex2D(image, uv.xy + offyx).x;
    float s12 = tex2D(image, uv.xy + offyz).x;
    float3 va = {size.x, size.y, s21-s01};
    float3 vb = {size.y, size.x, s12-s10};
    va = normalize(va);
    vb = normalize(vb);
    float4 bump = {(cross(va,vb)) / 2 + 0.5, 1.0};

    return bump; 
}

Where should I increase the intensity? On the heightmap, or after generating the normal like (cross(va,vb)) * n + 0.5, and just playing with n. In general if I want to add more effects like sharpen, noise, large/medium/small detail, should I do it before generating the normal map or after.

Edit:

Okay I was doing some testing and applying the normal map effect last seems to work nicely. The brightness/contrast, sharper effects are being applied on the height map.

Just sampling the neighbour pixels wasn't giving me the results I desired. I ended up using the Sobel operator method instead. This still wasn't giving me enough control though. I had to do three passes on the heightmap, for controlling the large, medium and small details which I combined at the end. I am pretty happy with the results.

4yI2kRh.jpg

Just sampling the neighbour pixels wasn't giving me the results I desired. I ended up using the Sobel operator method instead. This still wasn't giving me enough control though. I had to do three passes on the heightmap, for controlling the large, medium and small details which I combined at the end. I am pretty happy with the results.

That looks a lot like a lighting map / photo, rather than an actual heightmap.

If you view it using a colour gradient so that the height features are easier to see, there's lots of suspicious features, like the highest point on every "hill" being on it's left side, the centre of the buttons being as high as the leather bumps that surround them, the indentations from the stitching along the bottom being as deep as the crevices around the buttons, the bottom flat section of the image actually being extremely sloped, etc...

hrXnOQP.jpg

That looks a lot like a lighting map / photo, rather than an actual heightmap.

Yep. It's definitely either a grayscale photo, or a diffuse map with shadows and AO baked in. It has a light direction, and even some sheen and value coloration on the material.

It might be close enough to do the job in a pinch, but it's very difficult to get passable results from a photo. You might be able to remove some of the noise and tweak it to provide something that works... CDeniz, where did you get that from? How has it turned out in game?

This topic is closed to new replies.

Advertisement