Skip to main content
gamedev.net
🔒 Locked

grid normal interpolation errors

Started by Thergothon Sep 12, 2006 at 12:16 PM 1 replies 1k views
Original Post
Thergothon
Thergothon
I've got a nice water sim going, but I am having rendering problems. screenshot I'm using a normal map that is updated every frame, which has eliminated triangular interpolatoin problems by moving them to the graphics card and four point bilinear filtering. Things look marginally better now, but as you can see, there is now square shaped artifacts that look especially bad around the specular highlights but can be seen throughout the surface. The normals are generated using a sobel height discontinuity filter and then scaled to be in the range 0..1 and then stored in an 8bit/channel texture and then expanded back to -1..1 in the pixel shader. I'm thinking that the bit depth and/or conversion may be causing precision errors, but I'm not certain. I can't really think of any other reasons why the problem is occuring, as I'm certain the texture coordinates are being generated properly. So yeah, any ideas would be great. Thanks.
Thergothon
Thergothon
Alright, I've been playing around with this some more and the problem still persists. I thought it might be because the linear filter was oversampling the normals so that it was getting normal information from points that were further away than needed. So I turned filtering off and implemented my own in the pixel shader. It looks marginally better, but not by much.

I'll post the shader just in case there is something obviously wrong. The uv coordinates that are passed in are actually pixel indexes rather than 0..1 floats so that I could implement my own filter.

float4x4 world;float4x4 viewProj;float4x4 worldViewProj;float3 lightDir;float3 eyePos;float4 ambient = {0.0, 0.13, 0.26, 1.0};float4 diffuse = {0.0, 0.3, 0.7, 1.0};float4 specular = {1.0, 1.0, 1.0, 1.0};float4 constant; // [xTexelOffset, yTexelOffset, xTexelSize, yTexelSize]texture normalMap;sampler normalSampler = sampler_state {    Texture = <normalMap>;    MinFilter = None;    MagFilter = None;};struct vsIn {    float3 pos: POSITION;    float2 uv: TEXCOORD0;};struct vsOut {    float4 final: POSITION;    float3 pos: TEXCOORD0;    float2 uv: TEXCOORD1;};void VS(in vsIn input, out vsOut output) {    output.final = mul(float4(input.pos, 1.0), world);    output.pos = output.final.xyz;    output.final = mul(output.final, viewProj);    output.uv = input.uv;}struct psIn {    float3 pos: TEXCOORD0;    float2 uv: TEXCOORD1;};float4 PS(in psIn input) : COLOR {    float2 intPart = floor(input.uv);    float2 remainPart = input.uv - intPart;    //read in four sample points    float3 ul = tex2D(normalSampler, float2(constant[0] + intPart.x*constant[2],                                            constant[1] + intPart.y*constant[3]));    float3 ur = tex2D(normalSampler, float2(constant[0] + (intPart.x + 1.0)*constant[2],                                            constant[1] + intPart.y*constant[3]));    float3 ll = tex2D(normalSampler, float2(constant[0] + intPart.x*constant[2],                                            constant[1] + (intPart.y + 1.0)*constant[3]));    float3 lr = tex2D(normalSampler, float2(constant[0] + (intPart.x + 1.0)*constant[2],                                            constant[1] + (intPart.y + 1.0)*constant[3]));    //remap samples from 0..1 -> -1..1    ul = normalize((ul - 0.5)*2.0);    ur = normalize((ur - 0.5)*2.0);    ll = normalize((ll - 0.5)*2.0);    lr = normalize((lr - 0.5)*2.0);        //bilinear interpolation to get normal at this point    float3 i0 = lerp(ul, ur, remainPart.x);    float3 i1 = lerp(ll, lr, remainPart.x);    float3 norm = normalize(lerp(i0, i1, remainPart.y));    //get diffuse component    float d = saturate(dot(norm, -lightDir));    //get view vector    float3 v = normalize(eyePos - input.pos);    //get half vector    float3 h = normalize(-lightDir + v);    //get specular component    float s = saturate(dot(norm, h));    s = pow(s, 25);    //return colour    return ambient + diffuse*d + specular*s;}struct avsIn {    float3 pos: POSITION;};struct avsOut {    float4 pos: POSITION;};    void AmbientVS(in avsIn input, out avsOut output) {    output.pos = mul(float4(input.pos, 1.0), worldViewProj);}float4 AmbientPS() : COLOR {    return float4(1.0, 0.0, 0.0, 1.0);}technique Shader {    pass P0 {        VertexShader = compile vs_1_1 VS();        PixelShader = compile ps_2_0 PS();    }    pass P1 {        VertexShader = compile vs_1_1 AmbientVS();        PixelShader = compile ps_1_1 AmbientPS();    }}


