Skip to content

Commit

Permalink
[forest] both trees and grass will receive a white tint if they are a…
Browse files Browse the repository at this point in the history
…bove snow level
  • Loading branch information
PanosK92 committed Feb 23, 2025
1 parent 7c55afc commit 925763c
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 51 deletions.
3 changes: 2 additions & 1 deletion data/shaders/common.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
35 changes: 8 additions & 27 deletions data/shaders/common_sampling.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
{
Expand All @@ -112,25 +94,24 @@ 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
}
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;
Expand Down
51 changes: 51 additions & 0 deletions data/shaders/common_terrain.hlsl
Original file line number Diff line number Diff line change
@@ -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;
}
22 changes: 10 additions & 12 deletions data/shaders/common_vertex_processing.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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())
Expand Down Expand Up @@ -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;
}
Expand Down
16 changes: 8 additions & 8 deletions data/shaders/g_buffer.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
6 changes: 3 additions & 3 deletions runtime/Geometry/GeometryGeneration.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 925763c

Please sign in to comment.