diff --git a/3d/volumetric_fog/README.md b/3d/volumetric_fog/README.md new file mode 100644 index 00000000..1cd7e949 --- /dev/null +++ b/3d/volumetric_fog/README.md @@ -0,0 +1,20 @@ +# Volumetric Fog + +This is an example of Godot's volumetric fog capabilities with the Vulkan renderer. + +Showcased features are: + +- Positive/negative density volumes that affect albedo (incoming light) and emission. +- Box/ellipsoid shapes, height falloff, and density modulation using a 3D texture. +- Temporal reprojection for greater stability and avoiding flickering. + - The difference is demonstrated with a moving fog volume. +- Global density adjustment. With FogVolume nodes that have a positive density, + it's possible to apply volumetric fog only in specific areas. + +Language: GDScript + +Renderer: Vulkan Clustered + +## Screenshots + +![Screenshot](screenshots/volumetric_fog.png) diff --git a/3d/volumetric_fog/camera.gd b/3d/volumetric_fog/camera.gd new file mode 100644 index 00000000..e10f3fdd --- /dev/null +++ b/3d/volumetric_fog/camera.gd @@ -0,0 +1,69 @@ +extends Camera3D + +const MOUSE_SENSITIVITY = 0.002 +const MOVE_SPEED = 0.6 + +var rot = Vector3() +var velocity = Vector3() + +@onready var label = $Label + + +func _ready(): + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) + update_label() + + +func _process(delta): + var motion = Vector3( + Input.get_action_strength("move_right") - Input.get_action_strength("move_left"), + 0, + Input.get_action_strength("move_back") - Input.get_action_strength("move_forward") + ) + + # Normalize motion to prevent diagonal movement from being + # `sqrt(2)` times faster than straight movement. + motion = motion.normalized() + + velocity += MOVE_SPEED * delta * (transform.basis * motion) + velocity *= 0.85 + position += velocity + + +func _input(event): + # Mouse look (only if the mouse is captured). + if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: + # Horizontal mouse look. + rot.y -= event.relative.x * MOUSE_SENSITIVITY + # Vertical mouse look. + rot.x = clamp(rot.x - event.relative.y * MOUSE_SENSITIVITY, -1.57, 1.57) + transform.basis = Basis.from_euler(rot) + + if event.is_action_pressed("toggle_mouse_capture"): + if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + else: + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) + + if event.is_action_pressed("toggle_temporal_reprojection"): + get_world_3d().environment.volumetric_fog_temporal_reprojection_enabled = not get_world_3d().environment.volumetric_fog_temporal_reprojection_enabled + update_label() + elif event.is_action_pressed("increase_temporal_reprojection"): + get_world_3d().environment.volumetric_fog_temporal_reprojection_amount = clamp(get_world_3d().environment.volumetric_fog_temporal_reprojection_amount + 0.01, 0.0, 0.99) + update_label() + elif event.is_action_pressed("decrease_temporal_reprojection"): + get_world_3d().environment.volumetric_fog_temporal_reprojection_amount = clamp(get_world_3d().environment.volumetric_fog_temporal_reprojection_amount - 0.01, 0.0, 0.99) + update_label() + elif event.is_action_pressed("increase_fog_density"): + get_world_3d().environment.volumetric_fog_density = clamp(get_world_3d().environment.volumetric_fog_density + 0.01, 0.0, 1.0) + update_label() + elif event.is_action_pressed("decrease_fog_density"): + get_world_3d().environment.volumetric_fog_density = clamp(get_world_3d().environment.volumetric_fog_density - 0.01, 0.0, 1.0) + update_label() + + +func update_label(): + if get_world_3d().environment.volumetric_fog_temporal_reprojection_enabled: + label.text = "Fog density: %.2f\nTemporal reprojection: Enabled\nTemporal reprojection strength: %.3f" % [get_world_3d().environment.volumetric_fog_density, get_world_3d().environment.volumetric_fog_temporal_reprojection_amount] + else: + label.text = "Fog density: %.2f\nTemporal reprojection: Disabled" % get_world_3d().environment.volumetric_fog_density diff --git a/3d/volumetric_fog/icon_outlined.png b/3d/volumetric_fog/icon_outlined.png new file mode 100644 index 00000000..832b1797 Binary files /dev/null and b/3d/volumetric_fog/icon_outlined.png differ diff --git a/3d/volumetric_fog/icon_outlined.png.import b/3d/volumetric_fog/icon_outlined.png.import new file mode 100644 index 00000000..195fdb50 --- /dev/null +++ b/3d/volumetric_fog/icon_outlined.png.import @@ -0,0 +1,26 @@ +[remap] + +importer="3d_texture" +type="CompressedTexture3D" +uid="uid://bpvy0e34otmhk" +path="res://.godot/imported/icon_outlined.png-be09dc71a0cb04701900baffec3ab610.ctex3d" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon_outlined.png" +dest_files=["res://.godot/imported/icon_outlined.png-be09dc71a0cb04701900baffec3ab610.ctex3d"] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/bptc_ldr=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +slices/horizontal=1 +slices/vertical=1 diff --git a/3d/volumetric_fog/project.godot b/3d/volumetric_fog/project.godot new file mode 100644 index 00000000..4b12cf87 --- /dev/null +++ b/3d/volumetric_fog/project.godot @@ -0,0 +1,80 @@ +; 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="Volumetric Fog" +run/main_scene="res://volumetric_fog.tscn" +config/features=PackedStringArray("4.0") + +[display] + +window/stretch/mode="canvas_items" +window/stretch/aspect="expand" + +[input] + +toggle_temporal_reprojection={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"unicode":0,"echo":false,"script":null) +] +} +increase_temporal_reprojection={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":4,"pressed":false,"double_click":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":0,"physical_keycode":16777232,"unicode":0,"echo":false,"script":null) +] +} +decrease_temporal_reprojection={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":5,"pressed":false,"double_click":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":0,"physical_keycode":16777234,"unicode":0,"echo":false,"script":null) +] +} +increase_fog_density={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":0,"physical_keycode":16777233,"unicode":0,"echo":false,"script":null) +] +} +decrease_fog_density={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":0,"physical_keycode":16777231,"unicode":0,"echo":false,"script":null) +] +} +move_forward={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"unicode":0,"echo":false,"script":null) +] +} +move_back={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"unicode":0,"echo":false,"script":null) +] +} +move_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"unicode":0,"echo":false,"script":null) +] +} +move_left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"unicode":0,"echo":false,"script":null) +] +} +toggle_mouse_capture={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":0,"physical_keycode":16777217,"unicode":0,"echo":false,"script":null) +] +} + +[rendering] + +anti_aliasing/quality/screen_space_aa=1 +anti_aliasing/quality/use_debanding=true diff --git a/3d/volumetric_fog/screenshots/.gdignore b/3d/volumetric_fog/screenshots/.gdignore new file mode 100644 index 00000000..e69de29b diff --git a/3d/volumetric_fog/screenshots/volumetric_fog.png b/3d/volumetric_fog/screenshots/volumetric_fog.png new file mode 100644 index 00000000..dc41856d Binary files /dev/null and b/3d/volumetric_fog/screenshots/volumetric_fog.png differ diff --git a/3d/volumetric_fog/volumetric_fog.tscn b/3d/volumetric_fog/volumetric_fog.tscn new file mode 100644 index 00000000..364f6a4f --- /dev/null +++ b/3d/volumetric_fog/volumetric_fog.tscn @@ -0,0 +1,278 @@ +[gd_scene load_steps=17 format=3 uid="uid://d1d6jy84lcpg7"] + +[ext_resource type="Script" path="res://camera.gd" id="1_mfjvp"] +[ext_resource type="CompressedTexture3D" uid="uid://bpvy0e34otmhk" path="res://icon_outlined.png" id="2_8fjmj"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_fefkn"] +sky_top_color = Color(0.568627, 0.698039, 0.807843, 1) +sky_horizon_color = Color(0.672549, 0.704902, 0.734314, 1) +ground_bottom_color = Color(0.121569, 0.121569, 0.129412, 1) +ground_horizon_color = Color(0.672549, 0.704902, 0.734314, 1) + +[sub_resource type="Sky" id="Sky_5gwha"] +sky_material = SubResource( "ProceduralSkyMaterial_fefkn" ) + +[sub_resource type="Environment" id="Environment_gsgar"] +background_mode = 2 +sky = SubResource( "Sky_5gwha" ) +tonemap_mode = 2 +glow_enabled = true +volumetric_fog_enabled = true +volumetric_fog_density = 0.1 + +[sub_resource type="FogMaterial" id="FogMaterial_sfd8y"] +density = -1.6842 + +[sub_resource type="FogMaterial" id="FogMaterial_v1f5u"] +density = 0.2 +emission = Color(0, 1, 0.501961, 1) +edge_fade = 2.2779e-05 + +[sub_resource type="FogMaterial" id="FogMaterial_blauf"] +density = 0.2 +emission = Color(0, 1, 0.501961, 1) +height_falloff = 1e+06 +edge_fade = 2.2779e-05 + +[sub_resource type="FogMaterial" id="FogMaterial_yvnd5"] +density = 0.3 +emission = Color(1, 0.290196, 0.419608, 1) + +[sub_resource type="FogMaterial" id="FogMaterial_gcgmm"] +density = 0.3 +emission = Color(1, 0.290196, 0.419608, 1) +height_falloff = 1e+06 + +[sub_resource type="FogMaterial" id="FogMaterial_8xcc6"] +density = 0.1 +albedo = Color(1, 15, 10, 1) + +[sub_resource type="Animation" id="Animation_jg7qq"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("MovingBox:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector3(0, 2, 10)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("MovingBox:rotation") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector3(0, 0, 0)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("MovingBox:scale") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector3(1, 1, 1)] +} + +[sub_resource type="Animation" id="Animation_axnf8"] +resource_name = "move_fog_volume" +length = 10.0 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("MovingBox:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 3, 6), +"transitions": PackedFloat32Array(-2, -2, -2), +"update": 0, +"values": [Vector3(0, 2, 10), Vector3(-5.20189, 2, 12.7464), Vector3(-0.764091, 6.11944, 20.2665)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("MovingBox:rotation") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 3, 6), +"transitions": PackedFloat32Array(-2, -2, -2), +"update": 0, +"values": [Vector3(0, 0, 0), Vector3(0, 0.784368, 0), Vector3(-0.33128, 0.307663, 0.797063)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("MovingBox:scale") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 3, 6), +"transitions": PackedFloat32Array(-2, -2, -2), +"update": 0, +"values": [Vector3(1, 1, 1), Vector3(1, 1, 1), Vector3(3, 3, 3)] +} + +[sub_resource type="BoxMesh" id="BoxMesh_fi43t"] + +[sub_resource type="FogMaterial" id="FogMaterial_ktn3i"] +albedo = Color(0, 0, 4, 1) +edge_fade = 0.0 + +[sub_resource type="FogMaterial" id="FogMaterial_yomn8"] +density = 2.0 +emission = Color(0, 0.501961, 1, 1) +edge_fade = 0.0 +density_texture = ExtResource( "2_8fjmj" ) + +[node name="Node3D" type="Node3D"] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource( "Environment_gsgar" ) + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.592126, -0.697883, 0.402923, -1.19487e-09, 0.5, 0.866026, -0.805846, 0.512796, -0.296063, 0, 0, 0) +shadow_enabled = true +shadow_bias = 0.12 +directional_shadow_max_distance = 250.0 + +[node name="Camera3D" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8, 6, 18) +script = ExtResource( "1_mfjvp" ) + +[node name="Label" type="Label" parent="Camera3D"] +offset_left = 16.0 +offset_top = 16.0 +offset_right = 812.0 +offset_bottom = 299.0 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 0.501961) +theme_override_constants/shadow_offset_x = 2 +theme_override_constants/shadow_offset_y = 2 +theme_override_constants/shadow_outline_size = 0 +text = "Temporal reprojection: Disabled" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="HelpLabel" type="Label" parent="Camera3D"] +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 16.0 +offset_top = -369.0 +offset_right = 909.0 +offset_bottom = -16.0 +grow_vertical = 0 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 0.501961) +theme_override_constants/shadow_offset_x = 2 +theme_override_constants/shadow_offset_y = 2 +theme_override_constants/shadow_outline_size = 0 +text = "W/A/S/D/Mouse: Move +Left/Right arrows: Adjust global fog density +Space: Toggle temporal reprojection +Up/Down arrows or mouse wheel: Adjust temporal reprojection intensity" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Ground" type="CSGBox3D" parent="."] +size = Vector3(150, 2, 150) + +[node name="Ground2" type="CSGBox3D" parent="Ground"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10.282, 0, 5.62229) +operation = 2 +size = Vector3(3, 2, 4) + +[node name="Ground3" type="CSGBox3D" parent="Ground"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -7.87312, 0, 2.69763) +operation = 2 +size = Vector3(3, 2, 4) + +[node name="CSGBox3D" type="CSGBox3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 5, 0) +size = Vector3(8, 8, 8) + +[node name="CSGBox3D" type="CSGBox3D" parent="CSGBox3D"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.4) +operation = 2 +size = Vector3(7.5, 7.5, 7.5) + +[node name="FogVolumeNegativeDensity" type="FogVolume" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 5, 0) +extents = Vector3(4, 4, 4) +material = SubResource( "FogMaterial_sfd8y" ) + +[node name="FogVolumeEllipsoid" type="FogVolume" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -13, 6, 6) +extents = Vector3(2, 2, 2) +shape = 0 +material = SubResource( "FogMaterial_v1f5u" ) + +[node name="FogVolumeEllipsoidHeightFalloff" type="FogVolume" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -13, 6, 2) +extents = Vector3(2, 2, 2) +shape = 0 +material = SubResource( "FogMaterial_blauf" ) + +[node name="FogVolumeBox" type="FogVolume" parent="."] +transform = Transform3D(0.707107, 0.5, -0.5, 0, 0.707107, 0.707107, 0.707107, -0.5, 0.5, -7.5189, 4.42465, -2.97368) +extents = Vector3(2, 2, 2) +material = SubResource( "FogMaterial_yvnd5" ) + +[node name="FogVolumeBoxHeightFalloff" type="FogVolume" parent="."] +transform = Transform3D(0.707107, 0.5, -0.5, 0, 0.707107, 0.707107, 0.707107, -0.5, 0.5, -7.5189, 4.42465, -7.97368) +extents = Vector3(2, 2, 2) +material = SubResource( "FogMaterial_gcgmm" ) + +[node name="OmniLight3D" type="OmniLight3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.823152, 1.836, 4.31818) +light_color = Color(1, 0.929412, 0, 1) +light_energy = 2.0 +omni_range = 6.0 + +[node name="OmniLight3DShadow" type="OmniLight3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.82315, 1.836, 4.31818) +light_color = Color(1, 0.929412, 0, 1) +light_energy = 2.0 +shadow_enabled = true +omni_range = 6.0 + +[node name="SpotLight3D" type="SpotLight3D" parent="."] +transform = Transform3D(0.966384, -0.226046, -0.1225, 0, 0.476461, -0.879196, 0.257105, 0.84964, 0.460444, -7.19085, -3.7099, 10.7572) +light_color = Color(0, 0.439216, 1, 1) +light_energy = 4.0 + +[node name="FogVolumeAlbedoTweak" type="FogVolume" parent="."] +transform = Transform3D(-0.89393, -0.0897075, -0.439138, 0, 0.979766, -0.200148, 0.448207, -0.178918, -0.875842, -15.8913, -15.118, 8.26876) +extents = Vector3(4, 4, 4) +material = SubResource( "FogMaterial_8xcc6" ) + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +autoplay = "move_fog_volume" +anims/RESET = SubResource( "Animation_jg7qq" ) +anims/move_fog_volume = SubResource( "Animation_axnf8" ) + +[node name="MovingBox" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 10) +mesh = SubResource( "BoxMesh_fi43t" ) + +[node name="FogVolumeBlue" type="FogVolume" parent="MovingBox"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0) +material = SubResource( "FogMaterial_ktn3i" ) + +[node name="FogVolumeTexture3D" type="FogVolume" parent="."] +transform = Transform3D(-2.98023e-08, 4.82913e-08, -1, -4.2222e-08, -1, -4.82913e-08, -1, 4.2222e-08, -1.49012e-08, 17, 17, 12) +extents = Vector3(16, 16, 2) +material = SubResource( "FogMaterial_yomn8" )