## Height field normals using Noise derivatives

**category:**code [glöplog]

Using a 2d noise function based off iq's more noise article to generate a height field. I know the normal.x = dx, and normal.z = dy but how do you calculate normal.y without sampling neighboring points in the height field?

Suppose your height field is z=f(x,y) form and you can get df(x,y)/dx and df(x,y)/dy.

You can get 2 tangent vectors:

vec3 tx = (1, 0, df(x,y)/dx)

vec3 ty = (0, 1, df(x,y)/dy)

thus,

vec3 normal = cross(tx, ty) = (-df(x,y)/dx, -df(x,y)/dy, 1)

Can/May I use partial differential symbol or other math symbols in japanese charactor set in pouet?

partial differential symbol:∂

other math symbols:Δ×∇∑∫∬⇔≠≒⇒∵∃∀⊆⊇∪∩∋∈

You can get 2 tangent vectors:

vec3 tx = (1, 0, df(x,y)/dx)

vec3 ty = (0, 1, df(x,y)/dy)

thus,

vec3 normal = cross(tx, ty) = (-df(x,y)/dx, -df(x,y)/dy, 1)

Can/May I use partial differential symbol or other math symbols in japanese charactor set in pouet?

partial differential symbol:∂

other math symbols:Δ×∇∑∫∬⇔≠≒⇒∵∃∀⊆⊇∪∩∋∈

just use a constant that looks good and normalize the normal ;) It's all about looking good instead of beeing accurate :D

tomohiro:

this is what it looks like here:

this is what it looks like here:

for shading you really need something that is as close as possible to the real normal. if you manage to find the derivatives for the noise function you still need to evaluate them. So id say its not more efficient than sampling 2 extra points which is simpler to write and looks good too.

What xTr1m said.

**Quote:**

just use a constant that looks good and normalize the normal ;) It's all about looking good instead of beeing accurate :D

oh he only wants normal.y... CONSTANT!

The analytic derivative shares a lot of calculations with noise value itself. Thus the analytic derivative of Perlin in 3d is way faster than using a 6 points stencil .

rudi:

Thank you for your reply.

Your image is same to the display of my brower(Opera).

It seems super set symbol is converted to different string.

Thank you for your reply.

Your image is same to the display of my brower(Opera).

It seems super set symbol is converted to different string.

yep, start at a constant of 0.5, will look good already, depending on your light-vector.

from there just tweak around. i use some 0.1 and i like it, but thats a personal taste ;)

from there just tweak around. i use some 0.1 and i like it, but thats a personal taste ;)

pouet does split long strings, so if you use lots of unicode characters, it may get split at a bad position. just use spaces after some symbols and you're fine.

bartman:

Thank you!

I added spaces between each unicode characters.

∂ Δ × ÷ ∇ ∑ ∫ ∬ ⇔ ≠ ≒ ⇒ ∵ ∃ ∀ ⊆ ⊇ ∪ ∩ ∋ ∈ α β γ π

∂f(x,y)

--------

∂x

∧,,,,,,∧

(´ ・ Д ・ )

Thank you!

I added spaces between each unicode characters.

∂ Δ × ÷ ∇ ∑ ∫ ∬ ⇔ ≠ ≒ ⇒ ∵ ∃ ∀ ⊆ ⊇ ∪ ∩ ∋ ∈ α β γ π

∂f(x,y)

--------

∂x

∧,,,,,,∧

(´ ・ Д ・ )

(firefox/nightly)

ye :)

ye :)

Thanks guys, yeh was having a hard time finding a constant that look good everywhere.

tomohiro: Yeh, was thinking of something similar. Will give it a try.

tomohiro: Yeh, was thinking of something similar. Will give it a try.

Maybe some one can help. The normals still look like crap. Doing a quadtree terrain using a 2D grid to render it.

Vertex Shader

Pixel Shader

Vertex Shader

**Code:**

```
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
VertexShaderOutput output;
float3 gridPosition = input.Position.x * Tangent + input.Position.y * Binormal;
float4 worldPosition = mul(float4(gridPosition, 1), World);
float3 noise = saturate(fbm(float2(worldPosition.x, worldPosition.z) / 32.0, 3, 2.0, .5));
worldPosition.xyz += noise.x * Height * Normal;
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
output.Normal = normalize(float3(-noise.y, 1.0 , -noise.z));
output.WorldPosition = worldPosition.xyz;
return output;
}
```

Pixel Shader

**Code:**

```
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
float3 lightDir = float3(0, 1024, 0);
float4 diffuseLightColor = float4(.8, .8, 1, 0);
float4 ambientLightColor = float4(.2, .2, .2, 0);
float3 directionToLight = normalize(lightDir - input.WorldPosition);
float diffuseIntensity = saturate( dot(directionToLight, input.Normal));
float4 diffuse = diffuseLightColor * diffuseIntensity;
float4 color = diffuse + ambientLightColor;
color.a = 1.0;
return color;
}
```

This is what i'm using for noise on the GPU:

[code]

// http://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl

float random(float a,float b) {

return frac(sin(dot(float2(a,b) ,float2(12.9898,78.233))) * 43758.5453);

}

// http://www.iquilezles.org/www/articles/morenoise/morenoise.htm

