Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Terrain3D.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,13 @@
<None Include="src\shaders\debug_views.glsl" />
<None Include="src\shaders\displacement.glsl" />
<None Include="src\shaders\displacement_buffer.glsl" />
<None Include="src\shaders\macro_variation.glsl" />
<None Include="src\shaders\main.glsl" />
<None Include="src\shaders\overlays.glsl" />
<None Include="src\shaders\pbr_views.glsl">
<FileType>Document</FileType>
</None>
<None Include="src\shaders\projection.glsl" />
<None Include="src\shaders\samplers.glsl" />
<None Include="src\shaders\backgrounds.glsl" />
<None Include="src\shaders\editor_functions.glsl" />
Expand Down
6 changes: 6 additions & 0 deletions Terrain3D.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,12 @@
<None Include="doc\docs\displacement.md">
<Filter>2. Docs</Filter>
</None>
<None Include="src\shaders\macro_variation.glsl">
<Filter>4. Shaders</Filter>
</None>
<None Include="src\shaders\projection.glsl">
<Filter>4. Shaders</Filter>
</None>
</ItemGroup>
<ItemGroup>
<Text Include=".readthedocs.yaml">
Expand Down
102 changes: 54 additions & 48 deletions project/addons/terrain_3d/extras/shaders/lightweight.gdshader
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
// This shader is the minimum needed to allow the terrain to function, without any texturing.

shader_type spatial;
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;

Expand All @@ -13,9 +16,6 @@ render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlic
*/

// Defined Constants
#define SKIP_PASS 0
#define VERTEX_PASS 1
#define FRAGMENT_PASS 2
#define COLOR_MAP vec4(1.0, 1.0, 1.0, 0.5)
#define DIV_255 0.003921568627450 // 1. / 255.

Expand All @@ -33,6 +33,7 @@ render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlic
#endif

// Private uniforms
group_uniforms shader_uniforms;
uniform vec3 _target_pos = vec3(0.f);
uniform float _mesh_size = 48.f;
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
Expand All @@ -56,24 +57,25 @@ uniform highp sampler2DArray _control_maps : repeat_disable;
uniform highp sampler2DArray _color_maps : source_color, filter_linear_mipmap_anisotropic, repeat_disable;
uniform highp sampler2DArray _texture_array_albedo : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
uniform highp sampler2DArray _texture_array_normal : hint_normal, filter_linear_mipmap_anisotropic, repeat_enable;
group_uniforms;

// Public uniforms
group_uniforms general;
group_uniforms shader_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 bool textures_enabled = true;
uniform float blend_sharpness : hint_range(0, 1) = 0.5;
group_uniforms;

group_uniforms auto_shader;
group_uniforms shader_uniforms.auto_shader;
uniform float auto_slope : hint_range(0, 10) = 1.0;
uniform float auto_height_reduction : hint_range(0, 1) = 0.1;
uniform int auto_base_texture : hint_range(0, 31) = 0;
uniform int auto_overlay_texture : hint_range(0, 31) = 1;
group_uniforms;

group_uniforms macro_variation;
group_uniforms shader_uniforms.macro_variation;
uniform bool macro_variation_enabled = true;
uniform vec3 macro_variation1 : source_color = vec3(1.);
uniform vec3 macro_variation2 : source_color = vec3(1.);
Expand All @@ -95,24 +97,16 @@ varying mat3 TBN;
// Vertex
////////////////////////

