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 demo showing the use of RenderingEffects to do post processing #942

Closed
Closed
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
2 changes: 2 additions & 0 deletions compute/post_effects/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf
2 changes: 2 additions & 0 deletions compute/post_effects/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Godot 4+ specific ignores
.godot/
11 changes: 11 additions & 0 deletions compute/post_effects/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Post effects

This demo shows how to implement post effects using the new RenderingEffect class.

Renderer: Forward Plus

> Note: this demo requires Godot 4.2 or later

## Screenshots

![Screenshot](screenshots/compute_texture.webp)
78 changes: 78 additions & 0 deletions compute/post_effects/gray_scale/gray_scale.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
@tool
extends CompositorEffect
class_name CompositorEffectGrayScale

# This is a very simple effects demo that takes our color values and writes
# back gray scale values.

func _init() -> void:
effect_callback_type = CompositorEffect.EFFECT_CALLBACK_TYPE_POST_TRANSPARENT
RenderingServer.call_on_render_thread(_initialize_compute)


func _notification(what: int) -> void:
if what == NOTIFICATION_PREDELETE:
# When this is called it should be safe to clean up our shader.
# If not we'll crash anyway because we can no longer call our _render_callback.
if shader.is_valid():
rd.free_rid(shader)
Comment on lines +17 to +18
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pipeline RID is not being freed here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you free a shader, all dependencies of the shader will be freed, including all pipelines related to that shader.


###############################################################################
# Everything after this point is designed to run on our rendering thread

var rd: RenderingDevice

var shader: RID
var pipeline: RID

func _initialize_compute() -> void:
rd = RenderingServer.get_rendering_device()
if not rd:
OS.alert("RenderingDevice is not available, aborting.\nCompositor effects require RenderingDevice to be available, which means you have to use the Forward+ or Mobile rendering method.")
return

# Create our shader.
var shader_file := load("res://gray_scale/gray_scale.glsl")
var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
shader = rd.shader_create_from_spirv(shader_spirv)
pipeline = rd.compute_pipeline_create(shader)


func _render_callback(p_effect_callback_type: int, p_render_data: RenderData) -> void:
if rd and p_effect_callback_type == CompositorEffect.EFFECT_CALLBACK_TYPE_POST_TRANSPARENT:
# Get our render scene buffers object, this gives us access to our render buffers.
# Note that implementation differs per renderer hence the need for the cast.
var render_scene_buffers: RenderSceneBuffersRD = p_render_data.get_render_scene_buffers()
if render_scene_buffers:
# Get our render size. This is the 3D render resolution (which can be affected by the
# `scaling_3d_scale` property), not the window size.
var size := render_scene_buffers.get_internal_size()
if size.x <= 0 and size.y <= 0:
push_error("Render size is too small.")
return

# We can use a compute shader here.
@warning_ignore("integer_division")
var x_groups := (size.x - 1) / 8 + 1
@warning_ignore("integer_division")
var y_groups := (size.y - 1) / 8 + 1

# Loop through views just in case we're doing stereo rendering. No extra cost if this is mono.
var view_count := render_scene_buffers.get_view_count()
for view in view_count:
# Get the RID for our color image. We will be reading from and writing to it.
var input_image: RID = render_scene_buffers.get_color_layer(view)

# Create a uniform set. This will be cached: the cache will be cleared if our viewport's configuration is changed.
var uniform := RDUniform.new()
uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE
uniform.binding = 0
uniform.add_id(input_image)
var uniform_set := UniformSetCacheRD.get_cache(shader, 0, [ uniform ])

# Run our compute shader.
var compute_list := rd.compute_list_begin()
rd.compute_list_bind_compute_pipeline(compute_list, pipeline)
rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0)
rd.compute_list_dispatch(compute_list, x_groups, y_groups, 1)
rd.compute_list_end()
18 changes: 18 additions & 0 deletions compute/post_effects/gray_scale/gray_scale.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#[compute]
#version 450

// Invocations in the (x, y, z) dimension
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;

layout(rgba16f, set = 0, binding = 0) uniform image2D color_image;

// The code we want to execute in each invocation
void main() {
ivec2 uv = ivec2(gl_GlobalInvocationID.xy);

vec4 color = imageLoad(color_image, uv);
float gray = color.r * 0.2125 + color.g * 0.7154 + color.b * 0.0721;
color.rgb = vec3(gray);

imageStore(color_image, uv, color);
}
14 changes: 14 additions & 0 deletions compute/post_effects/gray_scale/gray_scale.glsl.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[remap]

