Skip to content

Instantly share code, notes, and snippets.

@HungryProton
Created June 7, 2023 17:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HungryProton/148d7da76854b342a9f2f34d9c099f6f to your computer and use it in GitHub Desktop.
Save HungryProton/148d7da76854b342a9f2f34d9c099f6f to your computer and use it in GitHub Desktop.
Under water shader with caustics
shader_type spatial;
uniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest;
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
uniform float v_depth_offset = 0.0;
uniform float v_depth_fade = 1.0;
uniform sampler2D underwater_color : repeat_disable;
uniform sampler2D caustics_texture : hint_default_black;
uniform float caustics_speed = 1.0;
uniform float caustics_scale = 1.0;
uniform float caustics_power : hint_range(0.0, 4.0) = 1.0;
uniform float caustics_strength : hint_range(0.0, 32.0) = 1.0;
varying vec3 scene_world_position;
varying vec2 uv1;
varying vec2 uv2;
vec3 position_from_depth(vec2 uv, mat4 inv_view, mat4 inv_proj) {
float depth_sample = texture(depth_texture, uv).x;
vec3 ndc = vec3(uv * 2.0 - 1.0, depth_sample);
vec4 scene_world = inv_view * inv_proj * vec4(ndc, 1.0);
return scene_world.xyz / scene_world.w;
}
// Source: https://github.com/paddy-exe/Godot-RealTimeCaustics
mat3 makeRotationDir(vec3 direction, vec3 up){
mat3 rm;
vec3 xaxis = cross(up, direction);
xaxis = normalize(xaxis);
vec3 yaxis = cross(direction, xaxis);
yaxis = normalize(yaxis);
rm[0].x = xaxis.x;
rm[0].y = yaxis.x;
rm[0].z = direction.x;
rm[1].x = xaxis.y;
rm[1].y = yaxis.y;
rm[1].z = direction.y;
rm[2].x = xaxis.z;
rm[2].y = yaxis.z;
rm[2].z = direction.z;
return rm;
}
void vertex() {
POSITION = vec4(VERTEX, 1.0);
}
void fragment() {
// Depth
scene_world_position = position_from_depth(SCREEN_UV, INV_VIEW_MATRIX, INV_PROJECTION_MATRIX);
// Get the vertical depth (in world coordinates)
float v_depth = scene_world_position.y + v_depth_offset;
v_depth /= v_depth_fade;
v_depth = clamp(1.0 - v_depth, 0.0, 1.0);
// Sample the gradient color and the original color
vec4 color = texture(underwater_color, vec2(v_depth, 0.5));
vec3 screen_color = texture(screen_texture, SCREEN_UV).rgb;
// Mix with the original screen color, based on the gradient alpha value
color = mix(vec4(screen_color, 1.0), color, color.a);
ALBEDO = color.rgb;
}
void light() {
// Two values to know if we're dealing with directional lights or point lights
float is_directional_light = float(LIGHT_IS_DIRECTIONAL);
float is_point_light = 1.0 - is_directional_light;
// Diffuse
DIFFUSE_LIGHT += ALBEDO * is_directional_light;
// Caustics
// Get the light vector in world position // TODO : not sure about the maths
vec3 world_light = (INV_VIEW_MATRIX * vec4(LIGHT, 0.0)).xyz;
mat3 lrm = makeRotationDir(world_light, vec3(0.0, 1.0, 0.0));
mat4 t = mat4(lrm);
t[3].xyz = scene_world_position;
// Get the light projected world UV
vec2 uv = (t * vec4(scene_world_position, 1.0)).xz;
// Get two samples from the caustic texture, scrolling in different directions
vec2 caustic_uv1 = uv * (1.0 / caustics_scale) + TIME * 0.75 * caustics_speed;
vec2 caustic_uv2 = uv * (-1.0 / (caustics_scale * 0.90)) + TIME * caustics_speed;
vec3 c1 = texture(caustics_texture, caustic_uv1).rgb;
vec3 c2 = texture(caustics_texture, caustic_uv2).rgb;
c1 = pow(c1, vec3(caustics_power));
c2 = pow(c2, vec3(caustics_power));
vec3 c = min(c1, c2) * caustics_strength * ALBEDO;
DIFFUSE_LIGHT += c * is_directional_light;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment