├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── addons └── compute_shader_studio │ ├── compute_shader_studio_2d.gd │ ├── compute_shader_studio_2d.gd.uid │ ├── compute_shader_studio_plugin.gd │ ├── compute_shader_studio_plugin.gd.uid │ ├── icon.png │ ├── icon.png.import │ └── plugin.cfg ├── doc └── ComputeShaderStudio.pdf ├── examples ├── LabelStepPass.gd ├── LabelStepPass.gd.uid ├── basic_examples │ ├── example_2.tscn │ ├── example_3.tscn │ ├── example_4.tscn │ ├── example_5.cpp │ └── example_5.tscn ├── board │ ├── board.cpp │ └── board.tscn ├── cells │ ├── cells.gd │ ├── cells.gd.uid │ ├── example_cells.c │ └── example_cells.tscn ├── circles │ ├── circles.cpp │ └── circles.tscn ├── example_1.cpp ├── example_1.glsl.import ├── example_1.tscn ├── grid_512x512.png ├── grid_512x512.png.import ├── grid_image_licence.txt ├── icon.svg ├── icon.svg.import ├── lenia.cpp ├── mandelbrot │ ├── example_mandelbrot.cpp │ ├── example_mandelbrot.gd │ ├── example_mandelbrot.gd.uid │ └── example_mandelbrot.tscn ├── misc │ ├── example_laser.tscn │ └── example_lenia_test.tscn └── simple_circle │ ├── simple_circle.cpp │ └── simple_circle.tscn ├── project.godot └── screenshots ├── .DS_Store ├── .gdignore ├── compute_shader_studio_ex2.png ├── compute_shader_studio_ext_glsl.png ├── compute_shader_studio_headline.png └── compute_shader_studio_mandelbrot.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | 4 | # Godot-specific ignores 5 | .import/ 6 | export.cfg 7 | export_presets.cfg 8 | 9 | # Imported translations (automatically generated from CSV files) 10 | *.translation 11 | 12 | # Mono-specific ignores 13 | .mono/ 14 | data_*/ 15 | mono_crash.*.json 16 | .DS_Store 17 | screenshots/.DS_Store 18 | *.tmp 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Pascal Ballet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Compute Shader Studio 2 | A plugin for Godot to create compute shaders 3 | 4 | ![logo](screenshots/compute_shader_studio_headline.png) 5 | 6 | **[Compute Shader Studio](https://virtulab.univ-brest.fr) is an addon for Godot Engine that enables you to write Compute Shaders in a minute:** 7 | 8 | ![logo](screenshots/compute_shader_studio_ex2.png) 9 | 10 | **Read [the tutorial](doc/ComputeShaderStudio.pdf) or look at the [2 minutes video](https://www.youtube.com/watch?v=3bEgPawi7fQ) to make your first Compute Shader in Godot** 11 | 12 | **Several examples are available: test them to understand how they work and what can be done** 13 | -------------------------------------------------------------------------------- /addons/compute_shader_studio/compute_shader_studio_2d.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | class_name ComputeShaderStudio2D 3 | 4 | var current_pass : int = 0 5 | 6 | # Put your GLSL code in the GLSL_main string below 7 | # Here are all the accessible variables (uniforms) inside your GLSL code: 8 | # uint x,y : from GlobalInvocationID.x and .y 9 | # uint p : the position [x][y] in the invocation 10 | # uint WSX,WSY : the Global WorkSpace of invocations (generally match the data size) 11 | # int* data_0, data_1, etc : are the data treated (can be displayed by Sprite2D, TextureRect, etc). 12 | # Access them by data_0[p], data_1[p], etc 13 | # uint step : simulation step of the execution. Incresed by 1 after nb_passes 14 | # uint nb_passes: the number of passes your code needs (by step). 15 | # There is a barrier between each pass. 16 | # uint current_pass: pass currently executed (one pass per frame, nb_passes by step) 17 | 18 | #region ComputeShaderStudio 19 | 20 | var GLSL_header = """ 21 | #version 450 22 | 23 | // Invocations in the (x, y, z) dimension 24 | layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; 25 | 26 | // Bindings to the buffers we create in our script 27 | layout(binding = 0) buffer Params { 28 | int step; 29 | int current_pass; 30 | int mousex; 31 | int mousey; 32 | int mouse_button; 33 | }; 34 | 35 | """ 36 | 37 | ## Print the current step. 38 | @export var print_step:bool = false 39 | ## Print the current pass. 40 | @export var print_passes:bool = false 41 | ## Print in Output all the generated code. 42 | ## Can be usefull for debugging or to understand all the GLSL code needed. 43 | @export var print_generated_code:bool = false 44 | ## Do not execute compute shader at launch. 45 | @export var pause:bool = false 46 | ## Number of passes into each execution step. 47 | ## Between two passes, your GLSL code is synchronized. 48 | @export var nb_passes : int = 1 49 | ## Workspace Size X, usually it matches the x size of your image (Sprite2D or TextureRect) 50 | @export var WSX : int = 128 51 | ## Workspace Size Y, usually it matches the y size of your image (Sprite2D or TextureRect) 52 | @export var WSY : int = 128 53 | 54 | ## Drag & drop your external GLSL file here (use .cpp for your source file extension) 55 | @export_file("*.cpp") var glsl_file: String 56 | ## Write your GLSL code just below or use an external file above 57 | @export_multiline var GLSL_code : String = """ 58 | // Usable variables 59 | // **************** 60 | // step: execution step 61 | // current_pass: pass number in one step 62 | // mousex: x mouse position, mousey: y mouse position (in pixels) 63 | // mouse_button: 0=none, 1=left, 2=right, 3=both, 4=middle mouse button 64 | // WSX: worksize X, WSY: worksize Y 65 | // data_0[], data_1[], etc: matrices of data. Can be viewed within a Sprite2D or TextureRect2D 66 | // **************** 67 | // Write your code HERE 68 | void main() { 69 | uint x = gl_GlobalInvocationID.x; 70 | uint y = gl_GlobalInvocationID.y; 71 | uint p = x + y * WSX; 72 | data_0[p] = 0xFFF00FFF - int(p)*(step+1); 73 | } 74 | """ 75 | ## Drag and drop here your Sprite2D or TextureRect. 76 | @export var data:Array[Node] 77 | 78 | var rd : RenderingDevice 79 | var shader : RID 80 | var buffers : Array[RID] 81 | var buffer_params : RID 82 | var buffer_user : RID 83 | 84 | var uniforms : Array[RDUniform] 85 | #var uniform_2 : RDUniform 86 | var uniform_params : RDUniform 87 | var uniform_user : RDUniform 88 | 89 | var uniform_user_data : PackedByteArray = PackedByteArray([0]) 90 | 91 | var bindings : Array = [] 92 | 93 | var pipeline : RID 94 | var uniform_set : RID 95 | 96 | # Called when the node enters the scene tree for the first time. 97 | #region _ready 98 | func _ready(): 99 | compile() 100 | 101 | func compile(): 102 | # Create a local rendering device. 103 | rd = RenderingServer.create_local_rendering_device() 104 | if not rd: 105 | set_process(false) 106 | printerr("Compute shaders are not available") 107 | return 108 | 109 | # ********************* 110 | # * SHADER CREATION * 111 | # ********************* 112 | 113 | var nb_buffers : int = data.size() 114 | 115 | # Create GLSL Header 116 | GLSL_header += """ 117 | uint WSX="""+str(WSX)+""";"""+""" 118 | uint WSY="""+str(WSY)+"""; 119 | """ 120 | 121 | for i in nb_buffers: 122 | GLSL_header += """ 123 | layout(binding = """+str(i+2)+""") buffer Data"""+str(i)+""" { 124 | int data_"""+str(i)+"""[]; 125 | }; 126 | 127 | """ 128 | 129 | # The external GLSL file takes priority 130 | if glsl_file != "": 131 | print("Load the GLSL file:" + glsl_file ) 132 | GLSL_code = load_glsl_file(glsl_file) 133 | var GLSL_all : String = GLSL_header + GLSL_code 134 | if print_generated_code == true: 135 | print(GLSL_all) 136 | 137 | # Compile the shader by passing a string 138 | var shader_src := RDShaderSource.new() 139 | shader_src.set_stage_source(RenderingDevice.SHADER_STAGE_COMPUTE, GLSL_all) 140 | var shader_spirv := rd.shader_compile_spirv_from_source(shader_src) 141 | 142 | var err:String=shader_spirv.compile_error_compute 143 | 144 | if err != "": 145 | printerr(err) 146 | get_tree().quit() 147 | 148 | shader = rd.shader_create_from_spirv(shader_spirv) 149 | 150 | 151 | # ********************* 152 | # * BUFFERS CREATION * 153 | # ********************* 154 | 155 | # Buffer for current_pass 156 | var input_params :PackedInt32Array = PackedInt32Array() 157 | input_params.append(step) 158 | input_params.append(current_pass) 159 | var input_params_bytes := input_params.to_byte_array() 160 | buffer_params = rd.storage_buffer_create(input_params_bytes.size(), input_params_bytes) 161 | buffer_user = rd.storage_buffer_create(uniform_user_data.size(), uniform_user_data) 162 | 163 | # Creation of nb_buffers Buffers of type Int32 164 | for b in nb_buffers: 165 | var input :PackedInt32Array = PackedInt32Array() 166 | for i in range(WSX): 167 | for j in range(WSY): 168 | input.append(randi()) 169 | var input_bytes :PackedByteArray = input.to_byte_array() 170 | buffers.append(rd.storage_buffer_create(input_bytes.size(), input_bytes)) 171 | 172 | # ********************* 173 | # * UNIFORMS CREATION * 174 | # ********************* 175 | 176 | # Create current_pass uniform pass 177 | uniform_params = RDUniform.new() 178 | uniform_params.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER 179 | uniform_params.binding = 0 # this needs to match the "binding" in our shader file 180 | uniform_params.add_id(buffer_params) 181 | 182 | # Create current_pass uniform pass 183 | uniform_user = RDUniform.new() 184 | uniform_user.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER 185 | uniform_user.binding = 1 # this needs to match the "binding" in our shader file 186 | uniform_user.add_id(buffer_user) 187 | 188 | var nb_uniforms : int = data.size() 189 | for b in nb_uniforms: 190 | var uniform = RDUniform.new() 191 | uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER 192 | uniform.binding = b+2 # this needs to match the "binding" in our shader file 193 | uniform.add_id(buffers[b]) 194 | uniforms.append(uniform) 195 | 196 | # Create the uniform SET between CPU & GPU 197 | bindings = [uniform_params, uniform_user] 198 | for b in nb_buffers: 199 | bindings.append(uniforms[b]) 200 | 201 | uniform_set = rd.uniform_set_create(bindings, shader, 0) # the last parameter (the 0) needs to match the "set" in our shader file 202 | 203 | # ************************** 204 | # * COMPUTE LIST CREATION * 205 | # ************************** 206 | # Create a compute pipeline 207 | pipeline = rd.compute_pipeline_create(shader) 208 | 209 | #endregion 210 | 211 | func load_glsl_file(file_name:String) -> String: 212 | var file = FileAccess.open(file_name, FileAccess.READ) 213 | if file == null: 214 | printerr("Unable to load GLSL file:" + file_name) 215 | return "void main() {}" 216 | var src_glsl:String = file.get_as_text() 217 | return src_glsl 218 | 219 | func display_all_values(): 220 | # Read back the data from the buffers 221 | for b in data.size(): 222 | var output_bytes : PackedByteArray = rd.buffer_get_data(buffers[b]) 223 | if is_instance_valid(data[b]): 224 | display_values(data[b], output_bytes) 225 | 226 | func display_values(disp : Node, values : PackedByteArray): # PackedInt32Array): 227 | var img : Image = Image.create_from_data(WSX, WSY, false, Image.FORMAT_RGBA8, values) 228 | var tex : Texture2D = ImageTexture.create_from_image(img) 229 | 230 | if disp is Sprite2D : 231 | var old_width : float = disp.texture.get_width() 232 | var old_height : float = disp.texture.get_height() 233 | disp.set_texture(tex) 234 | disp.scale *= Vector2(old_width/WSX, old_height/WSY) 235 | 236 | else : 237 | disp.set_texture(tex) 238 | 239 | 240 | var step : int = 0 241 | 242 | func compute(): 243 | if print_step == true && current_pass%nb_passes == 0: 244 | print("Step="+str(step)) 245 | if print_passes == true: 246 | print(" CurrentPass="+str(current_pass)) 247 | 248 | _update_uniforms() 249 | 250 | # Prepare the Computer List ############################################ 251 | var compute_list : int = rd.compute_list_begin() 252 | rd.compute_list_bind_compute_pipeline(compute_list, pipeline) 253 | rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0) 254 | rd.compute_list_dispatch(compute_list, WSX>>3, WSY>>3, 1) 255 | rd.compute_list_end() 256 | ####################################################################### 257 | 258 | # Submit to GPU and wait for sync 259 | rd.submit() 260 | rd.sync() 261 | 262 | # Update step and current_passe 263 | current_pass = (current_pass + 1) % nb_passes 264 | if current_pass == 0: 265 | step += 1 266 | 267 | func _process(_delta): 268 | if pause == false: 269 | compute() 270 | display_all_values() 271 | 272 | ## Pass the interesting values from CPU to GPU 273 | func _update_uniforms(): 274 | var input_params : PackedInt32Array = PackedInt32Array() 275 | 276 | # Current step 277 | input_params.append(step) 278 | # Current pass 279 | input_params.append(current_pass) 280 | 281 | # Mouse position 282 | var pos : Vector2 = screen_to_data0(get_viewport().get_mouse_position()) 283 | input_params.append(pos.x) 284 | input_params.append(pos.y) 285 | 286 | # Mouse button 287 | input_params.append(Input.get_mouse_button_mask()) 288 | 289 | # Binding of input_params (step, current_pass, mousex, mousey and mouse_button) 290 | var input_params_bytes := input_params.to_byte_array() 291 | buffer_params = rd.storage_buffer_create(input_params_bytes.size(), input_params_bytes) 292 | uniform_params = RDUniform.new() 293 | uniform_params.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER 294 | uniform_params.binding = 0 # this needs to match the "binding" in our shader file 295 | uniform_params.add_id(buffer_params) 296 | bindings[0] = uniform_params 297 | 298 | # Binding of buffer_user (data_0, data_1, etc) 299 | buffer_user = rd.storage_buffer_create(uniform_user_data.size(), uniform_user_data) 300 | uniform_user = RDUniform.new() 301 | uniform_user.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER 302 | uniform_user.binding = 1 # this needs to match the "binding" in our shader file 303 | uniform_user.add_id(buffer_user) 304 | bindings[1] = uniform_user 305 | 306 | # Uniform set of all the bindings 307 | uniform_set = rd.uniform_set_create(bindings, shader, 0) 308 | # Note: when changing the uniform set, use the same bindings Array (do not create a new Array) 309 | 310 | ## Cleaning up the GPU resources 311 | func _notification(what): 312 | # Object destructor, triggered before the engine deletes this Node. 313 | if what == NOTIFICATION_PREDELETE: 314 | cleanup_gpu() 315 | 316 | func cleanup_gpu(): 317 | if rd == null: 318 | return 319 | # All resources must be freed after use to avoid memory leaks. 320 | rd.free_rid(pipeline) 321 | pipeline = RID() 322 | 323 | rd.free_rid(uniform_set) 324 | uniform_set = RID() 325 | 326 | rd.free_rid(shader) 327 | shader = RID() 328 | 329 | rd.free() 330 | rd = null 331 | 332 | #endregion 333 | 334 | func _on_button_step(): 335 | pause = true 336 | compute() 337 | display_all_values() 338 | 339 | 340 | func _on_button_play(): 341 | pause = false # Replace with function body. 342 | 343 | 344 | func screen_to_data0(pos : Vector2): 345 | if data.size() <= 0 : 346 | return Vector2(0, 0) 347 | 348 | if data[0] is Sprite2D: 349 | var sprite : Sprite2D = data[0] 350 | pos.x = (pos.x - sprite.position.x) / sprite.scale.x + WSX/2 351 | pos.y = (pos.y - sprite.position.y) / sprite.scale.y + WSY/2 352 | return pos; 353 | else: 354 | return Vector2(0,0) 355 | -------------------------------------------------------------------------------- /addons/compute_shader_studio/compute_shader_studio_2d.gd.uid: -------------------------------------------------------------------------------- 1 | uid://c8esqdv0y26yp 2 | -------------------------------------------------------------------------------- /addons/compute_shader_studio/compute_shader_studio_plugin.gd: -------------------------------------------------------------------------------- 1 | ## BehaviorTreeForGroups 2 | ## ****************** 3 | ## Editor plugin configuration 4 | 5 | @tool 6 | extends EditorPlugin 7 | 8 | func _enter_tree(): 9 | # Initialization of the plugin goes here. 10 | # Main Node 11 | add_custom_type("ComputeShaderStudio2D", "Node",\ 12 | preload("compute_shader_studio_2d.gd"),preload("icon.png")) 13 | 14 | func _exit_tree(): 15 | # Clean-up of the plugin goes here. 16 | # Main Node 17 | remove_custom_type("ComputeShaderStudio2D") 18 | -------------------------------------------------------------------------------- /addons/compute_shader_studio/compute_shader_studio_plugin.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dsa025s8psj2p 2 | -------------------------------------------------------------------------------- /addons/compute_shader_studio/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pascal-ballet/ComputeShaderStudio/7170f7ce27918f2836ec7eda13e52626e94d3140/addons/compute_shader_studio/icon.png -------------------------------------------------------------------------------- /addons/compute_shader_studio/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bhxau0hh6k20" 6 | path="res://.godot/imported/icon.png-7c8d4788acc705d8be11d01aa0d4594b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/compute_shader_studio/icon.png" 14 | dest_files=["res://.godot/imported/icon.png-7c8d4788acc705d8be11d01aa0d4594b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/compute_shader_studio/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Compute Shader Studio" 4 | description="Create Compute Shaders in a minute." 5 | author="Pascal Ballet" 6 | version="1.0" 7 | script="compute_shader_studio_plugin.gd" 8 | -------------------------------------------------------------------------------- /doc/ComputeShaderStudio.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pascal-ballet/ComputeShaderStudio/7170f7ce27918f2836ec7eda13e52626e94d3140/doc/ComputeShaderStudio.pdf -------------------------------------------------------------------------------- /examples/LabelStepPass.gd: -------------------------------------------------------------------------------- 1 | extends Label 2 | 3 | 4 | # Called when the node enters the scene tree for the first time. 5 | func _ready(): 6 | pass # Replace with function body. 7 | 8 | 9 | # Called every frame. 'delta' is the elapsed time since the previous frame. 10 | func _process(_delta): 11 | var step : int = $"../ComputeShaderStudio2D2".step 12 | var current_pass : int = $"../ComputeShaderStudio2D2".current_pass 13 | self.text = "step="+str(step)+" current_pass="+str(current_pass) 14 | -------------------------------------------------------------------------------- /examples/LabelStepPass.gd.uid: -------------------------------------------------------------------------------- 1 | uid://jw3o2qu3a0pl 2 | -------------------------------------------------------------------------------- /examples/basic_examples/example_2.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cotswmubn7uvm"] 2 | 3 | [ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_2cs40"] 4 | [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_j6qu8"] 5 | 6 | [node name="Example2" type="Node2D"] 7 | 8 | [node name="ComputeShaderStudio2D2" type="Node" parent="." node_paths=PackedStringArray("data")] 9 | script = ExtResource("1_2cs40") 10 | GLSL_code = "// Write your GLSL code HERE 11 | void main() { 12 | uint x = gl_GlobalInvocationID.x; 13 | uint y = gl_GlobalInvocationID.y; 14 | uint p = x + y * WSX; 15 | int opacity = 0xFF000000 ; 16 | int blue = 0x550000; 17 | int green = 0x3300; 18 | int red = int(y*2) ; 19 | data_0[p] = opacity+blue+green+red; 20 | } 21 | " 22 | data = [NodePath("../Sprite2D")] 23 | 24 | [node name="Sprite2D" type="Sprite2D" parent="."] 25 | position = Vector2(847, 361) 26 | scale = Vector2(4.42, 4.42) 27 | texture = ExtResource("2_j6qu8") 28 | 29 | [node name="Label" type="Label" parent="."] 30 | offset_left = 371.0 31 | offset_top = 3.0 32 | offset_right = 647.0 33 | offset_bottom = 43.0 34 | scale = Vector2(1.72, 1.72) 35 | text = "Compute Shader Studio - Example 2" 36 | horizontal_alignment = 1 37 | vertical_alignment = 1 38 | 39 | [node name="Label2" type="Label" parent="."] 40 | offset_left = 29.0 41 | offset_top = 76.0 42 | offset_right = 336.0 43 | offset_bottom = 405.0 44 | scale = Vector2(1.72, 1.72) 45 | text = "// Select the node ComputeShader 46 | // to write your GLSL code 47 | void main() { 48 | uint x = gl_GlobalInvocationID.x; 49 | uint y = gl_GlobalInvocationID.y; 50 | uint p = x + y * WSX; 51 | int opacity = 0xFF000000 ; 52 | int blue = 0x550000; 53 | int green = 0x3300; 54 | int red = int(y*2) ; 55 | data_0[p] = opacity+blue+green+red; 56 | } 57 | " 58 | vertical_alignment = 1 59 | -------------------------------------------------------------------------------- /examples/basic_examples/example_3.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://cmucgsppcoo5e"] 2 | 3 | [ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_eimw3"] 4 | [ext_resource type="Script" uid="uid://jw3o2qu3a0pl" path="res://examples/LabelStepPass.gd" id="2_4aq4t"] 5 | [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_4upxj"] 6 | 7 | [node name="CompShadStudioEx3" type="Node2D"] 8 | 9 | [node name="ComputeShaderStudio2D2" type="Node" parent="." node_paths=PackedStringArray("data")] 10 | script = ExtResource("1_eimw3") 11 | pause = true 12 | nb_passes = 2 13 | GLSL_code = "// Write your code HERE 14 | #define ALIVE 0xFFFFFFFF 15 | #define DEAD 0xFF0000FF 16 | 17 | void compute_next_step(uint x, uint y, uint p) { 18 | if ( x > 0 && y > 0 && x < WSX-1 && y < WSY - 1) { // in sandbox 19 | uint n = 0 ; // Number of living neighbors 20 | for (uint i = x-1; i <= x+1; i++) { 21 | for (uint j = y-1; j <= y+1; j++) { 22 | uint k = i + j * WSX; 23 | if (k != p && data_0[k] == ALIVE ) 24 | n++; 25 | } 26 | } 27 | if (data_0[p] == DEAD) { 28 | if ( n == 3) 29 | data_1[p] = ALIVE; // Birth 30 | else 31 | data_1[p] = DEAD ; 32 | } else { // ALIVE 33 | if (n <=1 || n >= 4) // Under or over population 34 | data_1[p] = DEAD; 35 | else 36 | data_1[p] = ALIVE; // Survive 37 | } 38 | } 39 | } 40 | 41 | void main() { 42 | uint x = gl_GlobalInvocationID.x; 43 | uint y = gl_GlobalInvocationID.y; 44 | uint p = x + y * WSX; 45 | if ( step == 0 ) { // initialisation ************************ 46 | if ( current_pass == 0 ) { 47 | data_1[p] = DEAD ; 48 | if (data_0[p] < 0 || x==0 || y==0 || x==WSX-1 || y==WSY-1) 49 | data_0[p] = DEAD ; 50 | else 51 | data_0[p] = ALIVE ; 52 | } 53 | } else { // in process ********************************* 54 | if (current_pass == 0) 55 | compute_next_step(x, y, p); 56 | else 57 | data_0[p] = data_1[p]; // The future is now 58 | } 59 | } 60 | " 61 | data = [NodePath("../Matrix1"), NodePath("../Matrix2")] 62 | 63 | [node name="LabelTitle" type="Label" parent="."] 64 | offset_left = 56.0 65 | offset_top = 3.0 66 | offset_right = 655.0 67 | offset_bottom = 38.0 68 | scale = Vector2(1.72, 1.72) 69 | text = "Compute Shader Studio - Example 3: Game Of Life" 70 | horizontal_alignment = 1 71 | vertical_alignment = 1 72 | 73 | [node name="LabelData_0" type="Label" parent="."] 74 | offset_left = 103.0 75 | offset_top = 506.0 76 | offset_right = 332.0 77 | offset_bottom = 541.0 78 | scale = Vector2(1.72, 1.72) 79 | text = "data_0" 80 | horizontal_alignment = 1 81 | vertical_alignment = 1 82 | script = ExtResource("2_4aq4t") 83 | 84 | [node name="LabelData_1" type="Label" parent="."] 85 | offset_left = 654.0 86 | offset_top = 506.0 87 | offset_right = 883.0 88 | offset_bottom = 541.0 89 | scale = Vector2(1.72, 1.72) 90 | text = "data_1" 91 | horizontal_alignment = 1 92 | vertical_alignment = 1 93 | script = ExtResource("2_4aq4t") 94 | 95 | [node name="LabelStepPass" type="Label" parent="."] 96 | offset_left = 56.0 97 | offset_top = 56.0 98 | offset_right = 659.0 99 | offset_bottom = 91.0 100 | scale = Vector2(1.72, 1.72) 101 | text = "step=0 current_pass=0" 102 | horizontal_alignment = 1 103 | vertical_alignment = 1 104 | script = ExtResource("2_4aq4t") 105 | 106 | [node name="Matrix1" type="Sprite2D" parent="."] 107 | position = Vector2(294, 315) 108 | scale = Vector2(3.27344, 3.02344) 109 | texture = ExtResource("2_4upxj") 110 | 111 | [node name="Matrix2" type="Sprite2D" parent="."] 112 | position = Vector2(843, 317) 113 | scale = Vector2(3.27344, 3.02344) 114 | texture = ExtResource("2_4upxj") 115 | 116 | [node name="ButtonStep" type="Button" parent="."] 117 | offset_left = 346.0 118 | offset_top = 563.0 119 | offset_right = 437.0 120 | offset_bottom = 594.0 121 | scale = Vector2(2.4, 2.4) 122 | text = "Step" 123 | 124 | [node name="ButtonPlay" type="Button" parent="."] 125 | offset_left = 585.0 126 | offset_top = 564.0 127 | offset_right = 676.0 128 | offset_bottom = 595.0 129 | scale = Vector2(2.4, 2.4) 130 | text = "Play" 131 | 132 | [connection signal="pressed" from="ButtonStep" to="ComputeShaderStudio2D2" method="_on_button_step"] 133 | [connection signal="pressed" from="ButtonPlay" to="ComputeShaderStudio2D2" method="_on_button_play"] 134 | -------------------------------------------------------------------------------- /examples/basic_examples/example_4.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://b2ip5eftk3aij"] 2 | 3 | [ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_ke3fj"] 4 | [ext_resource type="Texture2D" uid="uid://2behgeplwycn" path="res://examples/grid_512x512.png" id="2_flqlh"] 5 | 6 | [node name="CompShadStudioEx4" type="Node2D"] 7 | 8 | [node name="Label" type="Label" parent="."] 9 | offset_left = 0.999908 10 | offset_top = 1.0 11 | offset_right = 232.0 12 | offset_bottom = 60.0 13 | scale = Vector2(1.72, 1.72) 14 | text = "Compute Shader Studio 15 | Example 4: BIG Game Of Life" 16 | horizontal_alignment = 1 17 | vertical_alignment = 1 18 | 19 | [node name="ComputeShaderStudio2D2" type="Node" parent="." node_paths=PackedStringArray("data")] 20 | script = ExtResource("1_ke3fj") 21 | pause = true 22 | nb_passes = 2 23 | WSX = 512 24 | WSY = 512 25 | GLSL_code = "// Write your code HERE 26 | #define ALIVE 0xFFFFFFFF 27 | #define DEAD 0xFF0000FF 28 | 29 | void compute_next_step(uint x, uint y, uint p) { 30 | if ( x > 0 && y > 0 && x < WSX-1 && y < WSY - 1) { // in sandbox 31 | uint n = 0 ; // Number of living neighbors 32 | for (uint i = x-1; i <= x+1; i++) { 33 | for (uint j = y-1; j <= y+1; j++) { 34 | uint k = i + j * WSX; 35 | if (k != p && data_0[k] == ALIVE ) 36 | n++; 37 | } 38 | } 39 | if (data_0[p] == DEAD) { 40 | if ( n == 3) 41 | data_1[p] = ALIVE; // Birth 42 | else 43 | data_1[p] = DEAD ; 44 | } else { // ALIVE 45 | if (n <=1 || n >= 4) // Under or over population 46 | data_1[p] = DEAD; 47 | else 48 | data_1[p] = ALIVE; // Survive 49 | } 50 | } 51 | } 52 | 53 | void main() { 54 | uint x = gl_GlobalInvocationID.x; 55 | uint y = gl_GlobalInvocationID.y; 56 | uint p = x + y * WSX; 57 | if ( step == 0 ) { // initialisation ************************ 58 | if ( current_pass == 0 ) { 59 | data_1[p] = DEAD ; 60 | if (data_0[p] < 0 || x==0 || y==0 || x==WSX-1 || y==WSY-1) 61 | data_0[p] = DEAD ; 62 | else 63 | data_0[p] = ALIVE ; 64 | } 65 | } else { // in process ********************************* 66 | if (current_pass == 0) 67 | compute_next_step(x, y, p); 68 | else 69 | data_0[p] = data_1[p]; // The future is now 70 | } 71 | } 72 | " 73 | data = [NodePath("../Grid512x512_0"), null] 74 | 75 | [node name="ButtonStep" type="Button" parent="."] 76 | offset_left = 91.0 77 | offset_top = 131.0 78 | offset_right = 182.0 79 | offset_bottom = 162.0 80 | scale = Vector2(2.4, 2.4) 81 | text = "Step" 82 | 83 | [node name="ButtonPlay" type="Button" parent="."] 84 | offset_left = 92.0 85 | offset_top = 219.0 86 | offset_right = 183.0 87 | offset_bottom = 250.0 88 | scale = Vector2(2.4, 2.4) 89 | text = "Play" 90 | 91 | [node name="Grid512x512_0" type="Sprite2D" parent="."] 92 | position = Vector2(762.7, 314.7) 93 | scale = Vector2(1.2, 1.2) 94 | texture = ExtResource("2_flqlh") 95 | 96 | [connection signal="pressed" from="ButtonStep" to="ComputeShaderStudio2D2" method="_on_button_step"] 97 | [connection signal="pressed" from="ButtonPlay" to="ComputeShaderStudio2D2" method="_on_button_play"] 98 | -------------------------------------------------------------------------------- /examples/basic_examples/example_5.cpp: -------------------------------------------------------------------------------- 1 | // Write your code HERE 2 | void main() { 3 | uint x = gl_GlobalInvocationID.x; 4 | uint y = gl_GlobalInvocationID.y; 5 | uint p = x + y * WSX; 6 | int dx = int(WSX)/2 - int(x); 7 | int dy = int(WSY)/2 - int(y); 8 | int r = int(64.0*sin(step/100.0)); 9 | int d = int(dx*dx+dy*dy) ; 10 | data_0[p] = 0xFF000000 - 600* (d + step) ; 11 | } 12 | -------------------------------------------------------------------------------- /examples/basic_examples/example_5.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://cets11mlsd8hb"] 2 | 3 | [ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_cogap"] 4 | 5 | [sub_resource type="FastNoiseLite" id="FastNoiseLite_pm7bb"] 6 | 7 | [sub_resource type="NoiseTexture2D" id="NoiseTexture2D_nxi5j"] 8 | width = 256 9 | height = 128 10 | noise = SubResource("FastNoiseLite_pm7bb") 11 | 12 | [node name="CompShadStudioEx5" type="Node2D"] 13 | 14 | [node name="ComputeShaderStudio2D2" type="Node" parent="." node_paths=PackedStringArray("data")] 15 | script = ExtResource("1_cogap") 16 | WSX = 256 17 | glsl_file = "res://examples/basic_examples/example_5.cpp" 18 | GLSL_code = "" 19 | data = [NodePath("../TextureRect")] 20 | 21 | [node name="Label" type="Label" parent="."] 22 | offset_left = 278.0 23 | offset_top = -1.0 24 | offset_right = 679.0 25 | offset_bottom = 101.0 26 | scale = Vector2(1.72, 1.72) 27 | text = "Compute Shader Studio 28 | Example 5: using an external GLSL file 29 | and a TextureRect" 30 | horizontal_alignment = 1 31 | vertical_alignment = 1 32 | 33 | [node name="TextureRect" type="TextureRect" parent="."] 34 | offset_left = 278.0 35 | offset_top = 192.0 36 | offset_right = 975.0 37 | offset_bottom = 629.0 38 | texture = SubResource("NoiseTexture2D_nxi5j") 39 | -------------------------------------------------------------------------------- /examples/board/board.cpp: -------------------------------------------------------------------------------- 1 | const int WIDTH = 16; 2 | void main() { 3 | 4 | uint x = gl_GlobalInvocationID.x; 5 | uint y = gl_GlobalInvocationID.y; 6 | uint p = x + y*WSX; 7 | 8 | int case_x = int(floor( (x / WIDTH)) ); // between 0 and WSX/16 - 1 9 | int case_y = int(floor( (y / WIDTH)) ); // between 0 and WSX/16 - 1 10 | 11 | if ( (case_x + case_y)%2 == 0) // Case Blanche 12 | data_0[p] = int(0xFFFF00FF) ; // Fond 13 | else { 14 | data_0[p] = 0xFF00FF00; // Case Noire 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/board/board.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://dwvfg8m5moed8"] 2 | 3 | [ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_7dugu"] 4 | [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_1fg0d"] 5 | 6 | [node name="Board" type="Node2D"] 7 | 8 | [node name="ComputeShaderStudio2D" type="Node" parent="." node_paths=PackedStringArray("data")] 9 | script = ExtResource("1_7dugu") 10 | WSX = 512 11 | WSY = 256 12 | glsl_file = "res://examples/board/board.cpp" 13 | GLSL_code = "" 14 | data = [NodePath("../Icon")] 15 | metadata/_custom_type_script = "uid://c8esqdv0y26yp" 16 | 17 | [node name="Icon" type="Sprite2D" parent="."] 18 | position = Vector2(584, 323) 19 | scale = Vector2(8.58594, 4.46875) 20 | texture = ExtResource("2_1fg0d") 21 | -------------------------------------------------------------------------------- /examples/cells/cells.gd: -------------------------------------------------------------------------------- 1 | # NOTE: modifier from ComputeShaderStudio 2 | 3 | extends Node 4 | 5 | # Put your GLSL code in the GLSL_main string below 6 | # Here are all the accessible variables (uniforms) inside your GLSL code: 7 | # uint x,y : from GlobalInvocationID.x and .y 8 | # uint p : the position [x][y] in the invocation 9 | # uint WSX,WSY : the Global WorkSpace of invocations (generally match the data size) 10 | # int* data_0, data_1, etc : are the data treated (can be displayed by Sprite2D). 11 | # Access them by data_0[p], data_1[p], etc 12 | # uint step : simulation step of the execution. Incresed by 1 after nb_passes 13 | # uint nb_passes: the number of passes your code needs (by step). 14 | # There is a barrier between each pass. 15 | # uint current_pass: pass currently executed (one pass per frame, nb_passes by step) 16 | 17 | var GLSL_header = """ 18 | #version 450 19 | 20 | // Invocations in the (x, y, z) dimension 21 | layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; 22 | 23 | // Bindings to the buffers we create in our script 24 | // layout(binding = 0) buffer Params 25 | layout(push_constant) uniform Params { 26 | // NOTE: push_constant uniform block size aligned 16 27 | int current_step; 28 | int current_pass; 29 | int click_x; 30 | int click_y; 31 | int display_mode; 32 | }; 33 | """ 34 | 35 | ## Print the current step. 36 | @export var print_step:bool = false 37 | ## Print in Output all the generated code. 38 | ## Can be usefull for debugging. 39 | @export var print_generated_code:bool = false 40 | ## Do not execute compute shader at launch. 41 | @export var pause : bool = false 42 | ## Number of passes (synchronized code) needed. 43 | var nb_passes : int = 1 44 | ## Workspace Size X, usually it matches the x size of your Sprite2D image 45 | @export var WSX : int = 128 46 | ## Workspace Size Y, usually it matches the y size of your Sprite2D image 47 | @export var WSY : int = 128 48 | 49 | @export var nb_passes_cyto : int = 10 50 | @export var nb_passes_memb : int = 10 51 | 52 | @export_file("*") 53 | var glsl_file : String = "" 54 | 55 | ## Drag and drop your Sprite2D here. 56 | @export var data : Array[Sprite2D] 57 | 58 | var rd : RenderingDevice 59 | var shader : RID 60 | var buffers : Array[RID] 61 | var buffer_params : RID 62 | 63 | var uniforms : Array[RDUniform] 64 | #var uniform_2 : RDUniform 65 | var uniform_params : RDUniform 66 | 67 | var bindings : Array = [] 68 | 69 | var pipeline : RID 70 | var uniform_set : RID 71 | 72 | # Called when the node enters the scene tree for the first time. 73 | #region _ready 74 | func _ready(): 75 | compile() 76 | 77 | func compile(): 78 | # Create a local rendering device. 79 | rd = RenderingServer.create_local_rendering_device() 80 | 81 | if not rd: 82 | set_process(false) 83 | print("Compute shaders are not available") 84 | return 85 | 86 | # ********************* 87 | # * SHADER CREATION * 88 | # ********************* 89 | 90 | var nb_buffers : int = data.size() 91 | 92 | # Create GLSL Header 93 | GLSL_header += """ 94 | const uint WSX="""+str(WSX)+""";"""+""" 95 | const uint WSY="""+str(WSY)+"""; 96 | """ 97 | 98 | var pass_acc := 0 99 | 100 | GLSL_header += "const int PASS_INIT = " + str(pass_acc) + ";\n" 101 | GLSL_header += "const int PASS_CYTO_INIT = " + str(pass_acc + 1) + ";\n" 102 | pass_acc += 1 + nb_passes_cyto * 2 103 | GLSL_header += "const int PASS_CYTO_FINI = " + str(pass_acc - 1) + ";\n" 104 | GLSL_header += "const int PASS_MEMB = " + str(pass_acc) + ";\n" 105 | GLSL_header += "const int PASS_MEMB_INIT = " + str(pass_acc + 1) + ";\n" 106 | pass_acc += 1 + nb_passes_memb * 2 107 | GLSL_header += "const int PASS_MEMB_FINI = " + str(pass_acc - 1) + ";\n" 108 | 109 | GLSL_header += "const int PASS_DIRECTION = " + str(pass_acc) + ";\n" 110 | GLSL_header += "const int PASS_MOVE = " + str(pass_acc + 1) + ";\n" 111 | GLSL_header += "const int PASS_MOVE_2 = " + str(pass_acc + 2) + ";\n" 112 | GLSL_header += "const int PASS_MOVE_3 = " + str(pass_acc + 3) + ";\n" 113 | pass_acc = pass_acc + 4 114 | 115 | GLSL_header += "const int PASS_LAST = " + str(pass_acc) + ";\n" 116 | nb_passes = pass_acc + 1 117 | 118 | GLSL_header += "const int PASS_COUNT = " + str(nb_passes) + ";\n" 119 | for i in nb_buffers: 120 | GLSL_header += """ 121 | layout(binding = """+str(i)+""") buffer Data"""+str(i)+""" { 122 | int data_"""+str(i)+"""[]; 123 | }; 124 | 125 | """ 126 | 127 | var GLSL_all : String 128 | 129 | if !glsl_file.is_empty(): 130 | var file := FileAccess.open(glsl_file, FileAccess.READ) 131 | GLSL_all = GLSL_header + file.get_as_text() 132 | 133 | if print_generated_code == true: 134 | print(GLSL_all) 135 | 136 | # Compile the shader by passing a string 137 | var shader_src := RDShaderSource.new() 138 | shader_src.set_stage_source(RenderingDevice.SHADER_STAGE_COMPUTE, GLSL_all) 139 | var shader_spirv := rd.shader_compile_spirv_from_source(shader_src) 140 | 141 | var err:String=shader_spirv.compile_error_compute 142 | 143 | if err != "": 144 | printerr(err) 145 | get_tree().quit() 146 | 147 | shader = rd.shader_create_from_spirv(shader_spirv) 148 | 149 | 150 | # ********************* 151 | # * BUFFERS CREATION * 152 | # ********************* 153 | 154 | # Buffers from/for data (Sprite2D) 155 | for b in nb_buffers: 156 | var input :PackedInt32Array = PackedInt32Array() 157 | for i in range(WSX): 158 | for j in range(WSY): 159 | input.append(randi()) 160 | var input_bytes :PackedByteArray = input.to_byte_array() 161 | buffers.append(rd.storage_buffer_create(input_bytes.size(), input_bytes)) 162 | 163 | # ********************* 164 | # * UNIFORMS CREATION * 165 | # ********************* 166 | 167 | var nb_uniforms : int = data.size() 168 | 169 | for b in nb_uniforms: 170 | var uniform := RDUniform.new() 171 | uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER 172 | uniform.binding = b # this needs to match the "binding" in our shader file 173 | uniform.add_id(buffers[b]) 174 | uniforms.append(uniform) 175 | 176 | # Create the uniform SET between CPU & GPU 177 | bindings = [] 178 | 179 | for b in nb_buffers: 180 | bindings.append(uniforms[b]) 181 | 182 | uniform_set = rd.uniform_set_create(bindings, shader, 0) # the last parameter (the 0) needs to match the "set" in our shader file 183 | 184 | # ************************** 185 | # * COMPUTE LIST CREATION * 186 | # ************************** 187 | # Create a compute pipeline 188 | pipeline = rd.compute_pipeline_create(shader) 189 | 190 | # Execute once (should it stay?) 191 | # compute() 192 | # Read back the data from the buffer 193 | # display_all_values() 194 | #endregion 195 | 196 | func display_all_values(): 197 | # Read back the data from the buffers 198 | # for b in data.size(): 199 | 200 | for b in range(len(buffers)): 201 | if is_instance_valid(data[b]): 202 | var output_bytes : PackedByteArray = rd.buffer_get_data(buffers[b]) 203 | display_values(data[b], output_bytes) 204 | 205 | func display_values(sprite : Sprite2D, values : PackedByteArray): # PackedInt32Array): 206 | var image_format : int = Image.FORMAT_RGBA8 207 | var image := Image.create_from_data(WSX, WSY, false, image_format, values) 208 | sprite.set_texture(ImageTexture.create_from_image(image)) 209 | 210 | var current_step : int = 0 211 | 212 | func compute(): 213 | if print_step: 214 | print("Step=" + str(current_step)) 215 | 216 | # Prepare the Computer List ############################################ 217 | var compute_list := rd.compute_list_begin() 218 | 219 | rd.compute_list_bind_compute_pipeline(compute_list, pipeline) 220 | rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0) 221 | 222 | for i in nb_passes: 223 | var push_constants := _pack_params(i) 224 | rd.compute_list_set_push_constant(compute_list, push_constants, len(push_constants)) 225 | rd.compute_list_dispatch(compute_list, WSX >> 3, WSY >> 3, 1) 226 | rd.compute_list_add_barrier(compute_list) # sync 227 | 228 | rd.compute_list_end() 229 | ####################################################################### 230 | 231 | current_step += 1 232 | 233 | func _process(_delta): 234 | if pause == false: 235 | var start := Time.get_ticks_usec() 236 | 237 | compute() 238 | 239 | var mid := Time.get_ticks_usec() 240 | 241 | # Submit to GPU and wait for sync 242 | rd.submit() 243 | rd.sync() 244 | 245 | var end := Time.get_ticks_usec() 246 | 247 | display_all_values() 248 | 249 | var post := Time.get_ticks_usec() 250 | 251 | print("prepare time: " + str(float(mid - start) / 1000.0) + "ms") 252 | print("compute time: " + str(float(end - mid) / 1000.0) + "ms") 253 | print("present time: " + str(float(post - end) / 1000.0) + "ms") 254 | 255 | # HACK: pass clicks 256 | 257 | # this is used to 258 | var was_click := false 259 | 260 | var display_mode := 0 261 | 262 | func _pack_params(which_pass : int) -> PackedByteArray: 263 | # HACK 264 | # TODO move this out of here and do this only once 265 | var click_pos := data[0].get_local_mouse_position() + (Vector2(WSX, WSY) / 2) 266 | var is_click := Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) 267 | var click_pos_clamp : Vector2 = clamp(click_pos, Vector2(0, 0), Vector2(WSX, WSY)) 268 | 269 | if is_click and not was_click and click_pos == click_pos_clamp: 270 | click_pos = click_pos_clamp 271 | 272 | else: 273 | click_pos = Vector2(-1, -1) 274 | 275 | was_click = is_click 276 | 277 | var input_params : PackedInt32Array = PackedInt32Array([current_step, which_pass, 278 | int(click_pos.x), int(click_pos.y), display_mode, 0, 0, 0]) 279 | return input_params.to_byte_array() 280 | 281 | func set_display_normal(): 282 | display_mode = 0 283 | 284 | func set_display_cytoplasm(): 285 | display_mode = 1 286 | 287 | func set_display_cytoplasm_flat(): 288 | display_mode = 2 289 | 290 | func set_display_membranes(): 291 | display_mode = 3 292 | 293 | func set_display_membrane_field(): 294 | display_mode = 4 295 | -------------------------------------------------------------------------------- /examples/cells/cells.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bs2f5sxok3d3i 2 | -------------------------------------------------------------------------------- /examples/cells/example_cells.c: -------------------------------------------------------------------------------- 1 | // NOTE: buffer names are aliases to generated names 2 | // this is a bit of a hack 3 | 4 | // TODO: adhesion can be based on membrane field strength 5 | 6 | #define Display data_0 7 | 8 | #define Centers data_1 9 | 10 | #define CytoplasmField data_2 11 | #define CytoplasmId data_3 12 | 13 | #define Membrane data_4 14 | #define MembraneField data_5 15 | 16 | #define BufA data_6 17 | #define BufB data_7 18 | 19 | // NOTE: those values are random and bad 20 | #define MEMB_SEUIL 10 21 | #define MOVE_SEUIL 0x100 22 | 23 | struct CellInfo 24 | { 25 | int cytoplasm_field_strength; 26 | int membrane_threshold; 27 | int default_membrane; // loose membranes 28 | int cytoplasm_color; 29 | }; 30 | 31 | struct MembInfo 32 | { 33 | int field_strength; 34 | int membrane_color; 35 | }; 36 | 37 | #define CELL_TYPE_COUNT 2 38 | 39 | const MembInfo memb_info[] = MembInfo[]( 40 | MembInfo(0x003FFFF, 0xFFBFBF00), // base membrane (used for defaults) 41 | MembInfo(0x000FFFF, 0xFF00BFBF), // sticky membrane (low strength) 42 | MembInfo(0x00FFFFF, 0xFFBF00BF) // pushy membrane (high strength) 43 | ); 44 | 45 | const CellInfo cell_info[CELL_TYPE_COUNT] = CellInfo[]( 46 | CellInfo(0x0FFFFFFF, 10, 0 /* base */, 0xFF7F007F), 47 | CellInfo(0x0FFFFFFF, 10, 0 /* base */, 0xFF7F7F00) 48 | ); 49 | 50 | // memb_table[self_type_id][neighbor_type_id] 51 | const int memb_table[CELL_TYPE_COUNT][CELL_TYPE_COUNT] = int[CELL_TYPE_COUNT][]( 52 | int[](1, 2), 53 | int[](2, 1) 54 | ); 55 | 56 | int get_cell_type(int id) 57 | { 58 | return id & 1; 59 | } 60 | 61 | int check_membrane(int id, int threshold, int p) 62 | { 63 | return (CytoplasmId[p] != id || CytoplasmField[p] < threshold) ? 1 : 0; 64 | } 65 | 66 | int get_membrane(int self_id, int threshold, int p) 67 | { 68 | int id = CytoplasmId[p]; 69 | 70 | return CytoplasmField[p] < threshold 71 | ? self_id 72 | : (id != self_id 73 | ? id : 0); 74 | } 75 | 76 | ivec2 unpack_ivec2(int val) 77 | { 78 | ivec2 res = ivec2((val & 0xFFFF), (val & 0xFFFF0000) >> 16); 79 | 80 | if ((res.x & 0x8000) != 0) { res.x |= 0xFFFF0000; } 81 | if ((res.y & 0x8000) != 0) { res.y |= 0xFFFF0000; } 82 | 83 | return res; 84 | } 85 | 86 | int pack_ivec2(ivec2 val) 87 | { 88 | return ((val.x) & 0xFFFF) | ((val.y << 16) & 0xFFFF0000); 89 | } 90 | 91 | void main() 92 | { 93 | int x = int(gl_GlobalInvocationID.x); 94 | int y = int(gl_GlobalInvocationID.y); 95 | int p = x + y * int(WSX); 96 | 97 | // generate initial config 98 | if (current_step == 0) 99 | { 100 | Centers[p] = 0; 101 | Display[p] = 0; 102 | } 103 | else 104 | { 105 | int xl = int(uint(x - 1 + WSX) % WSX); 106 | int xr = int(uint(x + 1) % WSX); 107 | int yu = int(uint(y - 1 + WSY) % WSY); 108 | int yd = int(uint(y + 1) % WSY); 109 | 110 | int pmu = x + yu * int(WSX); 111 | int pmd = x + yd * int(WSX); 112 | int plm = xl + y * int(WSX); 113 | int prm = xr + y * int(WSX); 114 | 115 | if (current_pass == PASS_INIT) 116 | { 117 | if (x == click_x && y == click_y) 118 | { 119 | Centers[p] = current_step; 120 | } 121 | 122 | CytoplasmId[p] = Centers[p]; 123 | 124 | if (Centers[p] != 0) 125 | { 126 | int cell_type_here = get_cell_type(Centers[p]); 127 | CytoplasmField[p] = cell_info[cell_type_here].cytoplasm_field_strength; 128 | } 129 | else 130 | { 131 | CytoplasmField[p] = 0; 132 | } 133 | 134 | Membrane[p] = 0; 135 | } 136 | 137 | if (current_pass >= PASS_CYTO_INIT && current_pass <= PASS_CYTO_FINI) 138 | { 139 | // HACK, this could be done more elegantly by swapping the input buffers every other pass 140 | if (((current_pass - PASS_CYTO_INIT) & 1) == 1) 141 | { 142 | CytoplasmField[p] = BufA[p]; 143 | } 144 | else 145 | { 146 | int id = CytoplasmId[p]; 147 | int mx = CytoplasmField[p]; 148 | 149 | if (CytoplasmField[pmu] > mx) { id = CytoplasmId[pmu]; mx = CytoplasmField[pmu]; } 150 | if (CytoplasmField[pmd] > mx) { id = CytoplasmId[pmd]; mx = CytoplasmField[pmd]; } 151 | if (CytoplasmField[plm] > mx) { id = CytoplasmId[plm]; mx = CytoplasmField[plm]; } 152 | if (CytoplasmField[prm] > mx) { id = CytoplasmId[prm]; mx = CytoplasmField[prm]; } 153 | 154 | BufA[p] = CytoplasmField[p] / 10 * 6 155 | + CytoplasmField[pmu] / 10 156 | + CytoplasmField[pmd] / 10 157 | + CytoplasmField[plm] / 10 158 | + CytoplasmField[prm] / 10; 159 | 160 | CytoplasmId[p] = id; 161 | } 162 | } 163 | 164 | if (current_pass == PASS_MEMB) 165 | { 166 | Membrane[p] = 0; 167 | MembraneField[p] = 0; 168 | 169 | int id_here = CytoplasmId[p]; 170 | int type_here = get_cell_type(CytoplasmId[p]); 171 | int threshold = cell_info[type_here].membrane_threshold; 172 | 173 | if (CytoplasmField[p] >= threshold) 174 | { 175 | ivec4 neighbors = ivec4( 176 | get_membrane(id_here, threshold, pmu), 177 | get_membrane(id_here, threshold, plm), 178 | get_membrane(id_here, threshold, prm), 179 | get_membrane(id_here, threshold, pmd)); 180 | 181 | ivec2 neighbors2 = max(neighbors.xy, neighbors.zw); 182 | int neighbor_id = max(neighbors2.x, neighbors2.y); 183 | 184 | /* 185 | int m = 0; 186 | 187 | m += check_membrane(id_here, threshold, pmu); 188 | m += check_membrane(id_here, threshold, plm); 189 | m += check_membrane(id_here, threshold, prm); 190 | m += check_membrane(id_here, threshold, pmd); 191 | */ 192 | 193 | if (neighbor_id != 0) 194 | { 195 | int self_type = get_cell_type(id_here); 196 | int memb_type = 0; 197 | 198 | if (neighbor_id == id_here) 199 | { 200 | memb_type = cell_info[self_type].default_membrane; 201 | } 202 | else 203 | { 204 | int neighbor_type = get_cell_type(neighbor_id); 205 | memb_type = memb_table[self_type][neighbor_type]; 206 | } 207 | 208 | Membrane[p] = memb_type + 1; // + 1 to distinguish from none if type == 0 209 | MembraneField[p] = memb_info[memb_type].field_strength; 210 | } 211 | } 212 | } 213 | 214 | if (current_pass >= PASS_MEMB_INIT && current_pass <= PASS_MEMB_FINI) 215 | { 216 | if (((current_pass - PASS_MEMB_INIT) & 1) == 1) 217 | { 218 | MembraneField[p] = BufA[p] / 10 * 6 219 | + BufA[pmu] / 10 220 | + BufA[pmd] / 10 221 | + BufA[plm] / 10 222 | + BufA[prm] / 10; 223 | } 224 | else 225 | { 226 | BufA[p] = MembraneField[p] / 10 * 6 227 | + MembraneField[pmu] / 10 228 | + MembraneField[pmd] / 10 229 | + MembraneField[plm] / 10 230 | + MembraneField[prm] / 10; 231 | } 232 | } 233 | 234 | if (current_pass == PASS_DIRECTION) 235 | { 236 | int dir = 0; 237 | int mx = 0; 238 | 239 | if (MembraneField[pmu] > MembraneField[pmd] + MOVE_SEUIL) 240 | { 241 | if (mx < MembraneField[pmu]) { mx = MembraneField[pmu]; dir = 1; } 242 | } 243 | 244 | if (MembraneField[pmd] > MembraneField[pmu] + MOVE_SEUIL) 245 | { 246 | if (mx < MembraneField[pmd]) { mx = MembraneField[pmd]; dir = 2; } 247 | } 248 | 249 | if (MembraneField[plm] > MembraneField[prm] + MOVE_SEUIL) 250 | { 251 | if (mx < MembraneField[plm]) { mx = MembraneField[plm]; dir = 3; } 252 | } 253 | 254 | if (MembraneField[prm] > MembraneField[plm] + MOVE_SEUIL) 255 | { 256 | if (mx < MembraneField[prm]) { mx = MembraneField[prm]; dir = 4; } 257 | } 258 | 259 | BufA[p] = dir; 260 | } 261 | 262 | if (current_pass == PASS_MOVE) 263 | { 264 | if (Centers[p] != 0) 265 | { 266 | if (BufA[p] != 0) 267 | BufB[p] = 0; 268 | else 269 | BufB[p] = Centers[p]; 270 | } 271 | else 272 | { 273 | if (BufA[pmu] == 1 && Centers[pmu] != 0) { BufB[p] = Centers[pmu]; } 274 | else if (BufA[pmd] == 2 && Centers[pmd] != 0) { BufB[p] = Centers[pmd]; } 275 | else if (BufA[plm] == 3 && Centers[plm] != 0) { BufB[p] = Centers[plm]; } 276 | else if (BufA[prm] == 4 && Centers[prm] != 0) { BufB[p] = Centers[prm]; } 277 | else { BufB[p] = 0; } 278 | } 279 | } 280 | 281 | if (current_pass == PASS_MOVE_2) 282 | { 283 | Centers[p] = BufB[p]; 284 | } 285 | 286 | if (current_pass == PASS_LAST) 287 | { 288 | // all of this is not very efficient 289 | 290 | Display[p] = 0xFF000000; 291 | 292 | if (display_mode == 0) 293 | { 294 | if (CytoplasmId[p] != 0) 295 | { 296 | int type_here = get_cell_type(CytoplasmId[p]); 297 | 298 | if (CytoplasmField[p] > cell_info[type_here].membrane_threshold) 299 | { 300 | Display[p] = cell_info[type_here].cytoplasm_color; 301 | } 302 | } 303 | } 304 | 305 | if (display_mode == 0 || display_mode == 3) 306 | { 307 | if (Membrane[p] != 0) 308 | { 309 | Display[p] = memb_info[Membrane[p] - 1].membrane_color; // 0xFF00BFBF; 310 | } 311 | } 312 | 313 | if (display_mode == 1) 314 | { 315 | if (CytoplasmField[p] != 0) 316 | { 317 | int type_here = get_cell_type(CytoplasmId[p]); 318 | int color_base = cell_info[type_here].cytoplasm_color & 0x010101; 319 | 320 | // values that look good found empirically 321 | float val = log2(float(CytoplasmField[p])) / log2(1.0675); 322 | Display[p] += color_base * (int(val)); 323 | } 324 | } 325 | 326 | if (display_mode == 0 || display_mode == 2) 327 | { 328 | if (Membrane[p] == 0 && MembraneField[p] > 0) 329 | { 330 | Display[p] += 0x007F0000; 331 | } 332 | } 333 | 334 | if (display_mode == 4) 335 | { 336 | if (MembraneField[p] != 0) 337 | { 338 | int color_base = 1; // red 339 | 340 | // values that look good found empirically 341 | float val = log2(float(MembraneField[p]) * 0x10) / log2(1.0675); 342 | Display[p] += color_base * (int(val)); 343 | } 344 | } 345 | 346 | if (Centers[p] != 0) 347 | { 348 | Display[p] = 0xFFFFFFFF; 349 | } 350 | } 351 | 352 | // MEMBRANE TEST VISU 353 | 354 | /* 355 | 356 | int type_here = get_cell_type(CytoplasmId[p]); 357 | 358 | if (CytoplasmField[p] > cell_info[type_here].membrane_threshold && MembraneField[p] > 0) 359 | { 360 | Display[p] = 0xFF000000 + MembraneField[p]; 361 | } 362 | else 363 | { 364 | Display[p] = 0xFF000000; 365 | } 366 | 367 | */ 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /examples/cells/example_cells.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cutxgtalwsp6q"] 2 | 3 | [ext_resource type="Script" uid="uid://bs2f5sxok3d3i" path="res://examples/cells/cells.gd" id="1_dw4h8"] 4 | [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_oji6h"] 5 | 6 | [node name="Cells" type="Control" node_paths=PackedStringArray("data")] 7 | layout_mode = 3 8 | anchors_preset = 15 9 | anchor_right = 1.0 10 | anchor_bottom = 1.0 11 | grow_horizontal = 2 12 | grow_vertical = 2 13 | script = ExtResource("1_dw4h8") 14 | print_generated_code = true 15 | WSX = 512 16 | WSY = 256 17 | nb_passes_cyto = 20 18 | nb_passes_memb = 14 19 | glsl_file = "res://examples/cells/example_cells.c" 20 | data = [NodePath("CenterAnchor/Display"), null, null, null, null, null, null, null, null, null, null, null, null] 21 | 22 | [node name="CenterAnchor" type="Control" parent="."] 23 | layout_mode = 1 24 | anchors_preset = 8 25 | anchor_left = 0.5 26 | anchor_top = 0.5 27 | anchor_right = 0.5 28 | anchor_bottom = 0.5 29 | grow_horizontal = 2 30 | grow_vertical = 2 31 | 32 | [node name="Display" type="Sprite2D" parent="CenterAnchor"] 33 | texture_filter = 1 34 | scale = Vector2(4, 4) 35 | texture = ExtResource("2_oji6h") 36 | 37 | [node name="DisplayNormalButton" type="Button" parent="."] 38 | layout_mode = 0 39 | offset_left = 8.0 40 | offset_top = 8.0 41 | offset_right = 248.0 42 | offset_bottom = 56.0 43 | text = "Display Regular" 44 | 45 | [node name="DisplayCytoplasmButton" type="Button" parent="."] 46 | layout_mode = 0 47 | offset_left = 8.0 48 | offset_top = 72.0 49 | offset_right = 248.0 50 | offset_bottom = 120.0 51 | text = "Display Cytoplasm Field" 52 | 53 | [node name="DisplayCytoplasmButton2" type="Button" parent="."] 54 | layout_mode = 0 55 | offset_left = 8.0 56 | offset_top = 136.0 57 | offset_right = 248.0 58 | offset_bottom = 184.0 59 | text = "Display Cytoplasm Field (Flat)" 60 | 61 | [node name="DisplayMembranes" type="Button" parent="."] 62 | layout_mode = 0 63 | offset_left = 8.0 64 | offset_top = 200.0 65 | offset_right = 248.0 66 | offset_bottom = 248.0 67 | text = "Display Membranes" 68 | 69 | [node name="DisplayMembraneField" type="Button" parent="."] 70 | layout_mode = 0 71 | offset_left = 8.0 72 | offset_top = 264.0 73 | offset_right = 248.0 74 | offset_bottom = 312.0 75 | text = "Display Membrane Field" 76 | 77 | [node name="RichTextLabel" type="RichTextLabel" parent="."] 78 | layout_mode = 1 79 | anchors_preset = 11 80 | anchor_left = 1.0 81 | anchor_right = 1.0 82 | anchor_bottom = 1.0 83 | offset_left = -320.0 84 | grow_horizontal = 0 85 | grow_vertical = 2 86 | theme_override_font_sizes/normal_font_size = 20 87 | text = "- Step 1: Plop cytoplasm fields at center positions 88 | - Step 2: Diffuse cytoplasm field 89 | - Step 3: At cytoplasm edges, plop membranes 90 | - Step 4: Diffuse membrane field 91 | - Step 5: At centers, push in direction where membrane field is weakest 92 | - Step 6: move centers 93 | - Display & Repeat" 94 | 95 | [connection signal="pressed" from="DisplayNormalButton" to="." method="set_display_normal"] 96 | [connection signal="pressed" from="DisplayCytoplasmButton" to="." method="set_display_cytoplasm"] 97 | [connection signal="pressed" from="DisplayCytoplasmButton2" to="." method="set_display_cytoplasm_flat"] 98 | [connection signal="pressed" from="DisplayMembranes" to="." method="set_display_membranes"] 99 | [connection signal="pressed" from="DisplayMembraneField" to="." method="set_display_membrane_field"] 100 | -------------------------------------------------------------------------------- /examples/circles/circles.cpp: -------------------------------------------------------------------------------- 1 | #define RADIUS 40 2 | 3 | void main() 4 | { 5 | int x = int(gl_GlobalInvocationID.x); 6 | int y = int(gl_GlobalInvocationID.y); 7 | int p = x + y * int(WSX); 8 | 9 | // initial background is black & opac 10 | if (step == 0) { 11 | data_0[p] = 0xFF000000 + y; 12 | } else { // draw a fading red circle 13 | float dx = float(mousex - x); 14 | float dy = float(mousey - y); 15 | float dist = sqrt(dx*dx+dy*dy); 16 | int pix = data_0[p]; 17 | int col = pix & 0x00FFFFFF ; 18 | float r = RADIUS + 10*sin(step/10.0f); 19 | if( dist < r-1 ) { // interior 20 | col = col + 0x04 ; 21 | data_0[p] = 0xFF000000 + col ; 22 | } 23 | if( dist >= r-1 && dist <= r+1 ) { // perimeter 24 | data_0[p] = 0xFF0000FF ; 25 | } 26 | if( dist > r + 1) { // ouside 27 | if (col > 0) 28 | data_0[p] = pix - 1; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/circles/circles.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://c1ux4sgouixua"] 2 | 3 | [ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_amro2"] 4 | [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_ntl1q"] 5 | 6 | [node name="Circles" type="Node2D"] 7 | 8 | [node name="ComputeShaderStudio2D" type="Node" parent="." node_paths=PackedStringArray("data")] 9 | script = ExtResource("1_amro2") 10 | WSX = 512 11 | WSY = 256 12 | glsl_file = "res://examples/circles/circles.cpp" 13 | GLSL_code = "" 14 | data = [NodePath("../Icon")] 15 | metadata/_custom_type_script = "uid://c8esqdv0y26yp" 16 | 17 | [node name="Label" type="Label" parent="."] 18 | offset_left = 278.0 19 | offset_top = -1.0 20 | offset_right = 679.0 21 | offset_bottom = 101.0 22 | scale = Vector2(1.72, 1.72) 23 | text = "Compute Shader Studio - Example Circles 24 | - uses an external GLSL file 25 | - and a Sprite2D" 26 | horizontal_alignment = 1 27 | vertical_alignment = 1 28 | 29 | [node name="Icon" type="Sprite2D" parent="."] 30 | position = Vector2(605.5, 411) 31 | scale = Vector2(5.63281, 3.17969) 32 | texture = ExtResource("2_ntl1q") 33 | -------------------------------------------------------------------------------- /examples/example_1.cpp: -------------------------------------------------------------------------------- 1 | // Write your code HERE 2 | void main() { 3 | uint x = gl_GlobalInvocationID.x; 4 | uint y = gl_GlobalInvocationID.y; 5 | uint p = x + y * WSX; 6 | data_0[p] = 0xFFFF0000 - int(x+y*1024)*(step); 7 | } 8 | -------------------------------------------------------------------------------- /examples/example_1.glsl.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="glsl" 4 | type="RDShaderFile" 5 | uid="uid://t2cxc4b2gxqm" 6 | path="res://.godot/imported/example_1.glsl-7b942579df45277fe553202d9675e7e5.res" 7 | 8 | [deps] 9 | 10 | source_file="res://examples/example_1.glsl" 11 | dest_files=["res://.godot/imported/example_1.glsl-7b942579df45277fe553202d9675e7e5.res"] 12 | 13 | [params] 14 | 15 | -------------------------------------------------------------------------------- /examples/example_1.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://ddr6qtwy1pesd"] 2 | 3 | [ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_6846p"] 4 | [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_s3fct"] 5 | 6 | [sub_resource type="FastNoiseLite" id="FastNoiseLite_dmk8h"] 7 | 8 | [sub_resource type="NoiseTexture2D" id="NoiseTexture2D_7n3ni"] 9 | width = 128 10 | height = 128 11 | noise = SubResource("FastNoiseLite_dmk8h") 12 | 13 | [node name="CompShadStudioEx1" type="Node2D"] 14 | 15 | [node name="ComputeShaderStudio2D2" type="Node" parent="." node_paths=PackedStringArray("data")] 16 | script = ExtResource("1_6846p") 17 | GLSL_code = " 18 | // Usable variables 19 | // **************** 20 | // step: execution step 21 | // current_pass: pass number in one step 22 | // mousex: x mouse position, mousey: y mouse position (in pixels) 23 | // mouse_button: 0=none, 1=left, 2=right, 3=both, 4=middle mouse button 24 | // WSX: worksize X, WSY: worksize Y 25 | // data_0[], data_1[], etc: matrices of data. Can be viewed within a Sprite2D or TextureRect2D 26 | // **************** 27 | // Write your code HERE 28 | void main() { 29 | uint x = gl_GlobalInvocationID.x; 30 | uint y = gl_GlobalInvocationID.y; 31 | uint p = x + y * WSX; 32 | data_0[p] = 0xFFF00FFF - int(p)*(step+1); 33 | data_1[p] = 0xFFF0FF00 + step +10 * int(y*(1.0f+sin((step+y)/30.0f))); 34 | } 35 | " 36 | data = [NodePath("../Matrix1"), NodePath("../Matrix2")] 37 | 38 | [node name="Matrix1" type="Sprite2D" parent="."] 39 | position = Vector2(294, 315) 40 | scale = Vector2(3.27344, 3.02344) 41 | texture = ExtResource("2_s3fct") 42 | 43 | [node name="Matrix2" type="TextureRect" parent="."] 44 | offset_left = 594.0 45 | offset_top = 119.0 46 | offset_right = 722.0 47 | offset_bottom = 247.0 48 | scale = Vector2(3.27344, 3.02344) 49 | texture = SubResource("NoiseTexture2D_7n3ni") 50 | 51 | [node name="Label" type="Label" parent="."] 52 | offset_left = 109.0 53 | offset_top = 3.0 54 | offset_right = 627.0 55 | offset_bottom = 62.0 56 | scale = Vector2(1.72, 1.72) 57 | text = "Compute Shader Studio - Example 1 58 | Select the node ComputeShader to write your GLSL code" 59 | horizontal_alignment = 1 60 | vertical_alignment = 1 61 | 62 | [node name="Label2" type="Label" parent="."] 63 | offset_left = 133.0 64 | offset_top = 525.0 65 | offset_right = 321.0 66 | offset_bottom = 561.0 67 | scale = Vector2(1.72, 1.72) 68 | text = "Sprite2D" 69 | horizontal_alignment = 1 70 | vertical_alignment = 1 71 | 72 | [node name="Label3" type="Label" parent="."] 73 | offset_left = 644.0 74 | offset_top = 522.0 75 | offset_right = 832.0 76 | offset_bottom = 558.0 77 | scale = Vector2(1.72, 1.72) 78 | text = "TextureRect" 79 | horizontal_alignment = 1 80 | vertical_alignment = 1 81 | -------------------------------------------------------------------------------- /examples/grid_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pascal-ballet/ComputeShaderStudio/7170f7ce27918f2836ec7eda13e52626e94d3140/examples/grid_512x512.png -------------------------------------------------------------------------------- /examples/grid_512x512.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://2behgeplwycn" 6 | path="res://.godot/imported/grid_512x512.png-2c8cc3aa508910332e4c1a499b7f5cbb.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://examples/grid_512x512.png" 14 | dest_files=["res://.godot/imported/grid_512x512.png-2c8cc3aa508910332e4c1a499b7f5cbb.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /examples/grid_image_licence.txt: -------------------------------------------------------------------------------- 1 | grid_512x512.png is from Daniel Maak. 2 | This image is licensed under the 3 | Creative Commons Attribution- 4 | Share Alike 4.0 International license. 5 | -------------------------------------------------------------------------------- /examples/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://demftcowdd5c6" 6 | path="res://.godot/imported/icon.svg-acd25bcdc80d9b3e751da37b20890fa9.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://examples/icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-acd25bcdc80d9b3e751da37b20890fa9.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /examples/lenia.cpp: -------------------------------------------------------------------------------- 1 | // Write your code HERE 2 | #define INTERVALL 1073741824 3 | // old intervall 2147483647 4 | 5 | //couleur de l'affichage 6 | #define DEAD 0 7 | #define DEAD_COLOR vec4(0.0f, 0.0f, 0.0f, 1.0f) // Noir 8 | #define ALIVE_COLOR vec4(0.8f, 0.8f, 0.0f, 1.0f) // Jaune 9 | #define MID_ALIVE_COLOR vec4(1.0f, 0.5f, 0.0f, 1.0f) // orange 10 | #define BELLOW_MID_ALIVE_COLOR vec4(1.0f, 0.0f, 1.0f, 1.0f) // violet 11 | 12 | 13 | //rayon de calcule de chaque pixel 14 | #define R 13 15 | 16 | #define INIT 2 17 | //init 0 random 18 | //init 1 cercle 19 | //init 2 orbium 20 | 21 | //un animal 22 | const float orbium[20][20] = float[20][20]( 23 | float[20](0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.1f, 0.14f, 0.1f, 0.0f, 0.0f, 0.03f, 0.03f, 0.0f, 0.0f, 0.3f, 0.0f, 0.0f, 0.0f, 0.0f), 24 | float[20](0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.08f, 0.24f, 0.3f, 0.3f, 0.18f, 0.14f, 0.15f, 0.16f, 0.15f, 0.09f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f), 25 | float[20](0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.15f, 0.34f, 0.44f, 0.46f, 0.38f, 0.18f, 0.14f, 0.11f, 0.13f, 0.19f, 0.18f, 0.45f, 0.0f, 0.0f, 0.0f), 26 | float[20](0.0f, 0.0f, 0.0f, 0.0f, 0.06f, 0.13f, 0.39f, 0.5f, 0.5f, 0.37f, 0.06f, 0.0f, 0.0f, 0.0f, 0.02f, 0.16f, 0.68f, 0.0f, 0.0f, 0.0f), 27 | float[20](0.0f, 0.0f, 0.0f, 0.11f, 0.17f, 0.17f, 0.33f, 0.4f, 0.38f, 0.28f, 0.14f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.18f, 0.42f, 0.0f, 0.0f), 28 | float[20](0.0f, 0.0f, 0.09f, 0.18f, 0.13f, 0.06f, 0.08f, 0.26f, 0.32f, 0.32f, 0.27f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.82f, 0.0f, 0.0f), 29 | float[20](0.27f, 0.0f, 0.16f, 0.12f, 0.0f, 0.0f, 0.0f, 0.25f, 0.38f, 0.44f, 0.45f, 0.34f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.22f, 0.17f, 0.0f), 30 | float[20](0.0f, 0.07f, 0.2f, 0.02f, 0.0f, 0.0f, 0.0f, 0.31f, 0.48f, 0.57f, 0.6f, 0.57f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.49f, 0.0f), 31 | float[20](0.0f, 0.59f, 0.19f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.57f, 0.69f, 0.76f, 0.76f, 0.49f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.36f, 0.0f), 32 | float[20](0.0f, 0.58f, 0.19f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.67f, 0.83f, 0.9f, 0.92f, 0.87f, 0.12f, 0.0f, 0.0f, 0.0f, 0.0f, 0.22f, 0.07f), 33 | float[20](0.0f, 0.0f, 0.46f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.7f, 0.93f, 1.0f, 1.0f, 1.0f, 0.61f, 0.0f, 0.0f, 0.0f, 0.0f, 0.18f, 0.11f), 34 | float[20](0.0f, 0.0f, 0.82f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.47f, 1.0f, 1.0f, 0.98f, 1.0f, 0.96f, 0.27f, 0.0f, 0.0f, 0.0f, 0.19f, 0.1f), 35 | float[20](0.0f, 0.0f, 0.46f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.25f, 1.0f, 1.0f, 0.84f, 0.92f, 0.97f, 0.54f, 0.14f, 0.04f, 0.1f, 0.21f, 0.05f), 36 | float[20](0.0f, 0.0f, 0.0f, 0.4f, 0.0f, 0.0f, 0.0f, 0.0f, 0.09f, 0.8f, 1.0f, 0.82f, 0.8f, 0.85f, 0.63f, 0.31f, 0.18f, 0.19f, 0.2f, 0.01f), 37 | float[20](0.0f, 0.0f, 0.0f, 0.36f, 0.1f, 0.0f, 0.0f, 0.0f, 0.05f, 0.54f, 0.86f, 0.79f, 0.74f, 0.72f, 0.6f, 0.39f, 0.28f, 0.24f, 0.13f, 0.0f), 38 | float[20](0.0f, 0.0f, 0.0f, 0.01f, 0.3f, 0.07f, 0.0f, 0.0f, 0.08f, 0.36f, 0.64f, 0.7f, 0.64f, 0.6f, 0.51f, 0.39f, 0.29f, 0.19f, 0.04f, 0.0f), 39 | float[20](0.0f, 0.0f, 0.0f, 0.0f, 0.1f, 0.24f, 0.14f, 0.1f, 0.15f, 0.29f, 0.45f, 0.53f, 0.52f, 0.46f, 0.4f, 0.31f, 0.21f, 0.08f, 0.0f, 0.0f), 40 | float[20](0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.08f, 0.21f, 0.21f, 0.22f, 0.29f, 0.36f, 0.39f, 0.37f, 0.33f, 0.26f, 0.18f, 0.09f, 0.0f, 0.0f, 0.0f), 41 | float[20](0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.03f, 0.13f, 0.19f, 0.22f, 0.24f, 0.24f, 0.23f, 0.18f, 0.13f, 0.05f, 0.0f, 0.0f, 0.0f, 0.0f), 42 | float[20](0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.02f, 0.06f, 0.08f, 0.09f, 0.07f, 0.05f, 0.01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) 43 | ); 44 | 45 | //fonction random 46 | float random (vec2 uv) { 47 | return fract(sin(dot(uv.xy, 48 | vec2(12.9898f,78.233f))) * 43758.5453123f); 49 | } 50 | 51 | float int2float(int v) { 52 | return float(v) / float(INTERVALL); 53 | } 54 | 55 | int float2int(float v) { 56 | return int( v * float(INTERVALL) ); 57 | } 58 | 59 | //fonction d'affichage de la matrix data_2 60 | void compute_affichage(uint x, uint y, uint p) { 61 | // Mappage lineaire de data_0[p] a l'intervalle [0, INTERVALL] 62 | float mappedValue = int2float( data_0[p] );//pour avoir un chiffre entre 1 et 0 63 | vec4 resultColor; 64 | 65 | //en fonction de la valeur utilise des fonction diffrente pour melanger differente couleur 66 | if(mappedValue > 0.66f){ 67 | mappedValue = 3*mappedValue -2; 68 | resultColor = mix(MID_ALIVE_COLOR,ALIVE_COLOR,mappedValue); 69 | }else if(mappedValue > 0.33f){ 70 | mappedValue = 3*mappedValue -1; 71 | resultColor = mix(BELLOW_MID_ALIVE_COLOR,MID_ALIVE_COLOR,mappedValue); 72 | }else{ 73 | mappedValue = 3*mappedValue; 74 | resultColor = mix(DEAD_COLOR,BELLOW_MID_ALIVE_COLOR,mappedValue); 75 | } 76 | 77 | // Convertir chaque composante en entier et les combiner 78 | int resultInt = int(resultColor.a * 255.0f) << 24 | 79 | int(resultColor.b * 255.0f) << 16 | 80 | int(resultColor.g * 255.0f) << 8 | 81 | int(resultColor.r * 255.0f); 82 | 83 | // Affecter le resultat a data_2[p] 84 | data_2[p] = resultInt; 85 | 86 | } 87 | 88 | 89 | float gauss(float x, float mu, float sigma) { 90 | return exp(-0.5f * pow((x - mu) / sigma, 2.0f)); 91 | } 92 | 93 | float calculateDistance(float x, float y) { 94 | // Vous pouvez ajuster le centre du filtre en changeant les valeurs 1.0f 95 | return sqrt((1.0f - x) * (1.0f - x) + (1.0f - y) * (1.0f - y)); 96 | } 97 | 98 | 99 | //fonction de croissance pour test de game of life n'est pas utilise actuellement 100 | float growth_GoL(float u) { 101 | bool mask1 = (u >= 1.0f) && (u <= 3.0f); 102 | bool mask2 = (u > 3.0f) && (u <= 4.0f); 103 | 104 | return -1.0f + (u - 1.0f) * float(mask1) + 8.0f * (1.0f - u/4.0f) * float(mask2); 105 | } 106 | 107 | //fonction de croissance 108 | float growth_lenia(float u) { 109 | float mu = 0.15f; 110 | float sigma =0.015f; 111 | return -1.0f + 2.0f * gauss(u, mu, sigma); 112 | } 113 | 114 | void compute_next_step(uint x, uint y, uint p,float K[R * 2 + 1][R * 2 + 1]) { 115 | float nmax = 0; 116 | float somme_k = 0; 117 | if ( x > 0 && y > 0 && x < WSX-1 && y < WSY - 1) { // in sandbox 118 | float n = 0.0f; // Somme des voisins ponderee 119 | for (int i = -R; i <= R; ++i) { 120 | for (int j = -R; j <= R; ++j) { 121 | uint ix = x + i; 122 | uint jy = y + j; 123 | if (ix >= 0 && jy >= 0 && ix < WSX && jy < WSY) { // Verifier les limites du bac a sable 124 | uint k = ix + jy * WSX; 125 | if (k != p){ 126 | float nb = float( int2float(data_0[k] ) ) * K[i + R][j + R]; 127 | n += nb; 128 | somme_k = somme_k + K[i + R][j + R]; 129 | } 130 | } 131 | } 132 | } 133 | if(somme_k != 0){ 134 | //n = n / somme_k; 135 | } 136 | float taux_croissance = growth_lenia(n); 137 | //data_1[p] = int(clamp(float(data_0[p]) + ((taux_croissance *0.1f) * float(INTERVALL)), 0.0f, float(INTERVALL))); 138 | float new_croissance = taux_croissance * 0.1f ; 139 | 140 | float old_value = int2float(data_0[p]) ; 141 | float new_value = clamp(new_croissance + old_value ,0.0f, 1.0f ); 142 | 143 | int new_value_int = float2int(new_value) ; 144 | 145 | data_1[p] = new_value_int ; 146 | } 147 | } 148 | 149 | void main() { 150 | uint x = gl_GlobalInvocationID.x; 151 | uint y = gl_GlobalInvocationID.y; 152 | uint p = x + y * WSX; 153 | vec2 center = vec2(WSX/2, WSY/2); // Coordonnees du premier point 154 | vec2 point2 = vec2(x,y); // Coordonnees du deuxieme point 155 | 156 | float K[R * 2 + 1][R * 2 + 1]; 157 | 158 | if ( step == 0 ) { // initialisation ************************ 159 | if ( current_pass == 0 ) { 160 | 161 | int ytab[R * 2 + 1]; 162 | int xtab[R * 2 + 1]; 163 | 164 | float distance[R * 2 + 1][R * 2 + 1]; 165 | 166 | 167 | float mu = 0.15f; 168 | float sigma =0.015f; 169 | 170 | for (int i = -R; i <= R; ++i) { 171 | xtab[i + R] = i; 172 | ytab[i + R] = i; 173 | } 174 | 175 | for (int i = -R; i <= R; ++i) { 176 | for (int j = -R; j <= R; ++j) { 177 | distance[i + R][j + R] = sqrt(float(i * i + j * j)) / float(R); 178 | } 179 | } 180 | 181 | for (int i = -R; i <= R; ++i) { 182 | for (int j = -R; j <= R; ++j) { 183 | K[i + R][j + R] = gauss(distance[i + R][j + R], mu, sigma); 184 | } 185 | } 186 | 187 | // Couper les valeurs de K a d=1 188 | for (int i = -R; i <= R; ++i) { 189 | for (int j = -R; j <= R; ++j) { 190 | if (distance[i + R][j + R] > 1.0f) { 191 | K[i + R][j + R] = 0.0f; 192 | } 193 | } 194 | } 195 | 196 | // Trouver la valeur maximale dans le tableau K 197 | float maxK = 0.0f; 198 | for (int i = -R; i <= R; ++i) { 199 | for (int j = -R; j <= R; ++j) { 200 | if (K[i + R][j + R] > maxK) { 201 | maxK = K[i + R][j + R]; 202 | } 203 | } 204 | } 205 | 206 | // Normaliser le tableau K 207 | for (int i = -R; i <= R; ++i) { 208 | for (int j = -R; j <= R; ++j) { 209 | K[i + R][j + R] = K[i + R][j + R] / maxK; 210 | } 211 | } 212 | 213 | data_1[p] = DEAD; 214 | data_2[p] = DEAD; 215 | if(INIT == 0){ 216 | data_0[p] = int(random(vec2(x, y))*float(INTERVALL)) ; 217 | }else if(INIT == 1){ 218 | float distanceP = length(point2 - center); 219 | if(distanceP < float(R)){ 220 | data_0[p] = int(INTERVALL * (float(R) - distanceP)); 221 | }else{ 222 | data_0[p] = DEAD; 223 | } 224 | }else if(INIT == 2){ 225 | 226 | if(x<20 || x>=40 || y<20 || y>=40){ 227 | data_0[p] = DEAD; 228 | }else{ 229 | data_0[p] = float2int(orbium[x-20][y-20]); 230 | } 231 | } 232 | 233 | 234 | 235 | 236 | } 237 | } else { // in process ********************************* 238 | if (current_pass == 0){ 239 | compute_next_step(x, y, p, K); 240 | } 241 | if (current_pass == 1){ 242 | data_0[p] = data_1[p]; 243 | } 244 | if (current_pass == 2){ 245 | compute_affichage(x,y,p); 246 | } 247 | } 248 | } 249 | 250 | -------------------------------------------------------------------------------- /examples/mandelbrot/example_mandelbrot.cpp: -------------------------------------------------------------------------------- 1 | /* MANDELBROT 2 | Josselin SCOUARNEC 3 | Feb 2024 4 | Adapted from https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set 5 | */ 6 | 7 | //#define MAX_IT 500 8 | #define MAX_IT 10 9 | #define PI 3.141592 10 | #define TWO_PI (2.0*3.141592) 11 | 12 | #define RADIUS 2.0 13 | 14 | //layout(binding = 1) buffer UserUniforms { 15 | float u_zoom = 1.5f; 16 | float u_pos_x = -0.75f; 17 | float u_pos_y = 0.0f; 18 | //}; 19 | 20 | #define PALETTE_SIZE 16 21 | const vec3 palette[PALETTE_SIZE] = { 22 | vec3(0.0, 0.5, 0.5), 23 | vec3(0.0, 0.0, 0.5), 24 | vec3(0.0, 0.0, 1.0), 25 | vec3(0.0, 0.5, 1.0), 26 | vec3(0.0, 1.0, 1.0), 27 | vec3(0.5, 1.0, 0.5), 28 | vec3(1.0, 1.0, 0.0), 29 | vec3(1.0, 0.5, 0.0), 30 | vec3(1.0, 0.0, 0.0), 31 | vec3(1.0, 0.5, 0.0), 32 | vec3(1.0, 1.0, 0.0), 33 | vec3(0.5, 1.0, 0.5), 34 | vec3(0.0, 1.0, 1.0), 35 | vec3(0.0, 0.5, 1.0), 36 | vec3(0.0, 0.0, 1.0), 37 | vec3(0.0, 0.0, 0.5), 38 | }; 39 | 40 | int rgb_to_int(vec3 c) { 41 | return 0xFF000000 42 | | int(255.0*c.x) 43 | | int(255.0*c.y) << 8 44 | | int(255.0*c.z) << 16; 45 | } 46 | 47 | vec3 coloring(vec2 z, uint it) { 48 | if (it >= MAX_IT) { 49 | return vec3(0.0); 50 | } 51 | 52 | // Smoothing 53 | float logz = log(z.x*z.x + z.y*z.y) / 2; 54 | float nu = log(logz / log(2)) / log(2); 55 | float smooth_iter = it + 1.0 - nu; 56 | 57 | // Index in palette 58 | float index = 3 * log(smooth_iter) * PALETTE_SIZE / log(MAX_IT); 59 | 60 | // Linear interpolation 61 | vec3 c1 = palette[int(index) % PALETTE_SIZE]; 62 | vec3 c2 = palette[(int(index) + 1 + mouse_button) % PALETTE_SIZE]; 63 | 64 | vec3 col = mix(c1, c2, fract(index)); 65 | return col; 66 | } 67 | 68 | vec3 fractal(vec2 z, vec2 c) { 69 | 70 | //return vec3(1,0,1); // Test 71 | 72 | uint it; 73 | for (it = 0; it < MAX_IT && z.x*z.x + z.y*z.y < RADIUS*RADIUS; it++) { 74 | // z := z*z + c (a+bi) * (a+bi) = a*a - b*b + 2abi 75 | z = vec2(z.x*z.x - z.y*z.y + c.x, 2*z.x*z.y + c.y); 76 | } 77 | return coloring(z, it); 78 | } 79 | 80 | vec2 screen_to_world(vec2 screen) { 81 | vec2 wsize = vec2(WSX, WSY); 82 | vec2 center = vec2(u_pos_x, u_pos_y); 83 | return center + (screen / wsize - 0.5) * 4.0 / u_zoom; 84 | } 85 | 86 | void main() { 87 | uint p = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * WSX; 88 | 89 | // Julia 90 | vec2 wsize = vec2(WSX, WSY); 91 | vec2 z = (gl_GlobalInvocationID.xy / wsize - 0.5) * 4.0; 92 | vec2 c = screen_to_world(vec2(mousex, mousey)); 93 | vec3 color = fractal(z, c); 94 | data_1[p] = rgb_to_int(color); 95 | 96 | // Cursor 97 | if ((mousex >= 0 && gl_GlobalInvocationID.x == mousex) 98 | || (mousey >= 0 && gl_GlobalInvocationID.y == mousey)) 99 | { 100 | data_0[p] = rgb_to_int(vec3(1.0, 0.0, 0.0)); 101 | } 102 | else { // Mandelbrot 103 | vec2 z = screen_to_world(gl_GlobalInvocationID.xy); 104 | vec2 c = z; 105 | vec3 color = fractal(z, c); 106 | data_0[p] = rgb_to_int(color); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /examples/mandelbrot/example_mandelbrot.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | # 3 | #var css : Node 4 | # 5 | #var zoom : float = 1.0 6 | #var pos : Vector2 = Vector2(-0.75, 0) 7 | # 8 | #var grab : Vector2 9 | #var grabbing : bool = false 10 | # 11 | #func screen_to_world(screen : Vector2): 12 | #var pos_data0 : Vector2 = css.screen_to_data0(screen) 13 | #var wsize : Vector2 = Vector2(css.WSX, css.WSY); 14 | #return pos + (pos_data0 / wsize - Vector2(0.5, 0.5)) * 4.0 / zoom; 15 | # 16 | #func _ready(): 17 | #css = get_node("ComputeShaderStudio2D") 18 | #css.uniform_user_data.resize(4*3) 19 | # 20 | #func _process(_delta): 21 | #css.uniform_user_data.encode_float(0, zoom) 22 | #css.uniform_user_data.encode_float(4, pos.x) 23 | #css.uniform_user_data.encode_float(8, pos.y) 24 | # 25 | #func _input(event): 26 | #if event is InputEventMouseButton: 27 | #if event.button_index == MOUSE_BUTTON_WHEEL_UP : 28 | #zoom *= 1.1 29 | #elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN : 30 | #zoom /= 1.1 31 | #elif event.button_index == MOUSE_BUTTON_LEFT : 32 | #if event.pressed : # Start panning 33 | #grab = screen_to_world(event.position) 34 | #grabbing = true 35 | #else : # Stop panning 36 | #grabbing = false 37 | # 38 | #elif event is InputEventMouse and grabbing : # Panning 39 | #var delta : Vector2 = screen_to_world(event.position) - grab 40 | #pos -= delta 41 | -------------------------------------------------------------------------------- /examples/mandelbrot/example_mandelbrot.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bib00xk6p8oo4 2 | -------------------------------------------------------------------------------- /examples/mandelbrot/example_mandelbrot.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://d1hhshnvrt86e"] 2 | 3 | [ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_t5cx1"] 4 | [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_dyrad"] 5 | 6 | [node name="example_mandelbrot" type="Node2D"] 7 | 8 | [node name="ComputeShaderStudio2D" type="Node" parent="." node_paths=PackedStringArray("data")] 9 | script = ExtResource("1_t5cx1") 10 | print_step = true 11 | print_passes = true 12 | print_generated_code = true 13 | WSX = 512 14 | WSY = 512 15 | glsl_file = "res://examples/mandelbrot/example_mandelbrot.cpp" 16 | GLSL_code = "" 17 | data = [NodePath("../mandelbrot"), NodePath("../julia")] 18 | 19 | [node name="mandelbrot" type="Sprite2D" parent="."] 20 | position = Vector2(310.856, 343.75) 21 | scale = Vector2(3.904, 4) 22 | texture = ExtResource("2_dyrad") 23 | 24 | [node name="julia" type="Sprite2D" parent="."] 25 | position = Vector2(845.356, 342) 26 | scale = Vector2(3.946, 4) 27 | texture = ExtResource("2_dyrad") 28 | 29 | [node name="Label" type="Label" parent="."] 30 | offset_left = 230.0 31 | offset_top = 36.0 32 | offset_right = 350.0 33 | offset_bottom = 59.0 34 | text = "Mandelbrot Set" 35 | 36 | [node name="Label3" type="Label" parent="."] 37 | offset_left = 388.0 38 | offset_top = 611.0 39 | offset_right = 751.0 40 | offset_bottom = 634.0 41 | text = "Click the mouse button(s) to change the colors." 42 | 43 | [node name="Label2" type="Label" parent="."] 44 | offset_left = 812.0 45 | offset_top = 36.0 46 | offset_right = 932.0 47 | offset_bottom = 59.0 48 | text = "Julia Set" 49 | -------------------------------------------------------------------------------- /examples/misc/example_laser.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cdtqwwdxs783m"] 2 | 3 | [ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_iu3i1"] 4 | [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_o5bv1"] 5 | 6 | [node name="exeample_psycho_seizure" type="Node2D"] 7 | 8 | [node name="ComputeShaderStudio2D" type="Node" parent="." node_paths=PackedStringArray("data")] 9 | script = ExtResource("1_iu3i1") 10 | WSX = 2000 11 | WSY = 2000 12 | GLSL_code = "#define red 0xFF0000FF 13 | #define blue 0x000088FF 14 | #define purple 0x880088FF 15 | #define black 0xFF000000 16 | 17 | 18 | 19 | float perlinNoise(vec2 p) { 20 | return sin(p.x * 10.0) * sin(p.y * 10.0); 21 | } 22 | 23 | /** 24 | * Calculates the variation in shape based on the given angle, radius, and time. 25 | * Uses Perlin noise to generate random values. 26 | * 27 | * @param angle The angle in radians. 28 | * @param radius The radius of the shape. 29 | * @param time The current time. 30 | * @return The variation in shape. 31 | */ 32 | float shapeVariation(float angle, float radius, float time) { 33 | float n = perlinNoise(vec2(cos(angle), sin(angle)) * time * 0.2); 34 | float n2 = perlinNoise(vec2(sin(angle * 2.0), cos(angle * 2.0)) * time * 0.1); 35 | return radius * (1.0 + 0.3 * n + 0.1 * n2); 36 | } 37 | 38 | 39 | /* Note that if you play with different WSX and WSY you might have nice results. 40 | as a general rule try to play with every parameter to see the effect, it's quite fun! 41 | */ 42 | void main() { 43 | uint x = gl_GlobalInvocationID.x; 44 | uint y = gl_GlobalInvocationID.y; 45 | uint p = x + y * WSX; 46 | 47 | float time = float(step); 48 | float angle, radius; 49 | 50 | // Center of the matrix 51 | float centerX = float(WSX) / 2.0; 52 | float centerY = float(WSY) / 2.0; 53 | 54 | //Converts the coordinates to polar coordinates. 55 | float dx = float(x) - centerX; 56 | float dy = float(y) - centerY; 57 | angle = atan(dy, dx); 58 | radius = sqrt(dx * dx + dy * dy); 59 | 60 | //create a dynamic shape 61 | float variableRadius = shapeVariation(angle, radius, time); 62 | 63 | 64 | if (radius <= variableRadius) { 65 | data_0[p] = red; 66 | /* if you want to blend colors use uncomment 67 | float blend = (sin(time) + 1.0) / 2.0; 68 | int(mix(float(blue), float(purple), blend)) 69 | */ 70 | } else { 71 | //if you want to update the background color (here it's black) 72 | data_0[p] = black;; 73 | } 74 | }" 75 | data = [NodePath("../Icon")] 76 | 77 | [node name="Icon" type="Sprite2D" parent="."] 78 | position = Vector2(580, 333) 79 | scale = Vector2(4.5625, 3.83594) 80 | texture = ExtResource("2_o5bv1") 81 | -------------------------------------------------------------------------------- /examples/misc/example_lenia_test.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://cljj33x767odp"] 2 | 3 | [ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_wci4f"] 4 | [ext_resource type="Script" uid="uid://jw3o2qu3a0pl" path="res://examples/LabelStepPass.gd" id="2_ndkd3"] 5 | [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="3_orsvs"] 6 | 7 | [node name="CompShadStudioEx3" type="Node2D"] 8 | 9 | [node name="ComputeShaderStudio2D2" type="Node" parent="." node_paths=PackedStringArray("data")] 10 | script = ExtResource("1_wci4f") 11 | pause = true 12 | nb_passes = 3 13 | glsl_file = "res://examples/lenia.cpp" 14 | GLSL_code = "" 15 | data = [NodePath("../Matrix1"), NodePath("../Matrix2"), NodePath("../Matrix3")] 16 | 17 | [node name="LabelTitle" type="Label" parent="."] 18 | offset_left = 56.0 19 | offset_top = 3.0 20 | offset_right = 655.0 21 | offset_bottom = 38.0 22 | scale = Vector2(1.72, 1.72) 23 | text = "Compute Shader Studio - LENIA Example (in progress...)" 24 | horizontal_alignment = 1 25 | vertical_alignment = 1 26 | 27 | [node name="LabelStepPass" type="Label" parent="."] 28 | offset_left = 56.0 29 | offset_top = 56.0 30 | offset_right = 329.0 31 | offset_bottom = 91.0 32 | scale = Vector2(1.72, 1.72) 33 | text = "step=0 current_pass=0" 34 | horizontal_alignment = 1 35 | vertical_alignment = 1 36 | script = ExtResource("2_ndkd3") 37 | 38 | [node name="Matrix1" type="Sprite2D" parent="."] 39 | position = Vector2(407, 247) 40 | scale = Vector2(2, 2) 41 | texture = ExtResource("3_orsvs") 42 | 43 | [node name="Matrix2" type="Sprite2D" parent="."] 44 | position = Vector2(406, 511) 45 | scale = Vector2(2, 2) 46 | texture = ExtResource("3_orsvs") 47 | 48 | [node name="Matrix3" type="Sprite2D" parent="."] 49 | position = Vector2(859, 369) 50 | scale = Vector2(4.26563, 4.09375) 51 | texture = ExtResource("3_orsvs") 52 | 53 | [node name="ButtonStep" type="Button" parent="."] 54 | offset_left = 27.0 55 | offset_top = 119.0 56 | offset_right = 118.0 57 | offset_bottom = 150.0 58 | scale = Vector2(2.4, 2.4) 59 | text = "Step" 60 | 61 | [node name="ButtonPlay" type="Button" parent="."] 62 | offset_left = 30.0 63 | offset_top = 210.0 64 | offset_right = 121.0 65 | offset_bottom = 241.0 66 | scale = Vector2(2.4, 2.4) 67 | text = "Play" 68 | 69 | [connection signal="pressed" from="ButtonStep" to="ComputeShaderStudio2D2" method="_on_button_step"] 70 | [connection signal="pressed" from="ButtonPlay" to="ComputeShaderStudio2D2" method="_on_button_play"] 71 | -------------------------------------------------------------------------------- /examples/simple_circle/simple_circle.cpp: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | uint x = gl_GlobalInvocationID.x; 4 | uint y = gl_GlobalInvocationID.y; 5 | uint p = x + y*WSX; 6 | 7 | float dx = float(mousex) - float(x) ; 8 | float dy = float(mousey) - float(y) ; 9 | float dist = sqrt( dx*dx + dy*dy ); 10 | 11 | if (dist < 10) { 12 | if(mouse_button == 0) // No mouse button 13 | data_0[p] = 0xFF00FF00; // Green circle 14 | else if(mouse_button == 1) // Left mouse button 15 | data_0[p] = 0xFFFFFF00; // Cyan circle 16 | else if(mouse_button == 2) // Right mouse button 17 | data_0[p] = 0xFF00FFFF; // Yellow circle 18 | else if(mouse_button == 3) // Both mouse button 19 | data_0[p] = 0xFFFFFFFF; // White circle 20 | else if(mouse_button == 4) // Middle mouse button 21 | data_0[p] = 0xFF000000; // Black circle 22 | } else { 23 | data_0[p] = int(0xFF0000FF - y) ; // Background 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /examples/simple_circle/simple_circle.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://c63rtjh0eurgm"] 2 | 3 | [ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_rlapi"] 4 | [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_y7slp"] 5 | 6 | [node name="SimpleCircle" type="Node2D"] 7 | 8 | [node name="ComputeShaderStudio2D" type="Node" parent="." node_paths=PackedStringArray("data")] 9 | script = ExtResource("1_rlapi") 10 | WSX = 256 11 | glsl_file = "res://examples/simple_circle/simple_circle.cpp" 12 | data = [NodePath("../Icon")] 13 | metadata/_custom_type_script = "uid://c8esqdv0y26yp" 14 | 15 | [node name="Icon" type="Sprite2D" parent="."] 16 | position = Vector2(622, 407) 17 | scale = Vector2(5.89063, 3.52344) 18 | texture = ExtResource("2_y7slp") 19 | 20 | [node name="Label" type="Label" parent="."] 21 | offset_left = 278.0 22 | offset_top = -1.0 23 | offset_right = 679.0 24 | offset_bottom = 101.0 25 | scale = Vector2(1.72, 1.72) 26 | text = "Compute Shader Studio - Simple Circle 27 | - move the mouse to move the circle 28 | - click the mouse buttons to change the circle color 29 | (left, right, both, middle mouse button)" 30 | horizontal_alignment = 1 31 | vertical_alignment = 1 32 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="compute_shader_studio" 14 | run/main_scene="res://examples/example_1.tscn" 15 | config/features=PackedStringArray("4.4", "Forward Plus") 16 | config/icon="res://addons/compute_shader_studio/icon.png" 17 | 18 | [editor_plugins] 19 | 20 | enabled=PackedStringArray("res://addons/compute_shader_studio/plugin.cfg") 21 | -------------------------------------------------------------------------------- /screenshots/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pascal-ballet/ComputeShaderStudio/7170f7ce27918f2836ec7eda13e52626e94d3140/screenshots/.DS_Store -------------------------------------------------------------------------------- /screenshots/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pascal-ballet/ComputeShaderStudio/7170f7ce27918f2836ec7eda13e52626e94d3140/screenshots/.gdignore -------------------------------------------------------------------------------- /screenshots/compute_shader_studio_ex2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pascal-ballet/ComputeShaderStudio/7170f7ce27918f2836ec7eda13e52626e94d3140/screenshots/compute_shader_studio_ex2.png -------------------------------------------------------------------------------- /screenshots/compute_shader_studio_ext_glsl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pascal-ballet/ComputeShaderStudio/7170f7ce27918f2836ec7eda13e52626e94d3140/screenshots/compute_shader_studio_ext_glsl.png -------------------------------------------------------------------------------- /screenshots/compute_shader_studio_headline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pascal-ballet/ComputeShaderStudio/7170f7ce27918f2836ec7eda13e52626e94d3140/screenshots/compute_shader_studio_headline.png -------------------------------------------------------------------------------- /screenshots/compute_shader_studio_mandelbrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pascal-ballet/ComputeShaderStudio/7170f7ce27918f2836ec7eda13e52626e94d3140/screenshots/compute_shader_studio_mandelbrot.png --------------------------------------------------------------------------------