importer="glsl"
type="RDShaderFile"
uid="uid://cycdb7vyg3b6g"
path="res://.godot/imported/gray_scale.glsl-cdec27ad421b3dcac596053515285dc6.res"

[deps]

source_file="res://gray_scale/gray_scale.glsl"
dest_files=["res://.godot/imported/gray_scale.glsl-cdec27ad421b3dcac596053515285dc6.res"]

[params]

1 change: 1 addition & 0 deletions compute/post_effects/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions compute/post_effects/icon.svg.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://cm88hfhyutq2d"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false
52 changes: 52 additions & 0 deletions compute/post_effects/main.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
extends Node3D

var create_depth_mips_effect: CompositorEffectCreateDepthMips
var apply_sssr_effect: CompositorEffectApplySSSR
var gray_scale_effect: CompositorEffectGrayScale

var camera_x := 0.0
var camera_y := 0.0

@onready var sssr_button: Button = $UI/MarginContainer/VBoxContainer/SSSR
@onready var gray_scale_button: Button = $UI/MarginContainer/VBoxContainer/GrayScaleBtn

func _ready() -> void:
var compositor: Compositor = $WorldEnvironment.compositor
for effect in compositor.compositor_effects:
if effect.get_script() == CompositorEffectCreateDepthMips:
create_depth_mips_effect = effect
elif effect.get_script() == CompositorEffectApplySSSR:
apply_sssr_effect = effect
elif effect.get_script() == CompositorEffectGrayScale:
gray_scale_effect = effect

if create_depth_mips_effect and apply_sssr_effect:
sssr_button.button_pressed = create_depth_mips_effect.enabled

if gray_scale_effect:
gray_scale_button.button_pressed = gray_scale_effect.enabled


func _input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
var mouse_event: InputEventMouseMotion = event

if mouse_event.button_mask & MOUSE_BUTTON_MASK_LEFT:
camera_x = clamp(camera_x + mouse_event.screen_relative.y * 0.01, -PI * 0.1, PI * 0.25)
camera_y -= mouse_event.screen_relative.x * 0.01

var b1 := Basis(Vector3.UP, camera_y)
var b2 := Basis(Vector3.LEFT, camera_x)

$Pivot.transform.basis = b1 * b2


func _on_simple_ssr_toggled(toggled_on: bool) -> void:
if create_depth_mips_effect and apply_sssr_effect:
create_depth_mips_effect.enabled = toggled_on
apply_sssr_effect.enabled = toggled_on


func _on_gray_scale_btn_toggled(toggled_on: bool) -> void:
if gray_scale_effect:
gray_scale_effect.enabled = toggled_on
174 changes: 174 additions & 0 deletions compute/post_effects/main.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
[gd_scene load_steps=24 format=3 uid="uid://hay7hbv1cp62"]

[ext_resource type="Script" path="res://main.gd" id="1_ekxfu"]
[ext_resource type="Script" path="res://simple_ssr/create_depth_mips.gd" id="2_0tpoa"]
[ext_resource type="Texture2D" uid="uid://bshdn70nudsun" path="res://pattern.png" id="2_d1qxb"]
[ext_resource type="Script" path="res://simple_ssr/apply_sssr.gd" id="3_gyc5w"]
[ext_resource type="Script" path="res://gray_scale/gray_scale.gd" id="4_6ukm1"]
[ext_resource type="Texture2D" uid="uid://d0e66wb8yplkw" path="res://uv.jpg" id="5_ybioc"]

[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_ptioq"]
sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)
ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)

[sub_resource type="Sky" id="Sky_gkygq"]
sky_material = SubResource("ProceduralSkyMaterial_ptioq")

[sub_resource type="Environment" id="Environment_kx13e"]
background_mode = 2
sky = SubResource("Sky_gkygq")
tonemap_mode = 2
ssr_depth_tolerance = 3.41
glow_enabled = true

[sub_resource type="CompositorEffect" id="CompositorEffect_7wi85"]
resource_local_to_scene = false
resource_name = ""
enabled = true
effect_callback_type = 0
access_resolved_color = false
access_resolved_depth = true
needs_motion_vectors = false
needs_normal_roughness = false
script = ExtResource("2_0tpoa")

[sub_resource type="CompositorEffect" id="CompositorEffect_57gju"]
resource_local_to_scene = false
resource_name = ""
enabled = true
effect_callback_type = 2
access_resolved_color = false
access_resolved_depth = false
needs_motion_vectors = false
needs_normal_roughness = true
needs_separate_specular = true
script = ExtResource("3_gyc5w")
max_distance = 1.0
max_steps = 32

