diff --git a/Assets/blue_noise64.png b/Assets/blue_noise64.png new file mode 100644 index 00000000..af5263ff Binary files /dev/null and b/Assets/blue_noise64.png differ diff --git a/Shaders/ssdo_pass/ssdo_pass.frag.glsl b/Shaders/ssdo_pass/ssdo_pass.frag.glsl deleted file mode 100755 index 74dc9951..00000000 --- a/Shaders/ssdo_pass/ssdo_pass.frag.glsl +++ /dev/null @@ -1,152 +0,0 @@ -// http://kayru.org/articles/dssdo/ -#version 450 - -#ifdef GL_ES -precision mediump float; -#endif - -#include "../compiled.glsl" -#include "../std/gbuffer.glsl" - -uniform sampler2D gbufferD; -uniform sampler2D gbuffer0; -uniform sampler2D gbuffer1; -uniform sampler2D snoise; -uniform mat4 invVP; -uniform vec3 eye; - -const vec2 screenSize = vec2(800.0, 600.0); -const vec2 aspectRatio = vec2(min(1.0, screenSize.y / screenSize.x), min(1.0, screenSize.x / screenSize.y)); - -const int num_samples = 32; -const float g_occlusion_radius = 0.279710; -const float g_occlusion_max_distance = 0.639419; -const float fudge_factor_l0 = 2.0; -const float fudge_factor_l1 = 10.0; -const float sh2_weight_l0 = fudge_factor_l0 * 0.28209; //0.5*sqrt(1.0/pi); -const vec3 sh2_weight_l1 = vec3(fudge_factor_l1 * 0.48860); //0.5*sqrt(3.0/pi); -const vec4 sh2_weight = vec4(sh2_weight_l1, sh2_weight_l0) / num_samples; - -in vec2 texCoord; -out vec4 fragColor; - -vec4 doDO(vec3 point, vec3 noise, float radius, vec3 center_pos, float max_distance_inv, vec3 center_normal) { - vec2 textureOffset = reflect( point.xy, noise.xy ).xy * radius; - vec2 sample_tex = texCoord + textureOffset; - - // float depth = 1.0 - texture(gbuffer0, sample_tex).a; - float depth = texture(gbufferD, sample_tex).r * 2.0 - 1.0; - vec3 sample_pos = getPos2(invVP, depth, sample_tex); - - vec3 center_to_sample = sample_pos - center_pos; - float dist = length(center_to_sample); - vec3 center_to_sample_normalized = center_to_sample / dist; - float attenuation = 1.0 - clamp(dist * max_distance_inv, 0.0, 1.0); - float dp = dot(center_normal, center_to_sample_normalized); - - const float attenuation_angle_threshold = 0.1; - attenuation = attenuation*attenuation * step(attenuation_angle_threshold, dp); - - return attenuation * sh2_weight * vec4(center_to_sample_normalized, 1.0); -} - -void main() { - float depth = texture(gbufferD, texCoord).r * 2.0 - 1.0; - if (depth == 1.0) { - fragColor.rgb = vec3(1.0); - return; - } - - vec3 points[num_samples]; - points[0] = vec3(-0.134, 0.044, -0.825); - points[1] = vec3(0.045, -0.431, -0.529); - points[2] = vec3(-0.537, 0.195, -0.371); - points[3] = vec3(0.525, -0.397, 0.713); - points[4] = vec3(0.895, 0.302, 0.139); - points[5] = vec3(-0.613, -0.408, -0.141); - points[6] = vec3(0.307, 0.822, 0.169); - points[7] = vec3(-0.819, 0.037, -0.388); - points[8] = vec3(0.376, 0.009, 0.193); - points[9] = vec3(-0.006, -0.103, -0.035); - points[10] = vec3(0.098, 0.393, 0.019); - points[11] = vec3(0.542, -0.218, -0.593); - points[12] = vec3(0.526, -0.183, 0.424); - points[13] = vec3(-0.529, -0.178, 0.684); - points[14] = vec3(0.066, -0.657, -0.570); - points[15] = vec3(-0.214, 0.288, 0.188); - points[16] = vec3(-0.689, -0.222, -0.192); - points[17] = vec3(-0.008, -0.212, -0.721); - points[18] = vec3(0.053, -0.863, 0.054); - points[19] = vec3(0.639, -0.558, 0.289); - points[20] = vec3(-0.255, 0.958, 0.099); - points[21] = vec3(-0.488, 0.473, -0.381); - points[22] = vec3(-0.592, -0.332, 0.137); - points[23] = vec3(0.080, 0.756, -0.494); - points[24] = vec3(-0.638, 0.319, 0.686); - points[25] = vec3(-0.663, 0.230, -0.634); - points[26] = vec3(0.235, -0.547, 0.664); - points[27] = vec3(0.164, -0.710, 0.086); - points[28] = vec3(-0.009, 0.493, -0.038); - points[29] = vec3(-0.322, 0.147, -0.105); - points[30] = vec3(-0.554, -0.725, 0.289); - points[31] = vec3(0.534, 0.157, -0.250); - - vec2 enc = texture(gbuffer0, texCoord).rg; - vec3 n; - n.z = 1.0 - abs(enc.x) - abs(enc.y); - n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy); - n = normalize(n); - - vec3 currentPos = getPos2(invVP, depth, texCoord); - // float currentDistance = length(currentPos); - - vec2 noise_texture_size = vec2(8.0,8.0); - vec3 center_pos = currentPos; - vec3 eye_pos = eye; - float center_depth = distance(eye_pos, center_pos); - float radius = g_occlusion_radius / center_depth; - float max_distance_inv = 1.0 / g_occlusion_max_distance; - // float attenuation_angle_threshold = 0.1; - - vec3 noise = texture(snoise, (texCoord * screenSize) / noise_texture_size).xyz * 2.0 - 1.0; - vec3 center_normal = n; - - vec4 occlusion_sh2 = vec4(0.0); - - // for( int i=0; i 0.01) { + return vec4(0.0); + } + // } + return vec4(getProjectedCoord(hitCoord), 0.0, 1.0); +} + +vec4 rayCast(vec3 dir) { + dir *= ssrRayStep; + + // for (int i = 0; i < maxSteps; i++) { + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + // } + return vec4(0.0); +} + +void main() { + vec4 g0 = texture(gbuffer0, texCoord); + float roughness = unpackFloat(g0.b).y; + + if (roughness == 1.0) { + fragColor.rgb = vec3(0.0); + return; + } + + float d = texture(gbufferD, texCoord).r * 2.0 - 1.0; + if (d == 1.0) { + fragColor.rgb = vec3(0.0); + return; + } + + vec2 enc = g0.rg; + vec3 n; + n.z = 1.0 - abs(enc.x) - abs(enc.y); + n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy); + n = normalize(n); + + if (n.z <= 0.9) { // Only up facing surfaces for now + fragColor.rgb = vec3(0.0); + return; + } + + vec4 viewNormal = vec4(n, 1.0); + viewNormal = tiV * viewNormal; + vec3 viewPos = getPosView(viewRay, d); + + vec3 reflected = normalize(reflect((viewPos), normalize(viewNormal.xyz))); + hitCoord = viewPos.xyz; + + vec3 dir = reflected * max(ssrMinRayStep, -viewPos.z) * (1.0 - rand(texCoord) * ssrJitter * roughness); + vec4 coords = rayCast(dir); + + vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy); + float screenEdgeFactor = clamp(1.0 - (deltaCoords.x + deltaCoords.y), 0.0, 1.0); + + float reflectivity = 1.0 - roughness; + float intensity = pow(reflectivity, ssrFalloffExp) * + screenEdgeFactor * clamp(-reflected.z, 0.0, 1.0) * + clamp((ssrSearchDist - length(viewPos.xyz - hitCoord)) * (1.0 / ssrSearchDist), 0.0, 1.0) * coords.w; + + intensity = clamp(intensity, 0.0, 1.0); + + if (intensity == 0.0) { + fragColor.rgb = vec3(0.0); + return; + } + + vec3 reflCol = texture(tex, coords.xy).rgb; + reflCol = clamp(reflCol, 0.0, 1.0); + fragColor.rgb = reflCol * intensity * 0.5; // +} diff --git a/Shaders/ssgi_pass/ssgi_pass.json b/Shaders/ssgi_pass/ssgi_pass.json new file mode 100755 index 00000000..c729d172 --- /dev/null +++ b/Shaders/ssgi_pass/ssgi_pass.json @@ -0,0 +1,29 @@ +{ + "contexts": [ + { + "name": "ssgi_pass", + "depth_write": false, + "color_write_alpha": false, + "compare_mode": "always", + "cull_mode": "none", + "links": [ + { + "name": "P", + "link": "_projectionMatrix" + }, + { + "name": "tiV", + "link": "_transposeInverseViewMatrix" + }, + { + "name": "invP", + "link": "_inverseProjectionMatrix" + } + ], + "texture_params": [], + "vertex_shader": "ssgi_pass.vert.glsl", + "vertex_shader_path": "../include/pass_viewray2.vert.glsl", + "fragment_shader": "ssgi_pass.frag.glsl" + } + ] +} diff --git a/blender/arm/make_renderer.py b/blender/arm/make_renderer.py index d8793147..36f86444 100644 --- a/blender/arm/make_renderer.py +++ b/blender/arm/make_renderer.py @@ -534,7 +534,10 @@ def make_deferred(rpdat): relink('SSS', 'SSR') if not rpdat.rp_ssr: - relink('SSR', 'Draw Compositor') + relink('SSR', 'SSGI') + + if not rpdat.rp_ssgi: + relink('SSGI', 'Draw Compositor') if rpdat.rp_motionblur != 'None': last_node = nodes['Draw Compositor'].inputs[0].links[0].from_node diff --git a/blender/arm/make_renderpath.py b/blender/arm/make_renderpath.py index 2f8fec42..6ef11772 100755 --- a/blender/arm/make_renderpath.py +++ b/blender/arm/make_renderpath.py @@ -374,6 +374,11 @@ def make_ssr_pass(stages, node_group, node): make_quad_pass(stages, node_group, node, target_index=3, bind_target_indices=[2, 6], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_adaptive_pass/blur_adaptive_pass/blur_adaptive_pass_x') make_quad_pass(stages, node_group, node, target_index=1, bind_target_indices=[3, 6], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_adaptive_pass/blur_adaptive_pass/blur_adaptive_pass_y3_blend') +def make_ssgi_pass(stages, node_group, node): + make_quad_pass(stages, node_group, node, target_index=2, bind_target_indices=[4, 5, 6], bind_target_constants=['tex', 'gbufferD', 'gbuffer0'], shader_context='ssgi_pass/ssgi_pass/ssgi_pass') + make_quad_pass(stages, node_group, node, target_index=3, bind_target_indices=[2, 6], bind_target_constants=['tex', 'gbuffer0'], shader_context='ssgi_blur_pass/ssgi_blur_pass/ssgi_blur_pass_x') + make_quad_pass(stages, node_group, node, target_index=1, bind_target_indices=[3, 6], bind_target_constants=['tex', 'gbuffer0'], shader_context='ssgi_blur_pass/ssgi_blur_pass/ssgi_blur_pass_y_blend_add') + def make_bloom_pass(stages, node_group, node): make_quad_pass(stages, node_group, node, target_index=2, bind_target_indices=[4], bind_target_constants=['tex'], shader_context='bloom_pass/bloom_pass/bloom_pass') make_quad_pass(stages, node_group, node, target_index=3, bind_target_indices=[2], bind_target_constants=['tex'], shader_context='blur_gaus_pass/blur_gaus_pass/blur_gaus_pass_x') @@ -665,6 +670,9 @@ def buildNode(stages, node, node_group): elif node.bl_idname == 'SSRPassNodeType': make_ssr_pass(stages, node_group, node) append_stage = False + elif node.bl_idname == 'SSGIPassNodeType': + make_ssgi_pass(stages, node_group, node) + append_stage = False elif node.bl_idname == 'BloomPassNodeType': make_bloom_pass(stages, node_group, node) append_stage = False @@ -815,7 +823,7 @@ def traverse_renderpath(node, node_group, render_targets, depth_buffers): if node.inputs[1].is_linked: tnode = nodes.find_node_by_link(node_group, node, node.inputs[1]) parse_render_target(tnode, node_group, render_targets, depth_buffers) - elif node.bl_idname == 'SSRPassNodeType' or node.bl_idname == 'ApplySSAOPassNodeType' or node.bl_idname == 'BloomPassNodeType' or node.bl_idname == 'SMAAPassNodeType': + elif node.bl_idname == 'SSRPassNodeType' or node.bl_idname == 'SSGIPassNodeType' or node.bl_idname == 'ApplySSAOPassNodeType' or node.bl_idname == 'BloomPassNodeType' or node.bl_idname == 'SMAAPassNodeType': for i in range(1, 4): if node.inputs[i].is_linked: tnode = nodes.find_node_by_link(node_group, node, node.inputs[i]) diff --git a/blender/arm/nodes_renderpath.py b/blender/arm/nodes_renderpath.py index 2f4c25aa..cbf57f76 100755 --- a/blender/arm/nodes_renderpath.py +++ b/blender/arm/nodes_renderpath.py @@ -97,6 +97,23 @@ class SSRPassNode(Node, CGPipelineTreeNode): self.outputs.new('NodeSocketShader', "Stage") +class SSGIPassNode(Node, CGPipelineTreeNode): + '''Screen-space global illumination node''' + bl_idname = 'SSGIPassNodeType' + bl_label = 'SSGI' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketShader', "A") + self.inputs.new('NodeSocketShader', "B") + self.inputs.new('NodeSocketShader', "Color") + self.inputs.new('NodeSocketShader', "GBufferD") + self.inputs.new('NodeSocketShader', "GBuffer0") + + self.outputs.new('NodeSocketShader', "Stage") + class BloomPassNode(Node, CGPipelineTreeNode): '''Bloom node''' bl_idname = 'BloomPassNodeType' @@ -870,6 +887,7 @@ node_categories = [ NodeItem("SSAOReprojectPassNodeType"), NodeItem("ApplySSAOPassNodeType"), NodeItem("SSRPassNodeType"), + NodeItem("SSGIPassNodeType"), NodeItem("BloomPassNodeType"), NodeItem("MotionBlurPassNodeType"), NodeItem("MotionBlurVelocityPassNodeType"), @@ -937,6 +955,7 @@ def register(): bpy.utils.register_class(SSAOReprojectPassNode) bpy.utils.register_class(ApplySSAOPassNode) bpy.utils.register_class(SSRPassNode) + bpy.utils.register_class(SSGIPassNode) bpy.utils.register_class(BloomPassNode) bpy.utils.register_class(MotionBlurPassNode) bpy.utils.register_class(MotionBlurVelocityPassNode) @@ -1002,6 +1021,7 @@ def unregister(): bpy.utils.unregister_class(SSAOReprojectPassNode) bpy.utils.unregister_class(ApplySSAOPassNode) bpy.utils.unregister_class(SSRPassNode) + bpy.utils.unregister_class(SSGIPassNode) bpy.utils.unregister_class(BloomPassNode) bpy.utils.unregister_class(MotionBlurPassNode) bpy.utils.unregister_class(MotionBlurVelocityPassNode) diff --git a/blender/arm/props_renderpath.py b/blender/arm/props_renderpath.py index 762584d6..af3acc02 100644 --- a/blender/arm/props_renderpath.py +++ b/blender/arm/props_renderpath.py @@ -103,6 +103,7 @@ class ArmRPListItem(bpy.types.PropertyGroup): rp_volumetriclight = bpy.props.BoolProperty(name="Volumetric Light", description="Use volumetric lighting", default=False, update=update_renderpath) rp_ssao = bpy.props.BoolProperty(name="SSAO", description="Screen space ambient occlusion", default=True, update=update_renderpath) rp_ssr = bpy.props.BoolProperty(name="SSR", description="Screen space reflections", default=False, update=update_renderpath) + rp_ssgi = bpy.props.BoolProperty(name="SSGI", description="Screen space global illumination", default=False, update=update_renderpath) rp_dfao = bpy.props.BoolProperty(name="DFAO", description="Distance field ambient occlusion", default=False) rp_dfrs = bpy.props.BoolProperty(name="DFRS", description="Distance field ray-traced shadows", default=False) rp_dfgi = bpy.props.BoolProperty(name="DFGI", description="Distance field global illumination", default=False) diff --git a/blender/arm/props_ui.py b/blender/arm/props_ui.py index dc85e66e..5436dbb7 100644 --- a/blender/arm/props_ui.py +++ b/blender/arm/props_ui.py @@ -1004,10 +1004,11 @@ class ArmRenderPathPanel(bpy.types.Panel): layout.prop(rpdat, "rp_compositornodes") layout.prop(rpdat, "rp_volumetriclight") layout.prop(rpdat, "rp_ssao") + # layout.prop(wrd, 'arm_ssao_half_res') layout.prop(rpdat, "rp_ssr") if rpdat.rp_ssr: layout.prop(rpdat, 'arm_ssr_half_res') - # layout.prop(wrd, 'arm_ssao_half_res') + layout.prop(rpdat, "rp_ssgi") # layout.prop(rpdat, "rp_dfao") # layout.prop(rpdat, "rp_dfrs") # layout.prop(rpdat, "rp_dfgi") diff --git a/blender/data/arm_data.blend b/blender/data/arm_data.blend index 8e2d6ddb..1a7c0fbd 100644 Binary files a/blender/data/arm_data.blend and b/blender/data/arm_data.blend differ