Anyway, any ideas would be excellent.

Thanks.
Thergothon
Thergothon
Change interpolation from bilinear to bicubic.
screenshot

It produces much better results, but it still could be improved and I'm out of ideas. If anybody can suggest any possible improvements I'd love to know.

Here is the improved shader for anyone who is interested. Unfotunately, moving from bilnear to bicubic pushed the instruction count well over the limit of ps_2_0 hardware.

float4x4 world;float4x4 viewProj;float4x4 worldViewProj;float3 lightDir;float3 eyePos;float4 ambient = {0.0, 0.13, 0.26, 1.0};float4 diffuse = {0.0, 0.3, 0.7, 1.0};float4 specular = {1.0, 1.0, 1.0, 1.0};float4 constant; // [xTexelOffset, yTexelOffset, xTexelSize, yTexelSize]texture normalMap;sampler normalSampler = sampler_state {    Texture = <normalMap>;    MinFilter = None;    MagFilter = None;};struct vsIn {    float3 pos: POSITION;    float2 uv: TEXCOORD0;};struct vsOut {    float4 final: POSITION;    float3 pos: TEXCOORD0;    float2 uv: TEXCOORD1;};void VS(in vsIn input, out vsOut output) {    output.final = mul(float4(input.pos, 1.0), world);    output.pos = output.final.xyz;    output.final = mul(output.final, viewProj);    output.uv = input.uv;}struct psIn {    float3 pos: TEXCOORD0;    float2 uv: TEXCOORD1;};float3 CubicInterp(float3 p0, float3 p1, float3 p2, float3 p3, float t) {    float t2 = t*t;    float3 a0 = p3 - p2 - p0 + p1;    float3 a1 = p0 - p1 - a0;    float3 a2 = p2 - p0;        return(a0*t*t2 + a1*t2 + a2*t + p1);}float4 PS(in psIn input) : COLOR {    //calculate texture index and interpolation amount     float2 intPart = floor(input.uv);    float2 remainPart = input.uv - intPart;    //read 16 surrounding 16 texture coords for interpolation    float3 tex[16];    int index = 0;    for(int j = -1; j < 3; j++) {        for(int i = -1; i < 3; i++) {            tex[index] = tex2D(normalSampler, float2(constant[0] + (intPart.x + i)*constant[2],                                                     constant[1] + (intPart.y + j)*constant[3]));            index++;        }    }    //do a cubic interpolation over texture reads to get normal    float3 i0 = CubicInterp(tex[0], tex[1], tex[2], tex[3], remainPart.x);    float3 i1 = CubicInterp(tex[4], tex[5], tex[6], tex[7], remainPart.x);    float3 i2 = CubicInterp(tex[8], tex[9], tex[10], tex[11], remainPart.x);    float3 i3 = CubicInterp(tex[12], tex[13], tex[14], tex[15], remainPart.x);    float3 norm = CubicInterp(i0, i1, i2, i3, remainPart.y);     //move norm values from 0..1 -> -1..1    norm = normalize((norm - 0.5)*2.0);    //get diffuse component    float d = saturate(dot(norm, -lightDir));    //get view vector    float3 v = normalize(eyePos - input.pos);    //get half vector    float3 h = normalize(-lightDir + v);    //get specular component    float s = saturate(dot(norm, h));    s = pow(s, 25);    //return colour    return ambient + diffuse*d + specular*s;}struct avsIn {    float3 pos: POSITION;};struct avsOut {    float4 pos: POSITION;};    void AmbientVS(in avsIn input, out avsOut output) {    output.pos = mul(float4(input.pos, 1.0), worldViewProj);}float4 AmbientPS() : COLOR {    return float4(1.0, 0.0, 0.0, 1.0);}technique Shader {    pass P0 {        VertexShader = compile vs_1_1 VS();        PixelShader = compile ps_3_0 PS();    }    pass P1 {        VertexShader = compile vs_1_1 AmbientVS();        PixelShader = compile ps_1_1 AmbientPS();    }}

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.