├── 2dgi_#1 ├── icon.png ├── assets │ ├── 1px.png │ ├── circle.png │ ├── 1px.png.import │ └── circle.png.import ├── .import │ ├── 1px.png-894ec182c48b4f590b69cc448f917bbd.md5 │ ├── 1px.png-8e5c138039fb15d6db85f5ee737ca70c.md5 │ ├── circle.png-10953cad44a8947fbdd4128a631e9e52.md5 │ ├── circle.png-6efbe600b7e2418cd5091089237d13c1.md5 │ ├── icon.png-487276ed1e3a0c39cad0279d744ee560.md5 │ ├── noise.png-a5cb495e1c9f6281c22a3b0a0f68704b.md5 │ ├── noise.png-c29f7e52963a096aeaf0c1efd1a79456.md5 │ ├── 1px.png-894ec182c48b4f590b69cc448f917bbd.stex │ ├── 1px.png-8e5c138039fb15d6db85f5ee737ca70c.stex │ ├── circle.png-10953cad44a8947fbdd4128a631e9e52.stex │ ├── circle.png-6efbe600b7e2418cd5091089237d13c1.stex │ ├── icon.png-487276ed1e3a0c39cad0279d744ee560.stex │ └── noise.png-a5cb495e1c9f6281c22a3b0a0f68704b.stex ├── shaders │ ├── Final.shader │ ├── VoronoiSeed.shader │ ├── JumpFloodPass.shader │ ├── DistanceField.shader │ └── GI.shader ├── icon.png.import ├── project.godot ├── MyFirstLightingEngine.gd └── MyFirstLightingEngine.tscn ├── images └── 2dgi_#1.png ├── README.md └── LICENSE.md /2dgi_#1/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelbigos/tutorial_projects/HEAD/2dgi_#1/icon.png -------------------------------------------------------------------------------- /images/2dgi_#1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelbigos/tutorial_projects/HEAD/images/2dgi_#1.png -------------------------------------------------------------------------------- /2dgi_#1/assets/1px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelbigos/tutorial_projects/HEAD/2dgi_#1/assets/1px.png -------------------------------------------------------------------------------- /2dgi_#1/assets/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelbigos/tutorial_projects/HEAD/2dgi_#1/assets/circle.png -------------------------------------------------------------------------------- /2dgi_#1/.import/1px.png-894ec182c48b4f590b69cc448f917bbd.md5: -------------------------------------------------------------------------------- 1 | source_md5="131e6b9c0c0bcff4596ec25596a7e32f" 2 | dest_md5="5767078b086ac875e219ef73dec09e10" 3 | 4 | -------------------------------------------------------------------------------- /2dgi_#1/.import/1px.png-8e5c138039fb15d6db85f5ee737ca70c.md5: -------------------------------------------------------------------------------- 1 | source_md5="131e6b9c0c0bcff4596ec25596a7e32f" 2 | dest_md5="5767078b086ac875e219ef73dec09e10" 3 | 4 | -------------------------------------------------------------------------------- /2dgi_#1/.import/circle.png-10953cad44a8947fbdd4128a631e9e52.md5: -------------------------------------------------------------------------------- 1 | source_md5="5d088a59cb8c7e819a129682a319d22c" 2 | dest_md5="40164b4211da75908ee96eb9253071ea" 3 | 4 | -------------------------------------------------------------------------------- /2dgi_#1/.import/circle.png-6efbe600b7e2418cd5091089237d13c1.md5: -------------------------------------------------------------------------------- 1 | source_md5="5d088a59cb8c7e819a129682a319d22c" 2 | dest_md5="40164b4211da75908ee96eb9253071ea" 3 | 4 | -------------------------------------------------------------------------------- /2dgi_#1/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5: -------------------------------------------------------------------------------- 1 | source_md5="f6ac8aaff9af5ddd89bf3e719ba173df" 2 | dest_md5="f8eedab728649174ceddc01995f3033c" 3 | 4 | -------------------------------------------------------------------------------- /2dgi_#1/.import/noise.png-a5cb495e1c9f6281c22a3b0a0f68704b.md5: -------------------------------------------------------------------------------- 1 | source_md5="166b42d4d4364a25b3310290005b6983" 2 | dest_md5="f0e33c4a1f14e3ba659dcd7765e6af78" 3 | 4 | -------------------------------------------------------------------------------- /2dgi_#1/.import/noise.png-c29f7e52963a096aeaf0c1efd1a79456.md5: -------------------------------------------------------------------------------- 1 | source_md5="166b42d4d4364a25b3310290005b6983" 2 | dest_md5="f0e33c4a1f14e3ba659dcd7765e6af78" 3 | 4 | -------------------------------------------------------------------------------- /2dgi_#1/.import/1px.png-894ec182c48b4f590b69cc448f917bbd.stex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelbigos/tutorial_projects/HEAD/2dgi_#1/.import/1px.png-894ec182c48b4f590b69cc448f917bbd.stex -------------------------------------------------------------------------------- /2dgi_#1/.import/1px.png-8e5c138039fb15d6db85f5ee737ca70c.stex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelbigos/tutorial_projects/HEAD/2dgi_#1/.import/1px.png-8e5c138039fb15d6db85f5ee737ca70c.stex -------------------------------------------------------------------------------- /2dgi_#1/.import/circle.png-10953cad44a8947fbdd4128a631e9e52.stex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelbigos/tutorial_projects/HEAD/2dgi_#1/.import/circle.png-10953cad44a8947fbdd4128a631e9e52.stex -------------------------------------------------------------------------------- /2dgi_#1/.import/circle.png-6efbe600b7e2418cd5091089237d13c1.stex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelbigos/tutorial_projects/HEAD/2dgi_#1/.import/circle.png-6efbe600b7e2418cd5091089237d13c1.stex -------------------------------------------------------------------------------- /2dgi_#1/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelbigos/tutorial_projects/HEAD/2dgi_#1/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex -------------------------------------------------------------------------------- /2dgi_#1/.import/noise.png-a5cb495e1c9f6281c22a3b0a0f68704b.stex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelbigos/tutorial_projects/HEAD/2dgi_#1/.import/noise.png-a5cb495e1c9f6281c22a3b0a0f68704b.stex -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tutorial Projects 2 | 3 | This is where I'll upload any projects and code snippets to go along with blog posts at https://samuelbigos.github.io/. 4 | 5 | --- 6 | 7 | ## 2DGI #1 - Global Illumination in Godot 8 | 9 | ![Preview Image](images/2dgi_%231.png) 10 | 11 | [-- Blog post here --](https://samuelbigos.github.io/posts/2dgi1-2d-global-illumination-in-godot.html) 12 | 13 | --- 14 | -------------------------------------------------------------------------------- /2dgi_#1/shaders/Final.shader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | 3 | uniform sampler2D u_GI_texture; 4 | 5 | vec3 lin_to_srgb(vec4 color) 6 | { 7 | vec3 x = color.rgb * 12.92; 8 | vec3 y = 1.055 * pow(clamp(color.rgb, 0.0, 1.0), vec3(0.4166667)) - 0.055; 9 | vec3 clr = color.rgb; 10 | clr.r = (color.r < 0.0031308) ? x.r : y.r; 11 | clr.g = (color.g < 0.0031308) ? x.g : y.g; 12 | clr.b = (color.b < 0.0031308) ? x.b : y.b; 13 | return clr.rgb; 14 | } 15 | 16 | void fragment() 17 | { 18 | vec4 GI = texture(u_GI_texture, UV); 19 | COLOR = vec4(lin_to_srgb(GI), 1.0); 20 | } -------------------------------------------------------------------------------- /2dgi_#1/shaders/VoronoiSeed.shader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | 3 | uniform sampler2D u_input_tex; 4 | 5 | void fragment() 6 | { 7 | // translate uvs from rectangular input texture to square voronoi texture. 8 | ivec2 tex_size = textureSize(u_input_tex, 0); 9 | vec2 uv = UV; 10 | if(tex_size.x > tex_size.y) 11 | uv.y = ((uv.y - 0.5) * (float(tex_size.x) / float(tex_size.y))) + 0.5; 12 | else 13 | uv.x = ((uv.x - 0.5) * (float(tex_size.y) / float(tex_size.x))) + 0.5; 14 | 15 | // for the voronoi seed texture we just store the UV of the pixel if the pixel is part 16 | // of an object (emissive or occluding), or black otherwise. 17 | vec4 scene_col = texture(u_input_tex, uv); 18 | COLOR = vec4(UV.x * scene_col.a, UV.y * scene_col.a, 0.0, 1.0); 19 | } -------------------------------------------------------------------------------- /2dgi_#1/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /2dgi_#1/assets/1px.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/1px.png-8e5c138039fb15d6db85f5ee737ca70c.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/1px.png" 13 | dest_files=[ "res://.import/1px.png-8e5c138039fb15d6db85f5ee737ca70c.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /2dgi_#1/assets/circle.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/circle.png-6efbe600b7e2418cd5091089237d13c1.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/circle.png" 13 | dest_files=[ "res://.import/circle.png-6efbe600b7e2418cd5091089237d13c1.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /2dgi_#1/shaders/JumpFloodPass.shader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | 3 | uniform sampler2D u_input_tex; 4 | uniform float u_offset = 0.0; 5 | uniform float u_level = 0.0; 6 | uniform float u_max_steps = 0.0; 7 | 8 | void fragment() 9 | { 10 | float closest_dist = 9999999.9; 11 | vec2 closest_pos = vec2(0.0); 12 | 13 | // insert jump flooding algorithm here. 14 | for(float x = -1.0; x <= 1.0; x += 1.0) 15 | { 16 | for(float y = -1.0; y <= 1.0; y += 1.0) 17 | { 18 | vec2 voffset = UV; 19 | voffset += vec2(x, y) * SCREEN_PIXEL_SIZE * u_offset; 20 | 21 | vec2 pos = texture(u_input_tex, voffset).xy; 22 | float dist = distance(pos.xy, UV.xy); 23 | 24 | if(pos.x != 0.0 && pos.y != 0.0 && dist < closest_dist) 25 | { 26 | closest_dist = dist; 27 | closest_pos = pos; 28 | } 29 | } 30 | } 31 | COLOR = vec4(closest_pos, 0.0, 1.0); 32 | } -------------------------------------------------------------------------------- /2dgi_#1/shaders/DistanceField.shader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | 3 | uniform sampler2D u_input_tex; 4 | uniform float u_dist_mod = 1.0; 5 | 6 | void fragment() 7 | { 8 | // translate uvs from the square voronoi buffer back to viewport size. 9 | vec2 uv = UV; 10 | if(SCREEN_PIXEL_SIZE.x < SCREEN_PIXEL_SIZE.y) 11 | uv.y = ((uv.y - 0.5) * (SCREEN_PIXEL_SIZE.x/ SCREEN_PIXEL_SIZE.y)) + 0.5; 12 | else 13 | uv.x = ((uv.x - 0.5) * (SCREEN_PIXEL_SIZE.y/ SCREEN_PIXEL_SIZE.x)) + 0.5; 14 | 15 | // input is the voronoi output which stores in each pixel the UVs of the closest surface. 16 | // here we simply take that value, calculate the distance between the closest surface and this 17 | // pixel, and return that distance. 18 | vec4 tex = texture(u_input_tex, uv); 19 | float dist = distance(tex.xy, uv); 20 | float mapped = clamp(dist * u_dist_mod, 0.0, 1.0); 21 | COLOR = vec4(vec3(mapped), 1.0); 22 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sam Bigos 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 | -------------------------------------------------------------------------------- /2dgi_#1/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=4 10 | 11 | _global_script_classes=[ ] 12 | _global_script_class_icons={ 13 | 14 | } 15 | 16 | [application] 17 | 18 | config/name="GITutorial" 19 | run/main_scene="res://MyFirstLightingEngine.tscn" 20 | config/icon="res://icon.png" 21 | 22 | [display] 23 | 24 | window/size/width=360 25 | window/size/height=180 26 | window/size/test_width=720 27 | window/size/test_height=360 28 | window/stretch/mode="viewport" 29 | 30 | [importer_defaults] 31 | 32 | texture={ 33 | "compress/bptc_ldr": 0, 34 | "compress/hdr_mode": 0, 35 | "compress/lossy_quality": 0.7, 36 | "compress/mode": 0, 37 | "compress/normal_map": 0, 38 | "detect_3d": true, 39 | "flags/anisotropic": false, 40 | "flags/filter": false, 41 | "flags/mipmaps": false, 42 | "flags/repeat": 0, 43 | "flags/srgb": 2, 44 | "process/HDR_as_SRGB": false, 45 | "process/fix_alpha_border": true, 46 | "process/invert_color": false, 47 | "process/premult_alpha": false, 48 | "size_limit": 0, 49 | "stream": false, 50 | "svg/scale": 1.0 51 | } 52 | -------------------------------------------------------------------------------- /2dgi_#1/shaders/GI.shader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | 3 | // constants 4 | uniform float PI = 3.141596; 5 | 6 | // uniforms 7 | uniform int u_rays_per_pixel = 32; 8 | uniform sampler2D u_distance_data; 9 | uniform sampler2D u_scene_data; 10 | uniform float u_emission_multi = 1.0; 11 | uniform int u_max_raymarch_steps = 64; 12 | uniform float u_dist_mod = 1.0; 13 | 14 | void get_surface(vec2 uv, out float emissive, out vec3 colour) 15 | { 16 | vec4 emissive_data = texture(u_scene_data, uv); 17 | emissive = max(emissive_data.r, max(emissive_data.g, emissive_data.b)) * u_emission_multi; 18 | colour = emissive_data.rgb; 19 | } 20 | 21 | bool raymarch(vec2 origin, vec2 dir, float aspect, out vec2 hit_pos) 22 | { 23 | float current_dist = 0.0; 24 | for(int i = 0; i < u_max_raymarch_steps; i++) 25 | { 26 | vec2 sample_point = origin + dir * current_dist; 27 | sample_point.x /= aspect; // when we sample the distance field we need to convert back to uv space. 28 | 29 | // early exit if we hit the edge of the screen. 30 | if(sample_point.x > 1.0 || sample_point.x < 0.0 || sample_point.y > 1.0 || sample_point.y < 0.0) 31 | return false; 32 | 33 | float dist_to_surface = texture(u_distance_data, sample_point).r / u_dist_mod; 34 | 35 | // we've hit a surface if distance field returns 0 or close to 0 (due to our distance field using a 16-bit float 36 | // the precision isn't enough to just check against 0). 37 | if(dist_to_surface < 0.001f) 38 | { 39 | hit_pos = sample_point; 40 | return true; 41 | } 42 | 43 | // if we don't hit a surface, continue marching along the ray. 44 | current_dist += dist_to_surface; 45 | } 46 | return false; 47 | } 48 | 49 | float random (vec2 st) 50 | { 51 | return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123); 52 | } 53 | 54 | void fragment() 55 | { 56 | float pixel_emis = 0.0; 57 | vec3 pixel_col = vec3(0.0); 58 | 59 | // convert from uv aspect to world aspect. 60 | vec2 uv = UV; 61 | float aspect = SCREEN_PIXEL_SIZE.y / SCREEN_PIXEL_SIZE.x; 62 | uv.x *= aspect; 63 | 64 | float rand2pi = random(UV * vec2(TIME, -TIME)) * 2.0 * PI; 65 | float golden_angle = PI * 0.7639320225; // magic number that gives us a good ray distribution. 66 | 67 | // cast our rays. 68 | for(int i = 0; i < u_rays_per_pixel; i++) 69 | { 70 | // get our ray dir by taking the random angle and adding golden_angle * ray number. 71 | float cur_angle = rand2pi + golden_angle * float(i); 72 | vec2 ray_dir = normalize(vec2(cos(cur_angle), sin(cur_angle))); 73 | 74 | vec2 ray_origin = uv; 75 | 76 | vec2 hit_pos; 77 | bool hit = raymarch(ray_origin, ray_dir, aspect, hit_pos); 78 | if(hit) 79 | { 80 | float mat_emissive; 81 | vec3 mat_colour; 82 | get_surface(hit_pos, mat_emissive, mat_colour); 83 | 84 | pixel_emis += mat_emissive; 85 | pixel_col += mat_colour; 86 | } 87 | } 88 | 89 | pixel_col /= pixel_emis; 90 | pixel_emis /= float(u_rays_per_pixel); 91 | 92 | COLOR = vec4(pixel_emis * pixel_col, 1.0); 93 | } -------------------------------------------------------------------------------- /2dgi_#1/MyFirstLightingEngine.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | var _voronoi_passes = [] 4 | 5 | func _ready(): 6 | # parent our emissive and occluding sprites to the EmittersAndOccluders viewport at runtime. 7 | var scene = $Scene 8 | remove_child(scene) 9 | $EmittersAndOccluders.add_child(scene) 10 | 11 | # setup our viewports and screen texture. 12 | # you can do this in the editor, but i prefer to do it in code since it's more visible and easier to update. 13 | $EmittersAndOccluders.transparent_bg = true 14 | $EmittersAndOccluders.render_target_update_mode = Viewport.UPDATE_ALWAYS 15 | $EmittersAndOccluders.render_target_v_flip = true 16 | $EmittersAndOccluders.size = get_viewport().size 17 | $Screen.rect_size = get_viewport().size 18 | 19 | # setup our voronoi seed render texture. 20 | $VoronoiSeed.render_target_update_mode = Viewport.UPDATE_ALWAYS 21 | $VoronoiSeed.render_target_v_flip = true 22 | var viewport_squared = Vector2(max(get_viewport().size.x, get_viewport().size.y), max(get_viewport().size.x, get_viewport().size.y)) 23 | $VoronoiSeed.size = viewport_squared 24 | $VoronoiSeed/Tex.rect_size = viewport_squared 25 | $VoronoiSeed/Tex.material.set_shader_param("u_input_tex", $EmittersAndOccluders.get_texture()) 26 | 27 | # setup our voronoi pass render texture. 28 | $JumpFloodPass.render_target_update_mode = Viewport.UPDATE_ALWAYS 29 | $JumpFloodPass.render_target_v_flip = true 30 | $JumpFloodPass.size = viewport_squared 31 | $JumpFloodPass/Tex.rect_size = viewport_squared 32 | 33 | # number of passes required is the log2 of the largest viewport dimension rounded up to the nearest power of 2. 34 | # i.e. 768x512 is log2(1024) == 10 35 | var passes = ceil(log(max(get_viewport().size.x, get_viewport().size.y)) / log(2.0)) 36 | 37 | # iterate through each pass and set up the required render pass objects. 38 | for i in range(0, passes): 39 | 40 | # offset for each pass is half the previous one, starting at half the square resolution rounded up to nearest power 2. 41 | # i.e. for 768x512 we round up to 1024x1024 and the offset for the first pass is 512x512, then 256x256, etc. 42 | var offset = pow(2, passes - i - 1) 43 | 44 | # on the first pass, use our existing render pass, on subsequent passes we duplicate the existing render pass. 45 | var render_pass 46 | if i == 0: 47 | render_pass = $JumpFloodPass 48 | else: 49 | render_pass = $JumpFloodPass.duplicate(0) 50 | add_child(render_pass) 51 | 52 | render_pass.get_child(0).material = render_pass.get_child(0).material.duplicate(0) 53 | _voronoi_passes.append(render_pass) 54 | 55 | # here we set the input texture for each pass, which is the previous pass, unless it's the first pass in which case it's 56 | # the seed texture. 57 | var input_texture = $VoronoiSeed.get_texture() 58 | if i > 0: 59 | input_texture = _voronoi_passes[i - 1].get_texture() 60 | 61 | # set size and shader uniforms for this pass. 62 | render_pass.get_child(0).material.set_shader_param("u_level", i) 63 | render_pass.get_child(0).material.set_shader_param("u_max_steps", passes) 64 | render_pass.get_child(0).material.set_shader_param("u_offset", offset) 65 | render_pass.get_child(0).material.set_shader_param("u_input_tex", input_texture) 66 | 67 | # setup our distance field render texture. 68 | $DistanceField.transparent_bg = true 69 | $DistanceField.render_target_update_mode = Viewport.UPDATE_ALWAYS 70 | $DistanceField.render_target_v_flip = true 71 | $DistanceField.size = get_viewport().size 72 | $DistanceField/Tex.rect_size = get_viewport().size 73 | $DistanceField/Tex.material.set_shader_param("u_input_tex", _voronoi_passes[_voronoi_passes.size() - 1].get_texture()) 74 | $DistanceField/Tex.material.set_shader_param("u_dist_mod", 1.0) 75 | 76 | # setup our distance field render texture. 77 | $GI.render_target_update_mode = Viewport.UPDATE_ALWAYS 78 | $GI.render_target_v_flip = true 79 | $GI.size = get_viewport().size 80 | $GI/Tex.rect_size = get_viewport().size 81 | $GI/Tex.material.set_shader_param("u_rays_per_pixel", 32) 82 | $GI/Tex.material.set_shader_param("u_distance_data", $DistanceField.get_texture()) 83 | $GI/Tex.material.set_shader_param("u_scene_data", $EmittersAndOccluders.get_texture()) 84 | $GI/Tex.material.set_shader_param("u_emission_multi", 1.0) 85 | $GI/Tex.material.set_shader_param("u_max_raymarch_steps", 128) 86 | -------------------------------------------------------------------------------- /2dgi_#1/MyFirstLightingEngine.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=15 format=2] 2 | 3 | [ext_resource path="res://MyFirstLightingEngine.gd" type="Script" id=1] 4 | [ext_resource path="res://assets/1px.png" type="Texture" id=2] 5 | [ext_resource path="res://assets/circle.png" type="Texture" id=3] 6 | [ext_resource path="res://shaders/Final.shader" type="Shader" id=4] 7 | [ext_resource path="res://shaders/GI.shader" type="Shader" id=5] 8 | [ext_resource path="res://shaders/VoronoiSeed.shader" type="Shader" id=6] 9 | [ext_resource path="res://shaders/JumpFloodPass.shader" type="Shader" id=7] 10 | [ext_resource path="res://shaders/DistanceField.shader" type="Shader" id=8] 11 | 12 | [sub_resource type="ViewportTexture" id=1] 13 | viewport_path = NodePath("GI") 14 | 15 | [sub_resource type="ShaderMaterial" id=2] 16 | resource_local_to_scene = true 17 | shader = ExtResource( 4 ) 18 | shader_param/u_GI_texture = SubResource( 1 ) 19 | 20 | [sub_resource type="ShaderMaterial" id=3] 21 | shader = ExtResource( 6 ) 22 | 23 | [sub_resource type="ShaderMaterial" id=4] 24 | shader = ExtResource( 7 ) 25 | shader_param/u_offset = 1.0 26 | shader_param/u_level = 0.0 27 | shader_param/u_max_steps = 0.0 28 | 29 | [sub_resource type="ShaderMaterial" id=5] 30 | shader = ExtResource( 8 ) 31 | shader_param/u_dist_mod = 100.0 32 | 33 | [sub_resource type="ShaderMaterial" id=6] 34 | shader = ExtResource( 5 ) 35 | shader_param/PI = 3.1416 36 | shader_param/u_rays_per_pixel = 32.0 37 | shader_param/u_emission_multi = 1.0 38 | shader_param/u_max_raymarch_steps = 32 39 | shader_param/u_dist_mod = 1.0 40 | 41 | [node name="MyFirstLightingEngine" type="Node2D"] 42 | script = ExtResource( 1 ) 43 | 44 | [node name="EmittersAndOccluders" type="Viewport" parent="."] 45 | transparent_bg = true 46 | render_target_v_flip = true 47 | render_target_clear_mode = 1 48 | render_target_update_mode = 3 49 | 50 | [node name="Scene" type="Node2D" parent="."] 51 | 52 | [node name="EmitterCyan" type="Sprite" parent="Scene"] 53 | modulate = Color( 0, 0.952941, 1, 1 ) 54 | position = Vector2( 58.93, 117.14 ) 55 | texture = ExtResource( 3 ) 56 | 57 | [node name="EmitterMagenta" type="Sprite" parent="Scene"] 58 | modulate = Color( 1, 0, 0.772549, 1 ) 59 | position = Vector2( 318.657, 146.788 ) 60 | texture = ExtResource( 3 ) 61 | 62 | [node name="EmitterOrange" type="Sprite" parent="Scene"] 63 | modulate = Color( 1, 0.560784, 0, 1 ) 64 | position = Vector2( 229.567, 31.741 ) 65 | texture = ExtResource( 3 ) 66 | 67 | [node name="Occluder1" type="Sprite" parent="Scene"] 68 | modulate = Color( 0, 0, 0, 1 ) 69 | position = Vector2( 182.639, 141.063 ) 70 | scale = Vector2( 170.706, 5.24263 ) 71 | texture = ExtResource( 2 ) 72 | 73 | [node name="Occluder2" type="Sprite" parent="Scene"] 74 | modulate = Color( 0, 0, 0, 1 ) 75 | position = Vector2( 198.903, 100.404 ) 76 | texture = ExtResource( 3 ) 77 | 78 | [node name="Occluder3" type="Sprite" parent="Scene"] 79 | modulate = Color( 0, 0, 0, 1 ) 80 | position = Vector2( 86.1192, 61.8668 ) 81 | rotation = -0.799009 82 | scale = Vector2( 12.3137, 115.551 ) 83 | texture = ExtResource( 2 ) 84 | 85 | [node name="Screen" type="TextureRect" parent="."] 86 | material = SubResource( 2 ) 87 | margin_right = 40.0 88 | margin_bottom = 40.0 89 | texture = ExtResource( 2 ) 90 | expand = true 91 | __meta__ = { 92 | "_edit_use_anchors_": false 93 | } 94 | 95 | [node name="VoronoiSeed" type="Viewport" parent="."] 96 | render_target_v_flip = true 97 | render_target_update_mode = 3 98 | 99 | [node name="Tex" type="TextureRect" parent="VoronoiSeed"] 100 | material = SubResource( 3 ) 101 | margin_right = 40.0 102 | margin_bottom = 40.0 103 | texture = ExtResource( 2 ) 104 | expand = true 105 | 106 | [node name="JumpFloodPass" type="Viewport" parent="."] 107 | 108 | [node name="Tex" type="TextureRect" parent="JumpFloodPass"] 109 | material = SubResource( 4 ) 110 | margin_right = 40.0 111 | margin_bottom = 40.0 112 | texture = ExtResource( 2 ) 113 | expand = true 114 | 115 | [node name="DistanceField" type="Viewport" parent="."] 116 | render_target_clear_mode = 1 117 | 118 | [node name="Tex" type="TextureRect" parent="DistanceField"] 119 | material = SubResource( 5 ) 120 | margin_right = 40.0 121 | margin_bottom = 40.0 122 | texture = ExtResource( 2 ) 123 | expand = true 124 | 125 | [node name="GI" type="Viewport" parent="."] 126 | 127 | [node name="Tex" type="TextureRect" parent="GI"] 128 | material = SubResource( 6 ) 129 | margin_right = 40.0 130 | margin_bottom = 40.0 131 | texture = ExtResource( 2 ) 132 | expand = true 133 | --------------------------------------------------------------------------------