Terrain Mesh Blending with Dithering
[WIP]
This shader is used to blend between meshes and terrain, it is applied to the mesh. The blending works by changing the depth. The problems of non-convex meshes, are avoided by setting the render_mode to depth_draw_alpha_prepass.
As height texture the height texture of the terrain is used, also the maximum height has to be set.
Alternatively, a height texture can be created with a viewport and an orthogonal camera.
Features:
– works with non-convex meshes
– no triplanar mapping
Problems:
– shadow artifacts
Shader code
/*
Terrain Mesh Blending with Dithering Shader by Firerabbit
MIT License
*/
shader_type spatial;
render_mode world_vertex_coords, depth_draw_alpha_prepass, cull_disabled;
// ---- Heightmap ---- //
// size of the terrain
// height_texture from heightmap terrain
uniform vec2 size = vec2(256);
uniform sampler2D height_texture : hint_black;
uniform float max_height = 32.0;
// ---- Transition ---- //
uniform float transition_length : hint_range(0.0, 2.0) = 0.157;
uniform float falloff : hint_range(0.0, 5.0) = 0.455;
// ---- Color and textures
uniform vec4 color : hint_color;
uniform sampler2D albedo : hint_white;
varying vec3 WORLD_VERTEX;
const vec4 dither[4] = {
vec4(0.0625, 0.5625, 0.1875, 0.6875),
vec4(0.8125, 0.3125, 0.9375, 0.4375),
vec4(0.25, 0.75, 0.125, 0.625),
vec4(1.0, 0.5, 0.875, 0.375)
};
float getValue(int x, int y) {
float res = 0.0;
switch(y) {
case 0:
res = dither[x].r;
break;
case 1:
res = dither[x].g;
break;
case 2:
res = dither[x].b;
break;
case 3:
res = dither[x].a;
break;
}
return res;
}
float get_height() {
float res = texture(height_texture, WORLD_VERTEX.xz / size).r;
res *= max_height;
return res;
}
void vertex() {
WORLD_VERTEX = VERTEX;
}
void fragment() {
float height = get_height();
// difference
float diff = WORLD_VERTEX.y - height;
diff = clamp(diff, 0.0, 1.0);
diff = smoothstep(0.0, transition_length, diff);
diff = pow(diff, falloff);
vec4 ndc = PROJECTION_MATRIX * INV_CAMERA_MATRIX * vec4(WORLD_VERTEX, 1.0);
float limit = getValue(int(FRAGCOORD.x) % 4, int(FRAGCOORD.y) % 4);
if (diff < limit && diff < 1.0) {
ndc.z += 0.01;
}
float depth = (ndc.z / ndc.w) * 0.5 + 0.5;
DEPTH = depth;
ALBEDO = pow(texture(albedo, UV).rgb, vec3(2.2)) * color.rgb;
}