Skip to content

Commit

Permalink
Add compute texture demo
Browse files Browse the repository at this point in the history
  • Loading branch information
BastiaanOlij committed Aug 2, 2023
1 parent f7e3ceb commit 27b4e76
Show file tree
Hide file tree
Showing 15 changed files with 598 additions and 0 deletions.
38 changes: 38 additions & 0 deletions compute/texture/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Compute texture

This demo shows how to use compute shaders to populate a texture that is used as an input for a material shader.

When the mouse cursor isn't hovering above the plane random "drops" of water are added that drive the ripple effect.
When the mouse cursor is above the plane you can "draw" on the plane to drive the ripple effect.

Language: GDScript

Renderer: Forward Plus

> Note: this demo requires Godot 4.2 or later
## Screenshots

![Screenshot](screenshots/compute_texture.webp)

## Technical description

The texture populated by the compute shader contains height data that is used in the material shader to create a rain drops/water ripple effect. It's a well known technique that has been around since the mid 90ies, adapted to a compute shader.

Three textures are created directly on the rendering device:
- One texture is used to write the heightmap to and used in the material shader.
- One texture is read from and contains the previous frames data.
- One texture is read from and contains data from the frame before that.

Instead of copying data from texture to texture to create this history, we simply cycle the RIDs.

Note that in this demo we are using the main rendering device to ensure we execute our compute shader before our normal rendering.

To use the texture with the latest height data we use a `Texture2DRD` resource, this is a special texture resource node that is able to use a texture directly created on the rendering device and expose it to material shaders.

The material shader uses a standard gradient approach by sampling the height map and calculating tangent and bi-normal vectors and adjust the normal accordingly.

## Licenses

Files in the `polyhaven/` folder are downloaded from <https://polyhaven.com/a/industrial_sunset_puresky>
and are licensed under CC0 1.0 Universal.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://d051ugdf65it1"
path="res://.godot/imported/industrial_sunset_puresky_2k.hdr-2273dddf6859dd4da64c4a85b4589512.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://assets/polyhaven/industrial_sunset_puresky_2k.hdr"
dest_files=["res://.godot/imported/industrial_sunset_puresky_2k.hdr-2273dddf6859dd4da64c4a85b4589512.ctex"]

[params]

compress/mode=3
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=0
1 change: 1 addition & 0 deletions compute/texture/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/texture/icon.svg.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://bonkdv3wikslq"
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
27 changes: 27 additions & 0 deletions compute/texture/main.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
extends Node3D

# Note, the code here just adds some control to our effects.
# Check res://water_plane/water_plane.gd for the real implementation

var y = 0.0

@onready var water_plane = $WaterPlane

func _ready():
$Container/RainSize/HSlider.value = $WaterPlane.rain_size
$Container/MouseSize/HSlider.value = $WaterPlane.mouse_size


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
if $Container/Rotate.button_pressed:
y += delta
water_plane.basis = Basis(Vector3.UP, y)


func _on_rain_size_changed(value):
$WaterPlane.rain_size = value


func _on_mouse_size_changed(value):
$WaterPlane.mouse_size = value
76 changes: 76 additions & 0 deletions compute/texture/main.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
[gd_scene load_steps=7 format=3 uid="uid://c7nfvt1chslyh"]

[ext_resource type="Script" path="res://main.gd" id="1_yvrvl"]
[ext_resource type="Texture2D" uid="uid://d051ugdf65it1" path="res://assets/polyhaven/industrial_sunset_puresky_2k.hdr" id="2_g2q6b"]
[ext_resource type="PackedScene" uid="uid://b2a5bjsxw63wr" path="res://water_plane/water_plane.tscn" id="2_k1nfp"]

[sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_obhcg"]
panorama = ExtResource("2_g2q6b")

[sub_resource type="Sky" id="Sky_s1sgk"]
sky_material = SubResource("PanoramaSkyMaterial_obhcg")

[sub_resource type="Environment" id="Environment_5dv8s"]
background_mode = 2
sky = SubResource("Sky_s1sgk")
tonemap_mode = 2
tonemap_white = 4.56

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

[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.5, -0.75, 0.433013, 2.78059e-08, 0.5, 0.866026, -0.866025, -0.433013, 0.25, 0, 1, 0)
shadow_enabled = true

[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_5dv8s")

[node name="WaterPlane" parent="." instance=ExtResource("2_k1nfp")]

[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(0.900266, -0.142464, 0.41137, -0.113954, 0.834877, 0.538512, -0.420162, -0.531681, 0.735377, 1.55343, 1.1434, 2.431)

[node name="Container" type="VBoxContainer" parent="."]
offset_right = 40.0
offset_bottom = 40.0

[node name="Rotate" type="CheckBox" parent="Container"]
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
text = "Rotate"

[node name="RainSize" type="HBoxContainer" parent="Container"]
layout_mode = 2

[node name="HSlider" type="HSlider" parent="Container/RainSize"]
custom_minimum_size = Vector2(250, 0)
layout_mode = 2
min_value = 1.0
max_value = 10.0
step = 0.1
value = 1.0

[node name="Label" type="Label" parent="Container/RainSize"]
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
text = "Rain size"

[node name="MouseSize" type="HBoxContainer" parent="Container"]
layout_mode = 2

[node name="HSlider" type="HSlider" parent="Container/MouseSize"]
custom_minimum_size = Vector2(250, 0)
layout_mode = 2
min_value = 1.0
max_value = 10.0
step = 0.1
value = 1.1

[node name="Label" type="Label" parent="Container/MouseSize"]
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
text = "Mouse size"

[connection signal="value_changed" from="Container/RainSize/HSlider" to="." method="_on_rain_size_changed"]
[connection signal="value_changed" from="Container/MouseSize/HSlider" to="." method="_on_mouse_size_changed"]
20 changes: 20 additions & 0 deletions compute/texture/project.godot
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
; 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="TestCustomTextures"
run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.2", "Forward Plus")
config/icon="res://icon.svg"

[rendering]

driver/threads/thread_model=2
Empty file.
Binary file added compute/texture/screenshots/compute_texture.webp
Binary file not shown.
47 changes: 47 additions & 0 deletions compute/texture/water_plane/water_compute.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#[compute]
#version 450

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

// Our textures
layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D current_image;
layout(r32f, set = 1, binding = 0) uniform restrict readonly image2D previous_image;
layout(r32f, set = 2, binding = 0) uniform restrict writeonly image2D output_image;

// Our push PushConstant
layout(push_constant, std430) uniform Params {
vec4 add_wave_point;
vec2 texture_size;
float damp;
float res2;
} params;

// The code we want to execute in each invocation
void main() {
ivec2 tl = ivec2(0, 0);
ivec2 size = ivec2(params.texture_size.x - 1, params.texture_size.y - 1);

ivec2 uv = ivec2(gl_GlobalInvocationID.xy);

float current_v = imageLoad(current_image, uv).r;
float up_v = imageLoad(current_image, clamp(uv - ivec2(0, 1), tl, size)).r;
float down_v = imageLoad(current_image, clamp(uv + ivec2(0, 1), tl, size)).r;
float left_v = imageLoad(current_image, clamp(uv - ivec2(1, 0), tl, size)).r;
float right_v = imageLoad(current_image, clamp(uv + ivec2(1, 0), tl, size)).r;
float previous_v = imageLoad(previous_image, uv).r;

float new_v = 2.0 * current_v - previous_v + 0.25 * (up_v + down_v + left_v + right_v - 4.0 * current_v);
new_v = new_v - (params.damp * new_v * 0.001);

if (params.add_wave_point.z > 0.0 && uv.x == floor(params.add_wave_point.x) && uv.y == floor(params.add_wave_point.y)) {
new_v = params.add_wave_point.z;
}

if (new_v < 0.0) {
new_v = 0.0;
}
vec4 result = vec4(new_v, new_v, new_v, 1.0);

imageStore(output_image, uv, result);
}
14 changes: 14 additions & 0 deletions compute/texture/water_plane/water_compute.glsl.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[remap]

importer="glsl"
type="RDShaderFile"
uid="uid://b6pdquh2n2jvn"
path="res://.godot/imported/water_compute.glsl-c7fe8f11197ba28412c4cdf6f7a9a21b.res"

[deps]

source_file="res://water_plane/water_compute.glsl"
dest_files=["res://.godot/imported/water_compute.glsl-c7fe8f11197ba28412c4cdf6f7a9a21b.res"]

[params]

Loading

0 comments on commit 27b4e76

Please sign in to comment.