[sub_resource type="CompositorEffect" id="CompositorEffect_jq1iy"]
resource_local_to_scene = false
resource_name = ""
enabled = true
effect_callback_type = 4
needs_motion_vectors = false
needs_normal_roughness = false
script = ExtResource("4_6ukm1")

[sub_resource type="Compositor" id="Compositor_2i651"]
compositor_effects = Array[CompositorEffect]([SubResource("CompositorEffect_7wi85"), SubResource("CompositorEffect_57gju"), SubResource("CompositorEffect_jq1iy")])

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_tvupy"]
albedo_texture = ExtResource("5_ybioc")
metallic = 0.5
roughness = 0.75
uv1_scale = Vector3(5, 5, 5)
texture_filter = 5

[sub_resource type="PlaneMesh" id="PlaneMesh_fbrjs"]
size = Vector2(20, 20)

[sub_resource type="SphereMesh" id="SphereMesh_nqw7b"]

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_mwci7"]
albedo_texture = ExtResource("2_d1qxb")
metallic = 0.78
roughness = 0.24
texture_filter = 5

[sub_resource type="SphereMesh" id="SphereMesh_lau6s"]

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_qdjdj"]
albedo_color = Color(1, 0.298039, 0.247059, 1)
albedo_texture = ExtResource("2_d1qxb")
metallic = 0.26
roughness = 0.59
texture_filter = 5

[sub_resource type="CylinderMesh" id="CylinderMesh_jepk0"]

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_xkdqw"]
albedo_color = Color(0.580392, 0.658824, 1, 1)
albedo_texture = ExtResource("2_d1qxb")
metallic = 0.86
roughness = 0.29
texture_filter = 5

[sub_resource type="BoxMesh" id="BoxMesh_syokj"]

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_chobt"]
albedo_color = Color(0.588235, 0.282353, 0.443137, 1)
albedo_texture = ExtResource("2_d1qxb")
metallic = 0.52
roughness = 0.42
texture_filter = 5

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

[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_kx13e")
compositor = SubResource("Compositor_2i651")

[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.959131, 0.245052, -0.141481, -0.0849703, 0.726354, 0.682048, 0.269902, -0.642152, 0.717491, 0, 1.94732, 0)
shadow_enabled = true
directional_shadow_mode = 0
directional_shadow_fade_start = 1.0
directional_shadow_max_distance = 15.0

[node name="Pivot" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.553607, 0.779114, 0)

[node name="Camera3D" type="Camera3D" parent="Pivot"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 3)

[node name="Floor" type="MeshInstance3D" parent="."]
material_override = SubResource("StandardMaterial3D_tvupy")
mesh = SubResource("PlaneMesh_fbrjs")

[node name="MetalSphere" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.721009, 0)
mesh = SubResource("SphereMesh_nqw7b")
surface_material_override/0 = SubResource("StandardMaterial3D_mwci7")

[node name="RedSphere" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.17882, 0.628256, -0.532507)
mesh = SubResource("SphereMesh_lau6s")
surface_material_override/0 = SubResource("StandardMaterial3D_qdjdj")

[node name="Cylinder" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.29466, 0, 0.409778)
mesh = SubResource("CylinderMesh_jepk0")
surface_material_override/0 = SubResource("StandardMaterial3D_xkdqw")

[node name="Box" type="MeshInstance3D" parent="."]
transform = Transform3D(0.682049, -0.297997, -0.667837, -1.49012e-08, 0.913212, -0.407486, 0.731306, 0.277925, 0.622855, 2.41454, 0.500351, 0.0996274)
mesh = SubResource("BoxMesh_syokj")
surface_material_override/0 = SubResource("StandardMaterial3D_chobt")

[node name="UI" type="CanvasLayer" parent="."]

[node name="MarginContainer" type="MarginContainer" parent="UI"]
offset_right = 40.0
offset_bottom = 40.0
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20

[node name="VBoxContainer" type="VBoxContainer" parent="UI/MarginContainer"]
layout_mode = 2

[node name="SSSR" type="CheckBox" parent="UI/MarginContainer/VBoxContainer"]
layout_mode = 2
button_pressed = true
text = "Stochastic Screen-Space Reflections"

[node name="GrayScaleBtn" type="CheckBox" parent="UI/MarginContainer/VBoxContainer"]
layout_mode = 2
button_pressed = true
text = "Grayscale"

[connection signal="toggled" from="UI/MarginContainer/VBoxContainer/SSSR" to="." method="_on_simple_ssr_toggled"]
[connection signal="toggled" from="UI/MarginContainer/VBoxContainer/GrayScaleBtn" to="." method="_on_gray_scale_btn_toggled"]
Binary file added compute/post_effects/pattern.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading