add cloud shadow - godrays

This commit is contained in:
Matteo Sonaglioni
2026-03-11 18:51:31 +01:00
parent 47380d3137
commit 609b70a114
44 changed files with 532 additions and 157 deletions

View File

@@ -0,0 +1,113 @@
shader_type spatial;
render_mode specular_disabled, ambient_light_disabled;
uniform sampler2D DEPTH_TEXTURE : hint_depth_texture, filter_linear_mipmap;
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_nearest;
uniform sampler2D NORMAL_TEXTURE : hint_normal_roughness_texture, filter_nearest;
uniform float lightIntensity = 1.25;
uniform float lineAlpha = 0.7;
uniform bool useLighting = true;
uniform float lineHighlight = 0.2;
uniform float lineShadow = 0.55;
varying vec2 screenUV;
varying float lineMask;
float GetLinearDepth(vec2 sUV, sampler2D depthTexture, mat4 invProjectionMat, float mask){
// Raw depth to linear depth code from:
// https://docs.godotengine.org/en/latest/tutorials/shaders/advanced_postprocessing.html
float depth = texture(depthTexture, sUV).x * mask;
vec3 ndc = vec3(sUV * 2.0 - 1.0, depth);
vec4 view = invProjectionMat * vec4(ndc, 1.0);
view.xyz /= view.w;
return -view.z;
}
vec3 GetNormal(vec2 uv, sampler2D normalTexture, float mask){
vec3 normal = texture(normalTexture, uv).rgb;
normal = normal * 2.0 - 1.0 * mask;
return normal;
}
float NormalEdgeIndicator(vec3 normalEdgeBias, vec3 normal, vec3 neighborNormal, float depthDifference){
//From Kody King: https://threejs.org/examples/webgl_postprocessing_pixel.html
float normalDifference = dot(normal - neighborNormal, normalEdgeBias);
float normalIndicator = clamp(smoothstep(-.01, .01, normalDifference), 0.0, 1.0);
float depthIndicator = clamp(sign(depthDifference * .25 + .0025), 0.0, 1.0);
return (1.0 - dot(normal, neighborNormal)) * depthIndicator * normalIndicator;
}
void vertex(){
POSITION = vec4(VERTEX.xy, 1.0, 1.0);
}
void fragment() {
vec2 texelSize = 1.0 / VIEWPORT_SIZE.xy;
screenUV = SCREEN_UV;
// UV offsets
vec2 UVOffsets[4];
UVOffsets[0] = SCREEN_UV + vec2(0.0, -1.0) * texelSize;
UVOffsets[1] = SCREEN_UV + vec2(0.0, 1.0) * texelSize;
UVOffsets[2] = SCREEN_UV + vec2(1.0, 0.0) * texelSize;
UVOffsets[3] = SCREEN_UV + vec2(-1.0, 0.0) * texelSize;
// Using alpha channel (screen roughness) to mask objects to not receive outlines
float outlineMask = texture(NORMAL_TEXTURE, SCREEN_UV).a;
outlineMask = ceil(outlineMask); // Objects with Roughness = 0 will not have and outline
// Edge detection with Depth
float depthDifference = 0.0;
float invDepthDifference = 0.5;
float depth = GetLinearDepth(SCREEN_UV, DEPTH_TEXTURE, INV_PROJECTION_MATRIX, outlineMask);
for (int i = 0; i < UVOffsets.length(); i++){
float dOff = GetLinearDepth(UVOffsets[i],DEPTH_TEXTURE, INV_PROJECTION_MATRIX, outlineMask);
depthDifference += clamp(dOff - depth, 0.0, 1.0);
invDepthDifference += depth - dOff;
}
invDepthDifference = clamp(invDepthDifference, 0.0, 1.0);
invDepthDifference = clamp(smoothstep(0.9, 0.9, invDepthDifference) * 10.0 , 0.0, 1.0);
depthDifference = smoothstep(0.25, 0.3, depthDifference);
// Edge detection with Normals
float normalDifference = 0.;
vec3 normalEdgeBias = vec3(1.0, 1.0, 1.0);
vec3 normal = GetNormal(SCREEN_UV, NORMAL_TEXTURE, outlineMask);
for (int i = 0; i < UVOffsets.length(); i++){
vec3 nOff = GetNormal(UVOffsets[i],NORMAL_TEXTURE, outlineMask);
normalDifference += NormalEdgeIndicator(normalEdgeBias, normal, nOff, depthDifference);
}
normalDifference = smoothstep(0.2, 0.2, normalDifference);
normalDifference = clamp(normalDifference - invDepthDifference, 0.0, 1.0);
ALBEDO = texture(SCREEN_TEXTURE, SCREEN_UV).rgb;
lineMask = clamp(0.1, lineAlpha, (depthDifference + normalDifference * 5.0));
if (!useLighting){
ALBEDO += clamp((normalDifference - depthDifference), 0.0, 1.0) * lineHighlight;
ALBEDO -= ALBEDO * depthDifference * lineShadow;
}
}
void light (){
if (useLighting){
vec4 normal = texture(NORMAL_TEXTURE, screenUV);
normal = normal * 2.0 - 1.0;
// Calculate light direction
float dotNL = dot(normal.rgb, LIGHT);
dotNL = pow(dotNL, 2.5);
dotNL = clamp(dotNL, 0.0, 1.0);
if(LIGHT_IS_DIRECTIONAL)
DIFFUSE_LIGHT += mix(vec3(1.0), dotNL * LIGHT_COLOR * lightIntensity, lineMask);
}
else
DIFFUSE_LIGHT = vec3(1.0);
}

