From b875c2089b023d6655dc7a1b1509258511da05ba Mon Sep 17 00:00:00 2001 From: Sara Date: Thu, 20 Nov 2025 23:33:21 +0100 Subject: [PATCH] feat: implemented terrain shader --- .../assets/materials/terrain_material.tres | 45 +++++++ project/assets/shaders/terrain.gdshader | 119 ++++++++++++++++++ .../{ => assets/shaders}/terrain.gdshader.uid | 0 project/scenes/editor.tscn | 76 +++++------ project/terrain.gdshader | 9 -- 5 files changed, 198 insertions(+), 51 deletions(-) create mode 100644 project/assets/materials/terrain_material.tres create mode 100644 project/assets/shaders/terrain.gdshader rename project/{ => assets/shaders}/terrain.gdshader.uid (100%) delete mode 100644 project/terrain.gdshader diff --git a/project/assets/materials/terrain_material.tres b/project/assets/materials/terrain_material.tres new file mode 100644 index 00000000..78cdfa26 --- /dev/null +++ b/project/assets/materials/terrain_material.tres @@ -0,0 +1,45 @@ +[gd_resource type="ShaderMaterial" load_steps=8 format=3 uid="uid://8j7uyk0vnllg"] + +[ext_resource type="Shader" uid="uid://dsbxpdveoilep" path="res://assets/shaders/terrain.gdshader" id="1_ho7uc"] + +[sub_resource type="Gradient" id="Gradient_ho7uc"] +colors = PackedColorArray(0.141108, 0.450995, 0.106614, 1, 0.259524, 0.499032, 0.232917, 1) + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_mqhx5"] + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_waqik"] +color_ramp = SubResource("Gradient_ho7uc") +noise = SubResource("FastNoiseLite_mqhx5") + +[sub_resource type="Gradient" id="Gradient_mqhx5"] + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_waqik"] + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_oj3sr"] +color_ramp = SubResource("Gradient_mqhx5") +noise = SubResource("FastNoiseLite_waqik") + +[resource] +render_priority = 0 +shader = ExtResource("1_ho7uc") +shader_parameter/floor_1_albedo = SubResource("NoiseTexture2D_waqik") +shader_parameter/floor_1_roughness = 1.0 +shader_parameter/floor_1_tiling = 500.0 +shader_parameter/floor_1_specular = 0.0 +shader_parameter/floor_1_metallic = 0.5 +shader_parameter/floor_2_roughness = 0.7 +shader_parameter/floor_2_tiling = 500.0 +shader_parameter/floor_2_specular = 0.5 +shader_parameter/floor_2_metallic = 0.5 +shader_parameter/floor_3_roughness = 0.7 +shader_parameter/floor_3_tiling = 500.0 +shader_parameter/floor_3_specular = 0.5 +shader_parameter/floor_3_metallic = 0.5 +shader_parameter/region_blending = 1.0 +shader_parameter/slope_threshold = 0.25 +shader_parameter/slope_blend_distance = 0.0 +shader_parameter/slope_albedo = SubResource("NoiseTexture2D_oj3sr") +shader_parameter/slope_tiling = 100.0 +shader_parameter/slope_roughness = 1.0 +shader_parameter/slope_specular = 1.0 +shader_parameter/slope_metallic = 0.5 diff --git a/project/assets/shaders/terrain.gdshader b/project/assets/shaders/terrain.gdshader new file mode 100644 index 00000000..fecf6e67 --- /dev/null +++ b/project/assets/shaders/terrain.gdshader @@ -0,0 +1,119 @@ +shader_type spatial; +render_mode blend_mix, depth_draw_opaque, diffuse_lambert, specular_schlick_ggx, skip_vertex_transform; + +varying vec3 var_model_normal; +varying vec3 var_vertex_position; + +group_uniforms Floor1; +uniform sampler2D floor_1_albedo : source_color, hint_default_black, filter_linear_mipmap; +uniform sampler2D floor_1_normal : hint_normal, filter_linear_mipmap; +uniform float floor_1_roughness = 0.7; +uniform float floor_1_tiling = 500.0; +uniform float floor_1_specular = 0.5; +uniform float floor_1_metallic = 0.5; + +group_uniforms Floor2; +uniform sampler2D floor_2_albedo : source_color, hint_default_black, filter_linear_mipmap; +uniform sampler2D floor_2_normal : hint_normal, filter_linear_mipmap; +uniform float floor_2_roughness = 0.7; +uniform float floor_2_tiling = 500.0; +uniform float floor_2_specular = 0.5; +uniform float floor_2_metallic = 0.5; + +group_uniforms Floor3; +uniform sampler2D floor_3_albedo : source_color, hint_default_black, filter_linear_mipmap; +uniform sampler2D floor_3_normal : hint_normal, filter_linear_mipmap; +uniform float floor_3_roughness = 0.7; +uniform float floor_3_tiling = 500.0; +uniform float floor_3_specular = 0.5; +uniform float floor_3_metallic = 0.5; + +group_uniforms Regions; +uniform sampler2D floor_region_map : hint_default_black, filter_linear_mipmap; +uniform float region_blending : hint_range(0, 10) = 1.0; + +group_uniforms Slope.Textures; +uniform sampler2D slope_albedo : source_color, hint_default_white; +uniform sampler2D slope_normal : hint_normal, filter_linear_mipmap; + +uniform float slope_tiling = 100.0; +uniform float slope_roughness = 0.7; +uniform float slope_specular = 0.5; +uniform float slope_metallic = 0.5; + +group_uniforms; group_uniforms Slope; +uniform float slope_threshold = 0.9; +uniform float slope_blend_distance = 0.05; + +void vertex() { + var_model_normal = NORMAL; + var_vertex_position = (vec4(VERTEX, 1.0) * MODEL_MATRIX).xyz; + VERTEX = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz; + NORMAL = normalize(MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz; + TANGENT = normalize(MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz; + BINORMAL = normalize(MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz; +} + +vec3 lerp(vec3 a, vec3 b, float t) { + return (1.0 - t) * a + b * t; +} + +float lerpf(float a, float b, float t) { + return (1.0 - t) * a + b * t; +} + +vec2 lerp2(vec2 a, vec2 b, float t) { + return (1.0 - t) * a + b * t; +} + +// adapted from https://github.com/Experience-Monks/glsl-fast-gaussian-blur/blob/master/9.glsl +vec4 sample_texture_blurred(sampler2D tex, vec2 uv) { + if(region_blending == 0.0) // short-circuit for zero case + return texture(tex, uv); + vec4 color = vec4(0.0); + vec2 resolution = vec2(textureSize(tex, 0)); + vec2 offset_1 = vec2(1.3846153846) * region_blending; + vec2 offset_2 = vec2(3.2307692308) * region_blending; + // add weighted samples. Magic numbers are pre-computed gaussian function results + color += texture(tex, uv) * 0.2270270270; + color += texture(tex, uv + (offset_1 / resolution)) * 0.3162162162; + color += texture(tex, uv - (offset_1 / resolution)) * 0.3162162162; + color += texture(tex, uv + (offset_2 / resolution)) * 0.0702702703; + color += texture(tex, uv - (offset_2 / resolution)) * 0.0702702703; + return color; +} + +void fragment() { + float slope_blend = clamp(slope_threshold + ((acos(abs(var_model_normal.y)) / (PI * 0.5) - slope_threshold) * 1.0/slope_blend_distance), 0.0, 1.0); + + vec4 region_masks = sample_texture_blurred(floor_region_map, UV); + vec2 sqr_normal = normalize(var_model_normal.xz * var_model_normal.xz); + float biplanar_index = round(abs(sqr_normal.x)); + vec2 slope_uv = lerp2(var_vertex_position.xy, var_vertex_position.yz, biplanar_index); + + ALBEDO = texture(floor_1_albedo, UV * floor_1_tiling).xyz; + ALBEDO = lerp(ALBEDO, texture(floor_2_albedo, UV * floor_2_tiling).xyz, region_masks.x); + ALBEDO = lerp(ALBEDO, texture(floor_3_albedo, UV * floor_3_tiling).xyz, region_masks.y); + ALBEDO = lerp(ALBEDO, texture(slope_albedo, slope_uv * slope_tiling).xyz, slope_blend); + //ALBEDO = region_masks.xyz; + + NORMAL_MAP = texture(floor_1_normal, UV * floor_1_tiling).xyz; + NORMAL_MAP = lerp(NORMAL_MAP, texture(floor_2_normal, UV * floor_2_tiling).xyz, region_masks.x); + NORMAL_MAP = lerp(NORMAL_MAP, texture(floor_3_normal, UV * floor_3_tiling).xyz, region_masks.y); + NORMAL_MAP = lerp(NORMAL_MAP, texture(slope_normal, slope_uv * slope_tiling).xyz, slope_blend); + + SPECULAR = floor_1_specular; + SPECULAR = lerpf(SPECULAR, floor_2_specular, region_masks.x); + SPECULAR = lerpf(SPECULAR, floor_3_specular, region_masks.y); + SPECULAR = lerpf(SPECULAR, slope_specular, slope_blend); + + ROUGHNESS = floor_1_roughness; + ROUGHNESS = lerpf(ROUGHNESS, floor_2_roughness, region_masks.x); + ROUGHNESS = lerpf(ROUGHNESS, floor_3_roughness, region_masks.y); + ROUGHNESS = lerpf(ROUGHNESS, slope_roughness, slope_blend); + + METALLIC = floor_1_metallic; + METALLIC = lerpf(METALLIC, floor_2_metallic, region_masks.x); + METALLIC = lerpf(METALLIC, floor_3_metallic, region_masks.y); + METALLIC = lerpf(METALLIC, slope_metallic, slope_blend); +} diff --git a/project/terrain.gdshader.uid b/project/assets/shaders/terrain.gdshader.uid similarity index 100% rename from project/terrain.gdshader.uid rename to project/assets/shaders/terrain.gdshader.uid diff --git a/project/scenes/editor.tscn b/project/scenes/editor.tscn index f1061b15..162a03e1 100644 --- a/project/scenes/editor.tscn +++ b/project/scenes/editor.tscn @@ -1,29 +1,7 @@ -[gd_scene load_steps=14 format=3 uid="uid://xm383pc5pcnn"] +[gd_scene load_steps=13 format=3 uid="uid://xm383pc5pcnn"] [ext_resource type="PackedScene" uid="uid://cnux2fqne284i" path="res://objects/primitive_nodes/point_primitive_node.tscn" id="1_b1cmn"] - -[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_sldah"] -sky_top_color = Color(0.22, 0.3575, 0.55, 1) -sky_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) -ground_bottom_color = Color(0.167133, 0.2, 0.132, 1) -ground_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) - -[sub_resource type="Sky" id="Sky_b1cmn"] -sky_material = SubResource("ProceduralSkyMaterial_sldah") - -[sub_resource type="Environment" id="Environment_pxqd5"] -background_mode = 2 -sky = SubResource("Sky_b1cmn") -ambient_light_source = 2 -ambient_light_color = Color(0.2546, 0.27132, 0.38, 1) -tonemap_mode = 2 -glow_enabled = true -volumetric_fog_density = 0.0 -volumetric_fog_emission_energy = 1024.0 -volumetric_fog_length = 831.81 -volumetric_fog_detail_spread = 0.5 -volumetric_fog_sky_affect = 0.904 -volumetric_fog_temporal_reprojection_amount = 0.99 +[ext_resource type="Material" uid="uid://8j7uyk0vnllg" path="res://assets/materials/terrain_material.tres" id="1_pxqd5"] [sub_resource type="PointPrimitive" id="PointPrimitive_pxqd5"] slope = -0.585 @@ -55,8 +33,28 @@ interpolation_color_space = 2 offsets = PackedFloat32Array(0.0743595, 0.315422, 0.678597, 0.725377) colors = PackedColorArray(0.17652, 0.24, 0.0744, 1, 0.129706, 0.117486, 0.129714, 1, 0.216666, 0.201746, 0.216677, 1, 0.786852, 0.768096, 0.786791, 1) -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_b1cmn"] -vertex_color_use_as_albedo = true +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_sldah"] +sky_top_color = Color(0.22, 0.3575, 0.55, 1) +sky_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) +ground_bottom_color = Color(0.167133, 0.2, 0.132, 1) +ground_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) + +[sub_resource type="Sky" id="Sky_b1cmn"] +sky_material = SubResource("ProceduralSkyMaterial_sldah") + +[sub_resource type="Environment" id="Environment_pxqd5"] +background_mode = 2 +sky = SubResource("Sky_b1cmn") +ambient_light_source = 2 +ambient_light_color = Color(0.2546, 0.27132, 0.38, 1) +tonemap_mode = 2 +glow_enabled = true +volumetric_fog_density = 0.0 +volumetric_fog_emission_energy = 1024.0 +volumetric_fog_length = 831.81 +volumetric_fog_detail_spread = 0.5 +volumetric_fog_sky_affect = 0.904 +volumetric_fog_temporal_reprojection_amount = 0.99 [sub_resource type="GDScript" id="GDScript_b1cmn"] script/source = "extends Camera3D @@ -103,10 +101,18 @@ func _unhandled_input(event: InputEvent) -> void: global_position = (global_position - pivot).normalized() * distance + pivot " -[sub_resource type="BoxMesh" id="BoxMesh_sldah"] - [node name="Node3D" type="Node3D"] +[node name="TerrainMeshEditor" type="TerrainMeshEditor" parent="."] +primitives = [SubResource("PointPrimitive_pxqd5"), SubResource("NoisePrimitive_ba0ut"), SubResource("NoisePrimitive_pxqd5")] +vertex_color_gradient = SubResource("Gradient_b1cmn") +color_gradient_end_height = 200.0 +chunk_size = 500.0 +lod1_detail = 30 +chunk_count = 5 +material = ExtResource("1_pxqd5") +point_primitive_object = ExtResource("1_b1cmn") + [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_pxqd5") @@ -117,24 +123,10 @@ shadow_enabled = true shadow_reverse_cull_face = true directional_shadow_max_distance = 300.0 -[node name="TerrainMeshEditor" type="TerrainMeshEditor" parent="."] -primitives = [SubResource("PointPrimitive_pxqd5"), SubResource("NoisePrimitive_ba0ut"), SubResource("NoisePrimitive_pxqd5")] -vertex_color_gradient = SubResource("Gradient_b1cmn") -color_gradient_end_height = 200.0 -chunk_size = 500.0 -lod1_detail = 10 -chunk_count = 5 -material = SubResource("StandardMaterial3D_b1cmn") -point_primitive_object = ExtResource("1_b1cmn") - [node name="Camera3D" type="Camera3D" parent="."] transform = Transform3D(0.707107, -0.377082, 0.598171, 0, 0.845942, 0.533275, -0.707107, -0.377082, 0.598171, 504.544, 443.732, 504.544) fov = 57.3 far = 2000.0 script = SubResource("GDScript_b1cmn") -[node name="ReferenceBox" type="MeshInstance3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 5.88292, 0) -mesh = SubResource("BoxMesh_sldah") - [connection signal="primitives_changed" from="TerrainMeshEditor" to="TerrainMeshEditor" method="_on_primitives_changed"] diff --git a/project/terrain.gdshader b/project/terrain.gdshader deleted file mode 100644 index 1919f83b..00000000 --- a/project/terrain.gdshader +++ /dev/null @@ -1,9 +0,0 @@ -shader_type spatial; - -void vertex() { - -} - -//void fragment() { - // Called for every pixel the material is visible on. -//} \ No newline at end of file