float3 dnoise2f(float2 p) {

float i = floor(p.x), j = floor(p.y);

float u = p.x-i, v = p.y-j;

float du = 30.*u*u*(u*(u-2.)+1.);

float dv = 30.*v*v*(v*(v-2.)+1.);

u=u*u*u*(u*(u*6.-15.)+10.);

v=v*v*v*(v*(v*6.-15.)+10.);

float a = random( i, j );

float b = random( i+1., j);

float c = random( i, j+1.);

float d = random( i+1., j+1.);

float k0 = a;

float k1 = b-a;

float k2 = c-a;

float k3 = a-b-c+d;

return float3(

k0 + k1*u + k2*v + k3*u*v,

du * (k1 + k3*v),

dv * (k2 + k3*u)

);

}

float3 fbm(float2 p, int octaves, float persistence, float gain){

float f = 0.0;

float w = 0.5;

float dx = 0.0;

float dz = 0.0;

for( int i=0; i < octaves; i++ )

{

float3 n = dnoise2f(p);

dx += n[1];

dz += n[2];

f += w * n[0] / (1.0 + dx*dx + dz*dz); // replace with "w * n[0]" for a classic fbm()

w *= gain;

p *= persistence;

}

return float3(f, dx, dz);

}

[code]

// http://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl

float random(float a,float b) {

return frac(sin(dot(float2(a,b) ,float2(12.9898,78.233))) * 43758.5453);

}

// http://www.iquilezles.org/www/articles/morenoise/morenoise.htm

float3 dnoise2f(float2 p) {

float i = floor(p.x), j = floor(p.y);

float u = p.x-i, v = p.y-j;

float du = 30.*u*u*(u*(u-2.)+1.);

float dv = 30.*v*v*(v*(v-2.)+1.);

u=u*u*u*(u*(u*6.-15.)+10.);

v=v*v*v*(v*(v*6.-15.)+10.);

float a = random( i, j );

float b = random( i+1., j);

float c = random( i, j+1.);

float d = random( i+1., j+1.);

float k0 = a;

float k1 = b-a;

float k2 = c-a;

float k3 = a-b-c+d;

return float3(

k0 + k1*u + k2*v + k3*u*v,

du * (k1 + k3*v),

dv * (k2 + k3*u)

);

}

float3 fbm(float2 p, int octaves, float persistence, float gain){

float f = 0.0;

float w = 0.5;

float dx = 0.0;

float dz = 0.0;

for( int i=0; i < octaves; i++ )

{

float3 n = dnoise2f(p);

dx += n[1];

dz += n[2];

f += w * n[0] / (1.0 + dx*dx + dz*dz); // replace with "w * n[0]" for a classic fbm()

w *= gain;

p *= persistence;

}

return float3(f, dx, dz);

}

Hey Jazz, did you ever get this working for the normal?

I'm at the same stage and would like to speed up my terrain by not calling the height map 3 times to get a normal.

I'm guessing your fmb function above doesn't call return the correct derivatives?

How did you do it in the end?

Thanks.

I'm at the same stage and would like to speed up my terrain by not calling the height map 3 times to get a normal.

I'm guessing your fmb function above doesn't call return the correct derivatives?

How did you do it in the end?

Thanks.

thing is, unless you are doing very simple noise-based terrains (simple fbm, etc), your height function won't be analytically derivable anyway, so in the end you must resort to the numerical method. plus by the time you introduce noise based wrapping, several noise modulations etc etc, computing the derivatives analytically can get more expensive that just sampling. and complexifying your noise constructions is what you want to get beautiful terrains. i mean, an analytic-derivative based fbm() terrain rendes super fast, but looks awful, so you don't want to make a demo with it anyway. so in the end of the day, imho, both for speed and aesthetic reasons, you just have to forget the analytic normals and sample the 4 points and get your gradient....

Thanks iq. In the document...

http://www.iquilezles.org/www/material/function2009/function2009.pdf

...you imply that I don't need to evaluate 4 times, and then you show the equivalent code to jazz's above. So in 'Elevated' are you not actually using the derivatives?

Sampling only once gave me an overall 2X render speed up, so I'm keen to know if that's what you did. :)

http://www.iquilezles.org/www/material/function2009/function2009.pdf

...you imply that I don't need to evaluate 4 times, and then you show the equivalent code to jazz's above. So in 'Elevated' are you not actually using the derivatives?

Sampling only once gave me an overall 2X render speed up, so I'm keen to know if that's what you did. :)

jazz's code (which he copied from my website here: http://www.iquilezles.org/www/articles/morenoise/morenoise.htm) was used for the heightfield. The normals were computed by sampling the field 4 times. You cannot sample only once. Perhaps you can sample it 3 times (center, right and bottom) instead of 4 (left, right, top and bottom), but it's not that much of a saving.

Ah OK you were talking about the 4 corners of the height field algorithm, and I got confused by this whole thread! Shame. And yeah I use three corners and it looks fine.

Here's a sneaky link to my stuff using 3 tests at variable widths depending on camera distance, which prevents aliasing issues.

http://www.youtube.com/watch?v=tw1GncYrowc : )

Thanks again for the help and code inspiration.

Here's a sneaky link to my stuff using 3 tests at variable widths depending on camera distance, which prevents aliasing issues.

http://www.youtube.com/watch?v=tw1GncYrowc : )

Thanks again for the help and code inspiration.

Bah. Real coders integrate the surface from normals instead of the other way around.

Is there any way you can explain what you mean? Or was that just a shrug off?

I'm sorry if I'm a 'fake' programmer, I've only been selling my software for 35 years, I'll try harder next time.

I'm sorry if I'm a 'fake' programmer, I've only been selling my software for 35 years, I'll try harder next time.

in fact, i think that if you manage to do a procedural integration algorithm (without height maps), then you have your way open for real implicit procedural erosion, a siggraph paper, and eternal glory.