# pouët.net

Go to bottom

## 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:Δ×∇∑∫∬⇔≠≒⇒∵∃∀⊆⊇∪∩∋∈
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: 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
Bingo!
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:
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 ;)
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

∧,,,,,,∧
(´ ・ Д ・ ) (firefox/nightly)

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.
Maybe some one can help. The normals still look like crap. Doing a quadtree terrain using a 2D grid to render it.

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; } ```

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;
dz += n;
f += w * n / (1.0 + dx*dx + dz*dz); // replace with "w * n" 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.
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. :)
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.