// Takes in world space XZ (UV) coordinates & search depth (only applicable for background mode none)
// Takes in world space XZ (UV) coordinates
// Returns ivec3 with:
// XY: (0 to _region_size - 1) coordinates within a region
// Z: layer index used for texturearrays, -1 if not in a region
ivec3 get_index_coord(const vec2 uv, const int search) {
ivec3 get_index_coord(const vec2 uv) {
vec2 r_uv = round(uv);
vec2 o_uv = mod(r_uv,_region_size);
ivec2 pos;
int bounds, layer_index = -1;
for (int i = -1; i < clamp(search, SKIP_PASS, FRAGMENT_PASS); i++) {
if ((layer_index == -1 && _background_mode == 0u ) || i < 0) {
r_uv -= i == -1 ? vec2(0.0) : vec2(float(o_uv.x <= o_uv.y), float(o_uv.y <= o_uv.x));
pos = ivec2(floor((r_uv) * _region_texel_size)) + (_region_map_size / 2);
bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
layer_index = (_region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1);
}
}
return ivec3(ivec2(mod(r_uv,_region_size)), layer_index);
ivec2 pos = ivec2(floor(r_uv * _region_texel_size)) + (_region_map_size / 2);
int bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
int layer_index = _region_map[pos.y * _region_map_size + pos.x] * bounds - 1;
return ivec3(ivec2(mod(r_uv, _region_size)), layer_index);
}

// Takes in descaled (world_space / region_size) world to region space XZ (UV2) coordinates, returns vec3 with:
Expand All @@ -125,6 +119,16 @@ vec3 get_index_uv(const vec2 uv2) {
return vec3(uv2 - _region_locations[layer_index], float(layer_index));
}

// Takes in world space XZ (UV) and returns if the coordinate should be part of the NONE background.
bool is_none_bg(const vec2 uv) {
ivec4 regions = ivec4(
get_index_coord(uv - vec2(0.5, 0.0)).z,
get_index_coord(uv - vec2(0.0, 0.5)).z,
get_index_coord(uv + vec2(1.0, 0.0)).z,
get_index_coord(uv + vec2(0.0, 1.0)).z);
return any(equal(regions, ivec4(-1)));
}

// 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);
Expand All @@ -137,7 +141,7 @@ 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 get_region_blend(vec2 uv2) {
uv2 -= 0.4989; // Offset from - 0.5 to account for FP errors highlighted by Tessellation.
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);
Expand All @@ -152,10 +156,10 @@ float interpolated_height(vec2 pos) {
const vec2 offsets = vec2(0, 1);
vec2 index_id = floor(pos);
ivec3 index[4];
index[0] = get_index_coord(index_id + offsets.xy, VERTEX_PASS);
index[1] = get_index_coord(index_id + offsets.yy, VERTEX_PASS);
index[2] = get_index_coord(index_id + offsets.yx, VERTEX_PASS);
index[3] = get_index_coord(index_id + offsets.xx, VERTEX_PASS);
index[0] = get_index_coord(index_id + offsets.xy);
index[1] = get_index_coord(index_id + offsets.yy);
index[2] = get_index_coord(index_id + offsets.yx);
index[3] = get_index_coord(index_id + offsets.xx);
float h0 = texelFetch(_height_maps, index[0], 0).r;
float h1 = texelFetch(_height_maps, index[1], 0).r;
float h2 = texelFetch(_height_maps, index[2], 0).r;
Expand Down Expand Up @@ -202,40 +206,41 @@ void vertex() {
UV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));

// Discard vertices for Holes. 1 lookup
ivec3 v_region = get_index_coord(start_pos, VERTEX_PASS);
ivec3 v_region = get_index_coord(start_pos);
uint control = floatBitsToUint(texelFetch(_control_maps, v_region, 0)).r;
bool hole = DECODE_HOLE(control);

// Show holes to all cameras except mouse camera (on exactly 1 layer)
if ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) &&
(hole || (_background_mode == 0u && v_region.z == -1))) {
(hole || (_background_mode == 0u && is_none_bg(UV)))) {
v_vertex.x = 0. / 0.;
} else {
// Set final vertex height, and vertex normal.
float h, u, v;
// This branch is static for each of the clipmap segments
// Interpolated reads only occur where sub-texel values are required.
if (scale < _vertex_spacing) {
h = mix(interpolated_height(start_pos), interpolated_height(end_pos), vertex_lerp);
u = mix(interpolated_height(start_pos + vec2(1, 0)), interpolated_height(end_pos + vec2(1, 0)), vertex_lerp);
v = mix(interpolated_height(start_pos + vec2(0, 1)), interpolated_height(end_pos + vec2(0, 1)), vertex_lerp);
h = interpolated_height(UV);
u = interpolated_height(UV + vec2(1, 0));
v = interpolated_height(UV + vec2(0, 1));
} else {
ivec3 coord_a = get_index_coord(start_pos, VERTEX_PASS);
ivec3 coord_b = get_index_coord(end_pos, VERTEX_PASS);
ivec3 coord_ua = get_index_coord(start_pos + vec2(1, 0), VERTEX_PASS);
ivec3 coord_ub = get_index_coord(end_pos + vec2(1, 0), VERTEX_PASS);
ivec3 coord_va = get_index_coord(start_pos + vec2(0, 1), VERTEX_PASS);
ivec3 coord_vb = get_index_coord(end_pos + vec2(0, 1), VERTEX_PASS);
ivec3 coord_a = get_index_coord(start_pos);
ivec3 coord_b = get_index_coord(end_pos);
ivec3 coord_ua = get_index_coord(start_pos + vec2(1, 0));
ivec3 coord_ub = get_index_coord(end_pos + vec2(1, 0));
ivec3 coord_va = get_index_coord(start_pos + vec2(0, 1));
ivec3 coord_vb = get_index_coord(end_pos + vec2(0, 1));
h = mix(texelFetch(_height_maps, coord_a, 0).r, texelFetch(_height_maps, coord_b, 0).r, vertex_lerp);
u = mix(texelFetch(_height_maps, coord_ua, 0).r, texelFetch(_height_maps, coord_ub, 0).r, vertex_lerp);
v = mix(texelFetch(_height_maps, coord_va, 0).r, texelFetch(_height_maps, coord_vb, 0).r, vertex_lerp);
}

// Apply background ground level and region blend
h += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2));
u += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2 + vec2(_region_texel_size, 0.)));
v += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2 + vec2(0., _region_texel_size)));

