Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a 3D sky shaders demo #809

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
570 changes: 570 additions & 0 deletions 3d/sky_shaders/Main.tscn

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions 3d/sky_shaders/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 3D Sky Shaders

An example of a sky shader in Godot. This shader features real-time volumetric
clouds and physical sky with Raleigh and Mie scattering, which causes the sky's
colors to adjust automatically depending on the sun angle. The sun's angle is set
automatically from the first DirectionalLight3D node in the scene
(unless its sky mode is **Light Only**).

Physical sky features are based on the built-in PhysicalSkyMaterial, while
volumetric clouds were added after converting PhysicalSkyMaterial to a
ShaderMaterial using the **Convert to ShaderMaterial** button in the editor
resource dropdown.

> **Warning**
>
> Sky shaders are rendered every frame if they use the `TIME` variable or are
> otherwise updated every frame (e.g. if an uniform is updated in `_process()`
> or with an AnimationPlayer). This has a significant performance impact for
> complex sky shaders. The performance impact can be reduced by adjusting the
> radiance map properties in Environment, but it will remain significant.
>
> The shader in this demo project is expensive, and is intended to be used in
> games where most of the sky remains visible at all times (such as flight
> simulators).
>
> Optimizations to sky shader rendering are planned in future Godot releases.

Language: GDScript

Renderer: Forward Plus

## How does it work?

The day/night cycle is performed using an AnimationPlayer node that adjusts node
properties and the sky shader parameters in real-time.

