Can normal maps be generated from a texture?

前端 未结 4 490
情深已故
情深已故 2021-01-31 23:56

If I have a texture, is it then possible to generate a normal-map for this texture, so it can be used for bump-mapping?

Or how are normal maps usually made?

4条回答
  •  野趣味
    野趣味 (楼主)
    2021-02-01 00:22

    Yes. Well, sort of. Normal maps can be accurately made from height-maps. Generally, you can also put a regular texture through and get decent results as well. Keep in mind there are other methods of making a normal map, such as taking a high-resolution model, making it low resolution, then doing ray casting to see what the normal should be for the low-resolution model to simulate the higher one.

    For height-map to normal-map, you can use the Sobel Operator. This operator can be run in the x-direction, telling you the x-component of the normal, and then the y-direction, telling you the y-component. You can calculate z with 1.0 / strength where strength is the emphasis or "deepness" of the normal map. Then, take that x, y, and z, throw them into a vector, normalize it, and you have your normal at that point. Encode it into the pixel and you're done.

    Here's some older incomplete-code that demonstrates this:

    // pretend types, something like this
    struct pixel
    {
        uint8_t red;
        uint8_t green;
        uint8_t blue;
    };
    
    struct vector3d; // a 3-vector with doubles
    struct texture; // a 2d array of pixels
    
    // determine intensity of pixel, from 0 - 1
    const double intensity(const pixel& pPixel)
    {
        const double r = static_cast(pPixel.red);
        const double g = static_cast(pPixel.green);
        const double b = static_cast(pPixel.blue);
    
        const double average = (r + g + b) / 3.0;
    
        return average / 255.0;
    }
    
    const int clamp(int pX, int pMax)
    {
        if (pX > pMax)
        {
            return pMax;
        }
        else if (pX < 0)
        {
            return 0;
        }
        else
        {
            return pX;
        }
    }
    
    // transform -1 - 1 to 0 - 255
    const uint8_t map_component(double pX)
    {
        return (pX + 1.0) * (255.0 / 2.0);
    }
    
    texture normal_from_height(const texture& pTexture, double pStrength = 2.0)
    {
        // assume square texture, not necessarily true in real code
        texture result(pTexture.size(), pTexture.size());
    
        const int textureSize = static_cast(pTexture.size());
        for (size_t row = 0; row < textureSize; ++row)
        {
            for (size_t column = 0; column < textureSize; ++column)
            {
                // surrounding pixels
                const pixel topLeft = pTexture(clamp(row - 1, textureSize), clamp(column - 1, textureSize));
                const pixel top = pTexture(clamp(row - 1, textureSize), clamp(column, textureSize));
                const pixel topRight = pTexture(clamp(row - 1, textureSize), clamp(column + 1, textureSize));
                const pixel right = pTexture(clamp(row, textureSize), clamp(column + 1, textureSize));
                const pixel bottomRight = pTexture(clamp(row + 1, textureSize), clamp(column + 1, textureSize));
                const pixel bottom = pTexture(clamp(row + 1, textureSize), clamp(column, textureSize));
                const pixel bottomLeft = pTexture(clamp(row + 1, textureSize), clamp(column - 1, textureSize));
                const pixel left = pTexture(clamp(row, textureSize), clamp(column - 1, textureSize));
    
                // their intensities
                const double tl = intensity(topLeft);
                const double t = intensity(top);
                const double tr = intensity(topRight);
                const double r = intensity(right);
                const double br = intensity(bottomRight);
                const double b = intensity(bottom);
                const double bl = intensity(bottomLeft);
                const double l = intensity(left);
    
                // sobel filter
                const double dX = (tr + 2.0 * r + br) - (tl + 2.0 * l + bl);
                const double dY = (bl + 2.0 * b + br) - (tl + 2.0 * t + tr);
                const double dZ = 1.0 / pStrength;
    
                math::vector3d v(dX, dY, dZ);
                v.normalize();
    
                // convert to rgb
                result(row, column) = pixel(map_component(v.x), map_component(v.y), map_component(v.z));
            }
        }
    
        return result;
    }
    

提交回复
热议问题