* 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,50 +375,86 @@ 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
SumR += Hr_sample * attenuation SumR += Hr_sample * attenuation
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

@ -5,4 +5,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

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,134 +1,171 @@
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;
float opticalDepthR = 0.0; float opticalDepthR = 0.0;
float opticalDepthM = 0.0; float opticalDepthM = 0.0;
vec3 atmosphere_atten = vec3(0.0); vec3 atmosphere_atten = vec3(0.0);
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;
@ -167,28 +211,92 @@ vec3[4] atmosphere(vec3 dir, vec3 pos){
SumM += Hm_sample * attenuation; SumM += Hm_sample * attenuation;
} }
} }
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;
// 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) +
// ground, with generic attenuation vec3 rotatedPoint;
(vec3(1.0)-exp(-sky[3])) * ground_color * max(precomputed_sun_dir.y, 0.0) * sky[2].r * ground_brightness ) * LIGHT0_ENERGY ; rotatedPoint.x = point.x * (oc * axis.x * axis.x + c) +
vec3 col = (sun + sky[0].xyz); point.y * (oc * axis.x * axis.y - axis.z * s) +
return {vec4(col * precomputed_sun_color, sky[2].r), vec4(sky[3] * (1.0 - sky[2].r), 1.0)}; 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
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
vec3 ground = (vec3(1.0) - exp(-sky[3])) * ground_color *
texture(ground_texture, uv).xyz * sky[2].r *
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 =
float height_fraction = (inPosition - cloud_bottom - earthRadius) / (cloud_top - cloud_bottom); (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;
// Take more steps towards horizon, less steps in foggy clouds, and less steps at night
float steps = ceil(mix(float(cloud_samples_horizon) * (1.0 - 0.25 * (1.0 - precomputed_sun_visible * (1.0-cloud_coverage))), // If the ray starts outside the atmosphere, set the origin
float(cloud_samples_sky) * (1.0 - 0.25 * (1.0 - precomputed_sun_visible)), to the edge of the atmosphere if (d1.y > 0.0){ start = camPos +
sqrt(clamp(dir.y+horizon, 0.0, 1.0))) ); dir * d1.y;
}
}*/
float shelldist = (cloud_end_distance - cloud_start_distance);
// Take more steps towards horizon, less steps in foggy clouds,
// 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