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\$
  • \$\begingroup\$ What is wrong with the (low intensity) bump map? \$\endgroup\$ – Roy T. Jul 27 '14 at 13:26
  • \$\begingroup\$ There are no normals(normals direction?) on it. Normal map should be imgur.com/6pex2Zu \$\endgroup\$ – m9_psy Jul 27 '14 at 13:36
  • \$\begingroup\$ I don't think there's enough information in the bump map to make a normal map from it. \$\endgroup\$ – Thane Brimhall Jul 27 '14 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 Jul 27 '14 at 17:34
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\$
  • \$\begingroup\$ Could i achive some advantages with using other operator to find gradient? For example, Prewitt operator, Scharr operator. \$\endgroup\$ – m9_psy Aug 15 '14 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 Aug 16 '14 at 15:12
  • \$\begingroup\$ Super cool example code, thanks for sharing! \$\endgroup\$ – Ray Hulha Aug 14 at 13:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.