3
\$\begingroup\$

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) normal map

And this with high intensity (>100);its normal map too

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

\$\endgroup\$
4
  • \$\begingroup\$ What is wrong with the (low intensity) bump map? \$\endgroup\$
    – Roy T.
    Commented Jul 27, 2014 at 13:26
  • \$\begingroup\$ There are no normals(normals direction?) on it. Normal map should be imgur.com/6pex2Zu \$\endgroup\$
    – m9_psy
    Commented Jul 27, 2014 at 13:36
  • \$\begingroup\$ I don't think there's enough information in the bump map to make a normal map from it. \$\endgroup\$ Commented Jul 27, 2014 at 15:42
  • \$\begingroup\$ 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. \$\endgroup\$
    – m9_psy
    Commented Jul 27, 2014 at 17:34

1 Answer 1

2
\$\begingroup\$

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: enter image description here

Or with a wrapping mode, produces what was shown in your comment: enter image description here (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
#define IMG_Load SDL_LoadBMP
#else
#include <SDL/SDL_image.h>
#endif

double extrusion = 2.0;

SDL_Surface* load32(const char* fn) {
    SDL_Surface* p_textureSurface = IMG_Load(fn);
    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]);

    SDL_Surface* hmap = load32(argv[1]);
    if (!hmap) {
        printf("Failed to load: %s\n", argv[1]);
        return 2;
    }
    SDL_Surface* nmap = SDL_CreateRGBSurface(SDL_SWSURFACE, hmap->w, hmap->h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);

    ComputeGradientMap(nmap, hmap, 1);
    SDL_SaveBMP(nmap, out_wrap);

    ComputeGradientMap(nmap, hmap, 0);
    SDL_SaveBMP(nmap, out_clamp);
    return 0;
}
\$\endgroup\$
3
  • \$\begingroup\$ Could i achive some advantages with using other operator to find gradient? For example, Prewitt operator, Scharr operator. \$\endgroup\$
    – m9_psy
    Commented Aug 15, 2014 at 19:38
  • \$\begingroup\$ Theoretically you can try any discrete differentiation operator. They are approximations, maybe you can achieve higher visual fidelity with other operators. I've chosen Sobel just because it's simple and fast. \$\endgroup\$
    – MickLH
    Commented Aug 16, 2014 at 15:12
  • \$\begingroup\$ Super cool example code, thanks for sharing! \$\endgroup\$
    – Ray Hulha
    Commented Aug 14, 2019 at 13:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .