Sign in to follow this  
Haroogan

Landscape lighting with Normal map

Recommended Posts

Haroogan    103
Each texel of Normal Map corresponds to each texel of Height Map, i. e. vertex : normal = 1 : 1 ratio. FS:

[code]
uniform sampler2D normalSampler2D;

uniform vec3 lightDirection;

varying vec2 fetcher;

void main()
{
vec3 normal = texture2D(normalSampler2D,fetcher).rbg;

normal = normalize(normal * 2.0 - 1.0);

vec3 lightDir = normalize(lightDirection);

float NdotL = max(dot(normal,-lightDir), 0.0);
vec4 diffuse = vec4(1.0,1.0,1.0,1.0);

gl_FragColor = NdotL * diffuse;
}
[/code]

Why does it look like a quaddy pattern?

[img]http://ximages.net/images/18675200103051703800.png[/img]

Bilinear filtering is on for sure.

Share this post


Link to post
Share on other sites
Hodgman    51345
That star pattern looks like... bilinear filtering.

What do your normals look like when you visualise them with:[code]void main()
{
vec3 normal = texture2D(normalSampler2D,fetcher).rbg;
normal = normalize(normal * 2.0 - 1.0);
gl_FragColor = normal*0.5+0.5;
}[/code]
Off-topic, but this is a strange approach to terrain shading -- if the normals are supplied per vertex, then they should be output by the vertex shader and interpolated via a 'varying' param ([i]fetching them per-fragment is a lot of extra work comparatively[/i]).

Share this post


Link to post
Share on other sites
Haroogan    103
[quote name='Hodgman' timestamp='1302444908' post='4796678']
That star pattern looks like... bilinear filtering.

What do your normals look like when you visualise them with:[code]void main()
{
vec3 normal = texture2D(normalSampler2D,fetcher).rbg;
normal = normalize(normal * 2.0 - 1.0);
gl_FragColor = normal*0.5+0.5;
}[/code]
[/quote]

Here you go:
[img]http://ximages.net/images/75934346163122532675.png[/img]

[quote]
Off-topic, but this is a strange approach to terrain shading -- if the normals are supplied per vertex, then they should be output by the vertex shader and interpolated via a 'varying' param ([i]fetching them per-fragment is a lot of extra work comparatively[/i]).
[/quote]

Wrong it is crap approach for several reasons:
1. This will not be per-pixel lighting, and since I'm using triangles it will interpolate between 3 normals of each triangle - and that will produce diamond pattern artifact, you have probably seen it somewhere (if not I can show you, if you ask) - this artifact is much more noticeable than this quaddy pattern artifact and looks ugly.

2. This is LOD-based terrain, therefore if you don't use per-pixel lighting you will see LOD switching, which looks extremely ugly and crappy. You may ask why - this happens because, when you switch LOD's - quantity of normals per block increases by 4 times (according to geo mipmapping) - because you are using discrete normals per vertex. But when I use per-pixel lighting - lighting stays consistent and constant across the whole terrain regardless my position in the world (i. e. the current LOD configuration) - you don't see this switching of lighting quality because you always use the same number of normals - i. e. all pixels of normal map.

Share this post


Link to post
Share on other sites
Haroogan    103
[quote name='Bow_vernon' timestamp='1302447417' post='4796693']
But what's stopping you from using a higher-res normal map? and btw did you rotate the normals using TBN matrix?
[/quote]

TBN matrix is not the case right now. And I know that it should work ok with 1 : 1 ratio, higher res NM will eat 4x more memory, why would I do that if it should work now with 1 : 1 ratio? I just don't get why this quaddy pattern appears...

Share this post


Link to post
Share on other sites
Haroogan    103
Look carefully these quaddy artifact appears on the normal map itself already:

[img]http://ximages.net/images/45390883985532600895.png[/img]

Who's failure is this? Is that due to bi-linear interpolation or because of 8-bits per component (i. e. HDR texture is needed)? I can't believe nobody has faced this problem before.

Share this post


Link to post
Share on other sites
rouncer    294
you need more vertices, thats what gouraud interpolation looks like, it sucks yeh, the way you fix it is to move to more polys.
or a higher res normal map.
just sticking a single texel every metre across is going to look shit yes, remember interpolation in linear its not some advanced quadradic thing,
linear interpolation is for crossing small boundaries not large ones like your trying to do.