vec2 ruv = UV * _region_texel_size;
h += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(ruv));
u += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(ruv + vec2(_region_texel_size, 0.)));
v += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(ruv + vec2(0., _region_texel_size)));

v_vertex.y = h;
v_normal = normalize(vec3(h - u, _vertex_spacing, h - v));
}
Expand Down Expand Up @@ -282,10 +287,10 @@ void fragment() {

ivec3 index[4];
// control map lookups, used for some normal lookups as well
index[0] = get_index_coord(index_id + offsets.xy, FRAGMENT_PASS);
index[1] = get_index_coord(index_id + offsets.yy, FRAGMENT_PASS);
index[2] = get_index_coord(index_id + offsets.yx, FRAGMENT_PASS);
index[3] = get_index_coord(index_id + offsets.xx, FRAGMENT_PASS);
index[0] = get_index_coord(index_id + offsets.xy);
index[1] = get_index_coord(index_id + offsets.yy);
index[2] = get_index_coord(index_id + offsets.yx);
index[3] = get_index_coord(index_id + offsets.xx);

vec3 base_ddx = dFdxCoarse(v_vertex);
vec3 base_ddy = dFdyCoarse(v_vertex);
Expand All @@ -309,7 +314,7 @@ void fragment() {
float ao = 1.0;
float ao_affect = 0.;

if (enable_textures) {
if (textures_enabled) {
// set to zero before accumulation
albedo_height = vec4(0.);
normal_rough = vec4(0.);
Expand All @@ -331,7 +336,8 @@ void fragment() {
float auto_blend = clamp(fma(auto_slope * 2.0, (v_normal.y - 1.0), 1.0)
- auto_height_reduction * 0.01 * v_vertex.y, 0.0, 1.0);
// Enable Autoshader if outside regions or painted in regions, otherwise manual painted
uvec4 is_auto = (control & uvec4(0x1u)) | uvec4(uint(region_uv.z < 0.0));
uvec4 is_auto = (control & uvec4(0x1u)) |
uvec4(lessThan(ivec4(index[0].z, index[1].z, index[2].z, index[3].z), ivec4(0)));
uint u_auto =
((uint(auto_base_texture) & 0x1Fu) << 27u) |
((uint(auto_overlay_texture) & 0x1Fu) << 22u) |
Expand Down
Loading