Pez
Pez

Reputation: 162

Pixel bender normal map

as usual thanks in advance..

I'm using pixel bender to generate lighting effects for a heightmap based terrain. I'd like to make a normal map of the terrain and from there dot each normal against a given sun direction. Pretty standard stuff really, but pixel bender isn't playing nicely. I have this code:

void
evaluatePixel()
{
    float2 pt = outCoord();

    pixel3 a = sampleNearest(src,pt);
    pixel3 b = sampleNearest(src,float2(pt.x + 1.0, pt.y));
    pixel3 c = sampleNearest(src,float2(pt.x, pt.y + 1.0));

    float3 v1 = float3(1.0, 0.0, b.r - a.r);
    float3 v2 = float3(0.0, 1.0, c.r - a.r);

    normalize(v1);
    normalize(v2);

    float3 n = cross(v1,v2);

    dst = pixel3(n.x,n.y,n.z);
}

I would expect this to produce a normal map. To test, I assumed that the light was pointing straight down and just use n.z as the output colour. This produces a solid colour. If you take the above code and run it, you'll see that whilst there is variation in red and green, blue is always full at 255. Why is this? I'd expect that considering v1 and v2 are normalized that this shouldnt always output full blue?

What am I doing wrong?!?

Upvotes: 0

Views: 290

Answers (1)

jordancpaul
jordancpaul

Reputation: 2964

I actually did something very similar here, with my triangle-strip heightmaps.

input image3 verts;
output pixel3 triNorms;

void
evaluatePixel()
{
    float2 tCoord = outCoord() - 0.5; //move coord from pixel center to lower-left corner
    float quadIdx = mod(tCoord[0], 2.0); //potentially faster to use sign(fract(tCoord[0] / 2.0))
    float2 vCoord0 = float2(floor(tCoord.x / 2.0), tCoord.y);
    float2 vCoord1 = float2(vCoord0.x + 1.0, vCoord0.y + 1.0 - quadIdx);
    float2 vCoord2 = float2(vCoord0.x + quadIdx, vCoord0.y + 1.0);
    float3 v0 = sampleNearest(verts, vCoord0 + 0.5);
    float3 v1 = sampleNearest(verts, vCoord1 + 0.5);
    float3 v2 = sampleNearest(verts, vCoord2 + 0.5);
    float3 s0 = v1 - v0;
    float3 s1 = v2 - v0;
    float3 n = cross(s0, s1);
    float len = 1.0 / length(n);
    n.x *= len;
    n.y *= len;
    n.z *= len;
    triNorms = float3(n.x, n.y, n.z);
}

This works amazingly well for me. Runs through 1.8M vertices in less than 100ms. Just to clarify, I define my triangles as the columns between vertices. This means there are two triangles for every 4 vertices. I identify the two triangles per quad with "quadIdx".

Note: You are normalizing and then calculating the cross - this is backwards, you need to normalize after. Also, I actually believe I found a bug in normalize(). It was creating NaN's where I did not expect. I implemented my own normalization with length() and it all works great!

Upvotes: 0

Related Questions