initial shit

This commit is contained in:
2026-06-04 16:53:41 -05:00
parent f019615187
commit d3779cff20
828 changed files with 512567 additions and 0 deletions
@@ -0,0 +1,195 @@
shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx;
uniform sampler2D blend_noise : source_color;
group_uniforms Surface_detail;
uniform float normal_scale : hint_range(-16.0, 16.0) = 1.0;
uniform float specular : hint_range(0.0, 1.0, 0.01);
uniform float metallic : hint_range(0.0, 1.0, 0.01);
uniform float roughness : hint_range(0.0, 1.0);
group_uniforms MainTexture;
uniform sampler2D main_texture : source_color, repeat_enable, filter_linear_mipmap;
uniform vec4 main_texture_color : source_color = vec4(1.0);
uniform float main_uv_scale = 1.0;
uniform vec3 main_uv_offset;
uniform bool main_uv_triplanar = false;
uniform float main_uv_blend_sharpness: hint_range(0.0, 150.0, 0.001) = 1.0;
uniform sampler2D main_normal: hint_roughness_normal, filter_linear_mipmap, repeat_enable;
group_uniforms SecondaryTexture;
uniform sampler2D secondary_texture : source_color, repeat_enable, filter_linear_mipmap;
uniform vec4 secondary_texture_color : source_color = vec4(1.0);
uniform float secondary_uv_scale = 1.0;
uniform vec3 secondary_uv_offset;
uniform bool secondary_uv_triplanar = false;
uniform float secondary_uv_blend_sharpness : hint_range(0.0, 150.0, 0.001) = 1.0;
uniform sampler2D secondary_normal: hint_roughness_normal, filter_linear_mipmap, repeat_enable;
varying vec3 main_uv_power_normal;
varying vec3 main_uv_triplanar_pos;
varying vec3 secondary_uv_power_normal;
varying vec3 secondary_uv_triplanar_pos;
varying vec3 world_position;
vec4 triplanar_texture(sampler2D p_sampler, vec3 p_weights, vec3 p_triplanar_pos) {
vec4 sampler = vec4(0.0);
sampler += texture(p_sampler, p_triplanar_pos.xy) * p_weights.z;
sampler += texture(p_sampler, p_triplanar_pos.xz) * p_weights.y;
sampler += texture(p_sampler, p_triplanar_pos.zy * vec2(-1.0, 1.0)) * p_weights.x;
return sampler;
}
// Cheapest method, better performance and good results.
vec3 simple_blending(vec3 a, vec3 b) {
a = a * 2.0 - 1.0;
b = b * 2.0 - 1.0;
vec3 result = vec3(a.xy + b.xy, 1.0);
return result * 0.5 + 0.5;
}
// Better results but more performant cost
vec3 unity_blending(vec3 a, vec3 b) {
a = a * 2.0 - 1.0;
b = b * 2.0 - 1.0;
mat3 basis = mat3(
vec3(a.z, a.y, -a.x),
vec3(a.x, a.z, -a.y),
a
);
vec3 result = normalize(basis * b);
return result * 0.5 + 0.5;
}
// Performance friendly, good results
vec3 whiteout_blending(vec3 a, vec3 b) {
a = a * 2.0 - 1.0;
b = b * 2.0 - 1.0;
vec3 result = normalize(vec3(a.xy + b.xy, a.z * b.z));
return result * 0.5 + 0.5;
}
vec3 udm_blending(vec3 a, vec3 b) {
a = a * 2.0 - 1.0;
b = b * 2.0 - 1.0;
vec3 result = normalize(vec3(a.xy + b.xy, a.z));
return result * 0.5 + 0.5;
}
void vertex() {
world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
vec3 normal = NORMAL;
TANGENT = vec3(0.0, 0.0, -1.0) * abs(normal.x);
TANGENT += vec3(1.0, 0.0, 0.0) * abs(normal.y);
TANGENT += vec3(1.0, 0.0, 0.0) * abs(normal.z);
TANGENT = normalize(TANGENT);
BINORMAL = vec3(0.0, 1.0, 0.0) * abs(normal.x);
BINORMAL += vec3(0.0, 0.0, -1.0) * abs(normal.y);
BINORMAL += vec3(0.0, 1.0, 0.0) * abs(normal.z);
BINORMAL = normalize(BINORMAL);
// Main UV Triplanar: Enabled
if (main_uv_triplanar) {
main_uv_power_normal = pow(abs(NORMAL), vec3(main_uv_blend_sharpness));
main_uv_triplanar_pos = VERTEX * main_uv_scale + main_uv_offset;
main_uv_power_normal /= dot(main_uv_power_normal, vec3(1.0));
main_uv_triplanar_pos *= vec3(1.0, -1.0, 1.0);
}
// Secondary UV Triplanar: Enabled
if (secondary_uv_triplanar) {
secondary_uv_power_normal = pow(abs(NORMAL), vec3(secondary_uv_blend_sharpness));
secondary_uv_triplanar_pos = VERTEX * secondary_uv_scale + secondary_uv_offset;
secondary_uv_power_normal /= dot(secondary_uv_power_normal, vec3(1.0));
secondary_uv_triplanar_pos *= vec3(1.0, -1.0, 1.0);
}
}
void fragment() {
vec4 main_albedo;
vec4 m_normal;
vec4 s_normal;
if (main_uv_triplanar) {
main_albedo = triplanar_texture(main_texture, main_uv_power_normal, main_uv_triplanar_pos);
m_normal = triplanar_texture(main_normal, main_uv_power_normal, main_uv_triplanar_pos);
} else {
main_albedo = texture(main_texture, UV * vec2(main_uv_scale));
m_normal = texture(main_normal, UV * vec2(main_uv_scale));
}
main_albedo *= main_texture_color;
vec4 secondary_albedo;
if (secondary_uv_triplanar) {
secondary_albedo = triplanar_texture(secondary_texture, secondary_uv_power_normal, secondary_uv_triplanar_pos);
s_normal = triplanar_texture(secondary_normal, secondary_uv_power_normal, secondary_uv_triplanar_pos);
} else {
secondary_albedo = texture(secondary_texture, UV * vec2(secondary_uv_scale));
s_normal = texture(secondary_normal, UV * vec2(secondary_uv_scale));
}
secondary_albedo *= secondary_texture_color;
vec4 blend_noise_texture = texture(blend_noise, UV);
vec3 blended_albedo = mix(vec3(main_albedo.xyz), vec3(secondary_albedo.xyz), vec3(blend_noise_texture.xyz));
vec3 combined_normal = whiteout_blending(m_normal.rgb, s_normal.rgb);
// Makes the final texture uniform decomposing & composing the rgb channels
vec3 albedo_rgb_to_hsv;
{
vec3 c = blended_albedo;
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
albedo_rgb_to_hsv = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
// VectorDecompose
float rgb_x = albedo_rgb_to_hsv.x;
float rgb_y = albedo_rgb_to_hsv.y;
float rgb_z = albedo_rgb_to_hsv.z;
// VectorCompose:
vec3 rgb_composed = vec3(rgb_x, rgb_y, rgb_z);
vec3 final_rgb_albedo;
{
vec3 c = rgb_composed;
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
final_rgb_albedo = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
ALBEDO = final_rgb_albedo;
NORMAL_MAP = combined_normal;
NORMAL_MAP_DEPTH = normal_scale;
ROUGHNESS = roughness;
METALLIC = metallic;
SPECULAR = specular;
}
@@ -0,0 +1 @@
uid://51hjja8kpjgn
@@ -0,0 +1,52 @@
shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx;
uniform sampler2D albedo_texture : source_color, repeat_enable, filter_linear_mipmap;
uniform sampler2D albedo_seam : source_color, repeat_enable, filter_linear_mipmap;
uniform float uv_scale = 1.0;
uniform vec2 pivot_point = vec2(0.500000, 0.500000);
// Noise type should be "cellular", fractal type "none" and return type "cell_value"
uniform sampler2D rotation_noise : source_color;
// This Noise type should be exactly the same as the rotation noise except for return type as "distance2sub"
// When changing any parameter of the rotation noise, the seam mending noise must be updated too
// The color ramp needs to be from white(left) and black(right) so the seams can be hidden.
uniform sampler2D seam_mending_noise : source_color;
void fragment() {
vec2 albedo_uv = UV * vec2(uv_scale);
float rotation_degrees;
// Remap noise value [0, 1] to degrees [0, 360]
float input_min_value = 0.00000;
float input_max_value = 1.00000;
float output_min_value = 0.00000;
float output_max_value = 360.00000;
{
float __input_range = input_max_value - input_min_value;
float __output_range = output_max_value - output_min_value;
rotation_degrees = output_min_value + __output_range * ((texture(rotation_noise, UV).x - input_min_value) / __input_range);
}
vec2 final_albedo;
// Expression:7
final_albedo = vec2(0.0, 0.0);
{
albedo_uv -= pivot_point;
float rotation_radians = radians(rotation_degrees);
float c = cos(rotation_radians);
float s = sin(rotation_radians);
final_albedo.x = albedo_uv.x * c + albedo_uv.y * s;
final_albedo.y = albedo_uv.y * c - albedo_uv.x * s;
final_albedo += pivot_point;
}
ALBEDO = mix(vec3(texture(albedo_seam, final_albedo).xyz), vec3(texture(albedo_texture, albedo_uv).xyz), vec3(texture(seam_mending_noise, UV).xyz));
}
@@ -0,0 +1 @@
uid://0tuphwonvajg
@@ -0,0 +1,369 @@
shader_type spatial;
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx;
uniform float blend_factor : hint_range(0.1, 50.0) = 5.0;
uniform float fade_range = 5.0;
uniform float height_dirt = 5.0;
uniform float height_grass = 15.0;
uniform float height_mountain = 30.0;
//uniform float roughness : hint_range(0.0, 1.0) = 1.0;
//uniform float ao_light_affect : hint_range(0.0, 1.0);
group_uniforms Dirt;
uniform sampler2D dirt_albedo : source_color, repeat_enable, filter_linear_mipmap, hint_default_white;
//uniform sampler2D dirt_orm : source_color;
uniform float dirt_blend_sharpness = 1.0;
uniform float dirt_uv_scale : hint_range(0.01, 10.0, 0.01) = 1.0;
uniform float dirt_stochastic_strength : hint_range(0.0, 1.0) = 0;
uniform float dirt_stochastic_rotation_max_angle : hint_range(0.0, 3.14159) = 0;
group_uniforms Grass;
uniform sampler2D grass_albedo : source_color, repeat_enable, filter_linear_mipmap, hint_default_white;
//uniform sampler2D grass_orm : source_color;
uniform float grass_blend_sharpness = 1.0;
uniform float grass_uv_scale : hint_range(0.01, 10.0, 0.01) = 1.0;
uniform float grass_stochastic_strength : hint_range(0.0, 1.0) = 0;
uniform float grass_stochastic_rotation_max_angle : hint_range(0.0, 3.14159) = 0;
group_uniforms Mountain;
uniform sampler2D mountain_albedo : source_color, repeat_enable, filter_linear_mipmap, hint_default_white;
//uniform sampler2D mountain_orm : source_color;
uniform float mountain_blend_sharpness = 1.0;
uniform float mountain_uv_scale : hint_range(0.01, 10.0, 0.01) = 1.0;
uniform float mountain_stochastic_strength : hint_range(0.0, 1.0) = 0;
uniform float mountain_stochastic_rotation_max_angle : hint_range(0.0, 3.14159) = 0;
// ================================================================
// Varyings
// Data passed from Vertex Shader to Fragment Shader
// ================================================================
varying vec3 world_pos; // Original vertex position in world space
varying vec3 triplanar_normal_abs; // Absolute world-space normal for triplanar blending weights
varying mat3 tangent_2_local;
// Simple hash function to generate pseudo-random numbers based on a 2D input.
// Used for consistent random offsets per "tile" in stochastic sampling.
float hash21(vec2 p) {
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}
mat2 rotate2d(float angle) {
float s = sin(angle);
float c = cos(angle);
return mat2(vec2(c, s), vec2(-s, c));
}
// Helper function to unpack a normal from 0-1 range (texture) to -1 to 1 range (vector).
vec3 unpack_normal(vec3 packed_normal) {
return packed_normal * 2.0 - 1.0;
}
// Helper function to pack a normal from -1 to 1 range (vector) to 0-1 range (for NORMAL_MAP output).
vec3 pack_normal(vec3 unpacked_normal) {
return unpacked_normal * 0.5 + 0.5;
}
vec2 rotate_uv(vec2 uv, float angle) {
float s = sin(angle);
float c = cos(angle);
return mat2(vec2(c, -s), vec2(s, c)) * uv;
}
vec2 stochastic_uv(vec2 uv, float strength, float max_rotation_angle) {
// Get the tile index based on floor of UV
vec2 tile_index = floor(uv);
// Use tile index to generate rotation and offset
float angle = hash21(tile_index) * max_rotation_angle;
vec2 offset = vec2(hash21(tile_index + 1.3), hash21(tile_index + 2.1)) - 0.5;
// Apply rotation and offset within each tile
vec2 local_uv = fract(uv) + offset * strength;
local_uv = rotate_uv(local_uv - 0.5, angle) + 0.5;
return local_uv;
}
// Triplanar texture sampling with 4-sample stochastic anti-tiling for RGB textures.
// Includes random per-tile rotation and offset.
// tex: The texture to sample.
// p: Scaled world position (uv_scale * world_pos).
// normal_abs: Absolute world normal for blending between X, Y, Z projections.
// k: Triplanar blend factor (blend_factor).
// strength: Stochastic displacement strength (stochastic_strength).
// max_rotation_angle: Maximum random rotation angle (stochastic_rotation_max_angle).
vec3 apply_triplanar_texture_stochastic_rgb(sampler2D tex, vec3 p, vec3 normal_abs, float k, float strength, float max_rotation_angle) {
// Calculate base UVs for each projection plane
vec2 uv_xy = p.xy; // For Z-axis projection (top-down)
vec2 uv_yz = p.yz; // For X-axis projection (side)
vec2 uv_xz = p.xz; // For Y-axis projection (side)
// --- XY Plane Projection (Z-axis) ---
// Get the integer part of UV for a consistent random seed per tile
vec2 tile_id_xy = floor(uv_xy);
// Generate random rotation angle for this tile
float random_angle_xy = hash21(tile_id_xy) * max_rotation_angle;
mat2 rot_mat_xy = rotate2d(random_angle_xy);
// Generate two random offsets for displacement (ensure distinct hashes for x,y components)
vec2 rand_offset_xy = vec2(hash21(tile_id_xy + vec2(0.1, 0.0)), hash21(tile_id_xy + vec2(0.0, 0.1))) * strength;
// Calculate the 'base' sampling UV by applying rotation around tile center
// and then adding the random offset.
// This 'base_sampling_uv' is the reference point for the 4 samples and the blending.
vec2 base_sampling_uv_xy = (uv_xy - tile_id_xy - vec2(0.5)) * rot_mat_xy + tile_id_xy + vec2(0.5) + rand_offset_xy;
// Use the fractional part of this base sampling UV for the blending weights
vec2 frac_uv_for_blend_xy = fract(base_sampling_uv_xy);
float blend_x_xy = smoothstep(0.4, 0.6, frac_uv_for_blend_xy.x);
float blend_y_xy = smoothstep(0.4, 0.6, frac_uv_for_blend_xy.y);
// Sample the texture 4 times. The (0,0), (1,0), etc. offsets are relative to the
// potentially rotated and offset grid.
vec3 s00_xy = texture(tex, base_sampling_uv_xy).rgb;
vec3 s10_xy = texture(tex, base_sampling_uv_xy + vec2(1.0, 0.0)).rgb;
vec3 s01_xy = texture(tex, base_sampling_uv_xy + vec2(0.0, 1.0)).rgb;
vec3 s11_xy = texture(tex, base_sampling_uv_xy + vec2(1.0, 1.0)).rgb;
// Blend the 4 samples using bilinear interpolation based on fractional UV
vec3 color_xy = mix(mix(s00_xy, s10_xy, blend_x_xy), mix(s01_xy, s11_xy, blend_x_xy), blend_y_xy);
// --- YZ Plane Projection (X-axis) ---
vec2 tile_id_yz = floor(uv_yz);
float random_angle_yz = hash21(tile_id_yz) * max_rotation_angle;
mat2 rot_mat_yz = rotate2d(random_angle_yz);
vec2 rand_offset_yz = vec2(hash21(tile_id_yz + vec2(0.1, 0.0)), hash21(tile_id_yz + vec2(0.0, 0.1))) * strength;
vec2 base_sampling_uv_yz = (uv_yz - tile_id_yz - vec2(0.5)) * rot_mat_yz + tile_id_yz + vec2(0.5) + rand_offset_yz;
vec2 frac_uv_for_blend_yz = fract(base_sampling_uv_yz);
float blend_x_yz = smoothstep(0.4, 0.6, frac_uv_for_blend_yz.x);
float blend_y_yz = smoothstep(0.4, 0.6, frac_uv_for_blend_yz.y);
vec3 s00_yz = texture(tex, base_sampling_uv_yz).rgb;
vec3 s10_yz = texture(tex, base_sampling_uv_yz + vec2(1.0, 0.0)).rgb;
vec3 s01_yz = texture(tex, base_sampling_uv_yz + vec2(0.0, 1.0)).rgb;
vec3 s11_yz = texture(tex, base_sampling_uv_yz + vec2(1.0, 1.0)).rgb;
vec3 color_yz = mix(mix(s00_yz, s10_yz, blend_x_yz), mix(s01_yz, s11_yz, blend_x_yz), blend_y_yz);
// --- XZ Plane Projection (Y-axis) ---
vec2 tile_id_xz = floor(uv_xz);
float random_angle_xz = hash21(tile_id_xz) * max_rotation_angle;
mat2 rot_mat_xz = rotate2d(random_angle_xz);
vec2 rand_offset_xz = vec2(hash21(tile_id_xz + vec2(0.1, 0.0)), hash21(tile_id_xz + vec2(0.0, 0.1))) * strength;
vec2 base_sampling_uv_xz = (uv_xz - tile_id_xz - vec2(0.5)) * rot_mat_xz + tile_id_xz + vec2(0.5) + rand_offset_xz;
vec2 frac_uv_for_blend_xz = fract(base_sampling_uv_xz);
float blend_x_xz = smoothstep(0.4, 0.6, frac_uv_for_blend_xz.x);
float blend_y_xz = smoothstep(0.4, 0.6, frac_uv_for_blend_xz.y);
vec3 s00_xz = texture(tex, base_sampling_uv_xz).rgb;
vec3 s10_xz = texture(tex, base_sampling_uv_xz + vec2(1.0, 0.0)).rgb;
vec3 s01_xz = texture(tex, base_sampling_uv_xz + vec2(0.0, 1.0)).rgb;
vec3 s11_xz = texture(tex, base_sampling_uv_xz + vec2(1.0, 1.0)).rgb;
vec3 color_xz = mix(mix(s00_xz, s10_xz, blend_x_xz), mix(s01_xz, s11_xz, blend_x_xz), blend_y_xz);
// Calculate blend weights for triplanar projection itself (based on normal direction)
vec3 blend_weights = pow(normal_abs, vec3(k));
blend_weights /= dot(blend_weights, vec3(1.0)); // Normalize weights to sum to 1
// Final triplanar blend: combine the three projected colors using the calculated weights
return color_yz * blend_weights.x + color_xz * blend_weights.y + color_xy * blend_weights.z;
}
vec4 apply_triplanar_texture_stochastic_rgba(
sampler2D tex,
vec3 p,
vec3 normal_abs,
float k,
float strength,
float max_rotation_angle
) {
vec2 uv_xy = p.xy;
vec2 uv_yz = p.yz;
vec2 uv_xz = p.xz;
// XY
vec2 tile_id_xy = floor(uv_xy);
float random_angle_xy = hash21(tile_id_xy) * max_rotation_angle;
mat2 rot_mat_xy = rotate2d(random_angle_xy);
vec2 rand_offset_xy = vec2(hash21(tile_id_xy + vec2(0.1, 0.0)), hash21(tile_id_xy + vec2(0.0, 0.1))) * strength;
vec2 base_uv_xy = (uv_xy - tile_id_xy - vec2(0.5)) * rot_mat_xy + tile_id_xy + vec2(0.5) + rand_offset_xy;
vec2 frac_uv_xy = fract(base_uv_xy);
float blend_x_xy = smoothstep(0.4, 0.6, frac_uv_xy.x);
float blend_y_xy = smoothstep(0.4, 0.6, frac_uv_xy.y);
vec4 s00_xy = texture(tex, base_uv_xy);
vec4 s10_xy = texture(tex, base_uv_xy + vec2(1.0, 0.0));
vec4 s01_xy = texture(tex, base_uv_xy + vec2(0.0, 1.0));
vec4 s11_xy = texture(tex, base_uv_xy + vec2(1.0, 1.0));
vec4 color_xy = mix(mix(s00_xy, s10_xy, blend_x_xy), mix(s01_xy, s11_xy, blend_x_xy), blend_y_xy);
// YZ
vec2 tile_id_yz = floor(uv_yz);
float random_angle_yz = hash21(tile_id_yz) * max_rotation_angle;
mat2 rot_mat_yz = rotate2d(random_angle_yz);
vec2 rand_offset_yz = vec2(hash21(tile_id_yz + vec2(0.1, 0.0)), hash21(tile_id_yz + vec2(0.0, 0.1))) * strength;
vec2 base_uv_yz = (uv_yz - tile_id_yz - vec2(0.5)) * rot_mat_yz + tile_id_yz + vec2(0.5) + rand_offset_yz;
vec2 frac_uv_yz = fract(base_uv_yz);
float blend_x_yz = smoothstep(0.4, 0.6, frac_uv_yz.x);
float blend_y_yz = smoothstep(0.4, 0.6, frac_uv_yz.y);
vec4 s00_yz = texture(tex, base_uv_yz);
vec4 s10_yz = texture(tex, base_uv_yz + vec2(1.0, 0.0));
vec4 s01_yz = texture(tex, base_uv_yz + vec2(0.0, 1.0));
vec4 s11_yz = texture(tex, base_uv_yz + vec2(1.0, 1.0));
vec4 color_yz = mix(mix(s00_yz, s10_yz, blend_x_yz), mix(s01_yz, s11_yz, blend_x_yz), blend_y_yz);
// XZ
vec2 tile_id_xz = floor(uv_xz);
float random_angle_xz = hash21(tile_id_xz) * max_rotation_angle;
mat2 rot_mat_xz = rotate2d(random_angle_xz);
vec2 rand_offset_xz = vec2(hash21(tile_id_xz + vec2(0.1, 0.0)), hash21(tile_id_xz + vec2(0.0, 0.1))) * strength;
vec2 base_uv_xz = (uv_xz - tile_id_xz - vec2(0.5)) * rot_mat_xz + tile_id_xz + vec2(0.5) + rand_offset_xz;
vec2 frac_uv_xz = fract(base_uv_xz);
float blend_x_xz = smoothstep(0.4, 0.6, frac_uv_xz.x);
float blend_y_xz = smoothstep(0.4, 0.6, frac_uv_xz.y);
vec4 s00_xz = texture(tex, base_uv_xz);
vec4 s10_xz = texture(tex, base_uv_xz + vec2(1.0, 0.0));
vec4 s01_xz = texture(tex, base_uv_xz + vec2(0.0, 1.0));
vec4 s11_xz = texture(tex, base_uv_xz + vec2(1.0, 1.0));
vec4 color_xz = mix(mix(s00_xz, s10_xz, blend_x_xz), mix(s01_xz, s11_xz, blend_x_xz), blend_y_xz);
// Triplanar blending with control over blending strength
vec3 weights = pow(normal_abs, vec3(k));
weights /= max(dot(weights, vec3(1.0)), 0.0001);
return color_yz * weights.x + color_xz * weights.y + color_xy * weights.z;
}
vec3 apply_triplanar_texture_stochastic_normal(
sampler2D normal_map,
vec3 pos,
vec3 normal_abs,
float blend_sharpness,
float strength,
float max_rotation_angle
) {
// === Weights ===
vec3 weights = pow(normal_abs, vec3(blend_sharpness));
weights /= max(dot(weights, vec3(1.0)), 0.0001);
// === Projected stochastic UVs ===
vec2 uv_yz = stochastic_uv(pos.yz, strength, max_rotation_angle);
vec2 uv_xz = stochastic_uv(pos.xz, strength, max_rotation_angle);
vec2 uv_xy = stochastic_uv(pos.xy, strength, max_rotation_angle);
// === Sample and unpack normal maps ===
vec3 n_yz = unpack_normal(texture(normal_map, uv_yz).rgb);
vec3 n_xz = unpack_normal(texture(normal_map, uv_xz).rgb);
vec3 n_xy = unpack_normal(texture(normal_map, uv_xy).rgb);
n_yz *= tangent_2_local;
n_xz *= tangent_2_local;
n_xy *= tangent_2_local;
// === Reorient normals from tangent space to world-space projection axis ===
// YZ projection (X facing) → local Z = +X
vec3 world_n_yz = vec3(n_yz.z, n_yz.x, n_yz.y);
// XZ projection (Y facing) → local Z = +Y
vec3 world_n_xz = vec3(n_xz.x, n_xz.z, n_xz.y);
// XY projection (Z facing) → local Z = +Z
vec3 world_n_xy = vec3(n_xy.x, n_xy.y, n_xy.z);
// === Blend and normalize ===
vec3 blended = normalize(
world_n_yz * weights.x +
world_n_xz * weights.y +
world_n_xy * weights.z
);
return blended;
}
// ================================================================
// Vertex Shader
// Prepares world-space position and absolute normal for triplanar mapping
// ================================================================
void vertex() {
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
triplanar_normal_abs = abs(NORMAL);
TANGENT = vec3(0.0,0.0,-1.0) * abs(triplanar_normal_abs.x);
TANGENT += vec3(1.0,0.0,0.0) * abs(triplanar_normal_abs.y);
TANGENT += vec3(1.0,0.0,0.0) * abs(triplanar_normal_abs.z);
TANGENT = normalize(TANGENT);
BINORMAL = vec3(0.0,1.0,0.0) * abs(triplanar_normal_abs.x);
BINORMAL += vec3(0.0,0.0,-1.0) * abs(triplanar_normal_abs.y);
BINORMAL += vec3(0.0,1.0,0.0) * abs(triplanar_normal_abs.z);
BINORMAL = normalize(BINORMAL);
tangent_2_local = MODEL_NORMAL_MATRIX * mat3(TANGENT, BINORMAL, NORMAL);
//normalize all components, so the UVs don't get scaled when scaling mesh
tangent_2_local[0] = normalize(tangent_2_local[0]);
tangent_2_local[1] = normalize(tangent_2_local[1]);
tangent_2_local[2] = normalize(tangent_2_local[2]);
}
// ================================================================
// Fragment Shader
// Calculates final material properties
// ================================================================
void fragment() {
float height = world_pos.y;
float blend_dirt = 1.0 - smoothstep(height_dirt, height_dirt + fade_range, height);
float blend_grass = smoothstep(height_dirt, height_grass, height) * (1.0 - smoothstep(height_grass, height_mountain, height));
float blend_mountain = smoothstep(height_grass, height_mountain, height);
//Apply blend sharpness
blend_dirt = pow(blend_dirt, dirt_blend_sharpness);
blend_grass = pow(blend_grass, grass_blend_sharpness);
blend_mountain = pow(blend_mountain, mountain_blend_sharpness);
// Normalize weights (if necessary — this makes sure they add up to 1.0)
float total = blend_dirt + blend_grass + blend_mountain;
blend_dirt /= total;
blend_grass /= total;
blend_mountain /= total;
vec3 albedo_dirt = apply_triplanar_texture_stochastic_rgb(
dirt_albedo,
world_pos * dirt_uv_scale,
triplanar_normal_abs,
blend_factor,
dirt_stochastic_strength,
dirt_stochastic_rotation_max_angle
);
vec3 albedo_grass = apply_triplanar_texture_stochastic_rgb(
grass_albedo,
world_pos * grass_uv_scale,
triplanar_normal_abs,
blend_factor,
grass_stochastic_strength,
grass_stochastic_rotation_max_angle
);
vec3 albedo_mountain = apply_triplanar_texture_stochastic_rgb(
mountain_albedo,
world_pos * mountain_uv_scale,
triplanar_normal_abs,
blend_factor,
mountain_stochastic_strength,
mountain_stochastic_rotation_max_angle
);
//vec3 orm_dirt = texture(dirt_orm, UV * dirt_uv_scale).rgb;
//vec3 orm_grass = texture(grass_orm, UV * grass_uv_scale).rgb;
//vec3 orm_mountain = texture(mountain_orm, UV * mountain_uv_scale).rgb;
//
//vec3 mixed_orm = mix(orm_dirt, orm_grass, blend_factor);
//mixed_orm = mix(mixed_orm, orm_mountain, blend_factor);
//
ALBEDO = albedo_dirt * blend_dirt + albedo_grass * blend_grass + albedo_mountain * blend_mountain;
//AO = mixed_orm.r;
//AO_LIGHT_AFFECT = ao_light_affect;
//ROUGHNESS = mixed_orm.g * roughness;
//METALLIC = mixed_orm.b;
}
@@ -0,0 +1 @@
uid://b4hwimgi2bi6n