View File

@@ -0,0 +1 @@
uid://bj20y6cydpck6

View File

@@ -0,0 +1,81 @@
shader_type spatial;
render_mode unshaded, depth_test_disabled, cull_disabled;
uniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest;
uniform sampler2D noise_texture : repeat_enable, filter_linear;
uniform float cloud_scale : hint_range(0.001, 0.2) = 0.05;
uniform float cloud_speed : hint_range(0.0, 1.0) = 0.05;
uniform vec2 direction = vec2(1.0, 0.2);
uniform vec2 sun_angle = vec2(0.5, 0.5);
group_uniforms Forma_Nuvola;
uniform float cloud_density : hint_range(0.0, 1.0) = 0.5;
uniform float center_sharpness : hint_range(0.0, 0.99) = 0.7;
group_uniforms Estetica;
uniform vec3 shadow_color : source_color = vec3(0.0, 0.0, 0.0);
uniform float opacity_center : hint_range(0.0, 1.0) = 0.5;
uniform float opacity_edge : hint_range(0.0, 1.0) = 0.2;
group_uniforms Distanza_Sfumo;
// Sfumo basato sulla distanza dalla telecamera (Profondità)
uniform float fade_start : hint_range(10.0, 1000.0) = 150.0;
uniform float fade_end : hint_range(50.0, 2000.0) = 300.0;
group_uniforms Altezza_Sfumo;
// Sfumo basato sull'altezza dal suolo (Asse Y)
uniform float height_fade_start : hint_range(0.0, 500.0) = 10.0; // Inizia a svanire sopra i 10 metri
uniform float height_fade_end : hint_range(0.0, 1000.0) = 50.0; // Scompare del tutto a 50 metri di altezza
void vertex() {
POSITION = vec4(VERTEX.xy, 1.0, 1.0);
}
void fragment() {
float depth = texture(depth_texture, SCREEN_UV).x;
if (depth >= 0.9999) {
ALPHA = 0.0;
} else {
vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth);
vec4 view_pos = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
view_pos /= view_pos.w;
// world_pos.y è l'altezza assoluta di questo pixel nel mondo 3D!
vec4 world_pos = INV_VIEW_MATRIX * view_pos;
// 1. Sfumo di Distanza
float dist = length(view_pos.xyz);
float dist_fade = smoothstep(fade_end, fade_start, dist);
// 2. Sfumo di Altezza (LA NOVITÀ)
// Se world_pos.y supera height_fade_start inizia a svanire. A height_fade_end è zero.
float height_fade = smoothstep(height_fade_end, height_fade_start, world_pos.y);
// Moltiplichiamo i due sfumi. Se uno dei due dice "zero", l'ombra sparisce.
float final_fade = dist_fade * height_fade;
if (final_fade <= 0.001) {
ALPHA = 0.0;
} else {
vec2 projected_xz = world_pos.xz + (world_pos.y * sun_angle);
vec2 uv = projected_xz * cloud_scale;
uv += TIME * cloud_speed * vec2(-direction.x, direction.y);
float n = texture(noise_texture, uv).r;
if (n < (1.0 - cloud_density)) {
ALPHA = 0.0;
} else {
float internal_n = (n - (1.0 - cloud_density)) / cloud_density;
float is_center = step(center_sharpness, internal_n);
ALBEDO = shadow_color;
// Applichiamo il colore e lo sbiadiamo in base a distanza e altezza!
ALPHA = mix(opacity_edge, opacity_center, is_center) * final_fade;
}
}
}
}

