# How to create normal map from bump map in runtime?

I am using this port of libnoise to generate noise. There are several methods (Noise2D.cs, 462 and 494 line) to output images. Second one, that should give me normal map does not work completely, it outputs this with low intensity (1.0f)

And this with high intensity (>100);

So i decided to use grayscale and default Bumped Diffuse shader works good, but only in Editor. How should i modify grayscale image for correct normal map? How should i use library in proper way?

• What is wrong with the (low intensity) bump map? – Roy T. Jul 27 '14 at 13:26
• There are no normals(normals direction?) on it. Normal map should be imgur.com/6pex2Zu – m9_psy Jul 27 '14 at 13:36
• I don't think there's enough information in the bump map to make a normal map from it. – Thane Brimhall Jul 27 '14 at 15:42
• It is possible. For example using Sobel filter: stackoverflow.com/questions/2368728/… But Unity have Dxtmn format for normal maps. I cant understand how to map dx, dy, dz from Sobel to this format. – m9_psy Jul 27 '14 at 17:34

You can generate each the normal map, pixel by pixel, by computing the respective the X and Y gradients from the height map using a Sobel operator.

Sampling the heightmap with a clamping mode produces an output like such:

Or with a wrapping mode, produces what was shown in your comment: (Including the errors around the border)

And for good measure, here's the tool I wrote to generate those images. Using SDL and plain C, I called it "hbm2tnm.c" (Height Bump Map to Tangent Normal Map)

#include <stdio.h>
#include <math.h>
#include <SDL/SDL.h>
#ifdef NO_SDL_IMAGE
#else
#include <SDL/SDL_image.h>
#endif

double extrusion = 2.0;

if (!p_textureSurface) return 0;
SDL_Surface* p_textureBGRA = SDL_CreateRGBSurface(SDL_SWSURFACE, p_textureSurface->w, p_textureSurface->h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
SDL_SetAlpha(p_textureSurface, 0, 0);
SDL_SetAlpha(p_textureBGRA, 0, 0);
SDL_BlitSurface(p_textureSurface, 0, p_textureBGRA, 0);
SDL_FreeSurface(p_textureSurface);
return p_textureBGRA;
}

double getPixel(SDL_Surface* surf, int wrap, int x, int y) {
unsigned char* in32 = (unsigned char*)surf->pixels;
if (x < 0) x = wrap ? (x + surf->w) : 0;
if (y < 0) y = wrap ? (y + surf->h) : 0;
if (x >= surf->w) x = wrap ? (x - surf->w) : (surf->w - 1);
if (y >= surf->h) y = wrap ? (y - surf->h) : (surf->h - 1);
int idx = x + y * surf->w;
return (in32[idx * 4 + 0] + in32[idx * 4 + 1] + in32[idx * 4 + 2]) / (256.0 * 3.0);
}

void ComputeGradientMap(SDL_Surface* nmap, SDL_Surface* hmap, int wrap) {
int x,y;
unsigned char* out32 = (unsigned char*)nmap->pixels;
for(y = 0; y < hmap->h; y++) {
for(x = 0; x < hmap->w; x++) {
double center = getPixel(hmap, wrap, x, y);
double up = getPixel(hmap, wrap, x, y - 1);
double down = getPixel(hmap, wrap, x, y + 1);
double left = getPixel(hmap, wrap, x - 1, y);
double right = getPixel(hmap, wrap, x + 1, y);
double upleft = getPixel(hmap, wrap, x - 1, y - 1);
double upright = getPixel(hmap, wrap, x + 1, y - 1);
double downleft = getPixel(hmap, wrap, x - 1, y + 1);
double downright = getPixel(hmap, wrap, x + 1, y + 1);

double vert = (down - up) * 2.0 + downright + downleft - upright - upleft;
double horiz = (right - left) * 2.0 + upright + downright - upleft - downleft;
double depth = 1.0 / extrusion;
double scale = 127.0 / sqrt(vert*vert + horiz*horiz + depth*depth);

unsigned char r = 128 - horiz * scale;
unsigned char g = 128 + vert * scale;
unsigned char b = 128 + depth * scale;

int idx = x + y * nmap->w;
out32[idx * 4 + 0] = b;
out32[idx * 4 + 1] = g;
out32[idx * 4 + 2] = r;
out32[idx * 4 + 3] = 255;
}
}
}

int main(int argc, const char** argv) {
if (argc <= 1) {
printf("Usage: %s <input image> [clamp output image] [wrap output image] [extrusion = %.1f]\n", argc ? argv[0] : "hbm2tnm", extrusion);
return 1;
}
const char* out_clamp = "normal_clamp.bmp";
const char* out_wrap = "normal_wrap.bmp";
if (argc >= 3) out_clamp = argv[2];
if (argc >= 4) out_wrap = argv[3];
if (argc >= 5) extrusion = atof(argv[4]);

if (!hmap) {
return 2;
}
SDL_Surface* nmap = SDL_CreateRGBSurface(SDL_SWSURFACE, hmap->w, hmap->h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);