* Added a realistically lit moon influenced by the sun, resulting in different moon phases, including Earth blocking moon (new moon phase, and blood moon).
* Moving camera very high on the Y axis, or changing the `Height` parameter brings the moon closer

# Misc
* Added support for moon and ground texture, accurate textures included.
* Moving the camera on the X and Z axis (very far) changes the sky and ground texture position.
* Cloud coverage now affects the brightness of the sky and sun.

# Regression
* Issues with cloud alpha at edges at high sun brightness, currently cutting out the sun from the clouds
This commit is contained in:
MMqd 2023-06-30 17:38:17 -04:00
parent ac76b913c9
commit a86eb99df7
29 changed files with 989 additions and 352 deletions

View File

@ -4,81 +4,119 @@ extends Node3D
var sun_color := Color.BLACK var sun_color := Color.BLACK
@export var sun_enabled := true @export var sun_enabled := true
@export var light_color := Color.WHITE @export var light_color := Color.WHITE
@export var sky_material : Material = null @export var sky_material: Material = null
@export var sun_object_path: NodePath @export var sun_object_path: NodePath
@export var moon_object_path: NodePath
@export var sun_ground_Height := 1000.0 @export var sun_ground_Height := 1000.0
@export var sun_saturation_scale := 100.0 @export var sun_saturation_scale := 100.0
@export var sun_saturation_mult := 0.3 @export var sun_saturation_mult := 0.3
@export_range(0.0000001, 1.0) var sun_desaturation_height := 0.25 @export_range(0.0000001, 1.0) var sun_desaturation_height := 0.25
@export var sun_gradient : GradientTexture1D = null @export var sun_gradient: GradientTexture1D = null
@export var sun_cloud_gradient : GradientTexture1D = null @export var sun_cloud_gradient: GradientTexture1D = null
@export var sun_cloud_ambient_gradient : GradientTexture1D = null @export var sun_cloud_ambient_gradient: GradientTexture1D = null
@export var sun_cloud_ground_gradient : GradientTexture1D = null @export var sun_cloud_ground_gradient: GradientTexture1D = null
@export var compute_gradient_toggle := false: @export var compute_gradient_toggle := false:
get: get:
return compute_gradient_toggle return compute_gradient_toggle
set(value): set(value):
if value: if value:
compute_gradient_toggle = false compute_gradient_toggle = false
var cloud_height = (sky_material.get_shader_parameter("cloud_bottom")+sky_material.get_shader_parameter("cloud_top"))*0.5 + sky_material.get_shader_parameter("Height") var cloud_height = (
(
(
get_param("cloud_bottom")
+ get_param("cloud_top")
)
* 0.5
)
+ get_param("Height")
)
var sun_min_angle_mult := 1.0 var sun_min_angle_mult := 1.0
var min_sun_y := sun_min_angle_mult*sin(acos(sky_material.get_shader_parameter("earthRadius") / (sky_material.get_shader_parameter("earthRadius") + sun_ground_Height))) var min_sun_y := (
var min_cloud_sun_y := sun_min_angle_mult*sin(acos(sky_material.get_shader_parameter("earthRadius") / (sky_material.get_shader_parameter("earthRadius") + cloud_height))) sun_min_angle_mult
* sin( acos(
get_param("earthRadius") / (get_param("earthRadius") + sun_ground_Height)
)
)
)
var min_cloud_sun_y := (
sun_min_angle_mult
* sin( acos(
get_param("earthRadius") / (get_param("earthRadius") + cloud_height)
)
)
)
sun_gradient = compute_sun_gradient(sun_ground_Height, min_sun_y) sun_gradient = compute_sun_gradient(sun_ground_Height, min_sun_y)
sun_cloud_gradient = compute_sun_gradient(min_cloud_sun_y, cloud_height) sun_cloud_gradient = compute_sun_gradient(min_cloud_sun_y, cloud_height)
sun_cloud_ambient_gradient = compute_sun_gradient(min_cloud_sun_y, sky_material.get_shader_parameter("cloud_top"), true) sun_cloud_ambient_gradient = compute_sun_gradient(
sun_cloud_ground_gradient = compute_sun_gradient(min_cloud_sun_y, sky_material.get_shader_parameter("cloud_bottom")) min_cloud_sun_y, get_param("cloud_top"), true
)
sun_cloud_ground_gradient = compute_sun_gradient(
min_cloud_sun_y, get_param("cloud_bottom")
)
func compute_sun_gradient(h: float, min_sun_y : float, ambient: bool = false): func set_param(param: String, value):
sky_material.set_shader_parameter(param, value)
func get_param(param: String):
return sky_material.get_shader_parameter(param)
func compute_sun_gradient(h: float, min_sun_y: float, ambient: bool = false):
var gradient := GradientTexture1D.new() var gradient := GradientTexture1D.new()
gradient.gradient = Gradient.new() gradient.gradient = Gradient.new()
var sample_count := 256 var sample_count := 256
var max_col := 0.0 var max_col := 0.0
var cols : Array[Color] = [] var cols: Array[Color] = []
var poss : Array[float] = [] var poss: Array[float] = []
var max_sky : Vector4 var max_sky: Vector4
if ambient: if ambient:
max_sky = sample_sky(Basis.from_euler(Vector3(PI*0.5, PI*0.5, 0.0)).z, Vector3.UP*h, Vector3.UP) max_sky = sample_sky(
Basis.from_euler(Vector3(PI * 0.5, PI * 0.5, 0.0)).z, Vector3.UP * h, Vector3.UP
)
else: else:
max_sky = sample_sky(Vector3.UP, Vector3.UP*h, Vector3.UP) max_sky = sample_sky(Vector3.UP, Vector3.UP * h, Vector3.UP)
for i in range(sample_count): for i in range(sample_count):
var new_i : float = i/(sample_count+1.0) var new_i: float = i / (sample_count + 1.0)
var dir : float = lerp(-0.5*PI, 0.5*PI, new_i) var dir: float = lerp(-0.5 * PI, 0.5 * PI, new_i)
var b_sun : Basis var b_sun: Basis
var sun_rot := Vector3(dir, 0.0, 0.0) var sun_rot := Vector3(dir, 0.0, 0.0)
sun_rot.x = min(Vector3(dir, 0.0, 0.0).x, asin(min_sun_y)) sun_rot.x = min(Vector3(dir, 0.0, 0.0).x, asin(min_sun_y))
b_sun = Basis.from_euler(sun_rot) b_sun = Basis.from_euler(sun_rot)
var b_sample : Basis = Basis.from_euler(Vector3(dir, 0.0, 0.0)) var b_sample: Basis = Basis.from_euler(Vector3(dir, 0.0, 0.0))
if ambient: if ambient:
b_sample = Basis.from_euler(Vector3(PI*0.5, PI*0.5, 0.0)) b_sample = Basis.from_euler(Vector3(PI * 0.5, PI * 0.5, 0.0))
var sky : Vector4 = sample_sky(b_sample.z, Vector3.UP*h, b_sun.z) var sky: Vector4 = sample_sky(b_sample.z, Vector3.UP * h, b_sun.z)
var col : Color = Color(sky.x, sky.y, sky.z).srgb_to_linear() var col: Color = Color(sky.x, sky.y, sky.z).srgb_to_linear()
if not ambient: if not ambient:
col = saturate(col, clamp((sun_desaturation_height-b_sun.z.y)/sun_desaturation_height, 0.0, 1.0)) col = saturate(
col,
clamp((sun_desaturation_height - b_sun.z.y) / sun_desaturation_height, 0.0, 1.0)
)
max_col = max(max_col, col.r, col.g, col.b) max_col = max(max_col, col.r, col.g, col.b)
cols.append(col) cols.append(col)
poss.append(new_i) poss.append(new_i)
for i in range(sample_count): for i in range(sample_count):
var new_i : float = i/(sample_count+1.0) var new_i: float = i / (sample_count + 1.0)
cols[i] /= max_col cols[i] /= max_col
cols[i].r *= light_color.r cols[i].r *= light_color.r
cols[i].g *= light_color.g cols[i].g *= light_color.g
cols[i].b *= light_color.b cols[i].b *= light_color.b
cols[i].a = 1.0 cols[i].a = 1.0
if i > 0 and cols[i]==cols[i-1]: if i > 0 and cols[i] == cols[i - 1]:
continue continue
gradient.gradient.add_point(poss[i], cols[i]) gradient.gradient.add_point(poss[i], cols[i])
gradient.gradient.remove_point(len(gradient.gradient.offsets)-1) gradient.gradient.remove_point(len(gradient.gradient.offsets) - 1)
gradient.gradient.remove_point(0) gradient.gradient.remove_point(0)
return gradient return gradient
#func rot_to_gradient(rot: float) -> float: #func rot_to_gradient(rot: float) -> float:
# if rot > 0.5*PI: # if rot > 0.5*PI:
# return fmod(rot, 0.5*PI)/PI - 0.5 # return fmod(rot, 0.5*PI)/PI - 0.5
@ -86,8 +124,10 @@ func compute_sun_gradient(h: float, min_sun_y : float, ambient: bool = false):
# return 0.5-fmod(rot, 0.5*PI)/PI # return 0.5-fmod(rot, 0.5*PI)/PI
# return rot/PI # return rot/PI
func rot_to_gradient(rot: float) -> float: func rot_to_gradient(rot: float) -> float:
return (1.0-rot)*0.5 return (1.0 - rot) * 0.5
func normalized_color(col: Vector4) -> Vector4: func normalized_color(col: Vector4) -> Vector4:
if max(col.x, col.y, col.z) == 0.0: if max(col.x, col.y, col.z) == 0.0:
@ -97,89 +137,185 @@ func normalized_color(col: Vector4) -> Vector4:
return col return col
func saturate(col: Color, saturation: float) -> Color: func saturate(col: Color, saturation: float) -> Color:
return Color.from_hsv(col.h, return Color.from_hsv(
clamp(log(col.s*saturation*sun_saturation_scale+1.0)*sun_saturation_mult, 0.0, 1.0), col.h,
col.v) clamp(log(col.s * saturation * sun_saturation_scale + 1.0) * sun_saturation_mult, 0.0, 1.0),
col.v
)
func loop(val: float, val_range: float) -> float: func loop(val: float, val_range: float) -> float:
if val > val_range: if val > val_range:
return fmod(val, val_range) - val_range return fmod(val, val_range) - val_range
elif val < -val_range: if val < -val_range:
return fmod(val, -val_range) + val_range return fmod(val, -val_range) + val_range
return val return val
func loop_angle(val: float) -> float:
if val > 2*PI:
return fmod(val, 2*PI) - 2*PI
elif val < -2*PI:
return -fmod(val, -2*PI) + 2*PI
return val
func _process(delta): func _process(delta):
var cloud_height = (sky_material.get_shader_parameter("cloud_bottom")+sky_material.get_shader_parameter("cloud_top"))*0.5 + sky_material.get_shader_parameter("Height") var cloud_height = (
var sun_dir : Vector3 = global_transform.basis.z (get_param("cloud_bottom") + get_param("cloud_top"))
* 0.5 + get_param("Height")
)
var sun_dir: Vector3 = global_transform.basis.z
var sun_min_angle_mult := 1.0 var sun_min_angle_mult := 1.0
var min_sun_y := sun_min_angle_mult*sin(acos(sky_material.get_shader_parameter("earthRadius") / (sky_material.get_shader_parameter("earthRadius") + sun_ground_Height))) var min_sun_y := (
var min_cloud_sun_y := sun_min_angle_mult*sin(acos(sky_material.get_shader_parameter("earthRadius") / (sky_material.get_shader_parameter("earthRadius") + cloud_height))) sun_min_angle_mult
* sin( acos(
get_param("earthRadius") / (get_param("earthRadius") + sun_ground_Height)
)
)
)
var min_cloud_sun_y := (
sun_min_angle_mult
* sin( acos(
get_param("earthRadius") / (get_param("earthRadius") + cloud_height)
)
)
)
var sun_object = get_node(sun_object_path) var sun_object = get_node(sun_object_path)
# print(loop_angle(rotation.x))
rotation.x = loop(rotation.x, PI) rotation.x = loop(rotation.x, PI)
rotation.y = loop(rotation.y, PI) rotation.y = loop(rotation.y, PI)
rotation.z = loop(rotation.z,PI) rotation.z = loop(rotation.z, PI)
var moon_object = get_node(moon_object_path)
set_param("precomputed_moon_dir", moon_object.global_transform.basis)
set_param(
"precomputed_sun_size", deg_to_rad(sun_object.light_angular_distance)
)
var precomputed_sun_size : float = deg_to_rad(sun_object.light_angular_distance)
var moonRadius : float = get_param("moonRadius")
var moonDistance : float = get_param("moonDistance")
var earthRadius : float = get_param("earthRadius")
var moon_dir : Vector3 = moon_object.global_transform.basis.z
var moon_size : float = (moonRadius /
((moonDistance + earthRadius) * moon_dir -
Vector3.UP * (get_viewport().get_camera_3d().global_position.y + earthRadius + get_param("Height"))).length() *
2.0) * get_param("moon_size_mult")
var sun_passthrough := 1.0
if (moon_size > 0.0):
var sun_atten_range := sin(precomputed_sun_size)
var moon_atten_range := sin(deg_to_rad(moon_size)) * 0.5
sun_passthrough = pow(clamp(1.0 - clamp(min(
moon_object.global_transform.basis.z.dot(sun_dir),
1.0) -
(1.0 - moon_atten_range),
0.0, 1.0) /
moon_atten_range,
0.0, 1.0),
2.0)
sun_object.light_energy = sun_passthrough * lerp(1.0, 0.0, pow(clamp((get_param("cloud_coverage") - 0.25) / 0.75, 0.0, 1.0), 0.5));
set_param(
"precomputed_sun_energy",
sun_object.light_intensity_lux / get_world_3d().get_environment().background_intensity
)
set_param("precomputed_background_intensity", get_world_3d().get_environment().background_intensity)
sun_object.rotation = rotation sun_object.rotation = rotation
sun_object.rotation.x = max(rotation.x, PI-asin(min_sun_y)) if (rotation.x > PI*0.5) else min(rotation.x, asin(min_sun_y)) sun_object.rotation.x = (
max(rotation.x, PI - asin(min_sun_y))
if (rotation.x > PI * 0.5)
else min(rotation.x, asin(min_sun_y))
)
if sun_enabled: if sun_enabled:
sun_object.visible = sun_dir.y > -sin(deg_to_rad(sun_object.light_angular_distance)+acos(sky_material.get_shader_parameter("earthRadius") / (sky_material.get_shader_parameter("earthRadius") + sky_material.get_shader_parameter("cloud_top") * float(sky_material.get_shader_parameter("clouds")) ))) sun_object.visible = (
sky_material.set_shader_parameter("precomputed_sun_visible", sun_object.visible) sun_dir.y > -sin(
sky_material.set_shader_parameter("precomputed_sun_enabled", sun_enabled) deg_to_rad(sun_object.light_angular_distance)
+ acos(
get_param("earthRadius")
/ (
get_param("earthRadius")
+ (
get_param("cloud_top")
* float(get_param("clouds"))
)
)
)
)
)
set_param("precomputed_sun_visible", sun_object.visible)
set_param("precomputed_sun_enabled", sun_enabled)
else: else:
sun_object.visible = false sun_object.visible = false
sky_material.set_shader_parameter("precomputed_sun_visible", false) set_param("precomputed_sun_visible", false)
sky_material.set_shader_parameter("precomputed_sun_enabled", false) set_param("precomputed_sun_enabled", false)
var gradient_pos := rot_to_gradient(sun_dir.y) var gradient_pos := rot_to_gradient(sun_dir.y)
sun_object.light_color = sun_gradient.gradient.sample(gradient_pos) var sun_ratio := asin(deg_to_rad(sun_object.light_angular_distance)) / PI
sky_material.set_shader_parameter("precomputed_sun_dir", sun_dir) var sun_gradient_offset: float = -clamp(1.0 - sun_dir.y / sun_ratio, 0.0, 1.0) * sun_ratio
sky_material.set_shader_parameter("precomputed_sun_color", light_color) sun_object.light_color = sun_gradient.gradient.sample(gradient_pos + sun_gradient_offset)
set_param("precomputed_sun_dir", sun_dir)
set_param("precomputed_sun_color", light_color)
#Precomputed cloud lighting #Precomputed cloud lighting
if sky_material.get_shader_parameter("clouds"): if get_param("clouds"):
var cloud_sun_rot := rotation var cloud_sun_rot := rotation
cloud_sun_rot.x = min(rotation.x, asin(min_cloud_sun_y)) cloud_sun_rot.x = min(rotation.x, asin(min_cloud_sun_y))
sky_material.set_shader_parameter("precomputed_Atmosphere_sun", sun_cloud_gradient.gradient.sample(gradient_pos)) set_param(
sky_material.set_shader_parameter("precomputed_Atmosphere_ambient", sun_cloud_ambient_gradient.gradient.sample(gradient_pos)) "precomputed_Atmosphere_sun",
sky_material.set_shader_parameter("precomputed_Atmosphere_ground", sun_cloud_ground_gradient.gradient.sample(gradient_pos)) sun_cloud_gradient.gradient.sample(gradient_pos + sun_gradient_offset)
)
set_param(
"precomputed_Atmosphere_ambient",
sun_cloud_ambient_gradient.gradient.sample(gradient_pos)
)
set_param(
"precomputed_Atmosphere_ground", sun_cloud_ground_gradient.gradient.sample(gradient_pos)
)
var ground_color : Vector3 = Vector3(0.1, 0.07, 0.034)
var ground_brightness : float = 1.0
func solve_quadratic(origin : Vector3, dir : Vector3, Radius : float) -> Vector3: var ground_color: Vector3 = Vector3(0.1, 0.07, 0.034)
var ground_brightness: float = 1.0
func solve_quadratic(origin: Vector3, dir: Vector3, Radius: float) -> Vector3:
var b := 2.0 * dir.dot(origin) var b := 2.0 * dir.dot(origin)
var c := origin.dot(origin) - Radius * Radius var c := origin.dot(origin) - Radius * Radius
var d := b*b - 4.0 * c var d := b * b - 4.0 * c
var det := sqrt(d) var det := sqrt(d)
return Vector3((-b + det) * 0.5, (-b - det) * 0.5, d) return Vector3((-b + det) * 0.5, (-b - det) * 0.5, d)
func atmosphere(Direction: Vector3, pos: Vector3, SunDirection: Vector3, intensity: float = 1.0) -> Array[Vector3]:
func atmosphere(
Direction: Vector3, pos: Vector3, SunDirection: Vector3, intensity: float = 1.0
) -> Array[Vector3]:
var shader_Height := 1.0 var shader_Height := 1.0
# var intensity : float = sky_material.get_shader_parameter("intensity") # var intensity : float = get_param("intensity")
var Re : float = sky_material.get_shader_parameter("earthRadius") var Re: float = get_param("earthRadius")
var Ra : float = sky_material.get_shader_parameter("atmosphereRadius") var Ra: float = get_param("atmosphereRadius")
var Hr : float = sky_material.get_shader_parameter("rayleighScaleHeight") var Hr: float = get_param("rayleighScaleHeight")
var Hm : float = sky_material.get_shader_parameter("mieScaleHeight") var Hm: float = get_param("mieScaleHeight")
var mie_eccentricity : float = sky_material.get_shader_parameter("mie_eccentricity") var mie_eccentricity: float = get_param("mie_eccentricity")
var turbidity : float = sky_material.get_shader_parameter("turbidity") var turbidity: float = get_param("turbidity")
var ground := 0.0 var ground := 0.0
var mu := Direction.dot(SunDirection) var mu := Direction.dot(SunDirection)
var phaseR := (3.0 / (16.0 * PI)) * (1.0 + mu * mu) var phaseR := (3.0 / (16.0 * PI)) * (1.0 + mu * mu)
var phaseM := (3.0 / (8.0 * PI)) * ((1.0 - mie_eccentricity * mie_eccentricity) * (1.0 + mu * mu) / ((2.0 + mie_eccentricity * mie_eccentricity) * pow(1.0 + mie_eccentricity * mie_eccentricity - 2.0 * mie_eccentricity * mu, 1.5))) var phaseM := (
(3.0 / (8.0 * PI))
* (
(1.0 - mie_eccentricity * mie_eccentricity)
* (1.0 + mu * mu)
/ (
(2.0 + mie_eccentricity * mie_eccentricity)
* pow(1.0 + mie_eccentricity * mie_eccentricity - 2.0 * mie_eccentricity * mu, 1.5)
)
)
)
var SumR := Vector3.ZERO var SumR := Vector3.ZERO
var SumM := Vector3.ZERO var SumM := Vector3.ZERO
@ -187,41 +323,48 @@ func atmosphere(Direction: Vector3, pos: Vector3, SunDirection: Vector3, intensi
var begin := Vector3.ZERO var begin := Vector3.ZERO
var end := Vector3.ZERO var end := Vector3.ZERO
var cameraPos := Vector3(0,Re + sun_ground_Height + max(0.0, pos.y),0) var cameraPos := Vector3(0, Re + sun_ground_Height + max(0.0, pos.y), 0)
begin = cameraPos begin = cameraPos
var d1 := solve_quadratic(cameraPos, Direction, Ra) var d1 := solve_quadratic(cameraPos, Direction, Ra)
if (d1.x > d1.y && d1.x > 0.0): if d1.x > d1.y && d1.x > 0.0:
end = cameraPos + Direction * d1.x end = cameraPos + Direction * d1.x
if (d1.y > 0.0): if d1.y > 0.0:
begin = cameraPos + Direction * d1.y begin = cameraPos + Direction * d1.y
else: else:
return [Vector3.ZERO, Vector3.ONE, Vector3.ONE] return [Vector3.ZERO, Vector3.ONE, Vector3.ONE]
var d2 = solve_quadratic(cameraPos, Direction, Re) var d2 = solve_quadratic(cameraPos, Direction, Re)
if (d2.x > 0.0 && d2.y > 0.0): if d2.x > 0.0 && d2.y > 0.0:
end = begin + Direction * d2.y end = begin + Direction * d2.y
ground=1.0 ground = 1.0
var numSamples := 16*16 var numSamples := 16 * 16
var numSamplesL := 8*2 var numSamplesL := 8 * 2
var segmentLength := begin.distance_to(end) / float(numSamples) var segmentLength := begin.distance_to(end) / float(numSamples)
var opticalDepthR := 0.0 var opticalDepthR := 0.0
var opticalDepthM := 0.0 var opticalDepthM := 0.0
var atmosphere_atten := Vector3.ZERO var atmosphere_atten := Vector3.ZERO
var BetaR : Vector3 = sky_material.get_shader_parameter("rayleigh_color") * 22.4e-6 * sky_material.get_shader_parameter("rayleigh") var BetaR: Vector3 = (
var BetaM : Vector3 = sky_material.get_shader_parameter("mie_color") * 20e-6 * sky_material.get_shader_parameter("mie") get_param("rayleigh_color")
* 22.4e-6
* get_param("rayleigh")
)
var BetaM: Vector3 = (
get_param("mie_color")
* 20e-6
* get_param("mie")
)
for i in range(numSamples): for i in range(numSamples):
var Px := begin + Direction * segmentLength * (float(i) + 0.5) var Px := begin + Direction * segmentLength * (float(i) + 0.5)
var sampleHeight := Px.length() - Re var sampleHeight := Px.length() - Re
var Hr_sample := exp(-sampleHeight/(Hr*turbidity)) * segmentLength var Hr_sample := exp(-sampleHeight / (Hr * turbidity)) * segmentLength
var Hm_sample := exp(-sampleHeight/(Hm*turbidity)) * segmentLength var Hm_sample := exp(-sampleHeight / (Hm * turbidity)) * segmentLength
opticalDepthR += Hr_sample opticalDepthR += Hr_sample
opticalDepthM += Hm_sample opticalDepthM += Hm_sample
@ -232,26 +375,29 @@ func atmosphere(Direction: Vector3, pos: Vector3, SunDirection: Vector3, intensi
var d3 = solve_quadratic(Px, SunDirection, Ra) var d3 = solve_quadratic(Px, SunDirection, Ra)
var d4 = solve_quadratic(Px, SunDirection, Re) var d4 = solve_quadratic(Px, SunDirection, Re)
if (d4.x > 0.0 and d4.y > 0.0): if d4.x > 0.0 and d4.y > 0.0:
continue continue
var j2 := 0 var j2 := 0
var segmentLengthL : float = max(d3.x, d3.y) / float(numSamplesL) var segmentLengthL: float = max(d3.x, d3.y) / float(numSamplesL)
for j in range(numSamplesL): for j in range(numSamplesL):
var Pl : Vector3 = Px + SunDirection * segmentLengthL * (j + 0.5) var Pl: Vector3 = Px + SunDirection * segmentLengthL * (j + 0.5)
var sampleHeightL : float = Pl.length() - Re var sampleHeightL: float = Pl.length() - Re
if (sampleHeightL < 0.0): if sampleHeightL < 0.0:
break break
opticalDepthLR += exp(-sampleHeightL/(Hr*turbidity)) opticalDepthLR += exp(-sampleHeightL / (Hr * turbidity))
opticalDepthLM += exp(-sampleHeightL/(Hm*turbidity)) opticalDepthLM += exp(-sampleHeightL / (Hm * turbidity))
j2 += 1 j2 += 1
if (j2 == numSamplesL): if j2 == numSamplesL:
opticalDepthLR *= segmentLengthL opticalDepthLR *= segmentLengthL
opticalDepthLM *= segmentLengthL opticalDepthLM *= segmentLengthL
var tau := BetaR * (opticalDepthR + opticalDepthLR) + BetaM * 1.1 * (opticalDepthM + opticalDepthLM) var tau := (
BetaR * (opticalDepthR + opticalDepthLR)
+ BetaM * 1.1 * (opticalDepthM + opticalDepthLM)
)
var attenuation := v3exp(-tau) var attenuation := v3exp(-tau)
atmosphere_atten += tau atmosphere_atten += tau
@ -259,23 +405,56 @@ func atmosphere(Direction: Vector3, pos: Vector3, SunDirection: Vector3, intensi
SumM += Hm_sample * attenuation SumM += Hm_sample * attenuation
var sky := SumR * phaseR * BetaR + SumM * phaseM * BetaM var sky := SumR * phaseR * BetaR + SumM * phaseM * BetaM
return [sky, atmosphere_atten*(1.0-ground), v3exp(-(opticalDepthR * BetaR + opticalDepthM * BetaM))] return [
sky,
atmosphere_atten * (1.0 - ground),
v3exp(-(opticalDepthR * BetaR + opticalDepthM * BetaM))
]
func v3exp(input: Vector3) -> Vector3: func v3exp(input: Vector3) -> Vector3:
return Vector3(exp(input.x), exp(input.y), exp(input.z)) return Vector3(exp(input.x), exp(input.y), exp(input.z))
func sample_sky(dir: Vector3, pos: Vector3, sun_dir: Vector3, LIGHT0_ENERGY: Vector3 = Vector3.ONE, LIGHT0_COLOR: Vector3 = Vector3.ONE) -> Vector4:
func sample_sky(
dir: Vector3,
pos: Vector3,
sun_dir: Vector3,
LIGHT0_ENERGY: Vector3 = Vector3.ONE,
LIGHT0_COLOR: Vector3 = Vector3.ONE
) -> Vector4:
var sun_object = get_node(sun_object_path) var sun_object = get_node(sun_object_path)
var sky : Array[Vector3] = atmosphere(dir, pos, sun_dir) var sky: Array[Vector3] = atmosphere(dir, pos, sun_dir)
var skyxyz : Vector3 = sky[0] var skyxyz: Vector3 = sky[0]
var sun : Vector3 = Vector3.ZERO var sun: Vector3 = Vector3.ZERO
sun = ( sun = (
(Vector3.ONE-v3exp(-sky[1])) * ( Vector3.ONE*max(max(dir.dot(sun_dir),0.0)-(cos( deg_to_rad(sun_object.light_angular_distance) )), 0.0) * sky_material.get_shader_parameter("sun_brightness") (Vector3.ONE - v3exp(-sky[1]))
+ (Vector3.ONE-v3exp(-sky[2])) * ground_color * max(sun_dir.y, 0.0) * sky[2].x * ground_brightness ) * (
* LIGHT0_ENERGY) (
Vector3.ONE
* max(
(
max(dir.dot(sun_dir), 0.0)
- (cos(deg_to_rad(sun_object.light_angular_distance)))
),
0.0
)
* get_param("sun_brightness")
)
+ (
(Vector3.ONE - v3exp(-sky[2]))
* ground_color
* max(sun_dir.y, 0.0)
* sky[2].x
* ground_brightness
)
)
* LIGHT0_ENERGY
)
var col := skyxyz + sun var col := skyxyz + sun
return Vector4(col.x, col.y, col.z, 1.0) return Vector4(col.x, col.y, col.z, 1.0)
func mix(start: Vector3, end: Vector3, factor: float): func mix(start: Vector3, end: Vector3, factor: float):
return lerp(start, end, factor) return lerp(start, end, factor)

107
README.md
View File

@ -1,67 +1,98 @@
# Nishita Sky With Volumetric Clouds # Nishita Sky With Volumetric Clouds
This is a Nishita sky shader for godot 4.0, with [Clay John's volumetric clouds](https://github.com/clayjohn/godot-volumetric-cloud-demo) based on [a tutorial by scratch pixel](https://www.scratchapixel.com/lessons/procedural-generation-virtual-worlds/simulating-sky/simulating-colors-of-the-sky.html), which is recommended to read to understand what the sky parameters represent, when configuring the sky. This is a Nishita sky shader for Godot 4.0, with [Clay John's volumetric clouds](https://github.com/clayjohn/godot-volumetric-cloud-demo) based on [a tutorial by scratch pixel](https://www.scratchapixel.com/lessons/procedural-generation-virtual-worlds/simulating-sky/simulating-colors-of-the-sky.html), which is recommended to read to understand what the sky parameters represent, when configuring the sky.
## Screenshots (stars amplified for demonstration purposes) ## Screenshots
<div style="display:flex">
<div style="flex:1;padding-right:10px;">
<img src="Screenshots/1%20day.webp" width="200"/>
<p>Day</p>
</div>
<div style="flex:1;padding-left:10px;">
<img src="Screenshots/2 sunset.webp" width="300"/>
<p>Sunset</p>
</div>
</div>
**Day** <div style="display:flex">
![day](Screenshots/day.png) <div style="flex:1;padding-right:10px;">
<img src="Screenshots/3%20cloud%20sky.webp" width="200"/>
<p>Cloudy Sky</p>
</div>
<div style="flex:1;padding-left:10px;">
<img src="Screenshots/4%20 partial%20eclipse.webp" width="300"/>
<p>Partial Eclipse</p>
</div>
</div>
**Day Without Clouds** <div style="display:flex">
![day without clouds](Screenshots/day%20without%20clouds.png) <div style="flex:1;padding-right:10px;">
<img src="Screenshots/5%20full%20eclipse.webp" width="200"/>
<p>Full Eclipse</p>
</div>
<div style="flex:1;padding-left:10px;">
<img src="Screenshots/6%20blood%20moon.webp" width="300"/>
<p>Blood Moon</p>
</div>
</div>
**High Quality Day** <div style="display:flex">
![high quality day](Screenshots/high%20quality%20day.png) <div style="flex:1;padding-right:10px;">
<img src="Screenshots/7%20night%20sky%20with%20clouds.webp" width="200"/>
<p>Night Sky</p>
</div>
<div style="flex:1;padding-left:10px;">
<img src="Screenshots/8%20night%20sky%20without%20clouds.webp" width="300"/>
<p>Night Sky Without Clouds</p>
</div>
</div>
**Sunset** <div style="display:flex">
![sunset](Screenshots/sunset.png) <div style="flex:1;padding-right:10px;">
<img src="Screenshots/9%20earth%20from%20above.webp" width="200"/>
**After Sunset** <p>Earth From Above</p>
![after sunset](Screenshots/after%20sunset.png) </div>
<div style="flex:1;padding-left:10px;">
**Cloudy Night Sky After Sunset** <img src="Screenshots/10%20earth%20from%20above%20sunset.webp" width="300"/>
![cloudy night sky after sunset](Screenshots/cloudy%20night%20sky%20after%20sunset.png) <p>Earth From Above Sunset</p>
</div>
**Cloudy Night Sky** </div>
![cloudy night sky](Screenshots/cloudy%20night%20sky.png)
**Atmosphere from 100km**
![atmosphere from 100km](Screenshots/atmosphere%20from%20100km.png)
**Low Sun Atmosphere from 100km**
![low sun atmosphere from 100km](Screenshots/low%20sun%20atmosphere%20from%20100km.png)
**Very Low Sun Atmosphere From 100km**
![very low sun atmosphere from 100km](Screenshots/very%20low%20sun%20atmosphere%20from%20100km.png)
## Features ## Features
* Game-ready asset (although in alpha) * Game-ready asset
* Raymarched sky * Raymarched sky
* Raymarched clouds that move with the camera * Raymarched clouds that move with the camera
* Different times of day by rotating the "NishitaSky" node * Different times of day by rotating the "NishitaSky" node
* Realistic lighting at different altitudes * Realistic lighting at different altitudes
* A night sky * A night sky, with Milky Way texture
* A directional light that takes on the color of the sun in the shader * A directional light that takes on the color of the sun in the shader
* All elements interact with each other: the night sky is blocked by the clouds and attenuated by the atmosphere * All elements interact with each other: the night sky is blocked by the clouds and attenuated by the atmosphere
* Ability to configure quality of the shader and turn the clouds on/off * Ability to configure quality of the shader and turn the clouds on/off
* Moon
* Realistically lit moon influenced by the sun, resulting in different moon phases, including Earth blocking moon (new moon phase, and blood moon)
* Support for moon and ground textures, accurate textures included
* Performance optimizations * Performance optimizations
## Bonus Features
* Raising the camera high on the Y axis brings the moon closer
* Moving the camera on the XZ axis (very far) changes the sky and ground texture position
## Limitations ## Limitations
* Performance heavy, especially with clouds on * Performance heavy, especially with clouds on
* The camera must remain below the clouds (but is clamped to cloud height if it goes higher), since the clouds do not actully exist * The camera must remain below the clouds (but is clamped to cloud height if it goes higher), since the clouds do not actually exist
## Improvements ## Improvements
* For the sky precompute the optical depth between the sun and an arbitrary point along the ray (from Nishita's paper) * For the sky precompute the optical depth between the sun and an arbitrary point along the ray (from Nishita's paper)
* Add multiple scattering to clouds and sky * Add multiple scattering to clouds and sky
* Physical raytraced clouds, with better lighting (curently the clouds are evenly lit) * Physical raytraced clouds, with better lighting (currently the clouds are evenly lit)
* Better cloud density textures * Better cloud density textures
* Use cloud sample distance for cloud fog (currently uses distance to clouds) * Use cloud sample distance for cloud fog (currently uses distance to clouds)
* Physically accurate ground material (currently the brightness is just a dot product to the sun) * Physically accurate ground material (currently the brightness is just a dot product to the sun)
* Better sun color saturation (currently some hacks are nessary to get the expected sun brightness and saturation) * Better sun color saturation (currently some hacks are necessary to get the expected sun brightness and saturation)
## How to Use ## How to Use
To implement this sky into a project To implement this sky into a project
1. Copy the "NishitaSky" node from the main scene into a the project 1. Copy the "NishitaSky" node from the main scene into the project
2. In the "NishitaSky" node set "sun_object_path" variable to the desired directional light, do not make this directional light a child of the "NishitaSky" node 2. In the "NishitaSky" node set "sun_object_path" variable to the desired directional light, do not make this directional light a child of the "NishitaSky" node
3. Create an "WorldEnvironment" node, set the sky material to the "nishita_sky" material 3. Create an "WorldEnvironment" node, set the sky material to the "nishita_sky" material
4. Click copy on the sky section of the "WorldEnvironment" node, and paste it into the "sky_material" section of the "NishitaSky" node. **THE MATERIALS MUST BE LINKED FOR THE SKY PARAMETERS TO BE THE SAME ON THE SCRIPT AND THE SHADER** 4. Click copy on the sky section of the "WorldEnvironment" node, and paste it into the "sky_material" section of the "NishitaSky" node. **THE MATERIALS MUST BE LINKED FOR THE SKY PARAMETERS TO BE THE SAME ON THE SCRIPT AND THE SHADER**
@ -72,5 +103,13 @@ To implement this sky into a project
## Todo ## Todo
* Fix clouds "jumping" after some time * Fix clouds "jumping" after some time
* Clean up code
* Rework sun saturation * Rework sun saturation
* Set WorldEnvironment fog color based on sky color * Set WorldEnvironment fog color based on sky color
* Make stars move with the sun
* Position sun, stars, and moon using a real world date/time
## Images
* Moon albedo image was rendered from [NASA](https://svs.gsfc.nasa.gov/cgi-bin/details.cgi?aid=4720)
* Night sky HDRI was underexposed and compressed to webp from [NASA](https://svs.gsfc.nasa.gov/4851#media_group_5169)
* Earth image was color corrected and converted to webp from [NASA](https://visibleearth.nasa.gov/images/74142/september-blue-marble-next-generation/74159l)

0
Screenshots/.gdignore Normal file
View File

BIN
Screenshots/1 day.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
Screenshots/2 sunset.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

BIN
earth albedo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

35
earth albedo.webp.import Normal file
View File

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bhpaqsnerf2bw"
path.bptc="res://.godot/imported/earth albedo.webp-487e903020ebde7daefa19f384bb1069.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://earth albedo.webp"
dest_files=["res://.godot/imported/earth albedo.webp-487e903020ebde7daefa19f384bb1069.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=true
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=true
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

View File

@ -1,10 +1,22 @@
extends Label extends Label
func _ready(): func _ready():
$"/root/Main/Camera3D".position.y = pow($"%HeightSlider".value, 4.0)-$"/root/Main/WorldEnvironment".environment.sky.sky_material.get_shader_parameter("Height")+1.0 $"/root/Main/Camera3D".position.y = (
pow($"%HeightSlider".value, 4.0)
- $"/root/Main/WorldEnvironment".environment.sky.sky_material.get_shader_parameter("Height")
+ 1.0
)
func _process(delta: float) -> void: func _process(delta: float) -> void:
set_text("FPS: " + str(Engine.get_frames_per_second())) set_text("FPS: " + str(Engine.get_frames_per_second()))
get_node("/root/Main/Moon Holder/Moon").transform.rotated(Vector3.UP, delta)
func _on_height_slider_value_changed(value): func _on_height_slider_value_changed(value):
$"/root/Main/Camera3D".position.y = pow(value, 4.0)-$"/root/Main/WorldEnvironment".environment.sky.sky_material.get_shader_parameter("Height")+1.0 $"/root/Main/Camera3D".position.y = (
pow(value, 4.0)
- $"/root/Main/WorldEnvironment".environment.sky.sky_material.get_shader_parameter("Height")
+ 1.0
)

View File

@ -3,22 +3,23 @@
importer="3d_texture" importer="3d_texture"
type="CompressedTexture3D" type="CompressedTexture3D"
uid="uid://dbfbysid168mx" uid="uid://dbfbysid168mx"
path="res://.godot/imported/perlworlnoise.tga-b9f270f21ab5670cff551f6d1621ed1d.ctex3d" path.bptc="res://.godot/imported/perlworlnoise.tga-c779d624a992ed3ab2c0ac1ff9dc86ba.bptc.ctex3d"
metadata={ metadata={
"vram_texture": false "imported_formats": ["s3tc_bptc"],
"vram_texture": true
} }
[deps] [deps]
source_file="res://godot-volumetric-cloud-demo-main/perlworlnoise.tga" source_file="res://godot-volumetric-cloud-demo textures/perlworlnoise.tga"
dest_files=["res://.godot/imported/perlworlnoise.tga-b9f270f21ab5670cff551f6d1621ed1d.ctex3d"] dest_files=["res://.godot/imported/perlworlnoise.tga-c779d624a992ed3ab2c0ac1ff9dc86ba.bptc.ctex3d"]
[params] [params]
compress/mode=3 compress/mode=2
compress/high_quality=true
compress/lossy_quality=0.7 compress/lossy_quality=0.7
compress/hdr_compression=1 compress/hdr_compression=1
compress/bptc_ldr=0
compress/channel_pack=0 compress/channel_pack=0
mipmaps/generate=true mipmaps/generate=true
mipmaps/limit=-1 mipmaps/limit=-1

View File

@ -3,26 +3,25 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://dfkye0uf4i6w1" uid="uid://dfkye0uf4i6w1"
path.s3tc="res://.godot/imported/weather.bmp-409063809cd2b4d1d20a0208167a841e.s3tc.ctex" path.bptc="res://.godot/imported/weather.bmp-875d1f4c165bb883d17be4b13da6595f.bptc.ctex"
path.etc2="res://.godot/imported/weather.bmp-409063809cd2b4d1d20a0208167a841e.etc2.ctex"
metadata={ metadata={
"imported_formats": ["s3tc", "etc2"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
} }
[deps] [deps]
source_file="res://godot-volumetric-cloud-demo-main/weather.bmp" source_file="res://godot-volumetric-cloud-demo textures/weather.bmp"
dest_files=["res://.godot/imported/weather.bmp-409063809cd2b4d1d20a0208167a841e.s3tc.ctex", "res://.godot/imported/weather.bmp-409063809cd2b4d1d20a0208167a841e.etc2.ctex"] dest_files=["res://.godot/imported/weather.bmp-875d1f4c165bb883d17be4b13da6595f.bptc.ctex"]
[params] [params]
compress/mode=2 compress/mode=2
compress/high_quality=true
compress/lossy_quality=0.7 compress/lossy_quality=0.7
compress/hdr_compression=1 compress/hdr_compression=1
compress/bptc_ldr=0
compress/normal_map=0 compress/normal_map=0
compress/channel_pack=0 compress/channel_pack=1
mipmaps/generate=false mipmaps/generate=false
mipmaps/limit=-1 mipmaps/limit=-1
roughness/mode=0 roughness/mode=0

View File

@ -3,24 +3,23 @@
importer="3d_texture" importer="3d_texture"
type="CompressedTexture3D" type="CompressedTexture3D"
uid="uid://c4dp6g6gouj2b" uid="uid://c4dp6g6gouj2b"
path.etc2="res://.godot/imported/worlnoise.bmp-1f9acdf96caf7345cd4596da56bb527c.etc2.ctex3d" path.bptc="res://.godot/imported/worlnoise.bmp-07c9d5c2e8f73e8a112f07c06bc532f3.bptc.ctex3d"
path.s3tc="res://.godot/imported/worlnoise.bmp-1f9acdf96caf7345cd4596da56bb527c.s3tc.ctex3d"
metadata={ metadata={
"imported_formats": ["etc2", "s3tc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
} }
[deps] [deps]
source_file="res://godot-volumetric-cloud-demo-main/worlnoise.bmp" source_file="res://godot-volumetric-cloud-demo textures/worlnoise.bmp"
dest_files=["res://.godot/imported/worlnoise.bmp-1f9acdf96caf7345cd4596da56bb527c.etc2.ctex3d", "res://.godot/imported/worlnoise.bmp-1f9acdf96caf7345cd4596da56bb527c.s3tc.ctex3d"] dest_files=["res://.godot/imported/worlnoise.bmp-07c9d5c2e8f73e8a112f07c06bc532f3.bptc.ctex3d"]
[params] [params]
compress/mode=2 compress/mode=2
compress/high_quality=true
compress/lossy_quality=0.7 compress/lossy_quality=0.7
compress/hdr_compression=1 compress/hdr_compression=1
compress/bptc_ldr=0
compress/channel_pack=0 compress/channel_pack=0
mipmaps/generate=true mipmaps/generate=true
mipmaps/limit=-1 mipmaps/limit=-1

View File

@ -3,25 +3,26 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://c4hd3ttt8f2x8" uid="uid://c4hd3ttt8f2x8"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" path.s3tc="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.s3tc.ctex"
metadata={ metadata={
"vram_texture": false "imported_formats": ["s3tc_bptc"],
"vram_texture": true
} }
[deps] [deps]
source_file="res://icon.svg" source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.s3tc.ctex"]
[params] [params]
compress/mode=0 compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7 compress/lossy_quality=0.7
compress/hdr_compression=1 compress/hdr_compression=1
compress/bptc_ldr=0
compress/normal_map=0 compress/normal_map=0
compress/channel_pack=0 compress/channel_pack=0
mipmaps/generate=false mipmaps/generate=true
mipmaps/limit=-1 mipmaps/limit=-1
roughness/mode=0 roughness/mode=0
roughness/src_normal="" roughness/src_normal=""
@ -31,7 +32,7 @@ process/normal_map_invert_y=false
process/hdr_as_srgb=false process/hdr_as_srgb=false
process/hdr_clamp_exposure=false process/hdr_clamp_exposure=false
process/size_limit=0 process/size_limit=0
detect_3d/compress_to=1 detect_3d/compress_to=0
svg/scale=1.0 svg/scale=1.0
editor/scale_with_editor_scale=false editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false editor/convert_colors_with_editor_theme=false

View File

@ -37,6 +37,7 @@ sky_material = ExtResource("2_txcwo")
[sub_resource type="Environment" id="Environment_fb38y"] [sub_resource type="Environment" id="Environment_fb38y"]
background_mode = 2 background_mode = 2
background_intensity = 100000.0
sky = SubResource("Sky_dunn8") sky = SubResource("Sky_dunn8")
tonemap_mode = 2 tonemap_mode = 2
tonemap_white = 16.0 tonemap_white = 16.0
@ -51,7 +52,6 @@ glow_levels/4 = 2.0
glow_levels/6 = 0.5 glow_levels/6 = 0.5
glow_levels/7 = 0.25 glow_levels/7 = 0.25
glow_normalized = true glow_normalized = true
glow_bloom = 1.0
glow_hdr_threshold = 0.0 glow_hdr_threshold = 0.0
[sub_resource type="CameraAttributesPhysical" id="CameraAttributesPhysical_5vtap"] [sub_resource type="CameraAttributesPhysical" id="CameraAttributesPhysical_5vtap"]
@ -85,19 +85,39 @@ albedo_color = Color(0.286275, 0.701961, 0.294118, 1)
[node name="Main" type="Node3D"] [node name="Main" type="Node3D"]
[node name="NishitaSky" type="Node3D" parent="."] [node name="NishitaSky" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -0.471007, 0.882119, 0, -0.882119, -0.471007, 0, 0, 0) transform = Transform3D(0.85681, -0.48743, 0.168147, 0.324956, 0.763631, 0.557873, -0.400348, -0.423352, 0.812679, 0, 0, 0)
script = ExtResource("1_jf2ik") script = ExtResource("1_jf2ik")
sky_material = ExtResource("2_txcwo") sky_material = ExtResource("2_txcwo")
sun_object_path = NodePath("../DirectionalLight3D") sun_object_path = NodePath("../Sun")
moon_object_path = NodePath("../Moon Holder/Moon")
sun_gradient = SubResource("GradientTexture1D_b4qlm") sun_gradient = SubResource("GradientTexture1D_b4qlm")
sun_cloud_gradient = SubResource("GradientTexture1D_hfemd") sun_cloud_gradient = SubResource("GradientTexture1D_hfemd")
sun_cloud_ambient_gradient = SubResource("GradientTexture1D_ihvx1") sun_cloud_ambient_gradient = SubResource("GradientTexture1D_ihvx1")
sun_cloud_ground_gradient = SubResource("GradientTexture1D_xb4xe") sun_cloud_ground_gradient = SubResource("GradientTexture1D_xb4xe")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] [node name="Press F - Space" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -0.471011, 0.882126, 0, -0.882126, -0.471011, 0, 0, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1e+07, 0)
light_color = Color(0.92075, 0.92075, 0.92075, 1)
light_angular_distance = 0.53 [node name="Press F - Space Other Side" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.00188e+07, 1e+07, 0)
[node name="Press F - Space Other Side 2" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1e+07, 1.00188e+07)
[node name="Sun" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.856811, -0.487443, 0.168152, 0.324956, 0.763651, 0.557889, -0.400348, -0.423363, 0.812702, 0, 0, 0)
light_color = Color(0.916235, 0.916235, 0.916235, 1)
light_angular_distance = 2.35
shadow_enabled = true
directional_shadow_blend_splits = true
[node name="Moon Holder" type="Node3D" parent="."]
transform = Transform3D(0.793073, -0.0738335, -0.604635, 0.498952, 0.648125, 0.575309, 0.349402, -0.757946, 0.55085, 0, 0, 0)
[node name="Moon" type="DirectionalLight3D" parent="Moon Holder"]
visible = false
light_intensity_lux = 1000.0
light_color = Color(0.918936, 0.918936, 0.918936, 1)
shadow_enabled = true shadow_enabled = true
directional_shadow_blend_splits = true directional_shadow_blend_splits = true
@ -106,6 +126,7 @@ environment = SubResource("Environment_fb38y")
camera_attributes = SubResource("CameraAttributesPhysical_5vtap") camera_attributes = SubResource("CameraAttributesPhysical_5vtap")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."] [node name="MeshInstance3D" type="MeshInstance3D" parent="."]
visible = false
mesh = SubResource("PlaneMesh_7tyt4") mesh = SubResource("PlaneMesh_7tyt4")
surface_material_override/0 = SubResource("StandardMaterial3D_34618") surface_material_override/0 = SubResource("StandardMaterial3D_34618")
@ -128,10 +149,12 @@ skeleton = NodePath("../..")
surface_material_override/0 = SubResource("StandardMaterial3D_70bv0") surface_material_override/0 = SubResource("StandardMaterial3D_70bv0")
[node name="Camera3D" type="Camera3D" parent="."] [node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(0.866025, 0, -0.5, 0, 1, 0, 0.5, 0, 0.866025, 0, 0, 0) transform = Transform3D(-0.948323, 0.133597, 0.287809, 0, 0.907044, -0.421036, -0.317304, -0.399278, -0.860171, 0, 0, 0)
cull_mask = 1048574 cull_mask = 1048574
fov = 90.0
[node name="Control" type="Control" parent="."] [node name="Control" type="Control" parent="."]
visible = false
layout_mode = 3 layout_mode = 3
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0

BIN
moon albedo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

35
moon albedo.webp.import Normal file
View File

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cendachjh3ous"
path.bptc="res://.godot/imported/moon albedo.webp-bb75edb645552e5cfb7fd5d75e51743f.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://moon albedo.webp"
dest_files=["res://.godot/imported/moon albedo.webp-bb75edb645552e5cfb7fd5d75e51743f.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=true
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=1
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=true
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

View File

@ -1,112 +1,147 @@
shader_type sky; shader_type sky;
render_mode use_half_res_pass, use_quarter_res_pass; render_mode use_half_res_pass, use_quarter_res_pass;
//These properties are precomputed by the NishitaSky script, and cannot be modified // These properties are precomputed by the NishitaSky script, and cannot be modified
uniform float precomputed_sun_visible = 1.0; uniform float precomputed_sun_visible = 1.0;
uniform float precomputed_sun_enabled = 1.0; uniform float precomputed_sun_enabled = 1.0;
uniform mat3 precomputed_moon_dir = mat3(0.0);
uniform vec3 precomputed_sun_dir = vec3(1.0, 0.0, 0.0); uniform vec3 precomputed_sun_dir = vec3(1.0, 0.0, 0.0);
uniform vec3 precomputed_sun_color : source_color = vec3(1.0); uniform vec3 precomputed_sun_color : source_color = vec3(1.0);
uniform vec3 precomputed_Atmosphere_sun : source_color = vec3(0.0); uniform vec3 precomputed_Atmosphere_sun : source_color = vec3(0.0);
uniform vec3 precomputed_Atmosphere_ambient : source_color = vec3(0.0); uniform vec3 precomputed_Atmosphere_ambient : source_color = vec3(0.0);
uniform vec3 precomputed_Atmosphere_ground : source_color = vec3(0.0); uniform vec3 precomputed_Atmosphere_ground : source_color = vec3(0.0);
uniform float precomputed_sun_size
: hint_range(0, 90.0) = 9.250245035569947e-03;
uniform float precomputed_sun_energy = 1.0;
uniform float precomputed_background_intensity = 1.0;
//Controls the scattering responsible for the blue color of a real life sky // Controls the scattering responsible for the blue color of a real life sky
uniform vec3 rayleigh_color = vec3(0.258928571428571, 0.580357142857143, 1.0); uniform vec3 rayleigh_color = vec3(0.258928571428571, 0.580357142857143, 1.0);
uniform float rayleigh : hint_range(0, 64) = 1.0; // higher values absorb more rayleigh_color, more blue by default uniform float rayleigh
: hint_range(0, 64) =
1.0; // higher values absorb more rayleigh_color, more blue by default
//Controls the scattering responsible for the white horizon of a real life sky // Controls the scattering responsible for the white horizon of a real life sky
uniform vec3 mie_color = vec3(1.0, 1.0, 1.0); uniform vec3 mie_color = vec3(1.0, 1.0, 1.0);
uniform float mie : hint_range(0, 64) = 1.0; // higher values make a "foggy" atmosphere uniform float mie
: hint_range(0, 64) = 1.0; // higher values make a "foggy" atmosphere
uniform float mie_eccentricity : hint_range(-1, 0.99999) = 0.76; uniform float mie_eccentricity : hint_range(-1, 0.99999) = 0.76;
//Sample counts for different parts of the sky // Sample counts for different parts of the sky
uniform int atmosphere_samples_max = 32; //maximum allowed atmosphere samples per pixel uniform int atmosphere_samples_max =
uniform int atmosphere_samples_min = 12; //minimum allowed atmosphere samples per pixel 32; // maximum allowed atmosphere samples per pixel
uniform float atmosphere_samples_horizon_bias = 0.5; //lower values bias more samples towards the horizon uniform int atmosphere_samples_min =
uniform int atmosphere_sun_samples = 32; //extra samples around sun, does not exceed maximum 12; // minimum allowed atmosphere samples per pixel
uniform int atmosphere_light_samples = 8; //scattering samples from each direction towards the sun uniform float atmosphere_samples_horizon_bias =
0.5; // lower values bias more samples towards the horizon
uniform int atmosphere_sun_samples =
32; // extra samples around sun, does not exceed maximum
uniform int atmosphere_light_samples =
8; // scattering samples from each direction towards the sun
uniform float turbidity : hint_range(0, 1000) = 1.0; uniform float turbidity : hint_range(0, 1000) = 1.0;
uniform vec3 ground_color : source_color = vec3(0.1, 0.07, 0.034); uniform vec3 ground_color : source_color = vec3(0.1, 0.07, 0.034);
//Brightness controls // Brightness controls
uniform float intensity = 10.0; //Intensity of sky. Does not affect clouds uniform float intensity = 10.0; // Intensity of sky. Does not affect clouds
uniform float sun_brightness = 100000.0; //brightness of only the solar disk uniform float sun_brightness = 10000.0; // brightness of only the solar disk
uniform float ground_brightness = 0.5; uniform float ground_brightness = 1.0;
uniform float night_sky_brightness = 0.001; uniform float night_sky_brightness = 1000.0;
//Night sky texture // Night sky texture
uniform sampler2D night_sky : source_color, hint_default_black; uniform sampler2D night_sky : source_color, hint_default_black;
//Height of viewer above the atmosphere, in addition to the camera's y position. Does not affect cloud height uniform sampler2D ground_texture : source_color, hint_default_white;
uniform vec3 ground_rotation = vec3(0.0);
uniform vec3 moon_eclipse_color : source_color = vec3(1.0, 0.1, 0.0);
uniform float moon_size_mult = 1.0;
uniform sampler2D moon_texture : source_color, hint_default_black;
// Height of viewer above the atmosphere, in addition to the camera's y
// position. Does not affect cloud height
uniform float Height = 1000.0; uniform float Height = 1000.0;
uniform float earthRadius = 6360e3; uniform float earthRadius = 6360e3;
uniform float moonRadius = 1738e3;
uniform float moonDistance = 384400e3;
uniform float atmosphereRadius = 6420e3; uniform float atmosphereRadius = 6420e3;
uniform float rayleighScaleHeight = 7994.0; // Scale height for Rayleigh scattering uniform float rayleighScaleHeight =
7994.0; // Scale height for Rayleigh scattering
uniform float mieScaleHeight = 1200.0; // Scale height for Mie scattering uniform float mieScaleHeight = 1200.0; // Scale height for Mie scattering
const float BetaRScale = 22.4e-6; const float BetaRScale = 22.4e-6;
const float BetaMScale = 20e-6; const float BetaMScale = 20e-6;
//const vec3 BetaR = vec3(5.8e-6,13.0e-6,22.4e-6); // const vec3 BetaR = vec3(5.8e-6,13.0e-6,22.4e-6);
//const vec3 BetaM = vec3(20e-6); // const vec3 BetaM = vec3(20e-6);
//dir is normalized // dir is normalized
vec3 solve_quadratic(vec3 origin, vec3 dir, float Radius){ vec3 solve_quadratic(vec3 origin, vec3 dir, float Radius) {
float b = 2.0 * dot(dir, origin); float b = 2.0 * dot(dir, origin);
float c = dot(origin, origin) - Radius * Radius; float c = dot(origin, origin) - Radius * Radius;
float d = b*b - 4.0 * c; float d = b * b - 4.0 * c;
float dsqrt = sqrt(d); float dsqrt = sqrt(d);
float x1 = (-b + dsqrt) * 0.5; float x1 = (-b + dsqrt) * 0.5;
float x2 = (-b - dsqrt) * 0.5; float x2 = (-b - dsqrt) * 0.5;
return vec3(x1, x2, d); return vec3(x1, x2, d);
} }
vec3[4] atmosphere(vec3 dir, vec3 pos){ vec3[5] atmosphere(vec3 dir, vec3 pos) {
vec3 SunDirection = precomputed_sun_dir; vec3 SunDirection = precomputed_sun_dir;
vec3 begin = vec3(0.0); vec3 start = vec3(0.0);
vec3 end = vec3(0.0); vec3 end = vec3(0.0);
vec3 cameraPos = vec3(0,earthRadius + Height + max(0.0, pos.y),0); vec3 cameraPos = vec3(0, earthRadius + Height + max(0.0, pos.y), 0);
begin = cameraPos; start = cameraPos;
vec3 d1 = solve_quadratic(cameraPos, dir, atmosphereRadius); vec3 d1 = solve_quadratic(cameraPos, dir, atmosphereRadius);
// Find atmosphere end point, exit if no intersection // Find atmosphere end point, exit if no intersection
if (d1.x > d1.y && d1.x > 0.0){ if (d1.x > d1.y && d1.x > 0.0) {
end = cameraPos + dir * d1.x; end = cameraPos + dir * d1.x;
// If the ray starts outside the atmosphere, set the origin to the edge of the atmosphere // If the ray starts outside the atmosphere, set the origin to the edge
if (d1.y > 0.0){ // of the atmosphere
begin = cameraPos + dir * d1.y; if (d1.y > 0.0) {
start = cameraPos + dir * d1.y;
} }
} else { } else {
return {vec3(0.0), vec3(1.0), vec3(0.0), vec3(1.0) }; return {vec3(0.0), vec3(1.0), vec3(0.0), vec3(1.0), end};
} }
float ground = 0.0; float ground = 0.0;
// Check if ray intersects with ground, and set the end point to the ground if it intersects // Check if ray intersects with ground, and set the end point to the ground
// if it intersects
vec3 d2 = solve_quadratic(cameraPos, dir, earthRadius); vec3 d2 = solve_quadratic(cameraPos, dir, earthRadius);
if (d2.x > 0.0 && d2.y > 0.0){ if (d2.x > 0.0 && d2.y > 0.0) {
end = cameraPos + dir * d2.y; ground=1.0; // enable ground end = cameraPos + dir * d2.y;
// end = cameraPos + dir * d1.y; // optionally disable ground, buggy at high altitudes and low sample counts, set a higher height instead. ground = 1.0; // enable ground
// return {vec3(0.0), vec3(1.0), vec3(1.0), vec3(1.0) }; // optionally disable atmosphere lighting, must set ground brightness to 0 as well. //end = cameraPos + dir * d1.y; // optionally disable ground, buggy at high altitudes and low sample counts, set a higher height instead.
//return {vec3(0.0), vec3(1.0), vec3(1.0), vec3(1.0), end}; // optionally disable atmosphere lighting, must set ground brightness to 0 as well.
} }
vec3 SumR = vec3(0.0); vec3 SumR = vec3(0.0);
vec3 SumM = vec3(0.0); vec3 SumM = vec3(0.0);
float mu = dot(dir, SunDirection); float mu = dot(dir, SunDirection);
float phaseR = (3.0 / (16.0 * PI)) * (1.0 + mu * mu); float phaseR = (3.0 / (16.0 * PI)) * (1.0 + mu * mu);
float phaseM = (3.0 / (8.0 * PI)) * ((1.0 - mie_eccentricity*mie_eccentricity) * (1.0 + mu * mu) / ((2.0 + mie_eccentricity * mie_eccentricity) * pow(1.0 + mie_eccentricity * mie_eccentricity - 2.0 * mie_eccentricity * mu, 1.5))); float phaseM =
(3.0 / (8.0 * PI)) *
((1.0 - mie_eccentricity * mie_eccentricity) * (1.0 + mu * mu) /
((2.0 + mie_eccentricity * mie_eccentricity) *
pow(1.0 + mie_eccentricity * mie_eccentricity -
2.0 * mie_eccentricity * mu,
1.5)));
float segmentLength = distance (begin, end); float segmentLength = distance(start, end);
float horizon = sin(acos(earthRadius / (earthRadius + Height))); float horizon = sin(acos(earthRadius / (earthRadius + Height)));
// Bias atmosphere samples towards sun and horizon // Bias atmosphere samples towards sun and horizon
float weighted_atmosphere_samples = ceil(clamp( float weighted_atmosphere_samples = ceil(
(max(clamp(1.0 - pow(abs(dir.y + horizon), atmosphere_samples_horizon_bias), 0.0, 1.0) clamp((max(clamp(1.0 - pow(abs(dir.y + horizon),
* float(atmosphere_samples_max) atmosphere_samples_horizon_bias),
, pow(max(mu, 0.0), 3.0) * float(atmosphere_sun_samples)) 0.0, 1.0) *
), float(atmosphere_samples_min), float(atmosphere_samples_max))); float(atmosphere_samples_max),
pow(max(mu, 0.0), 3.0) * float(atmosphere_sun_samples))),
float(atmosphere_samples_min), float(atmosphere_samples_max)));
segmentLength /= weighted_atmosphere_samples; segmentLength /= weighted_atmosphere_samples;
@ -117,18 +152,20 @@ vec3[4] atmosphere(vec3 dir, vec3 pos){
vec3 BetaR = rayleigh_color * BetaRScale * rayleigh; vec3 BetaR = rayleigh_color * BetaRScale * rayleigh;
vec3 BetaM = mie_color * BetaMScale * mie; vec3 BetaM = mie_color * BetaMScale * mie;
for (float i = 0.5; i < weighted_atmosphere_samples + 0.5; i++) { for (float i = 0.5; i < weighted_atmosphere_samples + 0.5; i++) {
vec3 Px = begin + dir * segmentLength * i; vec3 Px = start + dir * segmentLength * i;
float sampleHeight = length(Px) - earthRadius; float sampleHeight = length(Px) - earthRadius;
float Hr_sample = exp(-sampleHeight / (rayleighScaleHeight * turbidity)) * segmentLength; float Hr_sample =
float Hm_sample = exp(-sampleHeight / (mieScaleHeight * turbidity)) * segmentLength; exp(-sampleHeight / (rayleighScaleHeight * turbidity)) *
segmentLength;
float Hm_sample =
exp(-sampleHeight / (mieScaleHeight * turbidity)) * segmentLength;
opticalDepthR += Hr_sample; opticalDepthR += Hr_sample;
opticalDepthM += Hm_sample; opticalDepthM += Hm_sample;
if (precomputed_sun_enabled<0.5) if (precomputed_sun_enabled < 0.5)
continue; continue;
float opticalDepthLR = 0.0; float opticalDepthLR = 0.0;
@ -137,29 +174,36 @@ vec3[4] atmosphere(vec3 dir, vec3 pos){
vec3 d3 = solve_quadratic(Px, SunDirection, atmosphereRadius); vec3 d3 = solve_quadratic(Px, SunDirection, atmosphereRadius);
vec3 d4 = solve_quadratic(Px, SunDirection, earthRadius); vec3 d4 = solve_quadratic(Px, SunDirection, earthRadius);
// Ignore sample if sun is below horizon, used for performance boost at night time. Also fixes spots at edges at high altitudes // Ignore sample if sun is below horizon, used for performance boost at
// night time. Also fixes spots at edges at high altitudes
if ((d4.x > 0.0 && d4.y > 0.0) || d3.z < 0.0) if ((d4.x > 0.0 && d4.y > 0.0) || d3.z < 0.0)
continue; continue;
float segmentLengthL = max(d3.x, d3.y) / float(atmosphere_light_samples); float segmentLengthL =
max(d3.x, d3.y) / float(atmosphere_light_samples);
int j2 = 0; int j2 = 0;
for (float j = 0.5; j < float(atmosphere_light_samples) + 0.5; j++) { for (float j = 0.5; j < float(atmosphere_light_samples) + 0.5; j++) {
float sampleHeightL = length(Px + SunDirection * segmentLengthL * j) - earthRadius; float sampleHeightL =
length(Px + SunDirection * segmentLengthL * j) - earthRadius;
// Ignore light samples inside planet, used for performance boost at night time // Ignore light samples inside planet, used for performance boost at
// night time
if (sampleHeightL < 0.0) if (sampleHeightL < 0.0)
break; break;
opticalDepthLR += exp(-sampleHeightL/(rayleighScaleHeight*turbidity)); opticalDepthLR +=
opticalDepthLM += exp(-sampleHeightL/(mieScaleHeight*turbidity)); exp(-sampleHeightL / (rayleighScaleHeight * turbidity));
opticalDepthLM +=
exp(-sampleHeightL / (mieScaleHeight * turbidity));
j2++; j2++;
} }
// Attenuation // Attenuation
if (j2 == atmosphere_light_samples){ if (j2 == atmosphere_light_samples) {
opticalDepthLR *= segmentLengthL; opticalDepthLR *= segmentLengthL;
opticalDepthLM *= segmentLengthL; opticalDepthLM *= segmentLengthL;
vec3 tau = BetaR * (opticalDepthR + opticalDepthLR) + BetaM * 1.1 * (opticalDepthM + opticalDepthLM); vec3 tau = BetaR * (opticalDepthR + opticalDepthLR) +
BetaM * 1.1 * (opticalDepthM + opticalDepthLM);
vec3 attenuation = exp(-tau); vec3 attenuation = exp(-tau);
atmosphere_atten += attenuation; atmosphere_atten += attenuation;
@ -169,26 +213,90 @@ vec3[4] atmosphere(vec3 dir, vec3 pos){
} }
vec3 Sky = SumR * phaseR * BetaR + SumM * phaseM * BetaM; vec3 Sky = SumR * phaseR * BetaR + SumM * phaseM * BetaM;
return {Sky, atmosphere_atten, vec3(ground), exp(-(opticalDepthR * BetaR + opticalDepthM * BetaM)) }; return {Sky, atmosphere_atten, vec3(ground),
exp(-(opticalDepthR * BetaR + opticalDepthM * BetaM)), end};
} }
vec4[2] sample_sky(vec3 dir, vec3 pos){ vec3 rotate(vec3 point, vec3 axis, float angle) {
vec3[4] sky = atmosphere(dir, pos); float s = sin(angle);
vec3 sun = vec3(0.0); float c = cos(angle);
sun = float oc = 1.0 - c;
vec3 rotatedPoint;
rotatedPoint.x = point.x * (oc * axis.x * axis.x + c) +
point.y * (oc * axis.x * axis.y - axis.z * s) +
point.z * (oc * axis.x * axis.z + axis.y * s);
rotatedPoint.y = point.x * (oc * axis.x * axis.y + axis.z * s) +
point.y * (oc * axis.y * axis.y + c) +
point.z * (oc * axis.y * axis.z - axis.x * s);
rotatedPoint.z = point.x * (oc * axis.x * axis.z - axis.y * s) +
point.y * (oc * axis.y * axis.z + axis.x * s) +
point.z * (oc * axis.z * axis.z + c);
return rotatedPoint;
}
vec2 sphereToUV(vec3 point, vec3 rot) {
point = rotate(rotate(point, vec3(1.0, 0.0, 0.0), rot.x),
vec3(0.0, 1.0, 0.0), rot.y);
float theta = atan(point.x, point.y);
float phi = acos(-point.z / length(point));
vec2 uv = vec2((theta - (rot.z - 1.0)) * 0.5, // Rotate U
phi) /
PI;
return uv;
}
vec3 uvToSphere(vec2 uv) {
float theta = (1.0 - uv.x) * PI;
float phi = uv.y * TAU;
return vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
}
vec3 map_sphere_normal(vec3 x, vec3 y, vec3 z, vec2 point) {
return x * point.x + y * point.y +
z * sqrt(1.0 - point.x * point.x - point.y * point.y);
}
vec4[4] sample_sky(vec3 dir, vec3 pos, float visiblity) {
vec3[5] sky = atmosphere(dir, pos);
vec3 d2 =
solve_quadratic(vec3(0, earthRadius + Height + max(0.0, POSITION.y), 0),
dir, earthRadius);
vec3 end = sky[4] / earthRadius * 2.0;
vec2 uv =
sphereToUV(rotate(rotate(end, vec3(0.0, 0.0, 1.0),
pos.x * PI / (earthRadius + pos.y + Height)),
vec3(1.0, 0.0, 0.0),
pos.z * PI / (earthRadius + pos.y + Height)),
ground_rotation * 0.5);
// Sun, with sun-specific attenuation // Sun, with sun-specific attenuation
( (vec3(1.0)-exp(-sky[1])) * max(max(dot(dir, precomputed_sun_dir), 0.0) - (cos(LIGHT0_SIZE * 0.5)), 0.0) * sun_brightness * (1.0 - sky[2].r) + vec3 sun = (vec3(1.0) - exp(-sky[1])) *
max(max(dot(dir, precomputed_sun_dir), 0.0) -
(cos(precomputed_sun_size)),
0.0) *
sun_brightness * (1.0 - sky[2].r) * visiblity * precomputed_sun_energy;
// ground, with generic attenuation // ground, with generic attenuation
(vec3(1.0)-exp(-sky[3])) * ground_color * max(precomputed_sun_dir.y, 0.0) * sky[2].r * ground_brightness ) * LIGHT0_ENERGY ; vec3 ground = (vec3(1.0) - exp(-sky[3])) * ground_color *
vec3 col = (sun + sky[0].xyz); texture(ground_texture, uv).xyz * sky[2].r *
return {vec4(col * precomputed_sun_color, sky[2].r), vec4(sky[3] * (1.0 - sky[2].r), 1.0)}; ground_brightness;
ground *= clamp(dot(end, precomputed_sun_dir), 0.0, 1.0);
vec3 col = (ground + sky[0].xyz) * precomputed_sun_energy;
return {vec4(col * precomputed_sun_color, sky[2].r),
vec4(sky[3] * (1.0 - sky[2].r), 1.0), vec4(sun, 1.0),
vec4(end, 0.0)};
} }
/* Begin Cloud Parameters */ /* Begin Cloud Parameters */
// Cloud Raymarching based on: A. Schneider. “The earthRadiusal-Time Volumetric Cloudscapes Of Horizon: Zero Dawn”. ACM SIGGRAPH. Los Angeles, CA: ACM SIGGRAPH, 2015. Web. 26 Aug. 2015. // Cloud Raymarching based on: A. Schneider. “The earthRadiusal-Time Volumetric
// Cloudscapes Of Horizon: Zero Dawn”. ACM SIGGRAPH. Los Angeles, CA: ACM
// SIGGRAPH, 2015. Web. 26 Aug. 2015.
uniform sampler3D worlnoise : filter_linear_mipmap, repeat_enable; uniform sampler3D worlnoise : filter_linear_mipmap, repeat_enable;
uniform sampler3D perlworlnoise : filter_linear_mipmap, repeat_enable; uniform sampler3D perlworlnoise : filter_linear_mipmap, repeat_enable;
@ -204,19 +312,22 @@ uniform float _time_offset = 0.0;
uniform float cloud_bottom = 1500.0; uniform float cloud_bottom = 1500.0;
uniform float cloud_top = 4000.0; uniform float cloud_top = 4000.0;
uniform float cloud_brightness = 1.5; uniform float cloud_brightness = 1.0;
uniform float cloud_ambient_brightness = 0.5; uniform float cloud_ambient_brightness = 0.5;
// From: https://www.shadertoy.com/view/4sfGzS credit to iq // From: https://www.shadertoy.com/view/4sfGzS credit to iq
float hash(vec3 p) { float hash(vec3 p) {
p = fract( p * 0.3183099 + 0.1 ); p = fract(p * 0.3183099 + 0.1);
p *= 17.0; p *= 17.0;
return fract(p.x * p.y * p.z * (p.x + p.y + p.z)); return fract(p.x * p.y * p.z * (p.x + p.y + p.z));
} }
// Utility function that maps a value from one range to another. // Utility function that maps a value from one range to another.
float remap(float originalValue, float originalMin, float originalMax, float newMin, float newMax) { float remap(float originalValue, float originalMin, float originalMax,
return newMin + (((originalValue - originalMin) / (originalMax - originalMin)) * (newMax - newMin)); float newMin, float newMax) {
return newMin +
(((originalValue - originalMin) / (originalMax - originalMin)) *
(newMax - newMin));
} }
// Phase function // Phase function
@ -225,25 +336,27 @@ float henyey_greenstein(float cos_theta, float G) {
return k * (1.0 - G * G) / (pow(1.0 + G * G - 2.0 * G * cos_theta, 1.5)); return k * (1.0 - G * G) / (pow(1.0 + G * G - 2.0 * G * cos_theta, 1.5));
} }
float GetHeightFractionForPoint(float inPosition) { float GetHeightFractionForPoint(float inPosition) {
float height_fraction = (inPosition - cloud_bottom - earthRadius) / (cloud_top - cloud_bottom); float height_fraction =
(inPosition - cloud_bottom - earthRadius) / (cloud_top - cloud_bottom);
return clamp(height_fraction, 0.0, 1.0); return clamp(height_fraction, 0.0, 1.0);
} }
vec4 mixGradients(float cloudType){ vec4 mixGradients(float cloudType) {
const vec4 STRATUS_GRADIENT = vec4(0.02f, 0.05f, 0.09f, 0.11f); const vec4 STRATUS_GRADIENT = vec4(0.02f, 0.05f, 0.09f, 0.11f);
const vec4 STRATOCUMULUS_GRADIENT = vec4(0.02f, 0.2f, 0.48f, 0.625f); const vec4 STRATOCUMULUS_GRADIENT = vec4(0.02f, 0.2f, 0.48f, 0.625f);
const vec4 CUMULUS_GRADIENT = vec4(0.01f, 0.0625f, 0.78f, 1.0f); const vec4 CUMULUS_GRADIENT = vec4(0.01f, 0.0625f, 0.78f, 1.0f);
float stratus = 1.0f - clamp(cloudType * 2.0f, 0.0, 1.0); float stratus = 1.0f - clamp(cloudType * 2.0f, 0.0, 1.0);
float stratocumulus = 1.0f - abs(cloudType - 0.5f) * 2.0f; float stratocumulus = 1.0f - abs(cloudType - 0.5f) * 2.0f;
float cumulus = clamp(cloudType - 0.5f, 0.0, 1.0) * 2.0f; float cumulus = clamp(cloudType - 0.5f, 0.0, 1.0) * 2.0f;
return STRATUS_GRADIENT * stratus + STRATOCUMULUS_GRADIENT * stratocumulus + CUMULUS_GRADIENT * cumulus; return STRATUS_GRADIENT * stratus + STRATOCUMULUS_GRADIENT * stratocumulus +
CUMULUS_GRADIENT * cumulus;
} }
float densityHeightGradient(float heightFrac, float cloudType) { float densityHeightGradient(float heightFrac, float cloudType) {
vec4 cloudGradient = mixGradients(cloudType); vec4 cloudGradient = mixGradients(cloudType);
return smoothstep(cloudGradient.x, cloudGradient.y, heightFrac) - smoothstep(cloudGradient.z, cloudGradient.w, heightFrac); return smoothstep(cloudGradient.x, cloudGradient.y, heightFrac) -
smoothstep(cloudGradient.z, cloudGradient.w, heightFrac);
} }
// Returns density at a given point // Returns density at a given point
@ -253,28 +366,36 @@ float density(vec3 pip, vec3 weather, float mip) {
vec3 p = pip; vec3 p = pip;
p.x += time * 1.0 * _time_scale + _time_offset; p.x += time * 1.0 * _time_scale + _time_offset;
float height_fraction = GetHeightFractionForPoint(length(p)); float height_fraction = GetHeightFractionForPoint(length(p));
vec4 n = textureLod(perlworlnoise, p.xyz*0.00008, mip-2.0); vec4 n = textureLod(perlworlnoise, p.xyz * 0.00008, mip - 2.0);
float fbm = n.g*0.625+n.b*0.25+n.a*0.125; float fbm = n.g * 0.625 + n.b * 0.25 + n.a * 0.125;
float G = densityHeightGradient(height_fraction, weather.r); float G = densityHeightGradient(height_fraction, weather.r);
float base_cloud = remap(n.r, -(1.0-fbm), 1.0, 0.0, 1.0); float base_cloud = remap(n.r, -(1.0 - fbm), 1.0, 0.0, 1.0);
float weather_coverage = cloud_coverage*weather.b; float weather_coverage = cloud_coverage * weather.b;
base_cloud = remap(base_cloud*G, 1.0-(weather_coverage), 1.0, 0.0, 1.0); base_cloud = remap(base_cloud * G, 1.0 - (weather_coverage), 1.0, 0.0, 1.0);
base_cloud *= weather_coverage; base_cloud *= weather_coverage;
p.xy -= time * 4.0 * _time_scale + _time_offset; p.xy -= time * 4.0 * _time_scale + _time_offset;
vec3 hn = textureLod(worlnoise, p*0.001, mip).rgb; vec3 hn = textureLod(worlnoise, p * 0.001, mip).rgb;
float hfbm = hn.r*0.625+hn.g*0.25+hn.b*0.125; float hfbm = hn.r * 0.625 + hn.g * 0.25 + hn.b * 0.125;
hfbm = mix(hfbm, 1.0-hfbm, clamp(height_fraction*4.0, 0.0, 1.0)); hfbm = mix(hfbm, 1.0 - hfbm, clamp(height_fraction * 4.0, 0.0, 1.0));
base_cloud = remap(base_cloud, hfbm*0.4 * height_fraction, 1.0, 0.0, 1.0); base_cloud = remap(base_cloud, hfbm * 0.4 * height_fraction, 1.0, 0.0, 1.0);
return pow(clamp(base_cloud, 0.0, 1.0), (1.0 - height_fraction) * 0.8 + 0.5); return pow(clamp(base_cloud, 0.0, 1.0),
(1.0 - height_fraction) * 0.8 + 0.5);
} }
vec4 march(vec3 pos, vec3 end, vec3 dir, int depth, float sun_visible, float true_density) { vec4 march(vec3 pos, vec3 end, vec3 dir, int depth, float sun_visible,
const vec3 RANDOM_VECTORS[6] = {vec3( 0.38051305f, 0.92453449f, -0.02111345f),vec3(-0.50625799f, -0.03590792f, -0.86163418f),vec3(-0.32509218f, -0.94557439f, 0.01428793f),vec3( 0.09026238f, -0.27376545f, 0.95755165f),vec3( 0.28128598f, 0.42443639f, -0.86065785f),vec3(-0.16852403f, 0.14748697f, 0.97460106f)}; float true_density) {
const vec3 RANDOM_VECTORS[6] = {
vec3(0.38051305f, 0.92453449f, -0.02111345f),
vec3(-0.50625799f, -0.03590792f, -0.86163418f),
vec3(-0.32509218f, -0.94557439f, 0.01428793f),
vec3(0.09026238f, -0.27376545f, 0.95755165f),
vec3(0.28128598f, 0.42443639f, -0.86065785f),
vec3(-0.16852403f, 0.14748697f, 0.97460106f)};
float T = 1.0; float T = 1.0;
float alpha = 0.0; float alpha = 0.0;
vec3 ldir = precomputed_sun_dir; vec3 ldir = precomputed_sun_dir;
float ss = length(dir); float ss = length(dir);
dir = dir/ss; dir = dir / ss;
vec3 p = pos + hash(pos * 10.0) * ss; vec3 p = pos + hash(pos * 10.0) * ss;
float t_dist = cloud_top - cloud_bottom; float t_dist = cloud_top - cloud_bottom;
float lss = (t_dist / 36.0); float lss = (t_dist / 36.0);
@ -282,31 +403,40 @@ vec4 march(vec3 pos, vec3 end, vec3 dir, int depth, float sun_visible, float tru
float t = 1.0; float t = 1.0;
float costheta = dot(ldir, dir); float costheta = dot(ldir, dir);
// Stack multiple phase functions to emulate some backscattering // Stack multiple phase functions to emulate some backscattering
float phase = max(max(henyey_greenstein(costheta, 0.6), henyey_greenstein(costheta, (0.4 - 1.4 * ldir.y))), henyey_greenstein(costheta, -0.2)); float phase = max(max(henyey_greenstein(costheta, 0.6),
vec3 atmosphere_sun = precomputed_Atmosphere_sun * ss * cloud_brightness * LIGHT0_ENERGY * phase; henyey_greenstein(costheta, (0.4 - 1.4 * ldir.y))),
vec3 atmosphere_ambient = precomputed_Atmosphere_ambient * cloud_ambient_brightness * intensity; henyey_greenstein(costheta, -0.2));
vec3 atmosphere_ground = precomputed_Atmosphere_ground*ground_color.xyz*ground_brightness * LIGHT0_ENERGY * intensity; vec3 atmosphere_sun = precomputed_Atmosphere_sun * ss * cloud_brightness *
precomputed_sun_energy * phase;
vec3 atmosphere_ambient =
precomputed_Atmosphere_ambient * cloud_ambient_brightness * intensity;
vec3 atmosphere_ground = precomputed_Atmosphere_ground * ground_color.xyz *
ground_brightness * precomputed_sun_energy *
intensity;
const float weather_scale = 0.00006; const float weather_scale = 0.00006;
float time = mod(TIME, 100.0) * 0.0003 * _time_scale + 0.005*_time_offset; float time = mod(TIME, 10000.0) * 0.0003 * _time_scale + 0.005 * _time_offset;
vec2 weather_pos = vec2(time * 0.9, time); vec2 weather_pos = vec2(time * 0.9, time);
for (int i = 0; i < depth; i++) { for (int i = 0; i < depth; i++) {
p += dir * ss; p += dir * ss;
vec3 weather_sample = texture(weathermap, p.xz * weather_scale + 0.5 + weather_pos).xyz; vec3 weather_sample =
texture(weathermap, p.xz * weather_scale + 0.5 + weather_pos).xyz;
float height_fraction = GetHeightFractionForPoint(length(p)); float height_fraction = GetHeightFractionForPoint(length(p));
t = density(p, weather_sample, 0.0); t = density(p, weather_sample, 0.0);
float dt = exp(-true_density*t*ss); float dt = exp(-true_density * t * ss);
T *= dt; T *= dt;
vec3 lp = p; vec3 lp = p;
float lt = 1.0; float lt = 1.0;
float cd = 0.0; float cd = 0.0;
if (t > 0.0) { //calculate lighting, but only when we are in the cloud if (t > 0.0) { // calculate lighting, but only when we are in the cloud
for (float j = 0.0; j < 6.0 * sun_visible; j++) { for (float j = 0.0; j < 6.0 * sun_visible; j++) {
lp += (ldir + RANDOM_VECTORS[int(j)]*j)*lss; lp += (ldir + RANDOM_VECTORS[int(j)] * j) * lss;
vec3 lweather = texture(weathermap, lp.xz * weather_scale + 0.5 + weather_pos).xyz; vec3 lweather = texture(weathermap, lp.xz * weather_scale +
0.5 + weather_pos)
.xyz;
lt = density(lp, lweather, j); lt = density(lp, lweather, j);
cd += lt; cd += lt;
} }
@ -314,8 +444,10 @@ vec4 march(vec3 pos, vec3 end, vec3 dir, int depth, float sun_visible, float tru
// Take a single distant sample // Take a single distant sample
lp = p + ldir * 18.0 * lss; lp = p + ldir * 18.0 * lss;
float lheight_fraction = GetHeightFractionForPoint(length(lp)); float lheight_fraction = GetHeightFractionForPoint(length(lp));
vec3 lweather = texture(weathermap, lp.xz * weather_scale + 0.5).xyz; vec3 lweather =
lt = pow(density(lp, lweather, 5.0), (1.0 - lheight_fraction) * 0.8 + 0.5); texture(weathermap, lp.xz * weather_scale + 0.5).xyz;
lt = pow(density(lp, lweather, 5.0),
(1.0 - lheight_fraction) * 0.8 + 0.5);
cd += lt; cd += lt;
// captures the direct lighting from the sun // captures the direct lighting from the sun
@ -323,70 +455,219 @@ vec4 march(vec3 pos, vec3 end, vec3 dir, int depth, float sun_visible, float tru
float beers2 = exp(-true_density * cd * lss * 0.25) * 0.7; float beers2 = exp(-true_density * cd * lss * 0.25) * 0.7;
float beers_total = max(beers, beers2); float beers_total = max(beers, beers2);
vec3 ambient = mix(atmosphere_ground, atmosphere_ambient, smoothstep(0.0, 1.0, height_fraction)) * beers; vec3 ambient = mix(atmosphere_ground, atmosphere_ambient,
// vec3 ambient = mix(atmosphere_ground, vec3(1.0), smoothstep(0.0, 1.0, height_fraction)) * true_density * mix(atmosphere_ambient, vec3(1.0), 0.4); // * (ldir .y); smoothstep(0.0, 1.0, height_fraction)) *
beers;
// vec3 ambient = mix(atmosphere_ground, vec3(1.0),
//smoothstep(0.0, 1.0, height_fraction)) * true_density *
//mix(atmosphere_ambient, vec3(1.0), 0.4); // * (ldir .y);
alpha += (1.0 - dt) * (1.0 - alpha); alpha += (1.0 - dt) * (1.0 - alpha);
L += ((ambient + beers_total * atmosphere_sun) * alpha) * T * t; L += ((ambient + beers_total * atmosphere_sun) * alpha) * T * t;
} }
} }
return vec4(L*cloud_brightness, clamp(alpha, 0.0, 1.0)); return vec4(L * cloud_brightness, clamp(alpha, 0.0, 1.0));
} }
/* End Cloud Parameters */ /* End Cloud Parameters */
void sky() { void sky() {
vec3 dir = EYEDIR; vec3 dir = EYEDIR;
// float sun_visible = precomputed_sun_visible * max(sign(precomputed_sun_dir.y + sin(acos(earthRadius / (earthRadius + cloud_top)) + LIGHT0_SIZE)), 0.0); float sun_visible =
precomputed_sun_visible *
max(sign(precomputed_sun_dir.y +
sin(acos(earthRadius / (earthRadius + cloud_top)) +
precomputed_sun_size)),
0.0);
vec4 cloud_col = vec4(0.0); vec4 cloud_col = vec4(0.0);
float cloud_fade_stars = 1.0; float cloud_fade_stars = 1.0;
float cloud_fade = 1.0; float cloud_fade = 1.0;
bool AT_FULL_RES_PASS = !(AT_HALF_RES_PASS || AT_QUARTER_RES_PASS); bool AT_FULL_RES_PASS = !(AT_HALF_RES_PASS || AT_QUARTER_RES_PASS);
float horizon =
sin(acos(earthRadius / (earthRadius + max(POSITION.y, 0.0) + Height)));
float horizon_dist = dir.y + horizon;
if (clouds){ if (clouds) {
/* start Clouds */
/* Begin Clouds */ // if (POSITION.y < cloud_top){
float horizon = sin(acos(earthRadius / (earthRadius + max(POSITION.y, 0.0) + Height))); if ((AT_CUBEMAP_PASS && AT_QUARTER_RES_PASS) ||
(!AT_CUBEMAP_PASS && AT_HALF_RES_PASS)) {
if ((AT_CUBEMAP_PASS && AT_QUARTER_RES_PASS) || (!AT_CUBEMAP_PASS && AT_HALF_RES_PASS)){ float true_density =
float true_density = pow(1.0-clamp((POSITION.y-cloud_bottom)/(cloud_top-cloud_bottom), 0.0, 1.0),2.0)*_density; _density * pow(1.0 - clamp((POSITION.y - cloud_bottom) /
(cloud_top - cloud_bottom),
0.0, 1.0),
2.0);
vec4 volume = vec4(0.0); vec4 volume = vec4(0.0);
if (dir.y>-horizon && true_density>0.0){ if (horizon_dist > 0.0 && true_density > 0.0) {
vec3 camPos = vec3(POSITION.x, min(POSITION.y, cloud_bottom) + earthRadius, POSITION.z); vec3 camPos =
float cloud_start_distance = solve_quadratic(camPos, dir, cloud_bottom + earthRadius).x; vec3(POSITION.x, min(POSITION.y, cloud_bottom) + earthRadius, POSITION.z);
float cloud_end_distance = solve_quadratic(camPos, dir, cloud_top + earthRadius).x; vec3 d1 = solve_quadratic(camPos, dir, cloud_top + earthRadius);
vec3 d2 =
solve_quadratic(camPos, dir, cloud_bottom + earthRadius);
float cloud_start_distance = d2.x;
float cloud_end_distance = d1.x;
vec3 start = camPos + dir * cloud_start_distance; vec3 start = camPos + dir * cloud_start_distance;
vec3 end = camPos + dir * cloud_end_distance; vec3 end = camPos + dir * cloud_end_distance;
float shelldist = (cloud_end_distance-cloud_start_distance);
/*// Find atmosphere end point, exit if no intersection
if (d1.x > d1.y && d1.x > 0.0){
end = camPos + dir * d1.x;
// If the ray starts outside the atmosphere, set the origin
to the edge of the atmosphere if (d1.y > 0.0){ start = camPos +
dir * d1.y;
}
}*/
// Take more steps towards horizon, less steps in foggy clouds, and less steps at night float shelldist = (cloud_end_distance - cloud_start_distance);
float steps = ceil(mix(float(cloud_samples_horizon) * (1.0 - 0.25 * (1.0 - precomputed_sun_visible * (1.0-cloud_coverage))),
float(cloud_samples_sky) * (1.0 - 0.25 * (1.0 - precomputed_sun_visible)), // Take more steps towards horizon, less steps in foggy clouds,
sqrt(clamp(dir.y+horizon, 0.0, 1.0))) ); // and less steps at night
float steps = ceil(mix(
float(cloud_samples_horizon) *
(1.0 -
0.25 * (1.0 - sun_visible * (1.0 - cloud_coverage))),
float(cloud_samples_sky) *
(1.0 - 0.25 * (1.0 - sun_visible)),
sqrt(clamp(dir.y + horizon, 0.0, 1.0))));
vec3 raystep = dir * shelldist / steps; vec3 raystep = dir * shelldist / steps;
volume = march(start, end, raystep, int(steps), precomputed_sun_visible, true_density )*vec4(precomputed_sun_color, 1.0); volume = march(start, end, raystep, int(steps),
volume.xyz *= precomputed_sun_visible; precomputed_sun_visible, true_density);
volume.xyz *= precomputed_sun_visible * precomputed_sun_color;
} }
COLOR = volume.xyz; COLOR = volume.xyz;
ALPHA = volume.w; ALPHA = clamp(volume.w, 0.0, 1.0);
} else if (AT_FULL_RES_PASS){ } else if (AT_FULL_RES_PASS) {
cloud_fade = clamp(dir.y, 0.0, 1.0); cloud_fade = clamp(dir.y, 0.0, 1.0);
cloud_fade_stars = clamp(dir.y+horizon, 0.0, 1.0); cloud_fade_stars = clamp(horizon_dist, 0.0, 1.0);
cloud_col = AT_CUBEMAP_PASS ? QUARTER_RES_COLOR : HALF_RES_COLOR; cloud_col = AT_CUBEMAP_PASS ? QUARTER_RES_COLOR : HALF_RES_COLOR;
} }
// }
/* End Clouds */ /* End Clouds */
} }
if (AT_FULL_RES_PASS){ float moon_size =
vec4[2] background = sample_sky(dir, POSITION); (moonRadius /
vec3 col_stars = texture(night_sky, SKY_COORDS ).xyz * night_sky_brightness * background[1].xyz; length((moonDistance + earthRadius) * precomputed_moon_dir[2] -
vec3 col = background[0].xyz * intensity * precomputed_sun_enabled; vec3(0.0, POSITION.y + earthRadius + Height, 0.0)) *
2.0) *
moon_size_mult;
COLOR = col*(1.0-cloud_col.a*cloud_fade_stars) + cloud_col.xyz*cloud_fade + col_stars * (1.0-cloud_col.a)*cloud_fade_stars; float sun_visibility = 1.0 - clamp((dot(EYEDIR, precomputed_moon_dir[2]) -
sqrt(1.0 - 0.25 * (moon_size / 45.0))) *
100000.0,
0.0, 1.0);
if (AT_FULL_RES_PASS) {
vec4[4] background = sample_sky(dir, POSITION, sun_visibility);
vec4 col_moon = vec4(vec3(0.0), 0.0);
vec2 moon_cord = vec2(dot(EYEDIR, precomputed_moon_dir[1]),
dot(EYEDIR, precomputed_moon_dir[0])) /
sqrt(moon_size / 45.0);
vec3 moon_to_sun = precomputed_sun_dir;
if (dot(EYEDIR, precomputed_moon_dir[2]) >
sqrt(1.0 - 0.25 * (moon_size / 45.0))) {
vec3 moon_normal = normalize(map_sphere_normal(
precomputed_moon_dir[1], precomputed_moon_dir[0],
-precomputed_moon_dir[2] * 0.5, moon_cord * 2.0));
float earth_shadow_sharpness = 200.0;
col_moon = texture(moon_texture, vec2(0.5) - moon_cord.yx);
float moon_dist_earth_ratio =
1.0 - atan(earthRadius / (moonDistance + earthRadius)) / TAU;
float moon_eclipse = (clamp((moon_dist_earth_ratio -
max(dot(-normalize(
precomputed_moon_dir[2] *
(earthRadius + moonDistance) +
vec3(0.0, POSITION.y + earthRadius + Height,
0.0)),
normalize(precomputed_sun_dir)),
0.0)) /
moon_dist_earth_ratio * earth_shadow_sharpness,
0.0, 1.0));
col_moon.xyz *= precomputed_background_intensity *
precomputed_sun_energy *
(clamp(dot(moon_normal, moon_to_sun), 0.0, 1.0)) * moon_eclipse * mix(moon_eclipse_color, vec3(1.0), moon_eclipse)
;
}
vec3 col_stars =
(texture(night_sky,
sphereToUV(EYEDIR,
vec3(PI * 0.5 + POSITION.z * PI /
(earthRadius + POSITION.y +
Height),
-POSITION.x * PI /
(earthRadius + POSITION.y + Height),
-PI * 0.5) +
ground_rotation * 0.5))
.xyz *
night_sky_brightness * (1.0 - col_moon.w) +
col_moon.xyz * col_moon.w) *
background[1].xyz / precomputed_background_intensity;
cloud_col.a = clamp(cloud_col.a*precomputed_background_intensity/sun_brightness, 0.0, 1.0);
float cloud_passthrough = (1.0 - cloud_col.a) * cloud_fade_stars;
// Eclipse needs reworking
float sun_passthrough = 1.0;
if (moon_size > 0.0) {
float sun_atten_range = sin(precomputed_sun_size);
float moon_atten_range = sin(radians(moon_size)) * 0.5;
sun_passthrough =
pow(clamp(1.0 - clamp(min(dot(precomputed_moon_dir[2],
precomputed_sun_dir),
1.0) -
(1.0 - moon_atten_range),
0.0, 1.0) /
moon_atten_range,
0.0, 1.0),
2.0);
// sun_passthrough =
// sqrt(mix(clamp(sin(precomputed_sun_size)-sin(radians(moon_size)),
// 0.0, 1.0), 1.0, sun_passthrough));
}
// Eclipse needs reworking
// float moon_tint_ratio
//= 1.0-atan(moonRadius/(moonDistance+earthRadius))/TAU; sun_passthrough
//= 1.0-clamp((dot(normalize(normalize(map_sphere_normal(vec3(1.0, 0.0,
//0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0)*0.5,
//background[3].xz*0.5)))
// *earthRadius+precomputed_moon_dir[2]*(moonDistance+earthRadius),
//precomputed_sun_dir)-moon_tint_ratio)/(1.0-moon_tint_ratio)*10.0,0.0,1.0);
// COLOR = vec3(clamp(
// clamp(dot(normalize(normalize(
// map_sphere_normal(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0),
//vec3(0.0, 0.0, 1.0)*0.5, background[3].xz*2.0))*earthRadius
// -precomputed_moon_dir[2]*(moonDistance+earthRadius)),
// precomputed_sun_dir)+moon_tint_ratio, 0.0, 1.0)*100000.0
// , 0.0, 1.0));
// COLOR = vec3(clamp(dot(normalize(normalize(
// -map_sphere_normal(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0),
//vec3(0.0, 0.0, 1.0), background[3].xz*0.5))*earthRadius
// +precomputed_moon_dir[2]*(moonDistance+earthRadius)),
// -precomputed_sun_dir)+moon_tint_ratio,
//0.0, 1.0)/(1.0-moon_tint_ratio));
vec3 sky_col = background[0].xyz * intensity *
(max(precomputed_sun_dir.y,
clamp((POSITION.y + Height) /
(atmosphereRadius - earthRadius),
0.0, 1.0))) *
precomputed_sun_enabled * sun_passthrough;
sky_col =
mix(sky_col,
vec3(sky_col.x + sky_col.y + sky_col.z) * 0.0033333333,
pow(clamp((cloud_coverage - 0.25) / 0.75, 0.0, 1.0), 0.5));
cloud_col.xyz *= sun_passthrough;
vec3 sun_col = background[2].xyz * intensity * precomputed_sun_enabled;
// clamp(vec3(0.3)-clamp(cloud_col.www*cloud_fade_stars, 0.0, 1.0),
// 0.0, 1.0)
COLOR = sky_col
+ sun_col * (1.0 - cloud_col.w)
+ cloud_col.xyz * cloud_fade_stars * mix(cloud_fade_stars, 1.0, clamp(precomputed_sun_dir.y, 0.0, 1.0))
+ col_stars * (1.0 - cloud_col.w);
} }
} }

View File

@ -1,33 +1,25 @@
[gd_resource type="ShaderMaterial" load_steps=8 format=3 uid="uid://b45abiagp8tuf"] [gd_resource type="ShaderMaterial" load_steps=7 format=3 uid="uid://b45abiagp8tuf"]
[ext_resource type="Shader" path="res://nishita_sky.gdshader" id="1_3qhyv"] [ext_resource type="Shader" path="res://nishita_sky.gdshader" id="1_3qhyv"]
[ext_resource type="CompressedTexture3D" uid="uid://dbfbysid168mx" path="res://godot-volumetric-cloud-demo textures/perlworlnoise.tga" id="1_qd3aw"] [ext_resource type="Texture2D" uid="uid://bhpaqsnerf2bw" path="res://earth albedo.webp" id="2_8dcp8"]
[ext_resource type="Texture2D" uid="uid://dfkye0uf4i6w1" path="res://godot-volumetric-cloud-demo textures/weather.bmp" id="2_bohio"] [ext_resource type="Texture2D" uid="uid://cendachjh3ous" path="res://moon albedo.webp" id="3_2bh7o"]
[ext_resource type="CompressedTexture3D" uid="uid://c4dp6g6gouj2b" path="res://godot-volumetric-cloud-demo textures/worlnoise.bmp" id="3_5fbd6"] [ext_resource type="CompressedTexture3D" uid="uid://dbfbysid168mx" path="res://godot-volumetric-cloud-demo textures/perlworlnoise.tga" id="5_q6lkc"]
[ext_resource type="Texture2D" uid="uid://dfkye0uf4i6w1" path="res://godot-volumetric-cloud-demo textures/weather.bmp" id="6_gs20s"]
[sub_resource type="Gradient" id="Gradient_gllyc"] [ext_resource type="CompressedTexture3D" uid="uid://c4dp6g6gouj2b" path="res://godot-volumetric-cloud-demo textures/worlnoise.bmp" id="7_wkoma"]
interpolation_mode = 2
offsets = PackedFloat32Array(0.693133, 1)
[sub_resource type="FastNoiseLite" id="FastNoiseLite_d5sdi"]
seed = 309
frequency = 1.0
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_q4x3w"]
width = 2048
height = 2048
color_ramp = SubResource("Gradient_gllyc")
noise = SubResource("FastNoiseLite_d5sdi")
[resource] [resource]
shader = ExtResource("1_3qhyv") shader = ExtResource("1_3qhyv")
shader_parameter/precomputed_sun_visible = true shader_parameter/precomputed_sun_visible = 1.0
shader_parameter/precomputed_sun_enabled = true shader_parameter/precomputed_sun_enabled = 1.0
shader_parameter/precomputed_sun_dir = Vector3(0, 0.0132469, -0.999903) shader_parameter/precomputed_moon_dir = Basis(0.793073, -0.0738336, -0.604635, 0.498952, 0.648125, 0.575309, 0.349402, -0.757946, 0.55085)
shader_parameter/precomputed_sun_dir = Vector3(0.168147, 0.557873, 0.812679)
shader_parameter/precomputed_sun_color = Color(1, 1, 1, 1) shader_parameter/precomputed_sun_color = Color(1, 1, 1, 1)
shader_parameter/precomputed_Atmosphere_sun = Color(0.994039, 0.390172, 0.20851, 1) shader_parameter/precomputed_Atmosphere_sun = Color(0.891427, 0.891427, 0.891427, 1)
shader_parameter/precomputed_Atmosphere_ambient = Color(0.0282629, 0.0132965, 0.00359788, 1) shader_parameter/precomputed_Atmosphere_ambient = Color(0.262109, 0.50014, 0.759034, 1)
shader_parameter/precomputed_Atmosphere_ground = Color(0.994039, 0.390172, 0.20851, 1) shader_parameter/precomputed_Atmosphere_ground = Color(0.891427, 0.891427, 0.891427, 1)
shader_parameter/precomputed_sun_size = 0.0410152
shader_parameter/precomputed_sun_energy = 1.0
shader_parameter/precomputed_background_intensity = 100000.0
shader_parameter/rayleigh_color = Vector3(0.258929, 0.580357, 1) shader_parameter/rayleigh_color = Vector3(0.258929, 0.580357, 1)
shader_parameter/rayleigh = 1.0 shader_parameter/rayleigh = 1.0
shader_parameter/mie_color = Vector3(1, 1, 1) shader_parameter/mie_color = Vector3(1, 1, 1)
@ -39,13 +31,18 @@ shader_parameter/atmosphere_samples_horizon_bias = 0.5
shader_parameter/atmosphere_sun_samples = 32 shader_parameter/atmosphere_sun_samples = 32
shader_parameter/atmosphere_light_samples = 8 shader_parameter/atmosphere_light_samples = 8
shader_parameter/turbidity = 1.0 shader_parameter/turbidity = 1.0
shader_parameter/ground_color = Color(0.1, 0.07, 0.034, 1) shader_parameter/ground_color = Color(1, 1, 1, 1)
shader_parameter/intensity = 10.0 shader_parameter/intensity = 10.0
shader_parameter/sun_brightness = 100000.0 shader_parameter/sun_brightness = 10000.0
shader_parameter/ground_brightness = 0.5 shader_parameter/ground_brightness = 1.0
shader_parameter/night_sky_brightness = 1.0 shader_parameter/night_sky_brightness = 100000.0
shader_parameter/ground_rotation = Vector3(-0.79, 0, -5.015)
shader_parameter/moon_eclipse_color = Color(1, 0.1, 0, 1)
shader_parameter/moon_size_mult = 30.0
shader_parameter/Height = 1000.0 shader_parameter/Height = 1000.0
shader_parameter/earthRadius = 6.36e+06 shader_parameter/earthRadius = 6.36e+06
shader_parameter/moonRadius = 1.738e+06
shader_parameter/moonDistance = 3.844e+08
shader_parameter/atmosphereRadius = 6.42e+06 shader_parameter/atmosphereRadius = 6.42e+06
shader_parameter/rayleighScaleHeight = 7994.0 shader_parameter/rayleighScaleHeight = 7994.0
shader_parameter/mieScaleHeight = 1200.0 shader_parameter/mieScaleHeight = 1200.0
@ -58,9 +55,10 @@ shader_parameter/_time_scale = 1.0
shader_parameter/_time_offset = 0.0 shader_parameter/_time_offset = 0.0
shader_parameter/cloud_bottom = 1500.0 shader_parameter/cloud_bottom = 1500.0
shader_parameter/cloud_top = 4000.0 shader_parameter/cloud_top = 4000.0
shader_parameter/cloud_brightness = 1.5 shader_parameter/cloud_brightness = 1.0
shader_parameter/cloud_ambient_brightness = 0.5 shader_parameter/cloud_ambient_brightness = 0.5
shader_parameter/night_sky = SubResource("NoiseTexture2D_q4x3w") shader_parameter/ground_texture = ExtResource("2_8dcp8")
shader_parameter/worlnoise = ExtResource("3_5fbd6") shader_parameter/moon_texture = ExtResource("3_2bh7o")
shader_parameter/perlworlnoise = ExtResource("1_qd3aw") shader_parameter/worlnoise = ExtResource("7_wkoma")
shader_parameter/weathermap = ExtResource("2_bohio") shader_parameter/perlworlnoise = ExtResource("5_q6lkc")
shader_parameter/weathermap = ExtResource("6_gs20s")

View File

@ -12,7 +12,7 @@ config_version=5
config/name="Nishita Sky With Clouds" config/name="Nishita Sky With Clouds"
run/main_scene="res://main.tscn" run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.0", "Forward Plus") config/features=PackedStringArray("4.1", "Forward Plus")
config/icon="res://icon.svg" config/icon="res://icon.svg"
[rendering] [rendering]

BIN
star map.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

35
star map.webp.import Normal file
View File

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://byt2ctyhq5myd"
path.bptc="res://.godot/imported/star map.webp-6e0d858a55618062a5b78c84685f41f7.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://star map.webp"
dest_files=["res://.godot/imported/star map.webp-6e0d858a55618062a5b78c84685f41f7.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=true
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=1
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=true
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0