View File

@@ -0,0 +1 @@
uid://c0gopfoe0ij8g

62
Shaders/godrays.gdshader Normal file
View File

@@ -0,0 +1,62 @@
shader_type spatial;
// RENDER MODE ESSENZIALI PER EFFETTI LUCE
// blend_add: Somma i colori, perfetto per la luce pura.
// unshaded: Non riceve ombre, è lui che illumina.
// depth_draw_never: Non blocca gli oggetti dietro, evita artefatti.
// cull_disabled: Visibile da entrambi i lati.
render_mode blend_add, depth_draw_never, cull_disabled, unshaded;
uniform sampler2D depth_texture : hint_depth_texture, filter_nearest;
uniform sampler2D noise_texture : repeat_enable, filter_linear;
uniform vec3 ray_color : source_color = vec3(1.0, 0.9, 0.7);
uniform float base_alpha : hint_range(0.0, 1.0) = 0.5;
group_uniforms Noise_Stirato;
// --- I PARAMETRI CHE CERCAVI ---
uniform float noise_stretching : hint_range(1.0, 50.0) = 20.0; // Quanto allungare il noise
uniform float noise_scale : hint_range(0.1, 10.0) = 1.0; // Grandezza generale
uniform vec2 scrolling_speed = vec2(0.0, -0.1); // Velocità e direzione del movimento (Y negativa va in giù)
group_uniforms Fades;
uniform float depth_softness = 1.0; // Ammorbidisce il contatto con terreno/alberi
uniform float edge_fade_power = 2.0; // Sfuma i bordi laterali del piano
// --- IL PARAMETRO PER LO SCRIPT ---
uniform float intensity_multiplier : hint_range(0.0, 1.0) = 0.0;
void fragment() {
// 1. LA MAGIA DEL NOISE STIRATO E IN MOVIMENTO
// Prendiamo le coordinate UV standard
vec2 stretchy_uv = UV * noise_scale;
// STIRAMENTO: Moltiplichiamo X per allungare il noise in verticale
stretchy_uv.x *= noise_stretching;
// MOVIMENTO: Aggiungiamo il tempo moltiplicato per la velocità
stretchy_uv += TIME * scrolling_speed;
// Leggiamo il noise (basta un canale perché è in bianco e nero)
float noise = texture(noise_texture, stretchy_uv).r;
// 2. SISTEMA DI DISSOLVENZE (FADES) PER NASCONDERE IL PIANO
// A. Soft Intersection: Legge la profondità per sfumare il contatto con gli oggetti
float depth = textureLod(depth_texture, SCREEN_UV, 0.0).r;
vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth, 1.0);
vec3 pixel_position = upos.xyz / upos.w;
float depth_fade = clamp((VERTEX.z - pixel_position.z) / depth_softness, 0.0, 1.0);
// B. Edge Fade: Sfuma orizzontalmente (UV.x) per non avere bordi netti ai lati del piano
float edge_fade = smoothstep(0.0, 0.5, UV.x) * smoothstep(1.0, 0.5, UV.x);
edge_fade = pow(edge_fade, edge_fade_power); // Rende la sfumatura più o meno aggressiva
// C. Vertical Fade: Sfuma verticalmente (UV.y) per far sparire la base nel nulla
float vertical_fade = smoothstep(0.0, 0.2, UV.y) * smoothstep(1.0, 0.7, UV.y);
// 3. COMPOSIZIONE FINALE
ALBEDO = ray_color;
// Combiniamo tutte le sfumature con il noise e il moltiplicatore dello script
ALPHA = noise * base_alpha * depth_fade * edge_fade * vertical_fade * intensity_multiplier;
}

View File

@@ -0,0 +1 @@
uid://dkmdtejrbks8n

View File

@@ -100,6 +100,8 @@ void fragment() {
// Moltiplichiamo l'alpha della texture per il nuovo parametro opacity
ALPHA = tex.r * opacity;
ALPHA_SCISSOR_THRESHOLD = 0.5;
ROUGHNESS = 0.02;
}
// --- NUOVA LOGICA LUCE (Ombre Proiettate e Fix Quadrati) ---