diff --git a/data/shaders/common.hlsl b/data/shaders/common.hlsl index bc763846f..0a377e4f1 100644 --- a/data/shaders/common.hlsl +++ b/data/shaders/common.hlsl @@ -23,12 +23,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SPARTAN_COMMON //= INCLUDES ==================== +#include "common_terrain.hlsl" #include "common_resources.hlsl" #include "common_colorspace.hlsl" #include "common_sampling.hlsl" //=============================== -/*------------------------------------------------------------------------------ +/*----------------------------------------------------------------------------- CONSTANTS ------------------------------------------------------------------------------*/ static const float PI = 3.14159265f; diff --git a/data/shaders/common_sampling.hlsl b/data/shaders/common_sampling.hlsl index a3dc24c10..7c87ccf00 100644 --- a/data/shaders/common_sampling.hlsl +++ b/data/shaders/common_sampling.hlsl @@ -22,8 +22,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SPARTAN_COMMON_SAMPLING #define SPARTAN_COMMON_SAMPLING -static const float snow_level = 75.0f; - struct sampling { static float4 hash4(float2 p) @@ -69,33 +67,17 @@ struct sampling return float4(lerp(va / w1, res, v), 1.0f); } - static float apply_snow_level_variation(float3 position_world) - { - // define constants - const float frequency = 0.3f; - const float amplitude = 10.0f; - - // apply sine wave based on world position - float sine_value = sin(position_world.x * frequency); - - // map sine value from [-1, 1] to [0, 1] - sine_value = sine_value * 0.5 + 0.5; - - // apply height variation and add to base snow level - return snow_level + sine_value * amplitude; - } - static float4 smart(float3 position, float3 normal, float2 uv, uint texture_index, bool is_water, bool is_terrain) { // parameters - const float sea_level = 0.0f; const float sand_offset = 0.75f; - const float snow_blend_speed = 0.1f; const float2 direction_1 = float2(1.0, 0.5); const float2 direction_2 = float2(-0.5, 1.0); const float speed_1 = 0.2; const float speed_2 = 0.15; + float snow_blend_factor = get_snow_blend_factor(position); + float4 color; if (is_water) // animate using interleaved UVs { @@ -112,18 +94,13 @@ struct sampling float4 tex_sand = GET_TEXTURE(texture_index + 2).Sample(GET_SAMPLER(sampler_anisotropic_wrap), uv); // compute blend factors - const float snow_level = apply_snow_level_variation(position); - float slope = saturate(pow(saturate(dot(normal, float3(0.0f, 1.0f, 0.0f)) - -0.25f), 24.0f)); - float distance_to_snow = position.y - snow_level; - float snow_blend_factor = saturate(1.0 - max(0.0, -distance_to_snow) * snow_blend_speed); - float sand_blend_factor = saturate(position.y / sand_offset); - - // determine where the sand should appear: only below a certain elevation + float sand_blend_factor = saturate(position.y / sand_offset); float sand_blend_threshold = sea_level + sand_offset; // define a threshold above which no sand should appear float sand_factor = saturate((position.y - sea_level) / (sand_blend_threshold - sea_level)); sand_blend_factor = 1.0f - sand_factor; // invert factor: 1 near sea level, 0 above the threshold // blend textures + float slope = saturate(pow(saturate(dot(normal, float3(0.0f, 1.0f, 0.0f)) - -0.25f), 24.0f)); float4 terrain = lerp(tex_rock, tex_grass, slope); // blend base terrain with slope color = lerp(terrain, tex_sand, sand_blend_factor); // then blend in sand based on height color = lerp(color, 0.95f, snow_blend_factor); // blend in the snow @@ -131,6 +108,10 @@ struct sampling else // default texture sampling { color = GET_TEXTURE(texture_index).Sample(GET_SAMPLER(sampler_anisotropic_wrap), uv); + + // define snow color and blend with it (if needed) + float4 snow_color = float4(0.95f, 0.95f, 0.95f, 1.0f); + color = lerp(color, snow_color, snow_blend_factor); } return color; diff --git a/data/shaders/common_terrain.hlsl b/data/shaders/common_terrain.hlsl new file mode 100644 index 000000000..3c89d57cb --- /dev/null +++ b/data/shaders/common_terrain.hlsl @@ -0,0 +1,51 @@ +/* +Copyright(c) 2016-2025 Panos Karabelas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +static const float sea_level = 0.0f; +static const float snow_level = 75.0f; + +static float get_snow_level_with_variation(float3 position_world) +{ + // define constants + const float frequency = 0.3f; + const float amplitude = 10.0f; + + // apply sine wave based on world position + float sine_value = sin(position_world.x * frequency); + + // map sine value from [-1, 1] to [0, 1] + sine_value = sine_value * 0.5 + 0.5; + + // apply height variation and add to base snow level + return snow_level + sine_value * amplitude; +} + +static float get_snow_blend_factor(float3 position_world) +{ + const float snow_blend_speed = 0.1f; + const float max_blend_factor = 0.7f; // cap the max so we can still see the base texture + + const float snow_level = get_snow_level_with_variation(position_world); + float distance_to_snow = position_world.y - snow_level; + float snow_blend_factor = min(saturate(1.0 - max(0.0, -distance_to_snow) * snow_blend_speed), max_blend_factor); + + return snow_blend_factor; +} diff --git a/data/shaders/common_vertex_processing.hlsl b/data/shaders/common_vertex_processing.hlsl index 8ae5df668..93d43f7e3 100644 --- a/data/shaders/common_vertex_processing.hlsl +++ b/data/shaders/common_vertex_processing.hlsl @@ -47,7 +47,7 @@ struct gbuffer_vertex float3 normal : NORMAL_WORLD; float3 tangent : TANGENT_WORLD; float2 uv : TEXCOORD; - float3 color : COLOR_; + float3 color : COLOR; uint instance_id : INSTANCE_ID; matrix transform : TRANSFORM; matrix transform_previous : TRANSFORM_PREVIOUS; @@ -271,21 +271,13 @@ struct vertex_processing } } - static void process_world_space(Surface surface, inout float3 position_world, float3 normal_world, float3 position_local, float4x4 transform, float width_percent, float height_percent, uint instance_id, float time_offset = 0.0f) + static void process_world_space(Surface surface, inout float3 position_world, inout gbuffer_vertex vertex, float3 position_local, float4x4 transform, float width_percent, float height_percent, uint instance_id, float time_offset = 0.0f) { float time = (float)buffer_frame.time + time_offset; float3 wind = buffer_frame.wind; if (surface.is_grass_blade()) { - // the blade is super thin, so thicken it when viewed from the side - //float3 view_direction = get_view_direction(position_world); - //float v_dot_n = abs(dot(normal_world.xz, view_direction.xz)); - //float thickness_offset = 1.0f - v_dot_n; - //float blade_x_direction = width_percent < 0.5f ? -1.0f : 1.0f; - //float3 offset = thickness_offset * blade_x_direction * 0.2f; - //position_world.x += offset.x; - // wind simulation { const float wind_direction_scale = 0.05f; // scale for large-scale wind direction noise @@ -304,6 +296,12 @@ struct vertex_processing float2 wind_offset = float2(cos(wind_direction), sin(wind_direction)) * wind_lean_angle; position_world.xz += wind_offset * height_percent; } + + // color adjustment + { + float snow_blend_factor = get_snow_blend_factor(position_world); + vertex.color = lerp(vertex.color, float3(0.95f, 0.95f, 0.95f), snow_blend_factor); + } } if (surface.vertex_animate_wind() && !surface.is_grass_blade()) @@ -371,8 +369,8 @@ gbuffer_vertex transform_to_world_space(Vertex_PosUvNorTan input, uint instance_ vertex.transform_previous = transform_previous; // vertex processing - world space - vertex_processing::process_world_space(surface, vertex.position, vertex.normal, input.position.xyz, transform, width_percent, height_percent, instance_id); - vertex_processing::process_world_space(surface, vertex.position_previous, vertex.normal, input.position.xyz, transform_previous, width_percent, height_percent, instance_id, -buffer_frame.delta_time); + vertex_processing::process_world_space(surface, vertex.position, vertex, input.position.xyz, transform, width_percent, height_percent, instance_id); + vertex_processing::process_world_space(surface, vertex.position_previous, vertex, input.position.xyz, transform_previous, width_percent, height_percent, instance_id, -buffer_frame.delta_time); return vertex; } diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 7f915e07a..558e2da68 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -48,14 +48,14 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI gbuffer main_ps(gbuffer_vertex vertex) { // setup - MaterialParameters material = GetMaterial(); - float4 albedo = material.color; - float3 normal = vertex.normal.xyz; - float roughness = material.roughness; - float metalness = material.metallness; - float emission = 0.0f; - float2 velocity = 0.0f; - float occlusion = 1.0f; + MaterialParameters material = GetMaterial(); + float4 albedo = material.color; + float3 normal = vertex.normal.xyz; + float roughness = material.roughness; + float metalness = material.metallness; + float emission = 0.0f; + float2 velocity = 0.0f; + float occlusion = 1.0f; Surface surface; surface.flags = material.flags; diff --git a/runtime/Geometry/GeometryGeneration.h b/runtime/Geometry/GeometryGeneration.h index 514cbe94e..c558c78d2 100644 --- a/runtime/Geometry/GeometryGeneration.h +++ b/runtime/Geometry/GeometryGeneration.h @@ -336,10 +336,10 @@ namespace spartan::geometry_generation // constants const int blade_segment_count = 6; // segments that make up the blade - const float grass_width = 0.1f; // blade width at the base + const float grass_width = 0.2f; // blade width at the base const float grass_height = 1.0f; // blade height - const float thinning_start = 0.5f; // point (0 to 1) where thinning begins (0.5 = midpoint) - const float thinning_power = 1.0f; // controls sharpness of thinning after taper_start (higher = sharper) + const float thinning_start = 0.4f; // the point at which thinning begins (0 is base, 1 is top) + const float thinning_power = 1.0f; // thinning sharpness after thinning_start // number of vertices for one side (front face) int vertices_per_face = (blade_segment_count + 1) * 2 - 1; // total vertices per face, accounting for single top point