Skip to content

Commit

Permalink
now using alpha blending
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimev committed Aug 18, 2024
1 parent 9e78e88 commit 3069d45
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 103 deletions.
86 changes: 42 additions & 44 deletions godot/Atmosphere Showcase/Atmosphere/atmosphere.gdshader
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
shader_type spatial;
render_mode blend_mix, depth_test_disabled, unshaded;
render_mode blend_premul_alpha, depth_test_disabled, unshaded;

// depth texture, needed for proper blending
uniform sampler2D depth_texture: hint_depth_texture, filter_linear_mipmap;
Expand All @@ -11,7 +11,7 @@ uniform float atmo_radius = 32.0; // the radius of the atmosphere
uniform vec3 beta_ray =vec3(1.0, 2.0, 3.0); // the amount rayleigh scattering scatters the colors (for earth: causes the blue atmosphere)
uniform vec3 beta_mie = vec3(1.0); // the amount mie scattering scatters colors
uniform vec3 beta_ambient = vec3(0.0); // the amount of scattering that always occurs, can help make the back side of the atmosphere a bit brighter
uniform float beta_e = 0.0; // exponent, helps setting really small values of beta_ray, mie and ambient, as in beta_x * pow(10.0, beta_e)
uniform float beta_e = 0.0; // exponent, helps setting really small values of beta_ray, mie and ambient, as in beta_x * pow(10.0, beta_e)
uniform float g = 0.8; // the direction mie scatters the light in (like a cone). closer to -1 means more towards a single direction
uniform float height_ray = 0.5; // how high do you have to go before there is no rayleigh scattering?
uniform float height_mie = 0.25; // the same, but for mie
Expand All @@ -34,16 +34,16 @@ vec4 calculate_scattering(
float b = 2.0 * dot(dir, start);
float c = dot(start, start) - (atmo_radius * atmo_radius);
float d = (b * b) - 4.0 * a * c;

// stop early if there is no intersect
if (d < 0.0) return vec4(0.0);

// calculate the ray length
vec2 ray_length = vec2(
max((-b - sqrt(d)) / (2.0 * a), 0.0),
min((-b + sqrt(d)) / (2.0 * a), max_dist)
);

// if the ray did not hit the atmosphere, return a black color
if (ray_length.x > ray_length.y) return vec4(0.0);
// prevent the mie glow from appearing if there's an object in front of the camera
Expand All @@ -53,26 +53,26 @@ vec4 calculate_scattering(
ray_length.x = max(ray_length.x, 0.0);
// get the step size of the ray
float step_size_i = (ray_length.y - ray_length.x) / float(steps_i);

// helper for beta_e and mie
float e = pow(10.0, beta_e);

// next, set how far we are along the ray, so we can calculate the position of the sample
// if the camera is outside the atmosphere, the ray should start at the edge of the atmosphere
// if it's inside, it should start at the position of the camera
// the min statement makes sure of that
float ray_pos_i = ray_length.x;

// these are the values we use to gather all the scattered light
vec3 total_ray = vec3(0.0); // for rayleigh
vec3 total_mie = vec3(0.0); // for mie

// initialize the optical depth. This is used to calculate how much air was in the ray
vec2 opt_i = vec2(0.0);

// also init the scale height, avoids some vec2's later on
vec2 scale_height = vec2(height_ray, height_mie);

// Calculate the Rayleigh and Mie phases.
// This is the color that will be scattered for this ray
// mu, mumu and gg are used quite a lot in the calculation, so to speed it up, precalculate them
Expand All @@ -81,74 +81,74 @@ vec4 calculate_scattering(
float gg = g * g;
float phase_ray = 3.0 / (50.2654824574 /* (16 * pi) */) * (1.0 + mumu);
float phase_mie = allow_mie ? 3.0 / (25.1327412287 /* (8 * pi) */) * ((1.0 - gg) * (mumu + 1.0)) / (pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + gg)) : 0.0;

// now we need to sample the 'primary' ray. this ray gathers the light that gets scattered onto it
for (int i = 0; i < steps_i; ++i) {

// calculate where we are along this ray
vec3 pos_i = start + dir * (ray_pos_i + step_size_i * 0.5);

// and how high we are above the surface
float height_i = length(pos_i) - planet_radius;

// now calculate the density of the particles (both for rayleigh and mie)
vec2 density = exp(-height_i / scale_height) * step_size_i;

// Add these densities to the optical depth, so that we know how many particles are on this ray.
opt_i += density;

// Calculate the step size of the light ray.
// again with a ray sphere intersect
// a, b, c and d are already defined
a = dot(light_dir, light_dir);
b = 2.0 * dot(light_dir, pos_i);
c = dot(pos_i, pos_i) - (atmo_radius * atmo_radius);
d = (b * b) - 4.0 * a * c;

// no early stopping, this one should always be inside the atmosphere
// calculate the ray length
float step_size_l = (-b + sqrt(d)) / (2.0 * a * float(steps_l));

// and the position along this ray
// this time we are sure the ray is in the atmosphere, so set it to 0
float ray_pos_l = 0.0;

// and the optical depth of this ray
vec2 opt_l = vec2(0.0);

// now sample the light ray
// this is similar to what we did before
for (int l = 0; l < steps_l; ++l) {

// calculate where we are along this ray
vec3 pos_l = pos_i + light_dir * (ray_pos_l + step_size_l * 0.5);

// the heigth of the position
float height_l = length(pos_l) - planet_radius;

// calculate the particle density, and add it
opt_l += exp(-height_l / scale_height) * step_size_l;

// and increment where we are along the light ray.
ray_pos_l += step_size_l;

}

// Now we need to calculate the attenuation
// this is essentially how much light reaches the current sample point due to scattering
vec3 attn = exp(-((beta_mie * e * (opt_i.y + opt_l.y)) + (beta_ray * e * (opt_i.x + opt_l.x))));

// accumulate the scattered light (how much will be scattered towards the camera)
total_ray += density.x * attn;
total_mie += density.y * attn;

// and increment the position on this ray
ray_pos_i += step_size_i;
ray_pos_i += step_size_i;
}

// calculate how much light can pass through the atmosphere
float opacity = length(exp(-((beta_mie * e * opt_i.y) + (beta_ray * e * opt_i.x)) * density_multiplier));

// calculate and return the final color
return vec4((
phase_ray * beta_ray * e * total_ray // rayleigh color
Expand All @@ -164,20 +164,18 @@ void vertex() {
}

void fragment() {

// get the scene depth (what is the maximum ray length)
// https://docs.godotengine.org/en/stable/tutorials/shaders/advanced_postprocessing.html#depth-texture
float depth = texture(depth_texture, SCREEN_UV).r;
vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth);
vec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
view.xyz /= view.w;
float max_distance = length(view);

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 ppos = upos.xyz / upos.w;
float max_distance = length(ppos);

// calculate the scattering towards the camera
vec4 atm = calculate_scattering(cam_position, VIEW, max_distance, light_direction);

// godot doesn't have alpha compositing (yet), so improvise
atm.w = clamp(atm.w, 0.0001, 1.0);
ALBEDO = atm.xyz / atm.w;

// 4.3 got alpha blending!
ALBEDO = atm.xyz;
ALPHA = atm.w;
}
}
16 changes: 14 additions & 2 deletions godot/Atmosphere Showcase/Demo.tscn
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[gd_scene load_steps=10 format=3 uid="uid://hjd407bewuje"]
[gd_scene load_steps=12 format=3 uid="uid://hjd407bewuje"]

[ext_resource type="Script" path="res://settings_changer.gd" id="1"]
[ext_resource type="Shader" path="res://Atmosphere/atmosphere.gdshader" id="2_2lxo4"]
Expand Down Expand Up @@ -42,6 +42,11 @@ subdivide_width = 128
subdivide_height = 128
subdivide_depth = 128

[sub_resource type="SystemFont" id="SystemFont_fylws"]
font_names = PackedStringArray("Monospace")

[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_o4do7"]

[node name="Demo" type="Node3D"]
script = ExtResource("1")

Expand All @@ -60,9 +65,16 @@ current = true
script = ExtResource("4")

[node name="RichTextLabel" type="RichTextLabel" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_fonts/normal_font = SubResource("SystemFont_fylws")
bbcode_enabled = true
text = "Press escape to close
text = "Press ESC to close
"

[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = ExtResource("5_c1hy5")
camera_attributes = SubResource("CameraAttributesPractical_o4do7")
2 changes: 1 addition & 1 deletion godot/Atmosphere Showcase/project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ config_version=5

config/name="Atmosphere Showcase"
run/main_scene="res://Demo.tscn"
config/features=PackedStringArray("4.0")
config/features=PackedStringArray("4.3")
config/icon="res://icon.png"

[display]
Expand Down
24 changes: 12 additions & 12 deletions godot/Atmosphere Showcase/settings_changer.gd
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
extends Node

@export var speed : float = 2.0
var set = 0
var cam = 0

# Called when the node enters the scene tree for the first time.
func _ready():
set = 0
cam = 0
$AtmoMesh/Camera.speed = speed

func _process(_delta):
# get the time of the animation, wrapping every 10 seconds
var time = ((Time.get_ticks_msec() * speed) / 100000.0)

# leave the default colors
if time > 1 and set == 0:
if time > 1 and cam == 0:
$AtmoMesh.mesh.get_material().set_shader_parameter("beta_ray", Vector3(4, 1, 1))
set = 1
elif time > 1.1 and set == 1:
cam = 1
elif time > 1.1 and cam == 1:
$AtmoMesh.mesh.get_material().set_shader_parameter("beta_ray", Vector3(1, 4, 2))
set = 2
elif time > 1.2 * PI + 2 and set == 2:
cam = 2
elif time > 1.2 * PI + 2 and cam == 2:
$AtmoMesh.mesh.get_material().set_shader_parameter("beta_ray", Vector3(4, 1, 2))
$AtmoMesh.mesh.get_material().set_shader_parameter("intensity", 10.0)
set = 3
elif time > 1.3 * PI + 3 and set == 3:
cam = 3
elif time > 1.3 * PI + 3 and cam == 3:
$AtmoMesh.mesh.get_material().set_shader_parameter("beta_ray", Vector3(4, 2, 1))
$AtmoMesh.mesh.get_material().set_shader_parameter("beta_mie", Vector3(1, 4, 1))
$AtmoMesh.mesh.get_material().set_shader_parameter("mie_g", 0.6)
set = 4
elif time > 1.4 and set == 4:
cam = 4
elif time > 1.4 and cam == 4:
$AtmoMesh.mesh.get_material().set_shader_parameter("beta_ray", Vector3(1, 1, 3))
$AtmoMesh.mesh.get_material().set_shader_parameter("beta_mie", Vector3(4, 1, 1))
$AtmoMesh.mesh.get_material().set_shader_parameter("mie_g", 0.8)
$AtmoMesh.mesh.get_material().set_shader_parameter("intensity", 6.0)
set = 5
cam = 5
Loading

0 comments on commit 3069d45

Please sign in to comment.