Bigger normal map, you need to use more memory, theres no other way around it. Maybe try streaming if you cant fit in ram all at once.
And make sure its nicely compressed on disk, if you do that.

You dont just need twice the size either, maybe 16 times the size is what you need. :)

Share this post


Link to post
Share on other sites
Haroogan    103
Since these quaddy things appear right across the edges of dual triangles (which form a diamond or quad - consider the screenshot) - I believe that it is not about bi-linear interpolation - I just can't believe it!

[img]http://ximages.net/images/03084041237035579854.png[/img]

[img]http://ximages.net/images/21716376574339626808.png[/img]

These are screenshots of the same area. As you can see those quaddy artifact appear right across the 4 edges of dual triangles - there must a reason why that happens.

Share this post


Link to post
Share on other sites
Tessellator    1394
That's just the nature of bilinear filtering. If you want to improve it you'll need to try a higher order filtering method like Bicubic. Check out this chapter from GPU Gems 2: [url="http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter20.html"]http://http.develope..._chapter20.html[/url]. Or you can fake it pretty well using smoothstep() in the shader: [url="http://www.iquilezles.org/www/articles/texture/texture.htm"]http://www.iquilezle...ure/texture.htm[/url].

[quote name='Haroogan' timestamp='1302450389' post='4796711']
I can't believe nobody has faced this problem before.
[/quote]

Everybody suffers from this issue, but it's essentially unnoticeable once you get some colour and detail normal maps on there. Unless you plan on showing an untextured terrain, I wouldn't worry about it. :)

T

Share this post


Link to post
Share on other sites
Haroogan    103
[quote name='Tessellator' timestamp='1302463003' post='4796771']
That's just the nature of bilinear filtering. If you want to improve it you'll need to try a higher order filtering method like Bicubic. Check out this chapter from GPU Gems 2: [url="http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter20.html"]http://http.develope..._chapter20.html[/url]. Or you can fake it pretty well using smoothstep() in the shader: [url="http://www.iquilezles.org/www/articles/texture/texture.htm"]http://www.iquilezle...ure/texture.htm[/url].

[quote name='Haroogan' timestamp='1302450389' post='4796711']
I can't believe nobody has faced this problem before.
[/quote]

Everybody suffers from this issue, but it's essentially unnoticeable once you get some colour and detail normal maps on there. Unless you plan on showing an untextured terrain, I wouldn't worry about it. :)

T
[/quote]

Yep, I've also thought that when texturing will be applied this artifact will become unnoticeable. However, LOD-based terrain is my bachelor project and the time is running out and I still haven't written a word of documentation (which you know is quite important part of any degree work...), therefore I don't have time to integrate desired texturing into current landscape engine, that's sad but the time is really coming to an end. So all I want now is to make it look smooth without any texturing :)

Thanks for those links, I will try it right now and post some results xD

Share this post


Link to post
Share on other sites
Haroogan    103
That's the result of smoothstepping from the second link, I think it looks worse than simple hardware bilinear filtering from previous screenshots (sad):

[img]http://ximages.net/images/13144600840035597059.png[/img]

What am I supposed to do now?

Share this post


Link to post
Share on other sites
Tessellator    1394
Hmm, you don't really want to do the smoothstep() on the normalmap... Can you instead sample the heightmap directly several times, and then construct the normal in the pixel shader? E.g. You can sample the heightmap 3 times in an L shape and construct the normal with a crossproduct, or like so (taken from my own hlsl terrain shader):

[code]
float4 heights;
float t = texel_size;
heights[0] = tex2D(heightmap, uv + float2( 0, -t)).r * height_scale;
heights[1] = tex2D(heightmap, uv + float2(-t, 0)).r * height_scale;
heights[2] = tex2D(heightmap, uv + float2( t, 0)).r * height_scale;
heights[3] = tex2D(heightmap, uv + float2( 0, t)).r * height_scale;

float3 normal;
normal.x = heights[1] - heights[2];
normal.y = 2.0;
normal.z = heights[0] - heights[3];

normal = normalize(normal);
[/code]


Instead of doing the tex2D directly, you can use the filtered version and construct the normal from that.

Let me know if that works out ok,
T

Share this post


Link to post
Share on other sites
Haroogan    103
[quote name='Tessellator' timestamp='1302473301' post='4796822']
Hmm, you don't really want to do the smoothstep() on the normalmap... Can you instead sample the heightmap directly several times, and then construct the normal in the pixel shader? E.g. You can sample the heightmap 3 times in an L shape and construct the normal with a crossproduct, or like so (taken from my own hlsl terrain shader):

[code]
float4 heights;
float t = texel_size;
heights[0] = tex2D(heightmap, uv + float2( 0, -t)).r * height_scale;
heights[1] = tex2D(heightmap, uv + float2(-t, 0)).r * height_scale;
heights[2] = tex2D(heightmap, uv + float2( t, 0)).r * height_scale;
heights[3] = tex2D(heightmap, uv + float2( 0, t)).r * height_scale;

float3 normal;
normal.x = heights[1] - heights[2];
normal.y = 2.0;
normal.z = heights[0] - heights[3];

normal = normalize(normal);
[/code]


Instead of doing the tex2D directly, you can use the filtered version and construct the normal from that.

Let me know if that works out ok,
T
[/quote]

I've tried this before in the very beginning - and that's horrible especially for LOD-based terrain, moreover constructing normals using only 4 samples is crude. I'm constructing my normal map during preprocessing using canny edge detection approach - by filtering height map image with gaussian kernels - this gives me high quality normal maps. Kernels take into account more than 4 neighbours, in brief it takes as many neighbours as I will specify by sigma parameter (like in gaussian function). So I just don't get why would I compute normals in shader...

Share this post


Link to post
Share on other sites
rouncer    294
just looking at how your terrain is put together isnt very realistic as far as good terrains go.
Just stepping up a height level with one grid step across it very steeply, then doing it again a few more grid steps across isnt right.
Your supposed to gradually change anyway... i think if you made your terrain properly this wouldnt be a problem.

Share this post


Link to post
Share on other sites
wanderingbort    136
[quote name='Haroogan' timestamp='1302462219' post='4796769']
Since these quaddy things appear right across the edges of dual triangles (which form a diamond or quad - consider the screenshot) - I believe that it is not about bi-linear interpolation - I just can't believe it!
[/quote]

Actually, along the edges of the square formed from 4 texel centers is exactly where bilinear interpolation of normals breaks down. If you draw a line between the ends of any two normalized vectors and then evenly space points on that line (linear interpolation) the [b]angles[/b] formed from these points and the origin will not be equal. They will tend to bunch up at the beginning and end of your line.

In terms of a normal map it means that most of the normals that you reconstruct from linear interpolation(s) will be close to the normals at the edges of the interpolation. In 2D aka bilinear filtering, that makes your normals bunch up at the edges of a quad.

Spherical linear interpolation (or slerp) would evenly space the reconstructed normals in terms of angles on a sphere and would look better. Unfortunately, slerp is a lot more expensive than lerp and GPU hardware interpolators and instructions rarely support it.

Share this post


Link to post
Share on other sites
Tessellator    1394
[quote name='Haroogan' timestamp='1302478360' post='4796847']
I've tried this before in the very beginning - and that's horrible especially for LOD-based terrain, moreover constructing normals using only 4 samples is crude. I'm constructing my normal map during preprocessing using canny edge detection approach - by filtering height map image with gaussian kernels - this gives me high quality normal maps. Kernels take into account more than 4 neighbours, in brief it takes as many neighbours as I will specify by sigma parameter (like in gaussian function). So I just don't get why would I compute normals in shader...
[/quote]

Sampling the heightmap in the pixel shader works well for LOD based terrain, since it's independent of LOD.

Doing 4 taps to construct the normal isn't great - an offline approach is always going to beat it. The nice things about heightmaps though is that they're linearly filterable (unlike normal maps as Wanderingbort points out - although again, whether you notice or not depends on application) and more amenable to tricks (such as the filtering one).

T

Share this post


Link to post
Share on other sites
Pottuvoi    268
Use more than 8bit heightmap, try with realistic landscape with small variations as well.
If that's not enough use bicubic filtering (or better) for normals to get rid of bilinear artifacts.

Share this post


Link to post
Share on other sites
Hodgman    51345
[quote name='Haroogan' timestamp='1302446738' post='4796691'][color=#1C2837][size=2]i. e. vertex : normal = 1 : 1[/size][/color][quote][b][size="3"]if[/size] [/b]the normals are supplied per vertex, then they should be output by the vertex shader[/quote]Wrong it is crap approach for ... this is LOD-based terrain[/quote]Well that means the 'if' doesn't apply. No need to break out the W word.

Does increasing the radius / number of neighbors in your normal-map generation step help smooth things 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