boku-ilen / geodot-plugin

Godot plugin for loading geospatial data
GNU General Public License v3.0
109 stars 19 forks source link

Add function to GeoImage to get normal map for heightmap #14

Closed kb173 closed 4 years ago

kb173 commented 4 years ago

A typical use case of the GeoImage is to hold a heightmap. For such a heightmap, it is often requried to also get the corresponding normal map. We want to add this functionality.

Godot offers a method to turn a bump map into a normal map: Image::bumpmap_to_normalmap in https://github.com/godotengine/godot/blob/master/core/image.cpp. However, the normals created with this method are not very good looking. We might want to implement a custom method for this. If it works well, we could suggest merging this back into Godot and just calling that function here.

We've previously used this code for getting the normal in a shader and the results were good:

vec3 get_normal(vec2 normal_uv_pos) {
    // To calculate the normal vector, height values on the left/right/top/bottom of the current pixel are compared.
    // e is the offset factor.
    float texture_size = float(textureSize(heightmap, 0).x);
    float e = ((size / size_without_skirt) / texture_size);

    // Sobel filter for getting the normal at this position
    float bottom_left = get_height_no_falloff(normal_uv_pos + vec2(-e, -e));
    float bottom_center = get_height_no_falloff(normal_uv_pos + vec2(0, -e));
    float bottom_right = get_height_no_falloff(normal_uv_pos + vec2(e, -e));

    float center_left = get_height_no_falloff(normal_uv_pos + vec2(-e, 0));
    float center_center = get_height_no_falloff(normal_uv_pos + vec2(0, 0));
    float center_right = get_height_no_falloff(normal_uv_pos + vec2(e, 0));

    float top_left = get_height_no_falloff(normal_uv_pos + vec2(-e, e));
    float top_center = get_height_no_falloff(normal_uv_pos + vec2(0, e));
    float top_right = get_height_no_falloff(normal_uv_pos + vec2(e, e));

    vec3 long_normal;

    long_normal.x = -(bottom_right - bottom_left + 2.0 * (center_right - center_left) + top_right - top_left) / (size_without_skirt / texture_size);
    long_normal.y = (top_left - bottom_left + 2.0 * (top_center - bottom_center) + top_right - bottom_right) / (size_without_skirt / texture_size);
    long_normal.z = 1.0;

    return normalize(long_normal);
}

Maybe we can do something similar here.

kb173 commented 4 years ago

This was added in a01bb75.

I also created a godot-proposal do add this functionality directly to the engine: https://github.com/godotengine/godot-proposals/issues/659