Add ground_level, region_blend to flat background; Allow negative world noise height

This commit is contained in:
Cory Petkovsek 2025-11-23 09:49:28 +07:00
parent fcbfcdc7bf
commit e8fa334584
7 changed files with 107 additions and 25 deletions

View File

@ -224,7 +224,7 @@
<None Include="src\shaders\main.glsl" />
<None Include="src\shaders\overlays.glsl" />
<None Include="src\shaders\samplers.glsl" />
<None Include="src\shaders\world_noise.glsl" />
<None Include="src\shaders\backgrounds.glsl" />
<None Include="src\shaders\editor_functions.glsl" />
<None Include="src\shaders\dual_scaling.glsl" />
<None Include="src\shaders\auto_shader.glsl" />

View File

@ -163,7 +163,7 @@
<None Include="src\shaders\main.glsl">
<Filter>4. Shaders</Filter>
</None>
<None Include="src\shaders\world_noise.glsl">
<None Include="src\shaders\backgrounds.glsl">
<Filter>4. Shaders</Filter>
</None>
<None Include=".gitignore">

View File

@ -26,7 +26,7 @@ uniform float wind_strength : hint_range(0.0, 1.0, 0.01) = 1.0;
uniform float wind_dithering = 4.0;
uniform vec2 wind_direction = vec2(1.0,1.0);
group_uniforms shapeing;
group_uniforms shaping;
uniform float clod_scale_boost = 3.0;
uniform float clod_min_threshold : hint_range(0.0, 1.0, 0.001) = 0.2;
uniform float clod_max_threshold : hint_range(0.0, 1.0, 0.001) = 0.5;

View File

@ -56,6 +56,8 @@ uniform highp sampler2DArray _texture_array_normal : hint_normal, filter_linear_
// Public uniforms
group_uniforms general;
uniform float ground_level : hint_range(-1000., 1000.) = 0.0;
uniform float region_blend : hint_range(.001, 1., 0.001) = 0.25;
uniform bool flat_terrain_normals = false;
uniform bool enable_textures = true;
uniform float blend_sharpness : hint_range(0, 1) = 0.5;
@ -84,6 +86,7 @@ group_uniforms;
varying vec3 v_normal;
varying vec3 v_vertex;
varying mat3 TBN;
varying vec2 bg_ddxy;
////////////////////////
// Vertex
@ -119,6 +122,29 @@ vec3 get_index_uv(const vec2 uv2) {
return vec3(uv2 - _region_locations[layer_index], float(layer_index));
}
// Takes in UV2 region space coordinates, returns 1.0 or 0.0 if a region is present or not.
float check_region(const vec2 uv2) {
ivec2 pos = ivec2(floor(uv2)) + (_region_map_size / 2);
int layer_index = 0;
if (uint(pos.x | pos.y) < uint(_region_map_size)) {
layer_index = clamp(_region_map[ pos.y * _region_map_size + pos.x ] - 1, -1, 0) + 1;
}
return float(layer_index);
}
// Takes in UV2 region space coordinates, returns a blend value (0 - 1 range) between empty, and valid regions
float get_region_blend(vec2 uv2) {
uv2 -= 0.5;
const vec2 offset = vec2(0.0, 1.0);
float a = check_region(uv2 + offset.xy);
float b = check_region(uv2 + offset.yy);
float c = check_region(uv2 + offset.yx);
float d = check_region(uv2 + offset.xx);
vec2 w = smoothstep(vec2(0.0), vec2(1.0), fract(uv2));
float blend = mix(mix(d, c, w.x), mix(a, b, w.x), w.y);
return 1.0 - blend;
}
void vertex() {
// Get vertex of flat plane in world coordinates and set world UV
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
@ -162,15 +188,21 @@ void vertex() {
// Set final vertex height & calculate vertex normals. 3 lookups
ivec3 uv_a = get_index_coord(start_pos, VERTEX_PASS);
ivec3 uv_b = get_index_coord(end_pos, VERTEX_PASS);
float h = mix(texelFetch(_height_maps, uv_a, 0).r,texelFetch(_height_maps, uv_b, 0).r,vertex_lerp);
v_vertex.y = h;
float h = mix(texelFetch(_height_maps, uv_a, 0).r, texelFetch(_height_maps, uv_b, 0).r, vertex_lerp);
// Vertex normals
// Apply background ground level and region blend
float ground_h = ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2));
v_vertex.y = h + ground_h;
bg_ddxy.x = ground_h - ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2 + vec2(_region_texel_size, 0.)));
bg_ddxy.y = ground_h - ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2 + vec2(0., _region_texel_size)));
// Vertex normals adding background normals
float u = mix(texelFetch(_height_maps, get_index_coord(start_pos + vec2(1,0), VERTEX_PASS), 0).r,
texelFetch(_height_maps, get_index_coord(end_pos + vec2(1,0), VERTEX_PASS), 0).r, vertex_lerp);
float v = mix(texelFetch(_height_maps, get_index_coord(start_pos + vec2(0,1), VERTEX_PASS), 0).r,
texelFetch(_height_maps, get_index_coord(end_pos + vec2(0,1), VERTEX_PASS), 0).r, vertex_lerp);
u += bg_ddxy.x;
v += bg_ddxy.y;
v_normal = normalize(vec3(h - u, _vertex_spacing, h - v));
}

