Volumetric Clouds
Raymarched Clouds.
Shader code
shader_type spatial;
render_mode unshaded;
uniform sampler3D cloud_noise_texture;
uniform float cloud_scale : hint_range(0.001, 5.0) = 1.0;
uniform float cloud_threshold : hint_range(0.0, 1.0) = 0.5;
uniform float cloud_threshold_multiplier: hint_range(0.00, 10.00) = 0.05;
uniform int max_steps : hint_range(1, 200) = 64;
uniform float step_size : hint_range(0.01, 0.5) = 0.1;
uniform float cloud_scroll_speed : hint_range(-1.0, 1.0) = 0.5;
uniform vec3 minBounds = vec3(-1.0, -1.0, -1.0);
uniform vec3 maxBounds = vec3(1.0, 1.0, 1.0);
uniform sampler3D detail_noise_texture;
uniform float detail_intensity = 0.5;
float sample_detail_noise(vec3 position) {
return texture(detail_noise_texture, position).r;
}
float sample_cloud(vec3 position) {
vec3 offsetPos = position + cloud_scroll_speed * TIME;
float main_cloud = texture(cloud_noise_texture, offsetPos).r;
float detail = sample_detail_noise(position * 10.0); // 10.0 is an arbitrary value for higher frequency
return mix(main_cloud, detail, detail_intensity);
}
float raymarch(vec3 ro_model, vec3 rd_model, vec2 intersections) {
float total_density = 0.0;
float t = intersections.x; // start from the entry point
float max_t = intersections.y; // march up to the exit point
for (int i = 0; i < max_steps && t < max_t; i++) {
vec3 pos_model = ro_model + t * rd_model;
float density = sample_cloud(pos_model * cloud_scale);
density = smoothstep(cloud_threshold - 0.05, cloud_threshold + cloud_threshold_multiplier, density);
total_density += density * step_size;
t += step_size;
}
return total_density;
}
// Return both entry and exit intersections with the mesh
vec2 getRayIntersections(vec3 ro, vec3 rd) {
vec3 t1 = (minBounds - ro) / rd;
vec3 t2 = (maxBounds - ro) / rd;
vec3 tmin = min(t1, t2);
vec3 tmax = max(t1, t2);
float t_near = max(max(tmin.x, tmin.y), tmin.z);
float t_far = min(min(tmax.x, tmax.y), tmax.z);
if (t_near > t_far || t_far < 0.0) {
return vec2(-1.0, -1.0); // No intersection
}
return vec2(t_near, t_far);
}
void fragment() {
vec3 ro_world = INV_VIEW_MATRIX[3].xyz;
vec4 clipPos = vec4((FRAGCOORD.xy / VIEWPORT_SIZE.xy) * 2.0 - 1.0, FRAGCOORD.z, 1.0);
vec4 viewPos = INV_PROJECTION_MATRIX * clipPos;
vec3 rd_world = normalize(viewPos.xyz / viewPos.w);
rd_world = mat3(INV_VIEW_MATRIX) * rd_world;
vec2 intersections = getRayIntersections(ro_world, rd_world);
// Check if there's no intersection
if (intersections.x == -1.0) {
discard;
}
vec3 ro_model = mat3(inverse(MODEL_MATRIX)) * (ro_world - MODEL_MATRIX[3].xyz);
vec3 rd_model = mat3(inverse(MODEL_MATRIX)) * rd_world;
float cloud_intensity = raymarch(ro_model, rd_model, intersections);
ALBEDO = vec3(0.8, 0.8, 0.9) * cloud_intensity;
ALPHA = cloud_intensity;
}
How to actually use this shader?