Spheres with varying levels of roughness and metallic materials are instanced using
a [`@tool` script](https://docs.godotengine.org/en/latest/tutorials/plugins/running_code_in_the_editor.html)
so that they don't have to be created manually in the editor, yet they can still be previewed within the editor.

## Screenshots

![Screenshot](screenshots/sky_shaders.webp)
7 changes: 7 additions & 0 deletions 3d/sky_shaders/default_env.tres
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[gd_resource type="Environment" load_steps=2 format=2]

[sub_resource type="ProceduralSky" id=1]

[resource]
background_mode = 2
sky = SubResource( 1 )
Binary file added 3d/sky_shaders/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions 3d/sky_shaders/icon.png.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://ckaq2n0hvlwq1"
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://icon.png"
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
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=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
122 changes: 122 additions & 0 deletions 3d/sky_shaders/main.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
extends Node3D

var base_height = ProjectSettings.get_setting("display/window/size/viewport_height")

# The camera field of view to smoothly interpolate to.
@onready var desired_fov = $YawCamera/Camera3D.fov

func _ready():
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED


func _process(_delta):
# Make the slider follow the day/night cycle.
$Panel/MarginContainer/VBoxContainer/TimeOfDay/HSlider.value = $AnimationPlayer.current_animation_position
$WorldEnvironment.environment.sky.sky_material.set_shader_parameter("cloud_time_offset", $AnimationPlayer.current_animation_position)

$YawCamera/Camera3D.fov = lerpf($YawCamera/Camera3D.fov, desired_fov, 0.2)



func _input(event):
if event.is_action_pressed("toggle_gui"):
$Panel.visible = not $Panel.visible
$Help.visible = not $Help.visible

if event.is_action_pressed("toggle_spheres"):
$Spheres.visible = not $Spheres.visible

if event.is_action_pressed("toggle_mouse_capture"):
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
else:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and event is InputEventMouseMotion:
# Mouselook.
# Compensate motion speed to be resolution-independent (based on the window height).
var relative_motion = event.relative * DisplayServer.window_get_size().y / base_height
$YawCamera.rotation.x += relative_motion.y * 0.001
$YawCamera.rotation.y += relative_motion.x * 0.001

# Mouse wheel currently doesn't work in input actions. Hardcode mouse wheel as a workaround.
if event.is_action_pressed("increase_camera_fov") or Input.is_mouse_button_pressed(MOUSE_BUTTON_WHEEL_DOWN):
desired_fov = clampf(desired_fov + 5.0, 20.0, 150.0)
if event.is_action_pressed("decrease_camera_fov") or Input.is_mouse_button_pressed(MOUSE_BUTTON_WHEEL_UP):
desired_fov = clampf(desired_fov - 5.0, 20.0, 150.0)


func _on_time_of_day_value_changed(value):
# Update time of day.
$AnimationPlayer.seek(value)
# TODO: Display HH:MM time.
$Panel/MarginContainer/VBoxContainer/TimeOfDay/Value.text = str(value).pad_decimals(2)


func _on_speed_minus_pressed():
# Allow minimum value to be zero so that the special case can be reached for pausing.
$AnimationPlayer.speed_scale = clampf($AnimationPlayer.speed_scale * 0.5, 0.0, 12.8)
if $AnimationPlayer.speed_scale < 0.0499:
# Going below 0.5× speed; pause.
$AnimationPlayer.speed_scale = 0.0

update_speed_label()


func _on_speed_plus_pressed():
$AnimationPlayer.speed_scale = clampf($AnimationPlayer.speed_scale * 2.0, 0.05, 12.8)
if is_zero_approx($AnimationPlayer.speed_scale):
# Currently paused; resume playback.
$AnimationPlayer.speed_scale = 0.1

update_speed_label()


func update_speed_label():
# The default speed scale for this AnimationPlayer is internally 0.1, so multiply the displayed value by 10.
if is_zero_approx($AnimationPlayer.speed_scale):
$Panel/MarginContainer/VBoxContainer/TimeOfDay/CurrentSpeed.text = "Pause"
else:
$Panel/MarginContainer/VBoxContainer/TimeOfDay/CurrentSpeed.text = "%.2f×" % ($AnimationPlayer.speed_scale * 10)


func _on_cloud_coverage_value_changed(value):
$WorldEnvironment.environment.sky.sky_material.set_shader_parameter("cloud_coverage", value)
$Panel/MarginContainer/VBoxContainer/Clouds/CoverageValue.text = "%d%%" % (value * 100)


func _on_cloud_density_value_changed(value):
$WorldEnvironment.environment.sky.sky_material.set_shader_parameter("cloud_density", value)
$Panel/MarginContainer/VBoxContainer/Clouds/DensityValue.text = "%d%%" % (value * 100)


func _on_process_mode_item_selected(index):
match index:
0:
$WorldEnvironment.environment.sky.process_mode = Sky.PROCESS_MODE_QUALITY
$Panel/MarginContainer/VBoxContainer/RadianceSize.visible = true
# Reset radiance size as the engine forces radiance size to 256 after switching to the Real-Time process mode.
_on_radiance_size_item_selected($Panel/MarginContainer/VBoxContainer/RadianceSize/OptionButton.selected)
1:
$WorldEnvironment.environment.sky.process_mode = Sky.PROCESS_MODE_INCREMENTAL
$Panel/MarginContainer/VBoxContainer/RadianceSize.visible = true
# Reset radiance size as the engine forces radiance size to 256 after switching to the Real-Time process mode.
_on_radiance_size_item_selected($Panel/MarginContainer/VBoxContainer/RadianceSize/OptionButton.selected)
2:
$WorldEnvironment.environment.sky.process_mode = Sky.PROCESS_MODE_REALTIME
# Radiance size is forced to 256 by the engine when using Real-Time process mode.
$Panel/MarginContainer/VBoxContainer/RadianceSize.visible = false


func _on_radiance_size_item_selected(index):
match index:
0:
$WorldEnvironment.environment.sky.radiance_size = Sky.RADIANCE_SIZE_32
1:
$WorldEnvironment.environment.sky.radiance_size = Sky.RADIANCE_SIZE_64
2:
$WorldEnvironment.environment.sky.radiance_size = Sky.RADIANCE_SIZE_128
3:
$WorldEnvironment.environment.sky.radiance_size = Sky.RADIANCE_SIZE_256
4:
$WorldEnvironment.environment.sky.radiance_size = Sky.RADIANCE_SIZE_512
Binary file added 3d/sky_shaders/perlworlnoise.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions 3d/sky_shaders/perlworlnoise.png.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[remap]

importer="3d_texture"
type="CompressedTexture3D"
uid="uid://48aujjq3b6pj"
path="res://.godot/imported/perlworlnoise.png-3a040af7870a245e2e85a129517a2537.ctex3d"
metadata={
"vram_texture": false
}

[deps]

source_file="res://perlworlnoise.png"
dest_files=["res://.godot/imported/perlworlnoise.png-3a040af7870a245e2e85a129517a2537.ctex3d"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
slices/horizontal=128
slices/vertical=1
58 changes: 58 additions & 0 deletions 3d/sky_shaders/project.godot
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters

config_version=5

[application]

config/name="3D Sky Shaders"
config/description="A short educational sample showing one way of using sky shaders to render real time volumetric cloudscapes."
run/main_scene="res://Main.tscn"
config/features=PackedStringArray("4.0")
config/icon="res://icon.png"

[display]

window/stretch/mode="canvas_items"
window/stretch/aspect="expand"
window/vsync/use_vsync=false

[input]

toggle_mouse_capture={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194341,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}
toggle_gui={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}
toggle_spheres={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194333,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}
increase_camera_fov={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}
decrease_camera_fov={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}

[rendering]

anti_aliasing/quality/msaa_3d=2
anti_aliasing/quality/use_debanding=true
vram_compression/import_etc2=false
Empty file.
Binary file added 3d/sky_shaders/screenshots/sky_shaders.webp
Binary file not shown.
Loading