View File

@ -1,16 +1,56 @@
// Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
R"(
//INSERT: FLAT_UNIFORMS
uniform float ground_level : hint_range(-1000., 1000.) = 0.0;
uniform float region_blend : hint_range(.001, 1., 0.001) = 0.25;
varying vec2 bg_ddxy;
//INSERT: FLAT_FUNCTIONS
// Takes in UV2 region space coordinates, returns 1.0 or 0.0 if a region is present or not.
float check_region(const vec2 uv2) {
ivec2 pos = ivec2(floor(uv2)) + (_region_map_size / 2);
int layer_index = 0;
if (uint(pos.x | pos.y) < uint(_region_map_size)) {
layer_index = clamp(_region_map[ pos.y * _region_map_size + pos.x ] - 1, -1, 0) + 1;
}
return float(layer_index);
}
// Takes in UV2 region space coordinates, returns a blend value (0 - 1 range) between empty, and valid regions
float get_region_blend(vec2 uv2) {
uv2 -= 0.5;
const vec2 offset = vec2(0.0, 1.0);
float a = check_region(uv2 + offset.xy);
float b = check_region(uv2 + offset.yy);
float c = check_region(uv2 + offset.yx);
float d = check_region(uv2 + offset.xx);
vec2 w = smoothstep(vec2(0.0), vec2(1.0), fract(uv2));
float blend = mix(mix(d, c, w.x), mix(a, b, w.x), w.y);
return 1.0 - blend;
}
//INSERT: FLAT_VERTEX
// Apply background ground level and region blend
float ground_h = ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2));
h += ground_h;
bg_ddxy.x = ground_h - ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2 + vec2(_region_texel_size, 0.)));
bg_ddxy.y = ground_h - ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2 + vec2(0., _region_texel_size)));
//INSERT: FLAT_FRAGMENT
// Apply background normal
u += bg_ddxy.x;
v += bg_ddxy.y;
//INSERT: WORLD_NOISE_UNIFORMS
group_uniforms world_background_noise;
uniform float region_blend : hint_range(.001, 1., 0.001) = 0.25;
uniform bool world_noise_fragment_normals = false;
uniform float world_noise_region_blend : hint_range(0.05, 0.95, 0.01) = 0.33;
uniform int world_noise_max_octaves : hint_range(0, 15) = 4;
uniform int world_noise_min_octaves : hint_range(0, 15) = 2;
uniform float world_noise_lod_distance : hint_range(0, 40000, 1) = 7500.;
uniform float world_noise_lod_distance : hint_range(0., 40000., 1.) = 7500.;
uniform float world_noise_scale : hint_range(0.25, 20, 0.01) = 5.0;
uniform float world_noise_height : hint_range(0, 1000, 0.1) = 64.0;
uniform float world_noise_height : hint_range(-1000., 1000., 0.1) = 32.0;
uniform vec3 world_noise_offset = vec3(0.0);
group_uniforms;
varying vec2 world_noise_ddxy;
@ -29,9 +69,9 @@ float check_region(const vec2 uv2) {
}
// Takes in UV2 region space coordinates, returns a blend value (0 - 1 range) between empty, and valid regions
float region_blend(vec2 uv2) {
float get_region_blend(vec2 uv2) {
uv2 -= 0.5;
const vec2 offset = vec2(0.0,1.0);
const vec2 offset = vec2(0.0, 1.0);
float a = check_region(uv2 + offset.xy);
float b = check_region(uv2 + offset.yy);
float c = check_region(uv2 + offset.yx);
@ -95,16 +135,16 @@ float world_noise(vec2 p) {
}
float get_noise_height(const vec2 uv) {
float weight = region_blend(uv);
// only calculate world noise when it could be visibile.
if (weight <= 1.0 - world_noise_region_blend) {
float weight = get_region_blend(uv);
// Only calculate world noise when it would be visible.
if (weight <= 1.0 - region_blend) {
return 0.0;
}
//TODO: Offset/scale UVs are semi-dependent upon region size 1024. Base on v_vertex.xz instead
float noise = world_noise((uv + world_noise_offset.xz * 1024. / _region_size) * world_noise_scale * _region_size / 1024. * .1) *
world_noise_height * 10. + world_noise_offset.y * 100.;
weight = smoothstep(1.0 - world_noise_region_blend, 1.0, weight);
return mix(0.0, noise, weight);
weight = smoothstep(1.0 - region_blend, 1.0, weight);
return mix(0.0, noise, weight);
}
// World Noise Functions End
@ -114,11 +154,11 @@ float get_noise_height(const vec2 uv) {
if (_background_mode == 2u) {
vec2 nuv_a = fma(start_pos, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));
vec2 nuv_b = fma(end_pos, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));
float nh = mix(get_noise_height(nuv_a),get_noise_height(nuv_b),vertex_lerp);
float nh = mix(get_noise_height(nuv_a), get_noise_height(nuv_b), vertex_lerp);
float nu = mix(get_noise_height(nuv_a + vec2(_region_texel_size, 0.0)),
get_noise_height(nuv_b + vec2(_region_texel_size, 0.0)),vertex_lerp);
get_noise_height(nuv_b + vec2(_region_texel_size, 0.0)), vertex_lerp);
float nv = mix(get_noise_height(nuv_a + vec2(0.0, _region_texel_size)),
get_noise_height(nuv_b + vec2(0.0, _region_texel_size)),vertex_lerp);
get_noise_height(nuv_b + vec2(0.0, _region_texel_size)), vertex_lerp);
world_noise_ddxy = vec2(nh - nu, nh - nv);
h += nh;
}

View File

@ -72,6 +72,7 @@ uniform highp sampler2DArray _control_maps : repeat_disable;
// Public uniforms
group_uniforms general;
//INSERT: FLAT_UNIFORMS
uniform bool flat_terrain_normals = false;
uniform float blend_sharpness : hint_range(0, 1) = 0.5;
uniform bool vertical_projection = true;
@ -150,6 +151,7 @@ vec3 get_index_uv(const vec2 uv2) {
return vec3(uv2 - _region_locations[layer_index], float(layer_index));
}
//INSERT: FLAT_FUNCTIONS
//INSERT: WORLD_NOISE_FUNCTIONS
void vertex() {
// Get vertex of flat plane in world coordinates and set world UV
@ -194,7 +196,8 @@ void vertex() {
// Set final vertex height & calculate vertex normals. 3 lookups
ivec3 coord_a = get_index_coord(start_pos, VERTEX_PASS);
ivec3 coord_b = get_index_coord(end_pos, VERTEX_PASS);
float h = mix(texelFetch(_height_maps, coord_a, 0).r,texelFetch(_height_maps, coord_b, 0).r,vertex_lerp);
float h = mix(texelFetch(_height_maps, coord_a, 0).r, texelFetch(_height_maps, coord_b, 0).r, vertex_lerp);
//INSERT: FLAT_VERTEX
//INSERT: WORLD_NOISE_VERTEX
v_vertex.y = h;
}
@ -406,10 +409,11 @@ void fragment() {
// Terrain normals
vec3 index_normal[4];
float h[4];
// allows additional derivatives, eg world noise, brush previews etc
// Allows for additional derivatives, eg world background, brush previews etc
float u = 0.0;
float v = 0.0;
//INSERT: FLAT_FRAGMENT
//INSERT: WORLD_NOISE_FRAGMENT
// Re-use index[] for the first lookups, skipping some math. 3 lookups
h[3] = texelFetch(_height_maps, index[3], 0).r; // 0 (0,0)

View File

@ -24,8 +24,8 @@ void Terrain3DMaterial::_preload_shaders() {
#include "shaders/samplers.glsl"
, "samplers");
_parse_shader(
#include "shaders/world_noise.glsl"
, "world_noise");
#include "shaders/backgrounds.glsl"
, "backgrounds");
_parse_shader(
#include "shaders/auto_shader.glsl"
, "auto_shader");
@ -124,6 +124,12 @@ String Terrain3DMaterial::_apply_inserts(const String &p_shader, const Array &p_
String Terrain3DMaterial::_generate_shader_code() const {
LOG(INFO, "Generating default shader code");
Array excludes;
if (_world_background != FLAT) {
excludes.push_back("FLAT_UNIFORMS");
excludes.push_back("FLAT_FUNCTIONS");
excludes.push_back("FLAT_VERTEX");
excludes.push_back("FLAT_FRAGMENT");
}
if (_world_background != NOISE) {
excludes.push_back("WORLD_NOISE_UNIFORMS");
excludes.push_back("WORLD_NOISE_FUNCTIONS");