Generating a normal map from a height map?
NickName:Dawson Ask DateTime:2011-03-12T15:26:05

Generating a normal map from a height map?

I'm working on procedurally generating patches of dirt using randomized fractals for a video game. I've already generated a height map using the midpoint displacement algorithm and saved it to a texture. I have some ideas for how to turn that into a texture of normals, but some feedback would be much appreciated.

My height texture is currently a 257 x 257 gray-scale image (height values are scaled for visibility purposes):

enter image description here

My thinking is that each pixel of the image represents a lattice coordinate in a 256 x 256 grid (hence, why there are 257 x 257 heights). That would mean that the normal at coordinate (i, j) is determined by the heights at (i, j), (i, j + 1), (i + 1, j), and (i + 1, j + 1) (call those A, B, C, and D, respectively).

So given the 3D coordinates of A, B, C, and D, would it make sense to:

  1. split the four into two triangles: ABC and BCD
  2. calculate the normals of those two faces via cross product
  3. split into two triangles: ACD and ABD
  4. calculate the normals of those two faces
  5. average the four normals

...or is there a much easier method that I'm missing?

Copyright Notice:Content Author:「Dawson」,Reproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/5281261/generating-a-normal-map-from-a-height-map

Answers
kvark 2011-03-12T18:30:51

Example GLSL code from my water surface rendering shader:\n\n#version 130\nuniform sampler2D unit_wave\nnoperspective in vec2 tex_coord;\nconst vec2 size = vec2(2.0,0.0);\nconst ivec3 off = ivec3(-1,0,1);\n\n vec4 wave = texture(unit_wave, tex_coord);\n float s11 = wave.x;\n float s01 = textureOffset(unit_wave, tex_coord, off.xy).x;\n float s21 = textureOffset(unit_wave, tex_coord, off.zy).x;\n float s10 = textureOffset(unit_wave, tex_coord, off.yx).x;\n float s12 = textureOffset(unit_wave, tex_coord, off.yz).x;\n vec3 va = normalize(vec3(size.xy,s21-s01));\n vec3 vb = normalize(vec3(size.yx,s12-s10));\n vec4 bump = vec4( cross(va,vb), s11 );\n\n\nThe result is a bump vector: xyz=normal, a=height",


Yakov Galka 2011-03-12T12:02:11

\nMy thinking is that each pixel of the image represents a lattice coordinate in a 256 x 256 grid (hence, why there are 257 x 257 heights). That would mean that the normal at coordinate (i, j) is determined by the heights at (i, j), (i, j + 1), (i + 1, j), and (i + 1, j + 1) (call those A, B, C, and D, respectively).\n\nNo. Each pixel of the image represents a vertex of the grid, so intuitively, from symmetry, its normal is determined by heights of neighboring pixels (i-1,j), (i+1,j), (i,j-1), (i,j+1).\nGiven a function f : ℝ2 → ℝ that describes a surface in ℝ3, a unit normal at (x,y) is given by\nv = (−∂f/∂x, −∂f/∂y, 1) and n = v/|v|.\nIt can be proven that the best approximation to ∂f/∂x by two samples is archived by:\n∂f/∂x(x,y) = (f(x+ε,y) − f(x−ε,y))/(2ε)\nTo get a better approximation you need to use at least four points, thus adding a third point (i.e. (x,y)) doesn't improve the result.\nYour hightmap is a sampling of some function f on a regular grid. Taking ε=1 you get:\n2v = (f(x−1,y) − f(x+1,y), f(x,y−1) − f(x,y+1), 2)\nPutting it into code would look like:\n// sample the height map:\nfloat fx0 = f(x-1,y), fx1 = f(x+1,y);\nfloat fy0 = f(x,y-1), fy1 = f(x,y+1);\n\n// the spacing of the grid in same units as the height map\nfloat eps = ... ;\n\n// plug into the formulae above:\nvec3 n = normalize(vec3((fx0 - fx1)/(2*eps), (fy0 - fy1)/(2*eps), 1));\n",


jozxyqk 2014-10-14T09:34:43

A common method is using a Sobel filter for a weighted/smooth derivative in each direction.\n\nStart by sampling a 3x3 area of heights around each texel (here, [4] is the pixel we want the normal for).\n\n[6][7][8]\n[3][4][5]\n[0][1][2]\n\n\nThen,\n\n//float s[9] contains above samples\nvec3 n;\nn.x = scale * -(s[2]-s[0]+2*(s[5]-s[3])+s[8]-s[6]);\nn.y = scale * -(s[6]-s[0]+2*(s[7]-s[1])+s[8]-s[2]);\nn.z = 1.0;\nn = normalize(n);\n\n\nWhere scale can be adjusted to match the heightmap real world depth relative to its size.",


More about “Generating a normal map from a height map?” related questions

How do I create an Object-Space normal map from a height map?

I'm able to convert my existing height-map into a normal map by sampling the surrounding pixels, like in this question Generating a normal map from a height map? except I'm doing it on CPU. I have a

Show Detail

Generating a normal map from a height map?

I'm working on procedurally generating patches of dirt using randomized fractals for a video game. I've already generated a height map using the midpoint displacement algorithm and saved it to a te...

Show Detail

Generating a normal map from a height map in compute shader?

The problem is that when I tried converting height map to normal map. The results are wrong. For some reason there is 3 light sources that is emitting from top (green), right (red), and left (blue)...

Show Detail

Normal map from height map artifact

I'm baking normal map on the fragment shader from a height map. The height map looks great and looks smooth. However, when I generate the normal map I get very weird result. Here are two rendered ...

Show Detail

Normal map from height map

I am trying to create a normal map from a height map in HLSL. I followed this https://stackoverflow.com/a/5284527/451136 which is for GLSL. Here is how I translated GLSL to HLSL: GLSL: uniform

Show Detail

What is the purpose of a height map when you already have a normal map?

In 3D graphics, many textures have normal- and height maps to create an illusion of 3D features on a flat surface. Looking at the implementation of height maps, I see that it basically just affects...

Show Detail

convert a normal map to height map with imagemagick: how to recreate/integrate following algorithm

I'm trying to convert a tangent space normal map to a height/displacement map. For sure this will be not 100% accurate speaking in terms of "exact height" for each pixel. But the relative height from

Show Detail

OpenGL: Which shaders for normal map (bump map)?

I could not find a way to generate a normal/bump map in PyOpenGL. Specifically I want a map which I can read with glReadPixels(). I do not know how to get a map of the form: (width, height, normal...

Show Detail

which coordinate system the normal vector calculated from height map in?

I have read a lot of material about heightmap and normal map, and have many questions: We can generate a normal map from heightmap: If we have in the S and T direction: S(i,j)= <1,0,aH(i+1,j)...

Show Detail

Normal map generation - shaders and FBO

I want to generate a normal map from a heightmap. I know it's a pretty 'overasked question', but I haven't found any good topics about this. The normal map should be about 6-8x larger than the orig...

Show Detail