1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-21 14:57:09 +00:00

RenderingServer reorganization

This commit is contained in:
reduz
2020-12-04 15:26:24 -03:00
parent 3dc8aaaccc
commit 2787ad65be
107 changed files with 3171 additions and 2954 deletions

View File

@@ -0,0 +1,43 @@
#!/usr/bin/env python
Import("env")
if "RD_GLSL" in env["BUILDERS"]:
env.RD_GLSL("canvas.glsl")
env.RD_GLSL("canvas_occlusion.glsl")
env.RD_GLSL("canvas_sdf.glsl")
env.RD_GLSL("copy.glsl")
env.RD_GLSL("copy_to_fb.glsl")
env.RD_GLSL("cubemap_roughness.glsl")
env.RD_GLSL("cubemap_downsampler.glsl")
env.RD_GLSL("cubemap_filter.glsl")
env.RD_GLSL("scene_high_end.glsl")
env.RD_GLSL("sky.glsl")
env.RD_GLSL("tonemap.glsl")
env.RD_GLSL("cube_to_dp.glsl")
env.RD_GLSL("giprobe.glsl")
env.RD_GLSL("giprobe_debug.glsl")
env.RD_GLSL("giprobe_sdf.glsl")
env.RD_GLSL("luminance_reduce.glsl")
env.RD_GLSL("bokeh_dof.glsl")
env.RD_GLSL("ssao.glsl")
env.RD_GLSL("ssao_minify.glsl")
env.RD_GLSL("ssao_blur.glsl")
env.RD_GLSL("roughness_limiter.glsl")
env.RD_GLSL("screen_space_reflection.glsl")
env.RD_GLSL("screen_space_reflection_filter.glsl")
env.RD_GLSL("screen_space_reflection_scale.glsl")
env.RD_GLSL("subsurface_scattering.glsl")
env.RD_GLSL("specular_merge.glsl")
env.RD_GLSL("gi.glsl")
env.RD_GLSL("resolve.glsl")
env.RD_GLSL("sdfgi_preprocess.glsl")
env.RD_GLSL("sdfgi_integrate.glsl")
env.RD_GLSL("sdfgi_direct_light.glsl")
env.RD_GLSL("sdfgi_debug.glsl")
env.RD_GLSL("sdfgi_debug_probes.glsl")
env.RD_GLSL("volumetric_fog.glsl")
env.RD_GLSL("shadow_reduce.glsl")
env.RD_GLSL("particles.glsl")
env.RD_GLSL("particles_copy.glsl")
env.RD_GLSL("sort.glsl")

View File

@@ -0,0 +1,251 @@
#[compute]
#version 450
VERSION_DEFINES
#define BLOCK_SIZE 8
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
#ifdef MODE_GEN_BLUR_SIZE
layout(rgba16f, set = 0, binding = 0) uniform restrict image2D color_image;
layout(set = 1, binding = 0) uniform sampler2D source_depth;
#endif
#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL) || defined(MODE_BOKEH_CIRCULAR)
layout(set = 1, binding = 0) uniform sampler2D color_texture;
layout(rgba16f, set = 0, binding = 0) uniform restrict writeonly image2D bokeh_image;
#endif
#ifdef MODE_COMPOSITE_BOKEH
layout(rgba16f, set = 0, binding = 0) uniform restrict image2D color_image;
layout(set = 1, binding = 0) uniform sampler2D source_bokeh;
#endif
// based on https://www.shadertoy.com/view/Xd3GDl
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 size;
float z_far;
float z_near;
bool orthogonal;
float blur_size;
float blur_scale;
int blur_steps;
bool blur_near_active;
float blur_near_begin;
float blur_near_end;
bool blur_far_active;
float blur_far_begin;
float blur_far_end;
bool second_pass;
bool half_size;
bool use_jitter;
float jitter_seed;
uint pad[2];
}
params;
//used to work around downsampling filter
#define DEPTH_GAP 0.0
#ifdef MODE_GEN_BLUR_SIZE
float get_depth_at_pos(vec2 uv) {
float depth = textureLod(source_depth, uv, 0.0).x;
if (params.orthogonal) {
depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
}
return depth;
}
float get_blur_size(float depth) {
if (params.blur_near_active && depth < params.blur_near_begin) {
return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative
}
if (params.blur_far_active && depth > params.blur_far_begin) {
return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP;
}
return 0.0;
}
#endif
const float GOLDEN_ANGLE = 2.39996323;
//note: uniform pdf rand [0;1[
float hash12n(vec2 p) {
p = fract(p * vec2(5.3987, 5.4421));
p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
return fract(p.x * p.y * 95.4307);
}
#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL)
vec4 weighted_filter_dir(vec2 dir, vec2 uv, vec2 pixel_size) {
dir *= pixel_size;
vec4 color = texture(color_texture, uv);
vec4 accum = color;
float total = 1.0;
float blur_scale = params.blur_size / float(params.blur_steps);
if (params.use_jitter) {
uv += dir * (hash12n(uv + params.jitter_seed) - 0.5);
}
for (int i = -params.blur_steps; i <= params.blur_steps; i++) {
if (i == 0) {
continue;
}
float radius = float(i) * blur_scale;
vec2 suv = uv + dir * radius;
radius = abs(radius);
vec4 sample_color = texture(color_texture, suv);
float limit;
if (sample_color.a < color.a) {
limit = abs(sample_color.a);
} else {
limit = abs(color.a);
}
limit -= DEPTH_GAP;
float m = smoothstep(radius - 0.5, radius + 0.5, limit);
accum += mix(color, sample_color, m);
total += 1.0;
}
return accum / total;
}
#endif
void main() {
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThan(pos, params.size))) { //too large, do nothing
return;
}
vec2 pixel_size = 1.0 / vec2(params.size);
vec2 uv = vec2(pos) / vec2(params.size);
#ifdef MODE_GEN_BLUR_SIZE
uv += pixel_size * 0.5;
//precompute size in alpha channel
float depth = get_depth_at_pos(uv);
float size = get_blur_size(depth);
vec4 color = imageLoad(color_image, pos);
color.a = size;
imageStore(color_image, pos, color);
#endif
#ifdef MODE_BOKEH_BOX
//pixel_size*=0.5; //resolution is doubled
if (params.second_pass || !params.half_size) {
uv += pixel_size * 0.5; //half pixel to read centers
} else {
uv += pixel_size * 0.25; //half pixel to read centers from full res
}
vec2 dir = (params.second_pass ? vec2(0.0, 1.0) : vec2(1.0, 0.0));
vec4 color = weighted_filter_dir(dir, uv, pixel_size);
imageStore(bokeh_image, pos, color);
#endif
#ifdef MODE_BOKEH_HEXAGONAL
//pixel_size*=0.5; //resolution is doubled
if (params.second_pass || !params.half_size) {
uv += pixel_size * 0.5; //half pixel to read centers
} else {
uv += pixel_size * 0.25; //half pixel to read centers from full res
}
vec2 dir = (params.second_pass ? normalize(vec2(1.0, 0.577350269189626)) : vec2(0.0, 1.0));
vec4 color = weighted_filter_dir(dir, uv, pixel_size);
if (params.second_pass) {
dir = normalize(vec2(-1.0, 0.577350269189626));
vec4 color2 = weighted_filter_dir(dir, uv, pixel_size);
color.rgb = min(color.rgb, color2.rgb);
color.a = (color.a + color2.a) * 0.5;
}
imageStore(bokeh_image, pos, color);
#endif
#ifdef MODE_BOKEH_CIRCULAR
if (params.half_size) {
pixel_size *= 0.5; //resolution is doubled
}
uv += pixel_size * 0.5; //half pixel to read centers
vec4 color = texture(color_texture, uv);
float accum = 1.0;
float radius = params.blur_scale;
for (float ang = 0.0; radius < params.blur_size; ang += GOLDEN_ANGLE) {
vec2 suv = uv + vec2(cos(ang), sin(ang)) * pixel_size * radius;
vec4 sample_color = texture(color_texture, suv);
float sample_size = abs(sample_color.a);
if (sample_color.a > color.a) {
sample_size = clamp(sample_size, 0.0, abs(color.a) * 2.0);
}
float m = smoothstep(radius - 0.5, radius + 0.5, sample_size);
color += mix(color / accum, sample_color, m);
accum += 1.0;
radius += params.blur_scale / radius;
}
color /= accum;
imageStore(bokeh_image, pos, color);
#endif
#ifdef MODE_COMPOSITE_BOKEH
uv += pixel_size * 0.5;
vec4 color = imageLoad(color_image, pos);
vec4 bokeh = texture(source_bokeh, uv);
float mix_amount;
if (bokeh.a < color.a) {
mix_amount = min(1.0, max(0.0, max(abs(color.a), abs(bokeh.a)) - DEPTH_GAP));
} else {
mix_amount = min(1.0, max(0.0, abs(color.a) - DEPTH_GAP));
}
color.rgb = mix(color.rgb, bokeh.rgb, mix_amount); //blend between hires and lowres
color.a = 0; //reset alpha
imageStore(color_image, pos, color);
#endif
}

View File

@@ -0,0 +1,672 @@
#[vertex]
#version 450
VERSION_DEFINES
#ifdef USE_ATTRIBUTES
layout(location = 0) in vec2 vertex_attrib;
layout(location = 3) in vec4 color_attrib;
layout(location = 4) in vec2 uv_attrib;
layout(location = 10) in uvec4 bone_attrib;
layout(location = 11) in vec4 weight_attrib;
#endif
#include "canvas_uniforms_inc.glsl"
layout(location = 0) out vec2 uv_interp;
layout(location = 1) out vec4 color_interp;
layout(location = 2) out vec2 vertex_interp;
#ifdef USE_NINEPATCH
layout(location = 3) out vec2 pixel_size_interp;
#endif
#ifdef USE_MATERIAL_UNIFORMS
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
/* clang-format off */
MATERIAL_UNIFORMS
/* clang-format on */
} material;
#endif
/* clang-format off */
VERTEX_SHADER_GLOBALS
/* clang-format on */
void main() {
vec4 instance_custom = vec4(0.0);
#ifdef USE_PRIMITIVE
//weird bug,
//this works
vec2 vertex;
vec2 uv;
vec4 color;
if (gl_VertexIndex == 0) {
vertex = draw_data.points[0];
uv = draw_data.uvs[0];
color = vec4(unpackHalf2x16(draw_data.colors[0]), unpackHalf2x16(draw_data.colors[1]));
} else if (gl_VertexIndex == 1) {
vertex = draw_data.points[1];
uv = draw_data.uvs[1];
color = vec4(unpackHalf2x16(draw_data.colors[2]), unpackHalf2x16(draw_data.colors[3]));
} else {
vertex = draw_data.points[2];
uv = draw_data.uvs[2];
color = vec4(unpackHalf2x16(draw_data.colors[4]), unpackHalf2x16(draw_data.colors[5]));
}
uvec4 bones = uvec4(0, 0, 0, 0);
vec4 bone_weights = vec4(0.0);
#elif defined(USE_ATTRIBUTES)
vec2 vertex = vertex_attrib;
vec4 color = color_attrib;
vec2 uv = uv_attrib;
uvec4 bones = bone_attrib;
vec4 bone_weights = weight_attrib;
#else
vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
vec2 vertex_base = vertex_base_arr[gl_VertexIndex];
vec2 uv = draw_data.src_rect.xy + abs(draw_data.src_rect.zw) * ((draw_data.flags & FLAGS_TRANSPOSE_RECT) != 0 ? vertex_base.yx : vertex_base.xy);
vec4 color = draw_data.modulation;
vec2 vertex = draw_data.dst_rect.xy + abs(draw_data.dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(draw_data.src_rect.zw, vec2(0.0, 0.0)));
uvec4 bones = uvec4(0, 0, 0, 0);
#endif
mat4 world_matrix = mat4(vec4(draw_data.world_x, 0.0, 0.0), vec4(draw_data.world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data.world_ofs, 0.0, 1.0));
#if 0
if (draw_data.flags & FLAGS_INSTANCING_ENABLED) {
uint offset = draw_data.flags & FLAGS_INSTANCING_STRIDE_MASK;
offset *= gl_InstanceIndex;
mat4 instance_xform = mat4(
vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), 0.0, texelFetch(instancing_buffer, offset + 3)),
vec4(texelFetch(instancing_buffer, offset + 4), texelFetch(instancing_buffer, offset + 5), 0.0, texelFetch(instancing_buffer, offset + 7)),
vec4(0.0, 0.0, 1.0, 0.0),
vec4(0.0, 0.0, 0.0, 1.0));
offset += 8;
if (draw_data.flags & FLAGS_INSTANCING_HAS_COLORS) {
vec4 instance_color;
if (draw_data.flags & FLAGS_INSTANCING_COLOR_8_BIT) {
uint bits = floatBitsToUint(texelFetch(instancing_buffer, offset));
instance_color = unpackUnorm4x8(bits);
offset += 1;
} else {
instance_color = vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), texelFetch(instancing_buffer, offset + 2), texelFetch(instancing_buffer, offset + 3));
offset += 4;
}
color *= instance_color;
}
if (draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA) {
if (draw_data.flags & FLAGS_INSTANCING_CUSTOM_DATA_8_BIT) {
uint bits = floatBitsToUint(texelFetch(instancing_buffer, offset));
instance_custom = unpackUnorm4x8(bits);
} else {
instance_custom = vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), texelFetch(instancing_buffer, offset + 2), texelFetch(instancing_buffer, offset + 3));
}
}
}
#endif
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
if (bool(draw_data.flags & FLAGS_USING_PARTICLES)) {
//scale by texture size
vertex /= draw_data.color_texture_pixel_size;
}
#endif
#ifdef USE_POINT_SIZE
float point_size = 1.0;
#endif
{
/* clang-format off */
VERTEX_SHADER_CODE
/* clang-format on */
}
#ifdef USE_NINEPATCH
pixel_size_interp = abs(draw_data.dst_rect.zw) * vertex_base;
#endif
#if !defined(SKIP_TRANSFORM_USED)
vertex = (world_matrix * vec4(vertex, 0.0, 1.0)).xy;
#endif
color_interp = color;
if (canvas_data.use_pixel_snap) {
vertex = floor(vertex + 0.5);
// precision issue on some hardware creates artifacts within texture
// offset uv by a small amount to avoid
uv += 1e-5;
}
#ifdef USE_ATTRIBUTES
#if 0
if (bool(draw_data.flags & FLAGS_USE_SKELETON) && bone_weights != vec4(0.0)) { //must be a valid bone
//skeleton transform
ivec4 bone_indicesi = ivec4(bone_indices);
uvec2 tex_ofs = bone_indicesi.x * 2;
mat2x4 m;
m = mat2x4(
texelFetch(skeleton_buffer, tex_ofs + 0),
texelFetch(skeleton_buffer, tex_ofs + 1)) *
bone_weights.x;
tex_ofs = bone_indicesi.y * 2;
m += mat2x4(
texelFetch(skeleton_buffer, tex_ofs + 0),
texelFetch(skeleton_buffer, tex_ofs + 1)) *
bone_weights.y;
tex_ofs = bone_indicesi.z * 2;
m += mat2x4(
texelFetch(skeleton_buffer, tex_ofs + 0),
texelFetch(skeleton_buffer, tex_ofs + 1)) *
bone_weights.z;
tex_ofs = bone_indicesi.w * 2;
m += mat2x4(
texelFetch(skeleton_buffer, tex_ofs + 0),
texelFetch(skeleton_buffer, tex_ofs + 1)) *
bone_weights.w;
mat4 bone_matrix = skeleton_data.skeleton_transform * transpose(mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))) * skeleton_data.skeleton_transform_inverse;
//outvec = bone_matrix * outvec;
}
#endif
#endif
vertex = (canvas_data.canvas_transform * vec4(vertex, 0.0, 1.0)).xy;
vertex_interp = vertex;
uv_interp = uv;
gl_Position = canvas_data.screen_transform * vec4(vertex, 0.0, 1.0);
#ifdef USE_POINT_SIZE
gl_PointSize = point_size;
#endif
}
#[fragment]
#version 450
VERSION_DEFINES
#include "canvas_uniforms_inc.glsl"
layout(location = 0) in vec2 uv_interp;
layout(location = 1) in vec4 color_interp;
layout(location = 2) in vec2 vertex_interp;
#ifdef USE_NINEPATCH
layout(location = 3) in vec2 pixel_size_interp;
#endif
layout(location = 0) out vec4 frag_color;
#ifdef USE_MATERIAL_UNIFORMS
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
/* clang-format off */
MATERIAL_UNIFORMS
/* clang-format on */
} material;
#endif
vec2 screen_uv_to_sdf(vec2 p_uv) {
return canvas_data.screen_to_sdf * p_uv;
}
float texture_sdf(vec2 p_sdf) {
vec2 uv = p_sdf * canvas_data.sdf_to_tex.xy + canvas_data.sdf_to_tex.zw;
float d = texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv).r;
d = d * SDF_MAX_LENGTH - 1.0;
return d * canvas_data.tex_to_sdf;
}
vec2 texture_sdf_normal(vec2 p_sdf) {
vec2 uv = p_sdf * canvas_data.sdf_to_tex.xy + canvas_data.sdf_to_tex.zw;
const float EPSILON = 0.001;
return normalize(vec2(
texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv + vec2(EPSILON, 0.0)).r - texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv - vec2(EPSILON, 0.0)).r,
texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv + vec2(0.0, EPSILON)).r - texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv - vec2(0.0, EPSILON)).r));
}
vec2 sdf_to_screen_uv(vec2 p_sdf) {
return p_sdf * canvas_data.sdf_to_screen;
}
/* clang-format off */
FRAGMENT_SHADER_GLOBALS
/* clang-format on */
#ifdef LIGHT_SHADER_CODE_USED
vec4 light_compute(
vec3 light_vertex,
vec3 light_position,
vec3 normal,
vec4 light_color,
float light_energy,
vec4 specular_shininess,
inout vec4 shadow_modulate,
vec2 screen_uv,
vec2 uv,
vec4 color, bool is_directional) {
vec4 light = vec4(0.0);
/* clang-format off */
LIGHT_SHADER_CODE
/* clang-format on */
return light;
}
#endif
#ifdef USE_NINEPATCH
float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) {
float tex_size = 1.0 / tex_pixel_size;
if (pixel < margin_begin) {
return pixel * tex_pixel_size;
} else if (pixel >= draw_size - margin_end) {
return (tex_size - (draw_size - pixel)) * tex_pixel_size;
} else {
if (!bool(draw_data.flags & FLAGS_NINEPACH_DRAW_CENTER)) {
draw_center--;
}
// np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum.
if (np_repeat == 0) { // Stretch.
// Convert to ratio.
float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end);
// Scale to source texture.
return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;
} else if (np_repeat == 1) { // Tile.
// Convert to offset.
float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end);
// Scale to source texture.
return (margin_begin + ofs) * tex_pixel_size;
} else if (np_repeat == 2) { // Tile Fit.
// Calculate scale.
float src_area = draw_size - margin_begin - margin_end;
float dst_area = tex_size - margin_begin - margin_end;
float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5));
// Convert to ratio.
float ratio = (pixel - margin_begin) / src_area;
ratio = mod(ratio * scale, 1.0);
// Scale to source texture.
return (margin_begin + ratio * dst_area) * tex_pixel_size;
} else { // Shouldn't happen, but silences compiler warning.
return 0.0;
}
}
}
#endif
#ifdef USE_LIGHTING
vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) {
float cNdotL = max(0.0, dot(normal, light_vec));
if (specular_shininess_used) {
//blinn
vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough
vec3 half_vec = normalize(view + light_vec);
float cNdotV = max(dot(normal, view), 0.0);
float cNdotH = max(dot(normal, half_vec), 0.0);
float cVdotH = max(dot(view, half_vec), 0.0);
float cLdotH = max(dot(light_vec, half_vec), 0.0);
float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25;
float blinn = pow(cNdotH, shininess);
blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
return specular_shininess.rgb * light_color * s + light_color * base_color * cNdotL;
} else {
return light_color * base_color * cNdotL;
}
}
//float distance = length(shadow_pos);
vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv
#ifdef LIGHT_SHADER_CODE_USED
,
vec3 shadow_modulate
#endif
) {
float shadow;
uint shadow_mode = light_array.data[light_base].flags & LIGHT_FLAGS_FILTER_MASK;
if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) {
shadow = textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
} else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) {
vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
shadow = 0.0;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
shadow /= 5.0;
} else { //PCF13
vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
shadow = 0.0;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 6.0, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 5.0, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 4.0, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 3.0, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 3.0, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 4.0, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 5.0, 0.0).x;
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 6.0, 0.0).x;
shadow /= 13.0;
}
vec4 shadow_color = unpackUnorm4x8(light_array.data[light_base].shadow_color);
#ifdef LIGHT_SHADER_CODE_USED
shadow_color *= shadow_modulate;
#endif
shadow_color.a *= light_color.a; //respect light alpha
return mix(light_color, shadow_color, shadow);
}
void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) {
uint blend_mode = light_array.data[light_base].flags & LIGHT_FLAGS_BLEND_MASK;
switch (blend_mode) {
case LIGHT_FLAGS_BLEND_MODE_ADD: {
color.rgb += light_color.rgb * light_color.a;
} break;
case LIGHT_FLAGS_BLEND_MODE_SUB: {
color.rgb -= light_color.rgb * light_color.a;
} break;
case LIGHT_FLAGS_BLEND_MODE_MIX: {
color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
} break;
}
}
#endif
void main() {
vec4 color = color_interp;
vec2 uv = uv_interp;
vec2 vertex = vertex_interp;
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
#ifdef USE_NINEPATCH
int draw_center = 2;
uv = vec2(
map_ninepatch_axis(pixel_size_interp.x, abs(draw_data.dst_rect.z), draw_data.color_texture_pixel_size.x, draw_data.ninepatch_margins.x, draw_data.ninepatch_margins.z, int(draw_data.flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center),
map_ninepatch_axis(pixel_size_interp.y, abs(draw_data.dst_rect.w), draw_data.color_texture_pixel_size.y, draw_data.ninepatch_margins.y, draw_data.ninepatch_margins.w, int(draw_data.flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center));
if (draw_center == 0) {
color.a = 0.0;
}
uv = uv * draw_data.src_rect.zw + draw_data.src_rect.xy; //apply region if needed
#endif
if (bool(draw_data.flags & FLAGS_CLIP_RECT_UV)) {
uv = clamp(uv, draw_data.src_rect.xy, draw_data.src_rect.xy + abs(draw_data.src_rect.zw));
}
#endif
color *= texture(sampler2D(color_texture, texture_sampler), uv);
uint light_count = (draw_data.flags >> FLAGS_LIGHT_COUNT_SHIFT) & 0xF; //max 16 lights
bool using_light = light_count > 0 || canvas_data.directional_light_count > 0;
vec3 normal;
#if defined(NORMAL_USED)
bool normal_used = true;
#else
bool normal_used = false;
#endif
if (normal_used || (using_light && bool(draw_data.flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
normal.xy = texture(sampler2D(normal_texture, texture_sampler), uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
normal_used = true;
} else {
normal = vec3(0.0, 0.0, 1.0);
}
vec4 specular_shininess;
#if defined(SPECULAR_SHININESS_USED)
bool specular_shininess_used = true;
#else
bool specular_shininess_used = false;
#endif
if (specular_shininess_used || (using_light && normal_used && bool(draw_data.flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
specular_shininess = texture(sampler2D(specular_texture, texture_sampler), uv);
specular_shininess *= unpackUnorm4x8(draw_data.specular_shininess);
specular_shininess_used = true;
} else {
specular_shininess = vec4(1.0);
}
#if defined(SCREEN_UV_USED)
vec2 screen_uv = gl_FragCoord.xy * canvas_data.screen_pixel_size;
#else
vec2 screen_uv = vec2(0.0);
#endif
vec3 light_vertex = vec3(vertex, 0.0);
vec2 shadow_vertex = vertex;
{
float normal_depth = 1.0;
#if defined(NORMALMAP_USED)
vec3 normal_map = vec3(0.0, 0.0, 1.0);
normal_used = true;
#endif
/* clang-format off */
FRAGMENT_SHADER_CODE
/* clang-format on */
#if defined(NORMALMAP_USED)
normal = mix(vec3(0.0, 0.0, 1.0), normal_map * vec3(2.0, -2.0, 1.0) - vec3(1.0, -1.0, 0.0), normal_depth);
#endif
}
if (normal_used) {
//convert by item transform
normal.xy = mat2(normalize(draw_data.world_x), normalize(draw_data.world_y)) * normal.xy;
//convert by canvas transform
normal = normalize((canvas_data.canvas_normal_transform * vec4(normal, 0.0)).xyz);
}
vec3 base_color = color.rgb;
if (bool(draw_data.flags & FLAGS_USING_LIGHT_MASK)) {
color = vec4(0.0); //invisible by default due to using light mask
}
#ifdef MODE_LIGHT_ONLY
color = vec4(0.0);
#else
color *= canvas_data.canvas_modulation;
#endif
#if defined(USE_LIGHTING) && !defined(MODE_UNSHADED)
// Directional Lights
for (uint i = 0; i < canvas_data.directional_light_count; i++) {
uint light_base = i;
vec2 direction = light_array.data[light_base].position;
vec4 light_color = light_array.data[light_base].color;
#ifdef LIGHT_SHADER_CODE_USED
vec4 shadow_modulate = vec4(1.0);
light_color = light_compute(light_vertex, direction, normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, color, uv, true);
#else
if (normal_used) {
vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array.data[light_base].height));
light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used);
}
#endif
if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec4 shadow_uv = vec4(shadow_pos.x, light_array.data[light_base].shadow_y_ofs, shadow_pos.y * light_array.data[light_base].shadow_zfar_inv, 1.0);
light_color = light_shadow_compute(light_base, light_color, shadow_uv
#ifdef LIGHT_SHADER_CODE_USED
,
shadow_modulate
#endif
);
}
light_blend_compute(light_base, light_color, color.rgb);
}
// Positional Lights
for (uint i = 0; i < MAX_LIGHTS_PER_ITEM; i++) {
if (i >= light_count) {
break;
}
uint light_base;
if (i < 8) {
if (i < 4) {
light_base = draw_data.lights[0];
} else {
light_base = draw_data.lights[1];
}
} else {
if (i < 12) {
light_base = draw_data.lights[2];
} else {
light_base = draw_data.lights[3];
}
}
light_base >>= (i & 3) * 8;
light_base &= 0xFF;
vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array.data[light_base].texture_matrix[0], light_array.data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec2 tex_uv_atlas = tex_uv * light_array.data[light_base].atlas_rect.zw + light_array.data[light_base].atlas_rect.xy;
vec4 light_color = textureLod(sampler2D(atlas_texture, texture_sampler), tex_uv_atlas, 0.0);
vec4 light_base_color = light_array.data[light_base].color;
#ifdef LIGHT_SHADER_CODE_USED
vec4 shadow_modulate = vec4(1.0);
vec3 light_position = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
light_color.rgb *= light_base_color.rgb;
light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, color, uv, false);
#else
light_color.rgb *= light_base_color.rgb * light_base_color.a;
if (normal_used) {
vec3 light_pos = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
vec3 pos = light_vertex;
vec3 light_vec = normalize(light_pos - pos);
float cNdotL = max(0.0, dot(normal, light_vec));
light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used);
}
#endif
if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
//if outside the light texture, light color is zero
light_color.a = 0.0;
}
if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec2 pos_norm = normalize(shadow_pos);
vec2 pos_abs = abs(pos_norm);
vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y);
vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot?
float tex_ofs;
float distance;
if (pos_rot.y > 0) {
if (pos_rot.x > 0) {
tex_ofs = pos_box.y * 0.125 + 0.125;
distance = shadow_pos.x;
} else {
tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125);
distance = shadow_pos.y;
}
} else {
if (pos_rot.x < 0) {
tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125);
distance = -shadow_pos.x;
} else {
tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125);
distance = -shadow_pos.y;
}
}
distance *= light_array.data[light_base].shadow_zfar_inv;
//float distance = length(shadow_pos);
vec4 shadow_uv = vec4(tex_ofs, light_array.data[light_base].shadow_y_ofs, distance, 1.0);
light_color = light_shadow_compute(light_base, light_color, shadow_uv
#ifdef LIGHT_SHADER_CODE_USED
,
shadow_modulate
#endif
);
}
light_blend_compute(light_base, light_color, color.rgb);
}
#endif
frag_color = color;
}

View File

@@ -0,0 +1,59 @@
#[vertex]
#version 450
VERSION_DEFINES
layout(location = 0) in highp vec3 vertex;
layout(push_constant, binding = 0, std430) uniform Constants {
mat4 projection;
mat2x4 modelview;
vec2 direction;
float z_far;
float pad;
}
constants;
#ifdef MODE_SHADOW
layout(location = 0) out highp float depth;
#endif
void main() {
highp vec4 vtx = vec4(vertex, 1.0) * mat4(constants.modelview[0], constants.modelview[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
#ifdef MODE_SHADOW
depth = dot(constants.direction, vtx.xy);
#endif
gl_Position = constants.projection * vtx;
}
#[fragment]
#version 450
VERSION_DEFINES
layout(push_constant, binding = 0, std430) uniform Constants {
mat4 projection;
mat2x4 modelview;
vec2 direction;
float z_far;
float pad;
}
constants;
#ifdef MODE_SHADOW
layout(location = 0) in highp float depth;
layout(location = 0) out highp float distance_buf;
#else
layout(location = 0) out highp float sdf_buf;
#endif
void main() {
#ifdef MODE_SHADOW
distance_buf = depth / constants.z_far;
#else
sdf_buf = 1.0;
#endif
}

View File

@@ -0,0 +1,135 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(r8, set = 0, binding = 1) uniform restrict readonly image2D src_pixels;
layout(r16, set = 0, binding = 2) uniform restrict writeonly image2D dst_sdf;
layout(rg16i, set = 0, binding = 3) uniform restrict readonly iimage2D src_process;
layout(rg16i, set = 0, binding = 4) uniform restrict writeonly iimage2D dst_process;
layout(push_constant, binding = 0, std430) uniform Params {
ivec2 size;
int stride;
int shift;
ivec2 base_size;
uvec2 pad;
}
params;
#define SDF_MAX_LENGTH 16384.0
void main() {
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(pos, params.size))) { //too large, do nothing
return;
}
#ifdef MODE_LOAD
bool solid = imageLoad(src_pixels, pos).r > 0.5;
imageStore(dst_process, pos, solid ? ivec4(pos, 0, 0) : ivec4(ivec2(32767), 0, 0));
#endif
#ifdef MODE_LOAD_SHRINK
int s = 1 << params.shift;
ivec2 base = pos << params.shift;
ivec2 center = base + ivec2(params.shift);
ivec2 rel = ivec2(32767);
float d = 1e20;
for (int i = 0; i < s; i++) {
for (int j = 0; j < s; j++) {
ivec2 src_pos = base + ivec2(i, j);
if (any(greaterThanEqual(src_pos, params.base_size))) {
continue;
}
bool solid = imageLoad(src_pixels, src_pos).r > 0.5;
if (solid) {
float dist = length(vec2(src_pos - center));
if (dist < d) {
d = dist;
rel = src_pos;
}
}
}
}
imageStore(dst_process, pos, ivec4(rel, 0, 0));
#endif
#ifdef MODE_PROCESS
ivec2 base = pos << params.shift;
ivec2 center = base + ivec2(params.shift);
ivec2 rel = imageLoad(src_process, pos).xy;
if (center != rel) {
//only process if it does not point to itself
const int ofs_table_size = 8;
const ivec2 ofs_table[ofs_table_size] = ivec2[](
ivec2(-1, -1),
ivec2(0, -1),
ivec2(+1, -1),
ivec2(-1, 0),
ivec2(+1, 0),
ivec2(-1, +1),
ivec2(0, +1),
ivec2(+1, +1));
float dist = length(vec2(rel - center));
for (int i = 0; i < ofs_table_size; i++) {
ivec2 src_pos = pos + ofs_table[i] * params.stride;
if (any(lessThan(src_pos, ivec2(0))) || any(greaterThanEqual(src_pos, params.size))) {
continue;
}
ivec2 src_rel = imageLoad(src_process, src_pos).xy;
float src_dist = length(vec2(src_rel - center));
if (src_dist < dist) {
dist = src_dist;
rel = src_rel;
}
}
}
imageStore(dst_process, pos, ivec4(rel, 0, 0));
#endif
#ifdef MODE_STORE
ivec2 rel = imageLoad(src_process, pos).xy;
float d = length(vec2(rel - pos));
if (d > 0.01) {
d += 1.0; //make it signed
}
d /= SDF_MAX_LENGTH;
d = clamp(d, 0.0, 1.0);
imageStore(dst_sdf, pos, vec4(d));
#endif
#ifdef MODE_STORE_SHRINK
ivec2 base = pos << params.shift;
ivec2 center = base + ivec2(params.shift);
ivec2 rel = imageLoad(src_process, pos).xy;
float d = length(vec2(rel - center));
if (d > 0.01) {
d += 1.0; //make it signed
}
d /= SDF_MAX_LENGTH;
d = clamp(d, 0.0, 1.0);
imageStore(dst_sdf, pos, vec4(d));
#endif
}

View File

@@ -0,0 +1,162 @@
#define MAX_LIGHTS_PER_ITEM 16
#define M_PI 3.14159265359
#define SDF_MAX_LENGTH 16384.0
#define FLAGS_INSTANCING_STRIDE_MASK 0xF
#define FLAGS_INSTANCING_ENABLED (1 << 4)
#define FLAGS_INSTANCING_HAS_COLORS (1 << 5)
#define FLAGS_INSTANCING_COLOR_8BIT (1 << 6)
#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 7)
#define FLAGS_INSTANCING_CUSTOM_DATA_8_BIT (1 << 8)
#define FLAGS_CLIP_RECT_UV (1 << 9)
#define FLAGS_TRANSPOSE_RECT (1 << 10)
#define FLAGS_USING_LIGHT_MASK (1 << 11)
#define FLAGS_NINEPACH_DRAW_CENTER (1 << 12)
#define FLAGS_USING_PARTICLES (1 << 13)
#define FLAGS_NINEPATCH_H_MODE_SHIFT 16
#define FLAGS_NINEPATCH_V_MODE_SHIFT 18
#define FLAGS_LIGHT_COUNT_SHIFT 20
#define FLAGS_DEFAULT_NORMAL_MAP_USED (1 << 26)
#define FLAGS_DEFAULT_SPECULAR_MAP_USED (1 << 27)
#define SAMPLER_NEAREST_CLAMP 0
#define SAMPLER_LINEAR_CLAMP 1
#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
#define SAMPLER_NEAREST_REPEAT 6
#define SAMPLER_LINEAR_REPEAT 7
#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
// Push Constant
layout(push_constant, binding = 0, std430) uniform DrawData {
vec2 world_x;
vec2 world_y;
vec2 world_ofs;
uint flags;
uint specular_shininess;
#ifdef USE_PRIMITIVE
vec2 points[3];
vec2 uvs[3];
uint colors[6];
#else
vec4 modulation;
vec4 ninepatch_margins;
vec4 dst_rect; //for built-in rect and UV
vec4 src_rect;
vec2 pad;
#endif
vec2 color_texture_pixel_size;
uint lights[4];
}
draw_data;
// In vulkan, sets should always be ordered using the following logic:
// Lower Sets: Sets that change format and layout less often
// Higher sets: Sets that change format and layout very often
// This is because changing a set for another with a different layout or format,
// invalidates all the upper ones (as likely internal base offset changes)
/* SET0: Globals */
// The values passed per draw primitives are cached within it
layout(set = 0, binding = 1, std140) uniform CanvasData {
mat4 canvas_transform;
mat4 screen_transform;
mat4 canvas_normal_transform;
vec4 canvas_modulation;
vec2 screen_pixel_size;
float time;
bool use_pixel_snap;
vec4 sdf_to_tex;
vec2 screen_to_sdf;
vec2 sdf_to_screen;
uint directional_light_count;
float tex_to_sdf;
uint pad1;
uint pad2;
}
canvas_data;
#define LIGHT_FLAGS_BLEND_MASK (3 << 16)
#define LIGHT_FLAGS_BLEND_MODE_ADD (0 << 16)
#define LIGHT_FLAGS_BLEND_MODE_SUB (1 << 16)
#define LIGHT_FLAGS_BLEND_MODE_MIX (2 << 16)
#define LIGHT_FLAGS_BLEND_MODE_MASK (3 << 16)
#define LIGHT_FLAGS_HAS_SHADOW (1 << 20)
#define LIGHT_FLAGS_FILTER_SHIFT 22
#define LIGHT_FLAGS_FILTER_MASK (3 << 22)
#define LIGHT_FLAGS_SHADOW_NEAREST (0 << 22)
#define LIGHT_FLAGS_SHADOW_PCF5 (1 << 22)
#define LIGHT_FLAGS_SHADOW_PCF13 (2 << 22)
struct Light {
mat2x4 texture_matrix; //light to texture coordinate matrix (transposed)
mat2x4 shadow_matrix; //light to shadow coordinate matrix (transposed)
vec4 color;
uint shadow_color; // packed
uint flags; //index to light texture
float shadow_pixel_size;
float height;
vec2 position;
float shadow_zfar_inv;
float shadow_y_ofs;
vec4 atlas_rect;
};
layout(set = 0, binding = 2, std140) uniform LightData {
Light data[MAX_LIGHTS];
}
light_array;
layout(set = 0, binding = 3) uniform texture2D atlas_texture;
layout(set = 0, binding = 4) uniform texture2D shadow_atlas_texture;
layout(set = 0, binding = 5) uniform sampler shadow_sampler;
layout(set = 0, binding = 6) uniform texture2D screen_texture;
layout(set = 0, binding = 7) uniform texture2D sdf_texture;
layout(set = 0, binding = 8) uniform sampler material_samplers[12];
layout(set = 0, binding = 9, std430) restrict readonly buffer GlobalVariableData {
vec4 data[];
}
global_variables;
/* SET1: Is reserved for the material */
//
/* SET2: Instancing and Skeleton */
layout(set = 2, binding = 0, std430) restrict readonly buffer Transforms {
vec4 data[];
}
transforms;
/* SET3: Texture */
layout(set = 3, binding = 0) uniform texture2D color_texture;
layout(set = 3, binding = 1) uniform texture2D normal_texture;
layout(set = 3, binding = 2) uniform texture2D specular_texture;
layout(set = 3, binding = 3) uniform sampler texture_sampler;

View File

@@ -0,0 +1,95 @@
#define CLUSTER_COUNTER_SHIFT 20
#define CLUSTER_POINTER_MASK ((1 << CLUSTER_COUNTER_SHIFT) - 1)
#define CLUSTER_COUNTER_MASK 0xfff
struct LightData { //this structure needs to be as packed as possible
vec3 position;
float inv_radius;
vec3 direction;
float size;
uint attenuation_energy; //attenuation
uint color_specular; //rgb color, a specular (8 bit unorm)
uint cone_attenuation_angle; // attenuation and angle, (16bit float)
uint shadow_color_enabled; //shadow rgb color, a>0.5 enabled (8bit unorm)
vec4 atlas_rect; // rect in the shadow atlas
mat4 shadow_matrix;
float shadow_bias;
float shadow_normal_bias;
float transmittance_bias;
float soft_shadow_size; // for spot, it's the size in uv coordinates of the light, for omni it's the span angle
float soft_shadow_scale; // scales the shadow kernel for blurrier shadows
uint mask;
float shadow_volumetric_fog_fade;
uint pad;
vec4 projector_rect; //projector rect in srgb decal atlas
};
#define REFLECTION_AMBIENT_DISABLED 0
#define REFLECTION_AMBIENT_ENVIRONMENT 1
#define REFLECTION_AMBIENT_COLOR 2
struct ReflectionData {
vec3 box_extents;
float index;
vec3 box_offset;
uint mask;
vec4 params; // intensity, 0, interior , boxproject
vec3 ambient; // ambient color
uint ambient_mode;
mat4 local_matrix; // up to here for spot and omni, rest is for directional
// notes: for ambientblend, use distance to edge to blend between already existing global environment
};
struct DirectionalLightData {
vec3 direction;
float energy;
vec3 color;
float size;
float specular;
uint mask;
float softshadow_angle;
float soft_shadow_scale;
bool blend_splits;
bool shadow_enabled;
float fade_from;
float fade_to;
uvec3 pad;
float shadow_volumetric_fog_fade;
vec4 shadow_bias;
vec4 shadow_normal_bias;
vec4 shadow_transmittance_bias;
vec4 shadow_z_range;
vec4 shadow_range_begin;
vec4 shadow_split_offsets;
mat4 shadow_matrix1;
mat4 shadow_matrix2;
mat4 shadow_matrix3;
mat4 shadow_matrix4;
vec4 shadow_color1;
vec4 shadow_color2;
vec4 shadow_color3;
vec4 shadow_color4;
vec2 uv_scale1;
vec2 uv_scale2;
vec2 uv_scale3;
vec2 uv_scale4;
};
struct DecalData {
mat4 xform; //to decal transform
vec3 inv_extents;
float albedo_mix;
vec4 albedo_rect;
vec4 normal_rect;
vec4 orm_rect;
vec4 emission_rect;
vec4 modulate;
float emission_energy;
uint mask;
float upper_fade;
float lower_fade;
mat3x4 normal_xform;
vec3 normal;
float normal_fade;
};

View File

@@ -0,0 +1,279 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#define FLAG_HORIZONTAL (1 << 0)
#define FLAG_USE_BLUR_SECTION (1 << 1)
#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 2)
#define FLAG_DOF_NEAR_FIRST_TAP (1 << 3)
#define FLAG_GLOW_FIRST_PASS (1 << 4)
#define FLAG_FLIP_Y (1 << 5)
#define FLAG_FORCE_LUMINANCE (1 << 6)
#define FLAG_COPY_ALL_SOURCE (1 << 7)
#define FLAG_HIGH_QUALITY_GLOW (1 << 8)
#define FLAG_ALPHA_TO_ONE (1 << 9)
layout(push_constant, binding = 1, std430) uniform Params {
ivec4 section;
ivec2 target;
uint flags;
uint pad;
// Glow.
float glow_strength;
float glow_bloom;
float glow_hdr_threshold;
float glow_hdr_scale;
float glow_exposure;
float glow_white;
float glow_luminance_cap;
float glow_auto_exposure_grey;
// DOF.
float camera_z_far;
float camera_z_near;
uint pad2[2];
vec4 set_color;
}
params;
#ifdef MODE_CUBEMAP_ARRAY_TO_PANORAMA
layout(set = 0, binding = 0) uniform samplerCubeArray source_color;
#elif defined(MODE_CUBEMAP_TO_PANORAMA)
layout(set = 0, binding = 0) uniform samplerCube source_color;
#elif !defined(MODE_SET_COLOR)
layout(set = 0, binding = 0) uniform sampler2D source_color;
#endif
#ifdef GLOW_USE_AUTO_EXPOSURE
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
#endif
#if defined(MODE_LINEARIZE_DEPTH_COPY) || defined(MODE_SIMPLE_COPY_DEPTH)
layout(r32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
#elif defined(DST_IMAGE_8BIT)
layout(rgba8, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
#else
layout(rgba32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
#endif
#ifdef MODE_GAUSSIAN_GLOW
shared vec4 local_cache[256];
shared vec4 temp_cache[128];
#endif
void main() {
// Pixel being shaded
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
#ifndef MODE_GAUSSIAN_GLOW // Glow needs the extra threads
if (any(greaterThanEqual(pos, params.section.zw))) { //too large, do nothing
return;
}
#endif
#ifdef MODE_MIPMAP
ivec2 base_pos = (pos + params.section.xy) << 1;
vec4 color = texelFetch(source_color, base_pos, 0);
color += texelFetch(source_color, base_pos + ivec2(0, 1), 0);
color += texelFetch(source_color, base_pos + ivec2(1, 0), 0);
color += texelFetch(source_color, base_pos + ivec2(1, 1), 0);
color /= 4.0;
imageStore(dest_buffer, pos + params.target, color);
#endif
#ifdef MODE_GAUSSIAN_BLUR
//Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect
if (bool(params.flags & FLAG_HORIZONTAL)) {
ivec2 base_pos = (pos + params.section.xy) << 1;
vec4 color = texelFetch(source_color, base_pos + ivec2(0, 0), 0) * 0.214607;
color += texelFetch(source_color, base_pos + ivec2(1, 0), 0) * 0.189879;
color += texelFetch(source_color, base_pos + ivec2(2, 0), 0) * 0.131514;
color += texelFetch(source_color, base_pos + ivec2(3, 0), 0) * 0.071303;
color += texelFetch(source_color, base_pos + ivec2(-1, 0), 0) * 0.189879;
color += texelFetch(source_color, base_pos + ivec2(-2, 0), 0) * 0.131514;
color += texelFetch(source_color, base_pos + ivec2(-3, 0), 0) * 0.071303;
imageStore(dest_buffer, pos + params.target, color);
} else {
ivec2 base_pos = (pos + params.section.xy);
vec4 color = texelFetch(source_color, base_pos + ivec2(0, 0), 0) * 0.38774;
color += texelFetch(source_color, base_pos + ivec2(0, 1), 0) * 0.24477;
color += texelFetch(source_color, base_pos + ivec2(0, 2), 0) * 0.06136;
color += texelFetch(source_color, base_pos + ivec2(0, -1), 0) * 0.24477;
color += texelFetch(source_color, base_pos + ivec2(0, -2), 0) * 0.06136;
imageStore(dest_buffer, pos + params.target, color);
}
#endif
#ifdef MODE_GAUSSIAN_GLOW
// First pass copy texture into 16x16 local memory for every 8x8 thread block
vec2 quad_center_uv = clamp(vec2(gl_GlobalInvocationID.xy + gl_LocalInvocationID.xy - 3.5) / params.section.zw, vec2(0.5 / params.section.zw), vec2(1.0 - 1.5 / params.section.zw));
uint dest_index = gl_LocalInvocationID.x * 2 + gl_LocalInvocationID.y * 2 * 16;
if (bool(params.flags & FLAG_HIGH_QUALITY_GLOW)) {
vec2 quad_offset_uv = clamp((vec2(gl_GlobalInvocationID.xy + gl_LocalInvocationID.xy - 3.0)) / params.section.zw, vec2(0.5 / params.section.zw), vec2(1.0 - 1.5 / params.section.zw));
local_cache[dest_index] = (textureLod(source_color, quad_center_uv, 0) + textureLod(source_color, quad_offset_uv, 0)) * 0.5;
local_cache[dest_index + 1] = (textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.z, 0.0), 0) + textureLod(source_color, quad_offset_uv + vec2(1.0 / params.section.z, 0.0), 0)) * 0.5;
local_cache[dest_index + 16] = (textureLod(source_color, quad_center_uv + vec2(0.0, 1.0 / params.section.w), 0) + textureLod(source_color, quad_offset_uv + vec2(0.0, 1.0 / params.section.w), 0)) * 0.5;
local_cache[dest_index + 16 + 1] = (textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.zw), 0) + textureLod(source_color, quad_offset_uv + vec2(1.0 / params.section.zw), 0)) * 0.5;
} else {
local_cache[dest_index] = textureLod(source_color, quad_center_uv, 0);
local_cache[dest_index + 1] = textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.z, 0.0), 0);
local_cache[dest_index + 16] = textureLod(source_color, quad_center_uv + vec2(0.0, 1.0 / params.section.w), 0);
local_cache[dest_index + 16 + 1] = textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.zw), 0);
}
memoryBarrierShared();
barrier();
// Horizontal pass. Needs to copy into 8x16 chunk of local memory so vertical pass has full resolution
uint read_index = gl_LocalInvocationID.x + gl_LocalInvocationID.y * 32 + 4;
vec4 color_top = vec4(0.0);
color_top += local_cache[read_index] * 0.174938;
color_top += local_cache[read_index + 1] * 0.165569;
color_top += local_cache[read_index + 2] * 0.140367;
color_top += local_cache[read_index + 3] * 0.106595;
color_top += local_cache[read_index - 1] * 0.165569;
color_top += local_cache[read_index - 2] * 0.140367;
color_top += local_cache[read_index - 3] * 0.106595;
vec4 color_bottom = vec4(0.0);
color_bottom += local_cache[read_index + 16] * 0.174938;
color_bottom += local_cache[read_index + 1 + 16] * 0.165569;
color_bottom += local_cache[read_index + 2 + 16] * 0.140367;
color_bottom += local_cache[read_index + 3 + 16] * 0.106595;
color_bottom += local_cache[read_index - 1 + 16] * 0.165569;
color_bottom += local_cache[read_index - 2 + 16] * 0.140367;
color_bottom += local_cache[read_index - 3 + 16] * 0.106595;
// rotate samples to take advantage of cache coherency
uint write_index = gl_LocalInvocationID.y * 2 + gl_LocalInvocationID.x * 16;
temp_cache[write_index] = color_top;
temp_cache[write_index + 1] = color_bottom;
memoryBarrierShared();
barrier();
// Vertical pass
uint index = gl_LocalInvocationID.y + gl_LocalInvocationID.x * 16 + 4;
vec4 color = vec4(0.0);
color += temp_cache[index] * 0.174938;
color += temp_cache[index + 1] * 0.165569;
color += temp_cache[index + 2] * 0.140367;
color += temp_cache[index + 3] * 0.106595;
color += temp_cache[index - 1] * 0.165569;
color += temp_cache[index - 2] * 0.140367;
color += temp_cache[index - 3] * 0.106595;
color *= params.glow_strength;
if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) {
#ifdef GLOW_USE_AUTO_EXPOSURE
color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.glow_auto_exposure_grey;
#endif
color *= params.glow_exposure;
float luminance = max(color.r, max(color.g, color.b));
float feedback = max(smoothstep(params.glow_hdr_threshold, params.glow_hdr_threshold + params.glow_hdr_scale, luminance), params.glow_bloom);
color = min(color * feedback, vec4(params.glow_luminance_cap));
}
imageStore(dest_buffer, pos + params.target, color);
#endif
#ifdef MODE_SIMPLE_COPY
vec4 color;
if (bool(params.flags & FLAG_COPY_ALL_SOURCE)) {
vec2 uv = vec2(pos) / vec2(params.section.zw);
if (bool(params.flags & FLAG_FLIP_Y)) {
uv.y = 1.0 - uv.y;
}
color = textureLod(source_color, uv, 0.0);
} else {
color = texelFetch(source_color, pos + params.section.xy, 0);
if (bool(params.flags & FLAG_FLIP_Y)) {
pos.y = params.section.w - pos.y - 1;
}
}
if (bool(params.flags & FLAG_FORCE_LUMINANCE)) {
color.rgb = vec3(max(max(color.r, color.g), color.b));
}
if (bool(params.flags & FLAG_ALPHA_TO_ONE)) {
color.a = 1.0;
}
imageStore(dest_buffer, pos + params.target, color);
#endif
#ifdef MODE_SIMPLE_COPY_DEPTH
vec4 color = texelFetch(source_color, pos + params.section.xy, 0);
if (bool(params.flags & FLAG_FLIP_Y)) {
pos.y = params.section.w - pos.y - 1;
}
imageStore(dest_buffer, pos + params.target, vec4(color.r));
#endif
#ifdef MODE_LINEARIZE_DEPTH_COPY
float depth = texelFetch(source_color, pos + params.section.xy, 0).r;
depth = depth * 2.0 - 1.0;
depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - depth * (params.camera_z_far - params.camera_z_near));
vec4 color = vec4(depth / params.camera_z_far);
if (bool(params.flags & FLAG_FLIP_Y)) {
pos.y = params.section.w - pos.y - 1;
}
imageStore(dest_buffer, pos + params.target, color);
#endif
#if defined(MODE_CUBEMAP_TO_PANORAMA) || defined(MODE_CUBEMAP_ARRAY_TO_PANORAMA)
const float PI = 3.14159265359;
vec2 uv = vec2(pos) / vec2(params.section.zw);
uv.y = 1.0 - uv.y;
float phi = uv.x * 2.0 * PI;
float theta = uv.y * PI;
vec3 normal;
normal.x = sin(phi) * sin(theta) * -1.0;
normal.y = cos(theta);
normal.z = cos(phi) * sin(theta) * -1.0;
#ifdef MODE_CUBEMAP_TO_PANORAMA
vec4 color = textureLod(source_color, normal, params.camera_z_far); //the biggest the lod the least the acne
#else
vec4 color = textureLod(source_color, vec4(normal, params.camera_z_far), 0.0); //the biggest the lod the least the acne
#endif
imageStore(dest_buffer, pos + params.target, color);
#endif
#ifdef MODE_SET_COLOR
imageStore(dest_buffer, pos + params.target, params.set_color);
#endif
}

View File

@@ -0,0 +1,115 @@
#[vertex]
#version 450
VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
layout(push_constant, binding = 1, std430) uniform Params {
vec4 section;
vec2 pixel_size;
bool flip_y;
bool use_section;
bool force_luminance;
uint pad[3];
}
params;
void main() {
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
uv_interp = base_arr[gl_VertexIndex];
vec2 vpos = uv_interp;
if (params.use_section) {
vpos = params.section.xy + vpos * params.section.zw;
}
gl_Position = vec4(vpos * 2.0 - 1.0, 0.0, 1.0);
if (params.flip_y) {
uv_interp.y = 1.0 - uv_interp.y;
}
}
#[fragment]
#version 450
VERSION_DEFINES
layout(push_constant, binding = 1, std430) uniform Params {
vec4 section;
vec2 pixel_size;
bool flip_y;
bool use_section;
bool force_luminance;
bool alpha_to_zero;
bool srgb;
uint pad;
}
params;
layout(location = 0) in vec2 uv_interp;
layout(set = 0, binding = 0) uniform sampler2D source_color;
#ifdef MODE_TWO_SOURCES
layout(set = 1, binding = 0) uniform sampler2D source_color2;
#endif
layout(location = 0) out vec4 frag_color;
vec3 linear_to_srgb(vec3 color) {
//if going to srgb, clamp from 0 to 1.
color = clamp(color, vec3(0.0), vec3(1.0));
const vec3 a = vec3(0.055f);
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
}
void main() {
vec2 uv = uv_interp;
#ifdef MODE_PANORAMA_TO_DP
//obtain normal from dual paraboloid uv
#define M_PI 3.14159265359
float side;
uv.y = modf(uv.y * 2.0, side);
side = side * 2.0 - 1.0;
vec3 normal = vec3(uv * 2.0 - 1.0, 0.0);
normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
normal *= -side;
normal = normalize(normal);
//now convert normal to panorama uv
vec2 st = vec2(atan(normal.x, normal.z), acos(normal.y));
if (st.x < 0.0) {
st.x += M_PI * 2.0;
}
uv = st / vec2(M_PI * 2.0, M_PI);
if (side < 0.0) {
//uv.y = 1.0 - uv.y;
uv = 1.0 - uv;
}
#endif
vec4 color = textureLod(source_color, uv, 0.0);
#ifdef MODE_TWO_SOURCES
color += textureLod(source_color2, uv, 0.0);
#endif
if (params.force_luminance) {
color.rgb = vec3(max(max(color.r, color.g), color.b));
}
if (params.alpha_to_zero) {
color.rgb *= color.a;
}
if (params.srgb) {
color.rgb = linear_to_srgb(color.rgb);
}
frag_color = color;
}

View File

@@ -0,0 +1,69 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform samplerCube source_cube;
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 screen_size;
ivec2 offset;
float bias;
float z_far;
float z_near;
bool z_flip;
}
params;
layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D depth_buffer;
void main() {
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThan(pos, params.screen_size))) { //too large, do nothing
return;
}
vec2 pixel_size = 1.0 / vec2(params.screen_size);
vec2 uv = (vec2(pos) + 0.5) * pixel_size;
vec3 normal = vec3(uv * 2.0 - 1.0, 0.0);
normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
normal = normalize(normal);
normal.y = -normal.y; //needs to be flipped to match projection matrix
if (!params.z_flip) {
normal.z = -normal.z;
}
float depth = texture(source_cube, normal).r;
// absolute values for direction cosines, bigger value equals closer to basis axis
vec3 unorm = abs(normal);
if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
// x code
unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0);
} else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
// y code
unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0);
} else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
// z code
unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0);
} else {
// oh-no we messed up code
// has to be
unorm = vec3(1.0, 0.0, 0.0);
}
float depth_fix = 1.0 / dot(normal, unorm);
depth = 2.0 * depth - 1.0;
float linear_depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
depth = (linear_depth * depth_fix) / params.z_far;
imageStore(depth_buffer, pos + params.offset, vec4(depth));
}

View File

@@ -0,0 +1,191 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#[compute]
#version 450
VERSION_DEFINES
#define BLOCK_SIZE 8
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap;
layout(push_constant, binding = 1, std430) uniform Params {
uint face_size;
}
params;
#define M_PI 3.14159265359
void get_dir_0(out vec3 dir, in float u, in float v) {
dir[0] = 1.0;
dir[1] = v;
dir[2] = -u;
}
void get_dir_1(out vec3 dir, in float u, in float v) {
dir[0] = -1.0;
dir[1] = v;
dir[2] = u;
}
void get_dir_2(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = 1.0;
dir[2] = -v;
}
void get_dir_3(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = -1.0;
dir[2] = v;
}
void get_dir_4(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = v;
dir[2] = 1.0;
}
void get_dir_5(out vec3 dir, in float u, in float v) {
dir[0] = -u;
dir[1] = v;
dir[2] = -1.0;
}
float calcWeight(float u, float v) {
float val = u * u + v * v + 1.0;
return val * sqrt(val);
}
void main() {
uvec3 id = gl_GlobalInvocationID;
uint face_size = params.face_size;
if (id.x < face_size && id.y < face_size) {
float inv_face_size = 1.0 / float(face_size);
float u0 = (float(id.x) * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0;
float u1 = (float(id.x) * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0;
float v0 = (float(id.y) * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0;
float v1 = (float(id.y) * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0;
float weights[4];
weights[0] = calcWeight(u0, v0);
weights[1] = calcWeight(u1, v0);
weights[2] = calcWeight(u0, v1);
weights[3] = calcWeight(u1, v1);
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
for (int i = 0; i < 4; i++) {
weights[i] = weights[i] * wsum + .125;
}
vec3 dir;
vec4 color;
switch (id.z) {
case 0:
get_dir_0(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_0(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_0(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_0(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 1:
get_dir_1(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_1(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_1(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_1(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 2:
get_dir_2(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_2(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_2(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_2(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 3:
get_dir_3(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_3(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_3(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_3(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 4:
get_dir_4(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_4(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_4(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_4(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
default:
get_dir_5(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_5(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_5(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_5(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
}
imageStore(dest_cubemap, ivec3(id), color);
}
}

View File

@@ -0,0 +1,326 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#[compute]
#version 450
VERSION_DEFINES
#define GROUP_SIZE 64
layout(local_size_x = GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly imageCube dest_cubemap0;
layout(rgba16f, set = 2, binding = 1) uniform restrict writeonly imageCube dest_cubemap1;
layout(rgba16f, set = 2, binding = 2) uniform restrict writeonly imageCube dest_cubemap2;
layout(rgba16f, set = 2, binding = 3) uniform restrict writeonly imageCube dest_cubemap3;
layout(rgba16f, set = 2, binding = 4) uniform restrict writeonly imageCube dest_cubemap4;
layout(rgba16f, set = 2, binding = 5) uniform restrict writeonly imageCube dest_cubemap5;
layout(rgba16f, set = 2, binding = 6) uniform restrict writeonly imageCube dest_cubemap6;
#ifdef USE_HIGH_QUALITY
#define NUM_TAPS 32
#else
#define NUM_TAPS 8
#endif
#define BASE_RESOLUTION 128
#ifdef USE_HIGH_QUALITY
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][3][24] coeffs;
}
data;
#else
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][6] coeffs;
}
data;
#endif
void get_dir(out vec3 dir, in vec2 uv, in uint face) {
switch (face) {
case 0:
dir = vec3(1.0, uv[1], -uv[0]);
break;
case 1:
dir = vec3(-1.0, uv[1], uv[0]);
break;
case 2:
dir = vec3(uv[0], 1.0, -uv[1]);
break;
case 3:
dir = vec3(uv[0], -1.0, uv[1]);
break;
case 4:
dir = vec3(uv[0], uv[1], 1.0);
break;
default:
dir = vec3(-uv[0], uv[1], -1.0);
break;
}
}
void main() {
// INPUT:
// id.x = the linear address of the texel (ignoring face)
// id.y = the face
// -> use to index output texture
// id.x = texel x
// id.y = texel y
// id.z = face
uvec3 id = gl_GlobalInvocationID;
// determine which texel this is
#ifndef USE_TEXTURE_ARRAY
// NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name.
int mip_level = 0;
if (id.x < (128 * 128)) {
mip_level = 0;
} else if (id.x < (128 * 128 + 64 * 64)) {
mip_level = 1;
id.x -= (128 * 128);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32)) {
mip_level = 2;
id.x -= (128 * 128 + 64 * 64);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16)) {
mip_level = 3;
id.x -= (128 * 128 + 64 * 64 + 32 * 32);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8)) {
mip_level = 4;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4)) {
mip_level = 5;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2)) {
mip_level = 6;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4);
} else {
return;
}
int res = BASE_RESOLUTION >> mip_level;
#else // Using Texture Arrays so all levels are the same resolution
int res = BASE_RESOLUTION;
int mip_level = int(id.x / (BASE_RESOLUTION * BASE_RESOLUTION));
id.x -= mip_level * BASE_RESOLUTION * BASE_RESOLUTION;
#endif
// determine dir / pos for the texel
vec3 dir, adir, frameZ;
{
id.z = id.y;
id.y = id.x / res;
id.x -= id.y * res;
vec2 uv;
uv.x = (float(id.x) * 2.0 + 1.0) / float(res) - 1.0;
uv.y = -(float(id.y) * 2.0 + 1.0) / float(res) + 1.0;
get_dir(dir, uv, id.z);
frameZ = normalize(dir);
adir = abs(dir);
}
// GGX gather colors
vec4 color = vec4(0.0);
for (int axis = 0; axis < 3; axis++) {
const int otherAxis0 = 1 - (axis & 1) - (axis >> 1);
const int otherAxis1 = 2 - (axis >> 1);
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - .75) / .25;
if (frameweight > 0.0) {
// determine frame
vec3 UpVector;
switch (axis) {
case 0:
UpVector = vec3(1, 0, 0);
break;
case 1:
UpVector = vec3(0, 1, 0);
break;
default:
UpVector = vec3(0, 0, 1);
break;
}
vec3 frameX = normalize(cross(UpVector, frameZ));
vec3 frameY = cross(frameZ, frameX);
// calculate parametrization for polynomial
float Nx = dir[otherAxis0];
float Ny = dir[otherAxis1];
float Nz = adir[axis];
float NmaxXY = max(abs(Ny), abs(Nx));
Nx /= NmaxXY;
Ny /= NmaxXY;
float theta;
if (Ny < Nx) {
if (Ny <= -0.999)
theta = Nx;
else
theta = Ny;
} else {
if (Ny >= 0.999)
theta = -Nx;
else
theta = -Ny;
}
float phi;
if (Nz <= -0.999)
phi = -NmaxXY;
else if (Nz >= 0.999)
phi = NmaxXY;
else
phi = Nz;
float theta2 = theta * theta;
float phi2 = phi * phi;
// sample
for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) {
const int index = (NUM_TAPS / 4) * axis + iSuperTap;
#ifdef USE_HIGH_QUALITY
vec4 coeffsDir0[3];
vec4 coeffsDir1[3];
vec4 coeffsDir2[3];
vec4 coeffsLevel[3];
vec4 coeffsWeight[3];
for (int iCoeff = 0; iCoeff < 3; iCoeff++) {
coeffsDir0[iCoeff] = data.coeffs[mip_level][0][iCoeff][index];
coeffsDir1[iCoeff] = data.coeffs[mip_level][1][iCoeff][index];
coeffsDir2[iCoeff] = data.coeffs[mip_level][2][iCoeff][index];
coeffsLevel[iCoeff] = data.coeffs[mip_level][3][iCoeff][index];
coeffsWeight[iCoeff] = data.coeffs[mip_level][4][iCoeff][index];
}
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// determine sample attributes (dir, weight, mip_level)
vec3 sample_dir = frameX * (coeffsDir0[0][iSubTap] + coeffsDir0[1][iSubTap] * theta2 + coeffsDir0[2][iSubTap] * phi2) + frameY * (coeffsDir1[0][iSubTap] + coeffsDir1[1][iSubTap] * theta2 + coeffsDir1[2][iSubTap] * phi2) + frameZ * (coeffsDir2[0][iSubTap] + coeffsDir2[1][iSubTap] * theta2 + coeffsDir2[2][iSubTap] * phi2);
float sample_level = coeffsLevel[0][iSubTap] + coeffsLevel[1][iSubTap] * theta2 + coeffsLevel[2][iSubTap] * phi2;
float sample_weight = coeffsWeight[0][iSubTap] + coeffsWeight[1][iSubTap] * theta2 + coeffsWeight[2][iSubTap] * phi2;
#else
vec4 coeffsDir0 = data.coeffs[mip_level][0][index];
vec4 coeffsDir1 = data.coeffs[mip_level][1][index];
vec4 coeffsDir2 = data.coeffs[mip_level][2][index];
vec4 coeffsLevel = data.coeffs[mip_level][3][index];
vec4 coeffsWeight = data.coeffs[mip_level][4][index];
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// determine sample attributes (dir, weight, mip_level)
vec3 sample_dir = frameX * coeffsDir0[iSubTap] + frameY * coeffsDir1[iSubTap] + frameZ * coeffsDir2[iSubTap];
float sample_level = coeffsLevel[iSubTap];
float sample_weight = coeffsWeight[iSubTap];
#endif
sample_weight *= frameweight;
// adjust for jacobian
sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2])));
sample_level += 0.75 * log2(dot(sample_dir, sample_dir));
#ifndef USE_TEXTURE_ARRAY
sample_level += float(mip_level) / 6.0; // Hack to increase the perceived roughness and reduce upscaling artifacts
#endif
// sample cubemap
color.xyz += textureLod(source_cubemap, normalize(sample_dir), sample_level).xyz * sample_weight;
color.w += sample_weight;
}
}
}
}
color /= color.w;
// write color
color.xyz = max(vec3(0.0), color.xyz);
color.w = 1.0;
#ifdef USE_TEXTURE_ARRAY
id.xy *= uvec2(2, 2);
#endif
switch (mip_level) {
case 0:
imageStore(dest_cubemap0, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap0, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 1:
imageStore(dest_cubemap1, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap1, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 2:
imageStore(dest_cubemap2, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap2, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 3:
imageStore(dest_cubemap3, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap3, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 4:
imageStore(dest_cubemap4, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap4, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 5:
imageStore(dest_cubemap5, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap5, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
default:
imageStore(dest_cubemap6, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap6, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
}
}

View File

@@ -0,0 +1,142 @@
#[compute]
#version 450
VERSION_DEFINES
#define GROUP_SIZE 8
layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform samplerCube source_cube;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap;
layout(push_constant, binding = 1, std430) uniform Params {
uint face_id;
uint sample_count;
float roughness;
bool use_direct_write;
float face_size;
}
params;
#define M_PI 3.14159265359
vec3 texelCoordToVec(vec2 uv, uint faceID) {
mat3 faceUvVectors[6];
// -x
faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z
faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face
// +x
faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z
faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face
// -y
faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z
faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face
// +y
faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z
faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face
// -z
faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x
faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face
// +z
faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face
// out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2].
vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2];
return normalize(result);
}
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) {
float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph]
// Compute distribution direction
float Phi = 2.0 * M_PI * Xi.x;
float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y));
float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
// Convert to spherical direction
vec3 H;
H.x = SinTheta * cos(Phi);
H.y = SinTheta * sin(Phi);
H.z = CosTheta;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 TangentX = normalize(cross(UpVector, N));
vec3 TangentY = cross(N, TangentX);
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
}
// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html
float GGX(float NdotV, float a) {
float k = a / 2.0;
return NdotV / (NdotV * (1.0 - k) + k);
}
// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html
float G_Smith(float a, float nDotV, float nDotL) {
return GGX(nDotL, a * a) * GGX(nDotV, a * a);
}
float radicalInverse_VdC(uint bits) {
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
vec2 Hammersley(uint i, uint N) {
return vec2(float(i) / float(N), radicalInverse_VdC(i));
}
void main() {
uvec3 id = gl_GlobalInvocationID;
id.z += params.face_id;
vec2 uv = ((vec2(id.xy) * 2.0 + 1.0) / (params.face_size) - 1.0);
vec3 N = texelCoordToVec(uv, id.z);
//vec4 color = color_interp;
if (params.use_direct_write) {
imageStore(dest_cubemap, ivec3(id), vec4(texture(source_cube, N).rgb, 1.0));
} else {
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) {
vec2 xi = Hammersley(sampleNum, params.sample_count);
vec3 H = ImportanceSampleGGX(xi, params.roughness, N);
vec3 V = N;
vec3 L = (2.0 * dot(V, H) * H - V);
float ndotl = clamp(dot(N, L), 0.0, 1.0);
if (ndotl > 0.0) {
sum.rgb += textureLod(source_cube, L, 0.0).rgb * ndotl;
sum.a += ndotl;
}
}
sum /= sum.a;
imageStore(dest_cubemap, ivec3(id), vec4(sum.rgb, 1.0));
}
}

View File

@@ -0,0 +1,663 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#define M_PI 3.141592
#define SDFGI_MAX_CASCADES 8
//set 0 for SDFGI and render buffers
layout(set = 0, binding = 1) uniform texture3D sdf_cascades[SDFGI_MAX_CASCADES];
layout(set = 0, binding = 2) uniform texture3D light_cascades[SDFGI_MAX_CASCADES];
layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[SDFGI_MAX_CASCADES];
layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[SDFGI_MAX_CASCADES];
layout(set = 0, binding = 5) uniform texture3D occlusion_texture;
layout(set = 0, binding = 6) uniform sampler linear_sampler;
layout(set = 0, binding = 7) uniform sampler linear_sampler_with_mipmaps;
struct ProbeCascadeData {
vec3 position;
float to_probe;
ivec3 probe_world_offset;
float to_cell; // 1/bounds * grid_size
};
layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image2D ambient_buffer;
layout(rgba16f, set = 0, binding = 10) uniform restrict writeonly image2D reflection_buffer;
layout(set = 0, binding = 11) uniform texture2DArray lightprobe_texture;
layout(set = 0, binding = 12) uniform texture2D depth_buffer;
layout(set = 0, binding = 13) uniform texture2D normal_roughness_buffer;
layout(set = 0, binding = 14) uniform utexture2D giprobe_buffer;
layout(set = 0, binding = 15, std140) uniform SDFGI {
vec3 grid_size;
uint max_cascades;
bool use_occlusion;
int probe_axis_size;
float probe_to_uvw;
float normal_bias;
vec3 lightprobe_tex_pixel_size;
float energy;
vec3 lightprobe_uv_offset;
float y_mult;
vec3 occlusion_clamp;
uint pad3;
vec3 occlusion_renormalize;
uint pad4;
vec3 cascade_probe_size;
uint pad5;
ProbeCascadeData cascades[SDFGI_MAX_CASCADES];
}
sdfgi;
#define MAX_GI_PROBES 8
struct GIProbeData {
mat4 xform;
vec3 bounds;
float dynamic_range;
float bias;
float normal_bias;
bool blend_ambient;
uint texture_slot;
float anisotropy_strength;
float ambient_occlusion;
float ambient_occlusion_size;
uint mipmaps;
};
layout(set = 0, binding = 16, std140) uniform GIProbes {
GIProbeData data[MAX_GI_PROBES];
}
gi_probes;
layout(set = 0, binding = 17) uniform texture3D gi_probe_textures[MAX_GI_PROBES];
layout(push_constant, binding = 0, std430) uniform Params {
ivec2 screen_size;
float z_near;
float z_far;
vec4 proj_info;
uint max_giprobes;
bool high_quality_vct;
bool use_sdfgi;
bool orthogonal;
vec3 ao_color;
uint pad;
mat3x4 cam_rotation;
}
params;
vec2 octahedron_wrap(vec2 v) {
vec2 signVal;
signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
return (1.0 - abs(v.yx)) * signVal;
}
vec2 octahedron_encode(vec3 n) {
// https://twitter.com/Stubbesaurus/status/937994790553227264
n /= (abs(n.x) + abs(n.y) + abs(n.z));
n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
n.xy = n.xy * 0.5 + 0.5;
return n.xy;
}
vec4 blend_color(vec4 src, vec4 dst) {
vec4 res;
float sa = 1.0 - src.a;
res.a = dst.a * sa + src.a;
if (res.a == 0.0) {
res.rgb = vec3(0);
} else {
res.rgb = (dst.rgb * dst.a * sa + src.rgb * src.a) / res.a;
}
return res;
}
vec3 reconstruct_position(ivec2 screen_pos) {
vec3 pos;
pos.z = texelFetch(sampler2D(depth_buffer, linear_sampler), screen_pos, 0).r;
pos.z = pos.z * 2.0 - 1.0;
if (params.orthogonal) {
pos.z = ((pos.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
pos.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - pos.z * (params.z_far - params.z_near));
}
pos.z = -pos.z;
pos.xy = vec2(screen_pos) * params.proj_info.xy + params.proj_info.zw;
if (!params.orthogonal) {
pos.xy *= pos.z;
}
return pos;
}
void sdfgi_probe_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal, vec3 cam_specular_normal, float roughness, out vec3 diffuse_light, out vec3 specular_light) {
cascade_pos += cam_normal * sdfgi.normal_bias;
vec3 base_pos = floor(cascade_pos);
//cascade_pos += mix(vec3(0.0),vec3(0.01),lessThan(abs(cascade_pos-base_pos),vec3(0.01))) * cam_normal;
ivec3 probe_base_pos = ivec3(base_pos);
vec4 diffuse_accum = vec4(0.0);
vec3 specular_accum;
ivec3 tex_pos = ivec3(probe_base_pos.xy, int(cascade));
tex_pos.x += probe_base_pos.z * sdfgi.probe_axis_size;
tex_pos.xy = tex_pos.xy * (SDFGI_OCT_SIZE + 2) + ivec2(1);
vec3 diffuse_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size;
vec3 specular_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_specular_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size;
specular_accum = vec3(0.0);
vec4 light_accum = vec4(0.0);
float weight_accum = 0.0;
for (uint j = 0; j < 8; j++) {
ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1);
ivec3 probe_posi = probe_base_pos;
probe_posi += offset;
// Compute weight
vec3 probe_pos = vec3(probe_posi);
vec3 probe_to_pos = cascade_pos - probe_pos;
vec3 probe_dir = normalize(-probe_to_pos);
vec3 trilinear = vec3(1.0) - abs(probe_to_pos);
float weight = trilinear.x * trilinear.y * trilinear.z * max(0.005, dot(cam_normal, probe_dir));
// Compute lightprobe occlusion
if (sdfgi.use_occlusion) {
ivec3 occ_indexv = abs((sdfgi.cascades[cascade].probe_world_offset + probe_posi) & ivec3(1, 1, 1)) * ivec3(1, 2, 4);
vec4 occ_mask = mix(vec4(0.0), vec4(1.0), equal(ivec4(occ_indexv.x | occ_indexv.y), ivec4(0, 1, 2, 3)));
vec3 occ_pos = clamp(cascade_pos, probe_pos - sdfgi.occlusion_clamp, probe_pos + sdfgi.occlusion_clamp) * sdfgi.probe_to_uvw;
occ_pos.z += float(cascade);
if (occ_indexv.z != 0) { //z bit is on, means index is >=4, so make it switch to the other half of textures
occ_pos.x += 1.0;
}
occ_pos *= sdfgi.occlusion_renormalize;
float occlusion = dot(textureLod(sampler3D(occlusion_texture, linear_sampler), occ_pos, 0.0), occ_mask);
weight *= max(occlusion, 0.01);
}
// Compute lightprobe texture position
vec3 diffuse;
vec3 pos_uvw = diffuse_posf;
pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy;
pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z;
diffuse = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb;
diffuse_accum += vec4(diffuse * weight, weight);
{
vec3 specular = vec3(0.0);
vec3 pos_uvw = specular_posf;
pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy;
pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z;
if (roughness < 0.99) {
specular = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw + vec3(0, 0, float(sdfgi.max_cascades)), 0.0).rgb;
}
if (roughness > 0.2) {
specular = mix(specular, textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb, (roughness - 0.2) * 1.25);
}
specular_accum += specular * weight;
}
}
if (diffuse_accum.a > 0.0) {
diffuse_accum.rgb /= diffuse_accum.a;
}
diffuse_light = diffuse_accum.rgb;
if (diffuse_accum.a > 0.0) {
specular_accum /= diffuse_accum.a;
}
specular_light = specular_accum;
}
void sdfgi_process(vec3 vertex, vec3 normal, vec3 reflection, float roughness, out vec4 ambient_light, out vec4 reflection_light) {
//make vertex orientation the world one, but still align to camera
vertex.y *= sdfgi.y_mult;
normal.y *= sdfgi.y_mult;
reflection.y *= sdfgi.y_mult;
//renormalize
normal = normalize(normal);
reflection = normalize(reflection);
vec3 cam_pos = vertex;
vec3 cam_normal = normal;
vec4 light_accum = vec4(0.0);
float weight_accum = 0.0;
vec4 light_blend_accum = vec4(0.0);
float weight_blend_accum = 0.0;
float blend = -1.0;
// helper constants, compute once
uint cascade = 0xFFFFFFFF;
vec3 cascade_pos;
vec3 cascade_normal;
for (uint i = 0; i < sdfgi.max_cascades; i++) {
cascade_pos = (cam_pos - sdfgi.cascades[i].position) * sdfgi.cascades[i].to_probe;
if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, sdfgi.cascade_probe_size))) {
continue; //skip cascade
}
cascade = i;
break;
}
if (cascade < SDFGI_MAX_CASCADES) {
ambient_light = vec4(0, 0, 0, 1);
reflection_light = vec4(0, 0, 0, 1);
float blend;
vec3 diffuse, specular;
sdfgi_probe_process(cascade, cascade_pos, cam_pos, cam_normal, reflection, roughness, diffuse, specular);
{
//process blend
float blend_from = (float(sdfgi.probe_axis_size - 1) / 2.0) - 2.5;
float blend_to = blend_from + 2.0;
vec3 inner_pos = cam_pos * sdfgi.cascades[cascade].to_probe;
float len = length(inner_pos);
inner_pos = abs(normalize(inner_pos));
len *= max(inner_pos.x, max(inner_pos.y, inner_pos.z));
if (len >= blend_from) {
blend = smoothstep(blend_from, blend_to, len);
} else {
blend = 0.0;
}
}
if (blend > 0.0) {
//blend
if (cascade == sdfgi.max_cascades - 1) {
ambient_light.a = 1.0 - blend;
reflection_light.a = 1.0 - blend;
} else {
vec3 diffuse2, specular2;
cascade_pos = (cam_pos - sdfgi.cascades[cascade + 1].position) * sdfgi.cascades[cascade + 1].to_probe;
sdfgi_probe_process(cascade + 1, cascade_pos, cam_pos, cam_normal, reflection, roughness, diffuse2, specular2);
diffuse = mix(diffuse, diffuse2, blend);
specular = mix(specular, specular2, blend);
}
}
ambient_light.rgb = diffuse;
#if 1
if (roughness < 0.2) {
vec3 pos_to_uvw = 1.0 / sdfgi.grid_size;
vec4 light_accum = vec4(0.0);
float blend_size = (sdfgi.grid_size.x / float(sdfgi.probe_axis_size - 1)) * 0.5;
float radius_sizes[SDFGI_MAX_CASCADES];
cascade = 0xFFFF;
float base_distance = length(cam_pos);
for (uint i = 0; i < sdfgi.max_cascades; i++) {
radius_sizes[i] = (1.0 / sdfgi.cascades[i].to_cell) * (sdfgi.grid_size.x * 0.5 - blend_size);
if (cascade == 0xFFFF && base_distance < radius_sizes[i]) {
cascade = i;
}
}
cascade = min(cascade, sdfgi.max_cascades - 1);
float max_distance = radius_sizes[sdfgi.max_cascades - 1];
vec3 ray_pos = cam_pos;
vec3 ray_dir = reflection;
{
float prev_radius = cascade > 0 ? radius_sizes[cascade - 1] : 0.0;
float base_blend = (base_distance - prev_radius) / (radius_sizes[cascade] - prev_radius);
float bias = (1.0 + base_blend) * 1.1;
vec3 abs_ray_dir = abs(ray_dir);
//ray_pos += ray_dir * (bias / sdfgi.cascades[cascade].to_cell); //bias to avoid self occlusion
ray_pos += (ray_dir * 1.0 / max(abs_ray_dir.x, max(abs_ray_dir.y, abs_ray_dir.z)) + cam_normal * 1.4) * bias / sdfgi.cascades[cascade].to_cell;
}
float softness = 0.2 + min(1.0, roughness * 5.0) * 4.0; //approximation to roughness so it does not seem like a hard fade
while (length(ray_pos) < max_distance) {
for (uint i = 0; i < sdfgi.max_cascades; i++) {
if (i >= cascade && length(ray_pos) < radius_sizes[i]) {
cascade = max(i, cascade); //never go down
vec3 pos = ray_pos - sdfgi.cascades[i].position;
pos *= sdfgi.cascades[i].to_cell * pos_to_uvw;
float distance = texture(sampler3D(sdf_cascades[i], linear_sampler), pos).r * 255.0 - 1.1;
vec4 hit_light = vec4(0.0);
if (distance < softness) {
hit_light.rgb = texture(sampler3D(light_cascades[i], linear_sampler), pos).rgb;
hit_light.rgb *= 0.5; //approximation given value read is actually meant for anisotropy
hit_light.a = clamp(1.0 - (distance / softness), 0.0, 1.0);
hit_light.rgb *= hit_light.a;
}
distance /= sdfgi.cascades[i].to_cell;
if (i < (sdfgi.max_cascades - 1)) {
pos = ray_pos - sdfgi.cascades[i + 1].position;
pos *= sdfgi.cascades[i + 1].to_cell * pos_to_uvw;
float distance2 = texture(sampler3D(sdf_cascades[i + 1], linear_sampler), pos).r * 255.0 - 1.1;
vec4 hit_light2 = vec4(0.0);
if (distance2 < softness) {
hit_light2.rgb = texture(sampler3D(light_cascades[i + 1], linear_sampler), pos).rgb;
hit_light2.rgb *= 0.5; //approximation given value read is actually meant for anisotropy
hit_light2.a = clamp(1.0 - (distance2 / softness), 0.0, 1.0);
hit_light2.rgb *= hit_light2.a;
}
float prev_radius = i == 0 ? 0.0 : radius_sizes[i - 1];
float blend = clamp((length(ray_pos) - prev_radius) / (radius_sizes[i] - prev_radius), 0.0, 1.0);
distance2 /= sdfgi.cascades[i + 1].to_cell;
hit_light = mix(hit_light, hit_light2, blend);
distance = mix(distance, distance2, blend);
}
light_accum += hit_light;
ray_pos += ray_dir * distance;
break;
}
}
if (light_accum.a > 0.99) {
break;
}
}
vec3 light = light_accum.rgb / max(light_accum.a, 0.00001);
float alpha = min(1.0, light_accum.a);
float b = min(1.0, roughness * 5.0);
float sa = 1.0 - b;
reflection_light.a = alpha * sa + b;
if (reflection_light.a == 0) {
specular = vec3(0.0);
} else {
specular = (light * alpha * sa + specular * b) / reflection_light.a;
}
}
#endif
reflection_light.rgb = specular;
ambient_light.rgb *= sdfgi.energy;
reflection_light.rgb *= sdfgi.energy;
} else {
ambient_light = vec4(0);
reflection_light = vec4(0);
}
}
//standard voxel cone trace
vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) {
float dist = p_bias;
vec4 color = vec4(0.0);
while (dist < max_distance && color.a < 0.95) {
float diameter = max(1.0, 2.0 * tan_half_angle * dist);
vec3 uvw_pos = (pos + dist * direction) * cell_size;
float half_diameter = diameter * 0.5;
//check if outside, then break
if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + half_diameter * cell_size)))) {
break;
}
vec4 scolor = textureLod(sampler3D(probe, linear_sampler_with_mipmaps), uvw_pos, log2(diameter));
float a = (1.0 - color.a);
color += a * scolor;
dist += half_diameter;
}
return color;
}
vec4 voxel_cone_trace_45_degrees(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, float max_distance, float p_bias) {
float dist = p_bias;
vec4 color = vec4(0.0);
float radius = max(0.5, dist);
float lod_level = log2(radius * 2.0);
while (dist < max_distance && color.a < 0.95) {
vec3 uvw_pos = (pos + dist * direction) * cell_size;
//check if outside, then break
if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + radius * cell_size)))) {
break;
}
vec4 scolor = textureLod(sampler3D(probe, linear_sampler_with_mipmaps), uvw_pos, lod_level);
lod_level += 1.0;
float a = (1.0 - color.a);
scolor *= a;
color += scolor;
dist += radius;
radius = max(0.5, dist);
}
return color;
}
void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 normal_xform, float roughness, inout vec4 out_spec, inout vec4 out_diff, inout float out_blend) {
position = (gi_probes.data[index].xform * vec4(position, 1.0)).xyz;
ref_vec = normalize((gi_probes.data[index].xform * vec4(ref_vec, 0.0)).xyz);
normal = normalize((gi_probes.data[index].xform * vec4(normal, 0.0)).xyz);
position += normal * gi_probes.data[index].normal_bias;
//this causes corrupted pixels, i have no idea why..
if (any(bvec2(any(lessThan(position, vec3(0.0))), any(greaterThan(position, gi_probes.data[index].bounds))))) {
return;
}
mat3 dir_xform = mat3(gi_probes.data[index].xform) * normal_xform;
vec3 blendv = abs(position / gi_probes.data[index].bounds * 2.0 - 1.0);
float blend = clamp(1.0 - max(blendv.x, max(blendv.y, blendv.z)), 0.0, 1.0);
//float blend=1.0;
float max_distance = length(gi_probes.data[index].bounds);
vec3 cell_size = 1.0 / gi_probes.data[index].bounds;
//irradiance
vec4 light = vec4(0.0);
if (params.high_quality_vct) {
const uint cone_dir_count = 6;
vec3 cone_dirs[cone_dir_count] = vec3[](
vec3(0.0, 0.0, 1.0),
vec3(0.866025, 0.0, 0.5),
vec3(0.267617, 0.823639, 0.5),
vec3(-0.700629, 0.509037, 0.5),
vec3(-0.700629, -0.509037, 0.5),
vec3(0.267617, -0.823639, 0.5));
float cone_weights[cone_dir_count] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15);
float cone_angle_tan = 0.577;
for (uint i = 0; i < cone_dir_count; i++) {
vec3 dir = normalize(dir_xform * cone_dirs[i]);
light += cone_weights[i] * voxel_cone_trace(gi_probe_textures[index], cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
}
} else {
const uint cone_dir_count = 4;
vec3 cone_dirs[cone_dir_count] = vec3[](
vec3(0.707107, 0.0, 0.707107),
vec3(0.0, 0.707107, 0.707107),
vec3(-0.707107, 0.0, 0.707107),
vec3(0.0, -0.707107, 0.707107));
float cone_weights[cone_dir_count] = float[](0.25, 0.25, 0.25, 0.25);
for (int i = 0; i < cone_dir_count; i++) {
vec3 dir = normalize(dir_xform * cone_dirs[i]);
light += cone_weights[i] * voxel_cone_trace_45_degrees(gi_probe_textures[index], cell_size, position, dir, max_distance, gi_probes.data[index].bias);
}
}
if (gi_probes.data[index].ambient_occlusion > 0.001) {
float size = 1.0 + gi_probes.data[index].ambient_occlusion_size * 7.0;
float taps, blend;
blend = modf(size, taps);
float ao = 0.0;
for (float i = 1.0; i <= taps; i++) {
vec3 ofs = (position + normal * (i * 0.5 + 1.0)) * cell_size;
ao += textureLod(sampler3D(gi_probe_textures[index], linear_sampler_with_mipmaps), ofs, i - 1.0).a * i;
}
if (blend > 0.001) {
vec3 ofs = (position + normal * ((taps + 1.0) * 0.5 + 1.0)) * cell_size;
ao += textureLod(sampler3D(gi_probe_textures[index], linear_sampler_with_mipmaps), ofs, taps).a * (taps + 1.0) * blend;
}
ao = 1.0 - min(1.0, ao);
light.rgb = mix(params.ao_color, light.rgb, mix(1.0, ao, gi_probes.data[index].ambient_occlusion));
}
light.rgb *= gi_probes.data[index].dynamic_range;
if (!gi_probes.data[index].blend_ambient) {
light.a = 1.0;
}
out_diff += light * blend;
//radiance
vec4 irr_light = voxel_cone_trace(gi_probe_textures[index], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, gi_probes.data[index].bias);
irr_light.rgb *= gi_probes.data[index].dynamic_range;
if (!gi_probes.data[index].blend_ambient) {
irr_light.a = 1.0;
}
out_spec += irr_light * blend;
out_blend += blend;
}
vec4 fetch_normal_and_roughness(ivec2 pos) {
vec4 normal_roughness = texelFetch(sampler2D(normal_roughness_buffer, linear_sampler), pos, 0);
normal_roughness.xyz = normalize(normal_roughness.xyz * 2.0 - 1.0);
return normal_roughness;
}
void main() {
// Pixel being shaded
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(pos, params.screen_size))) { //too large, do nothing
return;
}
vec3 vertex = reconstruct_position(pos);
vertex.y = -vertex.y;
vec4 normal_roughness = fetch_normal_and_roughness(pos);
vec3 normal = normal_roughness.xyz;
vec4 ambient_light = vec4(0.0), reflection_light = vec4(0.0);
if (normal.length() > 0.5) {
//valid normal, can do GI
float roughness = normal_roughness.w;
vertex = mat3(params.cam_rotation) * vertex;
normal = normalize(mat3(params.cam_rotation) * normal);
vec3 reflection = normalize(reflect(normalize(vertex), normal));
if (params.use_sdfgi) {
sdfgi_process(vertex, normal, reflection, roughness, ambient_light, reflection_light);
}
if (params.max_giprobes > 0) {
uvec2 giprobe_tex = texelFetch(usampler2D(giprobe_buffer, linear_sampler), pos, 0).rg;
roughness *= roughness;
//find arbitrary tangent and bitangent, then build a matrix
vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
vec3 tangent = normalize(cross(v0, normal));
vec3 bitangent = normalize(cross(tangent, normal));
mat3 normal_mat = mat3(tangent, bitangent, normal);
vec4 amb_accum = vec4(0.0);
vec4 spec_accum = vec4(0.0);
float blend_accum = 0.0;
for (uint i = 0; i < params.max_giprobes; i++) {
if (any(equal(uvec2(i), giprobe_tex))) {
gi_probe_compute(i, vertex, normal, reflection, normal_mat, roughness, spec_accum, amb_accum, blend_accum);
}
}
if (blend_accum > 0.0) {
amb_accum /= blend_accum;
spec_accum /= blend_accum;
}
if (params.use_sdfgi) {
reflection_light = blend_color(spec_accum, reflection_light);
ambient_light = blend_color(amb_accum, ambient_light);
} else {
reflection_light = spec_accum;
ambient_light = amb_accum;
}
}
}
imageStore(ambient_buffer, pos, ambient_light);
imageStore(reflection_buffer, pos, reflection_light);
}

View File

@@ -0,0 +1,768 @@
#[compute]
#version 450
VERSION_DEFINES
#ifdef MODE_DYNAMIC
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#else
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
#endif
#ifndef MODE_DYNAMIC
#define NO_CHILDREN 0xFFFFFFFF
#define GREY_VEC vec3(0.33333, 0.33333, 0.33333)
struct CellChildren {
uint children[8];
};
layout(set = 0, binding = 1, std430) buffer CellChildrenBuffer {
CellChildren data[];
}
cell_children;
struct CellData {
uint position; // xyz 10 bits
uint albedo; //rgb albedo
uint emission; //rgb normalized with e as multiplier
uint normal; //RGB normal encoded
};
layout(set = 0, binding = 2, std430) buffer CellDataBuffer {
CellData data[];
}
cell_data;
#endif // MODE DYNAMIC
#define LIGHT_TYPE_DIRECTIONAL 0
#define LIGHT_TYPE_OMNI 1
#define LIGHT_TYPE_SPOT 2
#if defined(MODE_COMPUTE_LIGHT) || defined(MODE_DYNAMIC_LIGHTING)
struct Light {
uint type;
float energy;
float radius;
float attenuation;
vec3 color;
float spot_angle_radians;
vec3 position;
float spot_attenuation;
vec3 direction;
bool has_shadow;
};
layout(set = 0, binding = 3, std140) uniform Lights {
Light data[MAX_LIGHTS];
}
lights;
#endif // MODE COMPUTE LIGHT
#ifdef MODE_SECOND_BOUNCE
layout(set = 0, binding = 5) uniform texture3D color_texture;
#ifdef MODE_ANISOTROPIC
layout(set = 0, binding = 7) uniform texture3D aniso_pos_texture;
layout(set = 0, binding = 8) uniform texture3D aniso_neg_texture;
#endif // MODE ANISOTROPIC
#endif // MODE_SECOND_BOUNCE
#ifndef MODE_DYNAMIC
layout(push_constant, binding = 0, std430) uniform Params {
ivec3 limits;
uint stack_size;
float emission_scale;
float propagation;
float dynamic_range;
uint light_count;
uint cell_offset;
uint cell_count;
float aniso_strength;
uint pad;
}
params;
layout(set = 0, binding = 4, std430) buffer Outputs {
vec4 data[];
}
outputs;
#endif // MODE DYNAMIC
layout(set = 0, binding = 9) uniform texture3D texture_sdf;
layout(set = 0, binding = 10) uniform sampler texture_sampler;
#ifdef MODE_WRITE_TEXTURE
layout(rgba8, set = 0, binding = 5) uniform restrict writeonly image3D color_tex;
#ifdef MODE_ANISOTROPIC
layout(r16ui, set = 0, binding = 6) uniform restrict writeonly uimage3D aniso_pos_tex;
layout(r16ui, set = 0, binding = 7) uniform restrict writeonly uimage3D aniso_neg_tex;
#endif
#endif
#ifdef MODE_DYNAMIC
layout(push_constant, binding = 0, std430) uniform Params {
ivec3 limits;
uint light_count; //when not lighting
ivec3 x_dir;
float z_base;
ivec3 y_dir;
float z_sign;
ivec3 z_dir;
float pos_multiplier;
ivec2 rect_pos;
ivec2 rect_size;
ivec2 prev_rect_ofs;
ivec2 prev_rect_size;
bool flip_x;
bool flip_y;
float dynamic_range;
bool on_mipmap;
float propagation;
float pad[3];
}
params;
#ifdef MODE_DYNAMIC_LIGHTING
layout(rgba8, set = 0, binding = 5) uniform restrict readonly image2D source_albedo;
layout(rgba8, set = 0, binding = 6) uniform restrict readonly image2D source_normal;
layout(rgba8, set = 0, binding = 7) uniform restrict readonly image2D source_orm;
//layout (set=0,binding=8) uniform texture2D source_depth;
layout(rgba16f, set = 0, binding = 11) uniform restrict image2D emission;
layout(r32f, set = 0, binding = 12) uniform restrict image2D depth;
#endif
#ifdef MODE_DYNAMIC_SHRINK
layout(rgba16f, set = 0, binding = 5) uniform restrict readonly image2D source_light;
layout(r32f, set = 0, binding = 6) uniform restrict readonly image2D source_depth;
#ifdef MODE_DYNAMIC_SHRINK_WRITE
layout(rgba16f, set = 0, binding = 7) uniform restrict writeonly image2D light;
layout(r32f, set = 0, binding = 8) uniform restrict writeonly image2D depth;
#endif // MODE_DYNAMIC_SHRINK_WRITE
#ifdef MODE_DYNAMIC_SHRINK_PLOT
layout(rgba8, set = 0, binding = 11) uniform restrict image3D color_texture;
#ifdef MODE_ANISOTROPIC
layout(r16ui, set = 0, binding = 12) uniform restrict writeonly uimage3D aniso_pos_texture;
layout(r16ui, set = 0, binding = 13) uniform restrict writeonly uimage3D aniso_neg_texture;
#endif // MODE ANISOTROPIC
#endif //MODE_DYNAMIC_SHRINK_PLOT
#endif // MODE_DYNAMIC_SHRINK
//layout (rgba8,set=0,binding=5) uniform restrict writeonly image3D color_tex;
#endif // MODE DYNAMIC
#if defined(MODE_COMPUTE_LIGHT) || defined(MODE_DYNAMIC_LIGHTING)
float raymarch(float distance, float distance_adv, vec3 from, vec3 direction) {
vec3 cell_size = 1.0 / vec3(params.limits);
float occlusion = 1.0;
while (distance > 0.5) { //use this to avoid precision errors
float advance = texture(sampler3D(texture_sdf, texture_sampler), from * cell_size).r * 255.0 - 1.0;
if (advance < 0.0) {
occlusion = 0.0;
break;
}
occlusion = min(advance, occlusion);
advance = max(distance_adv, advance - mod(advance, distance_adv)); //should always advance in multiples of distance_adv
from += direction * advance;
distance -= advance;
}
return occlusion; //max(0.0,distance);
}
bool compute_light_vector(uint light, vec3 pos, out float attenuation, out vec3 light_pos) {
if (lights.data[light].type == LIGHT_TYPE_DIRECTIONAL) {
light_pos = pos - lights.data[light].direction * length(vec3(params.limits));
attenuation = 1.0;
} else {
light_pos = lights.data[light].position;
float distance = length(pos - light_pos);
if (distance >= lights.data[light].radius) {
return false;
}
attenuation = pow(clamp(1.0 - distance / lights.data[light].radius, 0.0001, 1.0), lights.data[light].attenuation);
if (lights.data[light].type == LIGHT_TYPE_SPOT) {
vec3 rel = normalize(pos - light_pos);
float angle = acos(dot(rel, lights.data[light].direction));
if (angle > lights.data[light].spot_angle_radians) {
return false;
}
float d = clamp(angle / lights.data[light].spot_angle_radians, 0, 1);
attenuation *= pow(1.0 - d, lights.data[light].spot_attenuation);
}
}
return true;
}
float get_normal_advance(vec3 p_normal) {
vec3 normal = p_normal;
vec3 unorm = abs(normal);
if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
// x code
unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0);
} else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
// y code
unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0);
} else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
// z code
unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0);
} else {
// oh-no we messed up code
// has to be
unorm = vec3(1.0, 0.0, 0.0);
}
return 1.0 / dot(normal, unorm);
}
void clip_segment(vec4 plane, vec3 begin, inout vec3 end) {
vec3 segment = begin - end;
float den = dot(plane.xyz, segment);
//printf("den is %i\n",den);
if (den < 0.0001) {
return;
}
float dist = (dot(plane.xyz, begin) - plane.w) / den;
if (dist < 0.0001 || dist > 1.0001) {
return;
}
end = begin + segment * -dist;
}
bool compute_light_at_pos(uint index, vec3 pos, vec3 normal, inout vec3 light, inout vec3 light_dir) {
float attenuation;
vec3 light_pos;
if (!compute_light_vector(index, pos, attenuation, light_pos)) {
return false;
}
light_dir = normalize(pos - light_pos);
if (attenuation < 0.01 || (length(normal) > 0.2 && dot(normal, light_dir) >= 0)) {
return false; //not facing the light, or attenuation is near zero
}
if (lights.data[index].has_shadow) {
float distance_adv = get_normal_advance(light_dir);
vec3 to = pos;
if (length(normal) > 0.2) {
to += normal * distance_adv * 0.51;
} else {
to -= sign(light_dir) * 0.45; //go near the edge towards the light direction to avoid self occlusion
}
//clip
clip_segment(mix(vec4(-1.0, 0.0, 0.0, 0.0), vec4(1.0, 0.0, 0.0, float(params.limits.x - 1)), bvec4(light_dir.x < 0.0)), to, light_pos);
clip_segment(mix(vec4(0.0, -1.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, float(params.limits.y - 1)), bvec4(light_dir.y < 0.0)), to, light_pos);
clip_segment(mix(vec4(0.0, 0.0, -1.0, 0.0), vec4(0.0, 0.0, 1.0, float(params.limits.z - 1)), bvec4(light_dir.z < 0.0)), to, light_pos);
float distance = length(to - light_pos);
if (distance < 0.1) {
return false; // hit
}
distance += distance_adv - mod(distance, distance_adv); //make it reach the center of the box always
light_pos = to - light_dir * distance;
//from -= sign(light_dir)*0.45; //go near the edge towards the light direction to avoid self occlusion
/*float dist = raymarch(distance,distance_adv,light_pos,light_dir);
if (dist > distance_adv) {
return false;
}
attenuation *= 1.0 - smoothstep(0.1*distance_adv,distance_adv,dist);
*/
float occlusion = raymarch(distance, distance_adv, light_pos, light_dir);
if (occlusion == 0.0) {
return false;
}
attenuation *= occlusion; //1.0 - smoothstep(0.1*distance_adv,distance_adv,dist);
}
light = lights.data[index].color * attenuation * lights.data[index].energy;
return true;
}
#endif // MODE COMPUTE LIGHT
void main() {
#ifndef MODE_DYNAMIC
uint cell_index = gl_GlobalInvocationID.x;
if (cell_index >= params.cell_count) {
return;
}
cell_index += params.cell_offset;
uvec3 posu = uvec3(cell_data.data[cell_index].position & 0x7FF, (cell_data.data[cell_index].position >> 11) & 0x3FF, cell_data.data[cell_index].position >> 21);
vec4 albedo = unpackUnorm4x8(cell_data.data[cell_index].albedo);
#endif
/////////////////COMPUTE LIGHT///////////////////////////////
#ifdef MODE_COMPUTE_LIGHT
vec3 pos = vec3(posu) + vec3(0.5);
vec3 emission = vec3(uvec3(cell_data.data[cell_index].emission & 0x1ff, (cell_data.data[cell_index].emission >> 9) & 0x1ff, (cell_data.data[cell_index].emission >> 18) & 0x1ff)) * pow(2.0, float(cell_data.data[cell_index].emission >> 27) - 15.0 - 9.0);
vec3 normal = unpackSnorm4x8(cell_data.data[cell_index].normal).xyz;
#ifdef MODE_ANISOTROPIC
vec3 accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0));
#else
vec3 accum = vec3(0.0);
#endif
for (uint i = 0; i < params.light_count; i++) {
vec3 light;
vec3 light_dir;
if (!compute_light_at_pos(i, pos, normal.xyz, light, light_dir)) {
continue;
}
light *= albedo.rgb;
#ifdef MODE_ANISOTROPIC
for (uint j = 0; j < 6; j++) {
accum[j] += max(0.0, dot(accum_dirs[j], -light_dir)) * light;
}
#else
if (length(normal) > 0.2) {
accum += max(0.0, dot(normal, -light_dir)) * light;
} else {
//all directions
accum += light;
}
#endif
}
#ifdef MODE_ANISOTROPIC
for (uint i = 0; i < 6; i++) {
vec3 light = accum[i];
if (length(normal) > 0.2) {
light += max(0.0, dot(accum_dirs[i], -normal)) * emission;
} else {
light += emission;
}
outputs.data[cell_index * 6 + i] = vec4(light, 0.0);
}
#else
outputs.data[cell_index] = vec4(accum + emission, 0.0);
#endif
#endif //MODE_COMPUTE_LIGHT
/////////////////SECOND BOUNCE///////////////////////////////
#ifdef MODE_SECOND_BOUNCE
vec3 pos = vec3(posu) + vec3(0.5);
ivec3 ipos = ivec3(posu);
vec4 normal = unpackSnorm4x8(cell_data.data[cell_index].normal);
#ifdef MODE_ANISOTROPIC
vec3 accum[6];
const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0));
/*vec3 src_color = texelFetch(sampler3D(color_texture,texture_sampler),ipos,0).rgb * params.dynamic_range;
vec3 src_aniso_pos = texelFetch(sampler3D(aniso_pos_texture,texture_sampler),ipos,0).rgb;
vec3 src_anisp_neg = texelFetch(sampler3D(anisp_neg_texture,texture_sampler),ipos,0).rgb;
accum[0]=src_col * src_aniso_pos.x;
accum[1]=src_col * src_aniso_neg.x;
accum[2]=src_col * src_aniso_pos.y;
accum[3]=src_col * src_aniso_neg.y;
accum[4]=src_col * src_aniso_pos.z;
accum[5]=src_col * src_aniso_neg.z;*/
accum[0] = outputs.data[cell_index * 6 + 0].rgb;
accum[1] = outputs.data[cell_index * 6 + 1].rgb;
accum[2] = outputs.data[cell_index * 6 + 2].rgb;
accum[3] = outputs.data[cell_index * 6 + 3].rgb;
accum[4] = outputs.data[cell_index * 6 + 4].rgb;
accum[5] = outputs.data[cell_index * 6 + 5].rgb;
#else
vec3 accum = outputs.data[cell_index].rgb;
#endif
if (length(normal.xyz) > 0.2) {
vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
vec3 tangent = normalize(cross(v0, normal.xyz));
vec3 bitangent = normalize(cross(tangent, normal.xyz));
mat3 normal_mat = mat3(tangent, bitangent, normal.xyz);
#define MAX_CONE_DIRS 6
vec3 cone_dirs[MAX_CONE_DIRS] = vec3[](
vec3(0.0, 0.0, 1.0),
vec3(0.866025, 0.0, 0.5),
vec3(0.267617, 0.823639, 0.5),
vec3(-0.700629, 0.509037, 0.5),
vec3(-0.700629, -0.509037, 0.5),
vec3(0.267617, -0.823639, 0.5));
float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15);
float tan_half_angle = 0.577;
for (int i = 0; i < MAX_CONE_DIRS; i++) {
vec3 direction = normal_mat * cone_dirs[i];
vec4 color = vec4(0.0);
{
float dist = 1.5;
float max_distance = length(vec3(params.limits));
vec3 cell_size = 1.0 / vec3(params.limits);
#ifdef MODE_ANISOTROPIC
vec3 aniso_normal = mix(direction, normal.xyz, params.aniso_strength);
#endif
while (dist < max_distance && color.a < 0.95) {
float diameter = max(1.0, 2.0 * tan_half_angle * dist);
vec3 uvw_pos = (pos + dist * direction) * cell_size;
float half_diameter = diameter * 0.5;
//check if outside, then break
//if ( any(greaterThan(abs(uvw_pos - 0.5),vec3(0.5f + half_diameter * cell_size)) ) ) {
// break;
//}
float log2_diameter = log2(diameter);
vec4 scolor = textureLod(sampler3D(color_texture, texture_sampler), uvw_pos, log2_diameter);
#ifdef MODE_ANISOTROPIC
vec3 aniso_neg = textureLod(sampler3D(aniso_neg_texture, texture_sampler), uvw_pos, log2_diameter).rgb;
vec3 aniso_pos = textureLod(sampler3D(aniso_pos_texture, texture_sampler), uvw_pos, log2_diameter).rgb;
scolor.rgb *= dot(max(vec3(0.0), (aniso_normal * aniso_pos)), vec3(1.0)) + dot(max(vec3(0.0), (-aniso_normal * aniso_neg)), vec3(1.0));
#endif
float a = (1.0 - color.a);
color += a * scolor;
dist += half_diameter;
}
}
color *= cone_weights[i] * vec4(albedo.rgb, 1.0) * params.dynamic_range; //restore range
#ifdef MODE_ANISOTROPIC
for (uint j = 0; j < 6; j++) {
accum[j] += max(0.0, dot(accum_dirs[j], direction)) * color.rgb;
}
#else
accum += color.rgb;
#endif
}
}
#ifdef MODE_ANISOTROPIC
outputs.data[cell_index * 6 + 0] = vec4(accum[0], 0.0);
outputs.data[cell_index * 6 + 1] = vec4(accum[1], 0.0);
outputs.data[cell_index * 6 + 2] = vec4(accum[2], 0.0);
outputs.data[cell_index * 6 + 3] = vec4(accum[3], 0.0);
outputs.data[cell_index * 6 + 4] = vec4(accum[4], 0.0);
outputs.data[cell_index * 6 + 5] = vec4(accum[5], 0.0);
#else
outputs.data[cell_index] = vec4(accum, 0.0);
#endif
#endif // MODE_SECOND_BOUNCE
/////////////////UPDATE MIPMAPS///////////////////////////////
#ifdef MODE_UPDATE_MIPMAPS
{
#ifdef MODE_ANISOTROPIC
vec3 light_accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
#else
vec3 light_accum = vec3(0.0);
#endif
float count = 0.0;
for (uint i = 0; i < 8; i++) {
uint child_index = cell_children.data[cell_index].children[i];
if (child_index == NO_CHILDREN) {
continue;
}
#ifdef MODE_ANISOTROPIC
light_accum[0] += outputs.data[child_index * 6 + 0].rgb;
light_accum[1] += outputs.data[child_index * 6 + 1].rgb;
light_accum[2] += outputs.data[child_index * 6 + 2].rgb;
light_accum[3] += outputs.data[child_index * 6 + 3].rgb;
light_accum[4] += outputs.data[child_index * 6 + 4].rgb;
light_accum[5] += outputs.data[child_index * 6 + 5].rgb;
#else
light_accum += outputs.data[child_index].rgb;
#endif
count += 1.0;
}
float divisor = mix(8.0, count, params.propagation);
#ifdef MODE_ANISOTROPIC
outputs.data[cell_index * 6 + 0] = vec4(light_accum[0] / divisor, 0.0);
outputs.data[cell_index * 6 + 1] = vec4(light_accum[1] / divisor, 0.0);
outputs.data[cell_index * 6 + 2] = vec4(light_accum[2] / divisor, 0.0);
outputs.data[cell_index * 6 + 3] = vec4(light_accum[3] / divisor, 0.0);
outputs.data[cell_index * 6 + 4] = vec4(light_accum[4] / divisor, 0.0);
outputs.data[cell_index * 6 + 5] = vec4(light_accum[5] / divisor, 0.0);
#else
outputs.data[cell_index] = vec4(light_accum / divisor, 0.0);
#endif
}
#endif
///////////////////WRITE TEXTURE/////////////////////////////
#ifdef MODE_WRITE_TEXTURE
{
#ifdef MODE_ANISOTROPIC
vec3 accum_total = vec3(0.0);
accum_total += outputs.data[cell_index * 6 + 0].rgb;
accum_total += outputs.data[cell_index * 6 + 1].rgb;
accum_total += outputs.data[cell_index * 6 + 2].rgb;
accum_total += outputs.data[cell_index * 6 + 3].rgb;
accum_total += outputs.data[cell_index * 6 + 4].rgb;
accum_total += outputs.data[cell_index * 6 + 5].rgb;
float accum_total_energy = max(dot(accum_total, GREY_VEC), 0.00001);
vec3 iso_positive = vec3(dot(outputs.data[cell_index * 6 + 0].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 2].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 4].rgb, GREY_VEC)) / vec3(accum_total_energy);
vec3 iso_negative = vec3(dot(outputs.data[cell_index * 6 + 1].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 3].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 5].rgb, GREY_VEC)) / vec3(accum_total_energy);
{
uint aniso_pos = uint(clamp(iso_positive.b * 31.0, 0.0, 31.0));
aniso_pos |= uint(clamp(iso_positive.g * 63.0, 0.0, 63.0)) << 5;
aniso_pos |= uint(clamp(iso_positive.r * 31.0, 0.0, 31.0)) << 11;
imageStore(aniso_pos_tex, ivec3(posu), uvec4(aniso_pos));
}
{
uint aniso_neg = uint(clamp(iso_negative.b * 31.0, 0.0, 31.0));
aniso_neg |= uint(clamp(iso_negative.g * 63.0, 0.0, 63.0)) << 5;
aniso_neg |= uint(clamp(iso_negative.r * 31.0, 0.0, 31.0)) << 11;
imageStore(aniso_neg_tex, ivec3(posu), uvec4(aniso_neg));
}
imageStore(color_tex, ivec3(posu), vec4(accum_total / params.dynamic_range, albedo.a));
#else
imageStore(color_tex, ivec3(posu), vec4(outputs.data[cell_index].rgb / params.dynamic_range, albedo.a));
#endif
}
#endif
///////////////////DYNAMIC LIGHTING/////////////////////////////
#ifdef MODE_DYNAMIC
ivec2 pos_xy = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(pos_xy, params.rect_size))) {
return; //out of bounds
}
ivec2 uv_xy = pos_xy;
if (params.flip_x) {
uv_xy.x = params.rect_size.x - pos_xy.x - 1;
}
if (params.flip_y) {
uv_xy.y = params.rect_size.y - pos_xy.y - 1;
}
#ifdef MODE_DYNAMIC_LIGHTING
{
float z = params.z_base + imageLoad(depth, uv_xy).x * params.z_sign;
ivec3 pos = params.x_dir * (params.rect_pos.x + pos_xy.x) + params.y_dir * (params.rect_pos.y + pos_xy.y) + abs(params.z_dir) * int(z);
vec3 normal = imageLoad(source_normal, uv_xy).xyz * 2.0 - 1.0;
normal = vec3(params.x_dir) * normal.x * mix(1.0, -1.0, params.flip_x) + vec3(params.y_dir) * normal.y * mix(1.0, -1.0, params.flip_y) - vec3(params.z_dir) * normal.z;
vec4 albedo = imageLoad(source_albedo, uv_xy);
//determine the position in space
vec3 accum = vec3(0.0);
for (uint i = 0; i < params.light_count; i++) {
vec3 light;
vec3 light_dir;
if (!compute_light_at_pos(i, vec3(pos) * params.pos_multiplier, normal, light, light_dir)) {
continue;
}
light *= albedo.rgb;
accum += max(0.0, dot(normal, -light_dir)) * light;
}
accum += imageLoad(emission, uv_xy).xyz;
imageStore(emission, uv_xy, vec4(accum, albedo.a));
imageStore(depth, uv_xy, vec4(z));
}
#endif // MODE DYNAMIC LIGHTING
#ifdef MODE_DYNAMIC_SHRINK
{
vec4 accum = vec4(0.0);
float accum_z = 0.0;
float count = 0.0;
for (int i = 0; i < 4; i++) {
ivec2 ofs = pos_xy * 2 + ivec2(i & 1, i >> 1) - params.prev_rect_ofs;
if (any(lessThan(ofs, ivec2(0))) || any(greaterThanEqual(ofs, params.prev_rect_size))) {
continue;
}
if (params.flip_x) {
ofs.x = params.prev_rect_size.x - ofs.x - 1;
}
if (params.flip_y) {
ofs.y = params.prev_rect_size.y - ofs.y - 1;
}
vec4 light = imageLoad(source_light, ofs);
if (light.a == 0.0) { //ignore empty
continue;
}
accum += light;
float z = imageLoad(source_depth, ofs).x;
accum_z += z * 0.5; //shrink half too
count += 1.0;
}
if (params.on_mipmap) {
accum.rgb /= mix(8.0, count, params.propagation);
accum.a /= 8.0;
} else {
accum /= 4.0;
}
if (count == 0.0) {
accum_z = 0.0; //avoid nan
} else {
accum_z /= count;
}
#ifdef MODE_DYNAMIC_SHRINK_WRITE
imageStore(light, uv_xy, accum);
imageStore(depth, uv_xy, vec4(accum_z));
#endif
#ifdef MODE_DYNAMIC_SHRINK_PLOT
if (accum.a < 0.001) {
return; //do not blit if alpha is too low
}
ivec3 pos = params.x_dir * (params.rect_pos.x + pos_xy.x) + params.y_dir * (params.rect_pos.y + pos_xy.y) + abs(params.z_dir) * int(accum_z);
float z_frac = fract(accum_z);
for (int i = 0; i < 2; i++) {
ivec3 pos3d = pos + abs(params.z_dir) * i;
if (any(lessThan(pos3d, ivec3(0))) || any(greaterThanEqual(pos3d, params.limits))) {
//skip if offlimits
continue;
}
vec4 color_blit = accum * (i == 0 ? 1.0 - z_frac : z_frac);
vec4 color = imageLoad(color_texture, pos3d);
color.rgb *= params.dynamic_range;
#if 0
color.rgb = mix(color.rgb,color_blit.rgb,color_blit.a);
color.a+=color_blit.a;
#else
float sa = 1.0 - color_blit.a;
vec4 result;
result.a = color.a * sa + color_blit.a;
if (result.a == 0.0) {
result = vec4(0.0);
} else {
result.rgb = (color.rgb * color.a * sa + color_blit.rgb * color_blit.a) / result.a;
color = result;
}
#endif
color.rgb /= params.dynamic_range;
imageStore(color_texture, pos3d, color);
//imageStore(color_texture,pos3d,vec4(1,1,1,1));
#ifdef MODE_ANISOTROPIC
//do not care about anisotropy for dynamic objects, just store full lit in all directions
imageStore(aniso_pos_texture, pos3d, uvec4(0xFFFF));
imageStore(aniso_neg_texture, pos3d, uvec4(0xFFFF));
#endif // ANISOTROPIC
}
#endif // MODE_DYNAMIC_SHRINK_PLOT
}
#endif
#endif // MODE DYNAMIC
}

View File

@@ -0,0 +1,229 @@
#[vertex]
#version 450
VERSION_DEFINES
struct CellData {
uint position; // xyz 10 bits
uint albedo; //rgb albedo
uint emission; //rgb normalized with e as multiplier
uint normal; //RGB normal encoded
};
layout(set = 0, binding = 1, std140) buffer CellDataBuffer {
CellData data[];
}
cell_data;
layout(set = 0, binding = 2) uniform texture3D color_tex;
layout(set = 0, binding = 3) uniform sampler tex_sampler;
#ifdef USE_ANISOTROPY
layout(set = 0, binding = 4) uniform texture3D aniso_pos_tex;
layout(set = 0, binding = 5) uniform texture3D aniso_neg_tex;
#endif
layout(push_constant, binding = 0, std430) uniform Params {
mat4 projection;
uint cell_offset;
float dynamic_range;
float alpha;
uint level;
ivec3 bounds;
uint pad;
}
params;
layout(location = 0) out vec4 color_interp;
void main() {
const vec3 cube_triangles[36] = vec3[](
vec3(-1.0f, -1.0f, -1.0f),
vec3(-1.0f, -1.0f, 1.0f),
vec3(-1.0f, 1.0f, 1.0f),
vec3(1.0f, 1.0f, -1.0f),
vec3(-1.0f, -1.0f, -1.0f),
vec3(-1.0f, 1.0f, -1.0f),
vec3(1.0f, -1.0f, 1.0f),
vec3(-1.0f, -1.0f, -1.0f),
vec3(1.0f, -1.0f, -1.0f),
vec3(1.0f, 1.0f, -1.0f),
vec3(1.0f, -1.0f, -1.0f),
vec3(-1.0f, -1.0f, -1.0f),
vec3(-1.0f, -1.0f, -1.0f),
vec3(-1.0f, 1.0f, 1.0f),
vec3(-1.0f, 1.0f, -1.0f),
vec3(1.0f, -1.0f, 1.0f),
vec3(-1.0f, -1.0f, 1.0f),
vec3(-1.0f, -1.0f, -1.0f),
vec3(-1.0f, 1.0f, 1.0f),
vec3(-1.0f, -1.0f, 1.0f),
vec3(1.0f, -1.0f, 1.0f),
vec3(1.0f, 1.0f, 1.0f),
vec3(1.0f, -1.0f, -1.0f),
vec3(1.0f, 1.0f, -1.0f),
vec3(1.0f, -1.0f, -1.0f),
vec3(1.0f, 1.0f, 1.0f),
vec3(1.0f, -1.0f, 1.0f),
vec3(1.0f, 1.0f, 1.0f),
vec3(1.0f, 1.0f, -1.0f),
vec3(-1.0f, 1.0f, -1.0f),
vec3(1.0f, 1.0f, 1.0f),
vec3(-1.0f, 1.0f, -1.0f),
vec3(-1.0f, 1.0f, 1.0f),
vec3(1.0f, 1.0f, 1.0f),
vec3(-1.0f, 1.0f, 1.0f),
vec3(1.0f, -1.0f, 1.0f));
vec3 vertex = cube_triangles[gl_VertexIndex] * 0.5 + 0.5;
#ifdef MODE_DEBUG_LIGHT_FULL
uvec3 posu = uvec3(gl_InstanceIndex % params.bounds.x, (gl_InstanceIndex / params.bounds.x) % params.bounds.y, gl_InstanceIndex / (params.bounds.y * params.bounds.x));
#else
uint cell_index = gl_InstanceIndex + params.cell_offset;
uvec3 posu = uvec3(cell_data.data[cell_index].position & 0x7FF, (cell_data.data[cell_index].position >> 11) & 0x3FF, cell_data.data[cell_index].position >> 21);
#endif
#ifdef MODE_DEBUG_EMISSION
color_interp.xyz = vec3(uvec3(cell_data.data[cell_index].emission & 0x1ff, (cell_data.data[cell_index].emission >> 9) & 0x1ff, (cell_data.data[cell_index].emission >> 18) & 0x1ff)) * pow(2.0, float(cell_data.data[cell_index].emission >> 27) - 15.0 - 9.0);
#endif
#ifdef MODE_DEBUG_COLOR
color_interp.xyz = unpackUnorm4x8(cell_data.data[cell_index].albedo).xyz;
#endif
#ifdef MODE_DEBUG_LIGHT
#ifdef USE_ANISOTROPY
#define POS_X 0
#define POS_Y 1
#define POS_Z 2
#define NEG_X 3
#define NEG_Y 4
#define NEG_Z 5
const uint triangle_aniso[12] = uint[](
NEG_X,
NEG_Z,
NEG_Y,
NEG_Z,
NEG_X,
NEG_Y,
POS_Z,
POS_X,
POS_X,
POS_Y,
POS_Y,
POS_Z);
color_interp.xyz = texelFetch(sampler3D(color_tex, tex_sampler), ivec3(posu), int(params.level)).xyz * params.dynamic_range;
vec3 aniso_pos = texelFetch(sampler3D(aniso_pos_tex, tex_sampler), ivec3(posu), int(params.level)).xyz;
vec3 aniso_neg = texelFetch(sampler3D(aniso_neg_tex, tex_sampler), ivec3(posu), int(params.level)).xyz;
uint side = triangle_aniso[gl_VertexIndex / 3];
float strength = 0.0;
switch (side) {
case POS_X:
strength = aniso_pos.x;
break;
case POS_Y:
strength = aniso_pos.y;
break;
case POS_Z:
strength = aniso_pos.z;
break;
case NEG_X:
strength = aniso_neg.x;
break;
case NEG_Y:
strength = aniso_neg.y;
break;
case NEG_Z:
strength = aniso_neg.z;
break;
}
color_interp.xyz *= strength;
#else
color_interp = texelFetch(sampler3D(color_tex, tex_sampler), ivec3(posu), int(params.level));
color_interp.xyz *params.dynamic_range;
#endif
#endif
float scale = (1 << params.level);
gl_Position = params.projection * vec4((vec3(posu) + vertex) * scale, 1.0);
#ifdef MODE_DEBUG_LIGHT_FULL
if (color_interp.a == 0.0) {
gl_Position = vec4(0.0); //force clip and not draw
}
#else
color_interp.a = params.alpha;
#endif
}
#[fragment]
#version 450
VERSION_DEFINES
layout(location = 0) in vec4 color_interp;
layout(location = 0) out vec4 frag_color;
void main() {
frag_color = color_interp;
#ifdef MODE_DEBUG_LIGHT_FULL
//there really is no alpha, so use dither
int x = int(gl_FragCoord.x) % 4;
int y = int(gl_FragCoord.y) % 4;
int index = x + y * 4;
float limit = 0.0;
if (x < 8) {
if (index == 0)
limit = 0.0625;
if (index == 1)
limit = 0.5625;
if (index == 2)
limit = 0.1875;
if (index == 3)
limit = 0.6875;
if (index == 4)
limit = 0.8125;
if (index == 5)
limit = 0.3125;
if (index == 6)
limit = 0.9375;
if (index == 7)
limit = 0.4375;
if (index == 8)
limit = 0.25;
if (index == 9)
limit = 0.75;
if (index == 10)
limit = 0.125;
if (index == 11)
limit = 0.625;
if (index == 12)
limit = 1.0;
if (index == 13)
limit = 0.5;
if (index == 14)
limit = 0.875;
if (index == 15)
limit = 0.375;
}
if (frag_color.a < limit) {
discard;
}
#endif
}

View File

@@ -0,0 +1,181 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
#define MAX_DISTANCE 100000
#define NO_CHILDREN 0xFFFFFFFF
#define GREY_VEC vec3(0.33333, 0.33333, 0.33333)
struct CellChildren {
uint children[8];
};
layout(set = 0, binding = 1, std430) buffer CellChildrenBuffer {
CellChildren data[];
}
cell_children;
struct CellData {
uint position; // xyz 10 bits
uint albedo; //rgb albedo
uint emission; //rgb normalized with e as multiplier
uint normal; //RGB normal encoded
};
layout(set = 0, binding = 2, std430) buffer CellDataBuffer {
CellData data[];
}
cell_data;
layout(r8ui, set = 0, binding = 3) uniform restrict writeonly uimage3D sdf_tex;
layout(push_constant, binding = 0, std430) uniform Params {
uint offset;
uint end;
uint pad0;
uint pad1;
}
params;
void main() {
vec3 pos = vec3(gl_GlobalInvocationID);
float closest_dist = 100000.0;
for (uint i = params.offset; i < params.end; i++) {
vec3 posu = vec3(uvec3(cell_data.data[i].position & 0x7FF, (cell_data.data[i].position >> 11) & 0x3FF, cell_data.data[i].position >> 21));
float dist = length(pos - posu);
if (dist < closest_dist) {
closest_dist = dist;
}
}
uint dist_8;
if (closest_dist < 0.0001) { // same cell
dist_8 = 0; //equals to -1
} else {
dist_8 = clamp(uint(closest_dist), 0, 254) + 1; //conservative, 0 is 1, so <1 is considered solid
}
imageStore(sdf_tex, ivec3(gl_GlobalInvocationID), uvec4(dist_8));
//imageStore(sdf_tex,pos,uvec4(pos*2,0));
}
#if 0
layout(push_constant, binding = 0, std430) uniform Params {
ivec3 limits;
uint stack_size;
}
params;
float distance_to_aabb(ivec3 pos, ivec3 aabb_pos, ivec3 aabb_size) {
vec3 delta = vec3(max(ivec3(0), max(aabb_pos - pos, pos - (aabb_pos + aabb_size - ivec3(1)))));
return length(delta);
}
void main() {
ivec3 pos = ivec3(gl_GlobalInvocationID);
uint stack[10] = uint[](0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
uint stack_indices[10] = uint[](0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
ivec3 stack_positions[10] = ivec3[](ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0));
const uint cell_orders[8] = uint[](
0x11f58d1,
0xe2e70a,
0xd47463,
0xbb829c,
0x8d11f5,
0x70ae2e,
0x463d47,
0x29cbb8);
bool cell_found = false;
bool cell_found_exact = false;
ivec3 closest_cell_pos;
float closest_distance = MAX_DISTANCE;
int stack_pos = 0;
while (true) {
uint index = stack_indices[stack_pos] >> 24;
if (index == 8) {
//go up
if (stack_pos == 0) {
break; //done going through octree
}
stack_pos--;
continue;
}
stack_indices[stack_pos] = (stack_indices[stack_pos] & ((1 << 24) - 1)) | ((index + 1) << 24);
uint cell_index = (stack_indices[stack_pos] >> (index * 3)) & 0x7;
uint child_cell = cell_children.data[stack[stack_pos]].children[cell_index];
if (child_cell == NO_CHILDREN) {
continue;
}
ivec3 child_cell_size = params.limits >> (stack_pos + 1);
ivec3 child_cell_pos = stack_positions[stack_pos];
child_cell_pos += mix(ivec3(0), child_cell_size, bvec3(uvec3(index & 1, index & 2, index & 4) != uvec3(0)));
bool is_leaf = stack_pos == (params.stack_size - 2);
if (child_cell_pos == pos && is_leaf) {
//we may actually end up in the exact cell.
//if this happens, just abort
cell_found_exact = true;
break;
}
if (cell_found) {
//discard by distance
float distance = distance_to_aabb(pos, child_cell_pos, child_cell_size);
if (distance >= closest_distance) {
continue; //pointless, just test next child
} else if (is_leaf) {
//closer than what we have AND end of stack, save and continue
closest_cell_pos = child_cell_pos;
closest_distance = distance;
continue;
}
} else if (is_leaf) {
//first solid cell we find, save and continue
closest_distance = distance_to_aabb(pos, child_cell_pos, child_cell_size);
closest_cell_pos = child_cell_pos;
cell_found = true;
continue;
}
bvec3 direction = greaterThan((pos - (child_cell_pos + (child_cell_size >> 1))), ivec3(0));
uint cell_order = 0;
cell_order |= mix(0, 1, direction.x);
cell_order |= mix(0, 2, direction.y);
cell_order |= mix(0, 4, direction.z);
stack[stack_pos + 1] = child_cell;
stack_indices[stack_pos + 1] = cell_orders[cell_order]; //start counting
stack_positions[stack_pos + 1] = child_cell_pos;
stack_pos++; //go up stack
}
uint dist_8;
if (cell_found_exact) {
dist_8 = 0; //equals to -1
} else {
float closest_distance = length(vec3(pos - closest_cell_pos));
dist_8 = clamp(uint(closest_distance), 0, 254) + 1; //conservative, 0 is 1, so <1 is considered solid
}
imageStore(sdf_tex, pos, uvec4(dist_8));
}
#endif

View File

@@ -0,0 +1,321 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
#define NO_CHILDREN 0xFFFFFFFF
#define GREY_VEC vec3(0.33333, 0.33333, 0.33333)
struct CellChildren {
uint children[8];
};
layout(set = 0, binding = 1, std430) buffer CellChildrenBuffer {
CellChildren data[];
}
cell_children;
struct CellData {
uint position; // xyz 10 bits
uint albedo; //rgb albedo
uint emission; //rgb normalized with e as multiplier
uint normal; //RGB normal encoded
};
layout(set = 0, binding = 2, std430) buffer CellDataBuffer {
CellData data[];
}
cell_data;
#define LIGHT_TYPE_DIRECTIONAL 0
#define LIGHT_TYPE_OMNI 1
#define LIGHT_TYPE_SPOT 2
#ifdef MODE_COMPUTE_LIGHT
struct Light {
uint type;
float energy;
float radius;
float attenuation;
vec3 color;
float spot_angle_radians;
vec3 position;
float spot_attenuation;
vec3 direction;
bool has_shadow;
};
layout(set = 0, binding = 3, std140) uniform Lights {
Light data[MAX_LIGHTS];
}
lights;
#endif
layout(push_constant, binding = 0, std430) uniform Params {
ivec3 limits;
uint stack_size;
float emission_scale;
float propagation;
float dynamic_range;
uint light_count;
uint cell_offset;
uint cell_count;
uint pad[2];
}
params;
layout(set = 0, binding = 4, std140) uniform Outputs {
vec4 data[];
}
output;
#ifdef MODE_COMPUTE_LIGHT
uint raymarch(float distance, float distance_adv, vec3 from, vec3 direction) {
uint result = NO_CHILDREN;
ivec3 size = ivec3(max(max(params.limits.x, params.limits.y), params.limits.z));
while (distance > -distance_adv) { //use this to avoid precision errors
uint cell = 0;
ivec3 pos = ivec3(from);
if (all(greaterThanEqual(pos, ivec3(0))) && all(lessThan(pos, size))) {
ivec3 ofs = ivec3(0);
ivec3 half_size = size / 2;
for (int i = 0; i < params.stack_size - 1; i++) {
bvec3 greater = greaterThanEqual(pos, ofs + half_size);
ofs += mix(ivec3(0), half_size, greater);
uint child = 0; //wonder if this can be done faster
if (greater.x) {
child |= 1;
}
if (greater.y) {
child |= 2;
}
if (greater.z) {
child |= 4;
}
cell = cell_children.data[cell].children[child];
if (cell == NO_CHILDREN) {
break;
}
half_size >>= ivec3(1);
}
if (cell != NO_CHILDREN) {
return cell; //found cell!
}
}
from += direction * distance_adv;
distance -= distance_adv;
}
return NO_CHILDREN;
}
bool compute_light_vector(uint light, uint cell, vec3 pos, out float attenuation, out vec3 light_pos) {
if (lights.data[light].type == LIGHT_TYPE_DIRECTIONAL) {
light_pos = pos - lights.data[light].direction * length(vec3(params.limits));
attenuation = 1.0;
} else {
light_pos = lights.data[light].position;
float distance = length(pos - light_pos);
if (distance >= lights.data[light].radius) {
return false;
}
attenuation = pow(clamp(1.0 - distance / lights.data[light].radius, 0.0001, 1.0), lights.data[light].attenuation);
if (lights.data[light].type == LIGHT_TYPE_SPOT) {
vec3 rel = normalize(pos - light_pos);
float angle = acos(dot(rel, lights.data[light].direction));
if (angle > lights.data[light].spot_angle_radians) {
return false;
}
float d = clamp(angle / lights.data[light].spot_angle_radians, 0, 1);
attenuation *= pow(1.0 - d, lights.data[light].spot_attenuation);
}
}
return true;
}
float get_normal_advance(vec3 p_normal) {
vec3 normal = p_normal;
vec3 unorm = abs(normal);
if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
// x code
unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0);
} else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
// y code
unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0);
} else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
// z code
unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0);
} else {
// oh-no we messed up code
// has to be
unorm = vec3(1.0, 0.0, 0.0);
}
return 1.0 / dot(normal, unorm);
}
#endif
void main() {
uint cell_index = gl_GlobalInvocationID.x;
if (cell_index >= params.cell_count) {
return;
}
cell_index += params.cell_offset;
uvec3 posu = uvec3(cell_data.data[cell_index].position & 0x7FF, (cell_data.data[cell_index].position >> 11) & 0x3FF, cell_data.data[cell_index].position >> 21);
vec4 albedo = unpackUnorm4x8(cell_data.data[cell_index].albedo);
#ifdef MODE_COMPUTE_LIGHT
vec3 pos = vec3(posu) + vec3(0.5);
vec3 emission = vec3(ivec3(cell_data.data[cell_index].emission & 0x3FF, (cell_data.data[cell_index].emission >> 10) & 0x7FF, cell_data.data[cell_index].emission >> 21)) * params.emission_scale;
vec4 normal = unpackSnorm4x8(cell_data.data[cell_index].normal);
#ifdef MODE_ANISOTROPIC
vec3 accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0));
#else
vec3 accum = vec3(0.0);
#endif
for (uint i = 0; i < params.light_count; i++) {
float attenuation;
vec3 light_pos;
if (!compute_light_vector(i, cell_index, pos, attenuation, light_pos)) {
continue;
}
vec3 light_dir = pos - light_pos;
float distance = length(light_dir);
light_dir = normalize(light_dir);
if (length(normal.xyz) > 0.2 && dot(normal.xyz, light_dir) >= 0) {
continue; //not facing the light
}
if (lights.data[i].has_shadow) {
float distance_adv = get_normal_advance(light_dir);
distance += distance_adv - mod(distance, distance_adv); //make it reach the center of the box always
vec3 from = pos - light_dir * distance; //approximate
from -= sign(light_dir) * 0.45; //go near the edge towards the light direction to avoid self occlusion
uint result = raymarch(distance, distance_adv, from, light_dir);
if (result != cell_index) {
continue; //was occluded
}
}
vec3 light = lights.data[i].color * albedo.rgb * attenuation * lights.data[i].energy;
#ifdef MODE_ANISOTROPIC
for (uint j = 0; j < 6; j++) {
accum[j] += max(0.0, dot(accum_dir, -light_dir)) * light + emission;
}
#else
if (length(normal.xyz) > 0.2) {
accum += max(0.0, dot(normal.xyz, -light_dir)) * light + emission;
} else {
//all directions
accum += light + emission;
}
#endif
}
#ifdef MODE_ANISOTROPIC
output.data[cell_index * 6 + 0] = vec4(accum[0], 0.0);
output.data[cell_index * 6 + 1] = vec4(accum[1], 0.0);
output.data[cell_index * 6 + 2] = vec4(accum[2], 0.0);
output.data[cell_index * 6 + 3] = vec4(accum[3], 0.0);
output.data[cell_index * 6 + 4] = vec4(accum[4], 0.0);
output.data[cell_index * 6 + 5] = vec4(accum[5], 0.0);
#else
output.data[cell_index] = vec4(accum, 0.0);
#endif
#endif //MODE_COMPUTE_LIGHT
#ifdef MODE_UPDATE_MIPMAPS
{
#ifdef MODE_ANISOTROPIC
vec3 light_accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
#else
vec3 light_accum = vec3(0.0);
#endif
float count = 0.0;
for (uint i = 0; i < 8; i++) {
uint child_index = cell_children.data[cell_index].children[i];
if (child_index == NO_CHILDREN) {
continue;
}
#ifdef MODE_ANISOTROPIC
light_accum[1] += output.data[child_index * 6 + 0].rgb;
light_accum[2] += output.data[child_index * 6 + 1].rgb;
light_accum[3] += output.data[child_index * 6 + 2].rgb;
light_accum[4] += output.data[child_index * 6 + 3].rgb;
light_accum[5] += output.data[child_index * 6 + 4].rgb;
light_accum[6] += output.data[child_index * 6 + 5].rgb;
#else
light_accum += output.data[child_index].rgb;
#endif
count += 1.0;
}
float divisor = mix(8.0, count, params.propagation);
#ifdef MODE_ANISOTROPIC
output.data[cell_index * 6 + 0] = vec4(light_accum[0] / divisor, 0.0);
output.data[cell_index * 6 + 1] = vec4(light_accum[1] / divisor, 0.0);
output.data[cell_index * 6 + 2] = vec4(light_accum[2] / divisor, 0.0);
output.data[cell_index * 6 + 3] = vec4(light_accum[3] / divisor, 0.0);
output.data[cell_index * 6 + 4] = vec4(light_accum[4] / divisor, 0.0);
output.data[cell_index * 6 + 5] = vec4(light_accum[5] / divisor, 0.0);
#else
output.data[cell_index] = vec4(light_accum / divisor, 0.0);
#endif
}
#endif
#ifdef MODE_WRITE_TEXTURE
{
}
#endif
}

View File

@@ -0,0 +1,82 @@
#[compute]
#version 450
VERSION_DEFINES
#define BLOCK_SIZE 8
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
shared float tmp_data[BLOCK_SIZE * BLOCK_SIZE];
#ifdef READ_TEXTURE
//use for main texture
layout(set = 0, binding = 0) uniform sampler2D source_texture;
#else
//use for intermediate textures
layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D source_luminance;
#endif
layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_luminance;
#ifdef WRITE_LUMINANCE
layout(set = 2, binding = 0) uniform sampler2D prev_luminance;
#endif
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 source_size;
float max_luminance;
float min_luminance;
float exposure_adjust;
float pad[3];
}
params;
void main() {
uint t = gl_LocalInvocationID.y * BLOCK_SIZE + gl_LocalInvocationID.x;
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(lessThan(pos, params.source_size))) {
#ifdef READ_TEXTURE
vec3 v = texelFetch(source_texture, pos, 0).rgb;
tmp_data[t] = max(v.r, max(v.g, v.b));
#else
tmp_data[t] = imageLoad(source_luminance, pos).r;
#endif
} else {
tmp_data[t] = 0.0;
}
groupMemoryBarrier();
barrier();
uint size = (BLOCK_SIZE * BLOCK_SIZE) >> 1;
do {
if (t < size) {
tmp_data[t] += tmp_data[t + size];
}
groupMemoryBarrier();
barrier();
size >>= 1;
} while (size >= 1);
if (t == 0) {
//compute rect size
ivec2 rect_size = min(params.source_size - pos, ivec2(BLOCK_SIZE));
float avg = tmp_data[0] / float(rect_size.x * rect_size.y);
//float avg = tmp_data[0] / float(BLOCK_SIZE*BLOCK_SIZE);
pos /= ivec2(BLOCK_SIZE);
#ifdef WRITE_LUMINANCE
float prev_lum = texelFetch(prev_luminance, ivec2(0, 0), 0).r; //1 pixel previous exposure
avg = clamp(prev_lum + (avg - prev_lum) * params.exposure_adjust, params.min_luminance, params.max_luminance);
#endif
imageStore(dest_luminance, pos, vec4(avg));
}
}

View File

@@ -0,0 +1,549 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
#define SAMPLER_NEAREST_CLAMP 0
#define SAMPLER_LINEAR_CLAMP 1
#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
#define SAMPLER_NEAREST_REPEAT 6
#define SAMPLER_LINEAR_REPEAT 7
#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
/* SET 0: GLOBAL DATA */
layout(set = 0, binding = 1) uniform sampler material_samplers[12];
layout(set = 0, binding = 2, std430) restrict readonly buffer GlobalVariableData {
vec4 data[];
}
global_variables;
/* Set 1: FRAME AND PARTICLE DATA */
// a frame history is kept for trail deterministic behavior
#define MAX_ATTRACTORS 32
#define ATTRACTOR_TYPE_SPHERE 0
#define ATTRACTOR_TYPE_BOX 1
#define ATTRACTOR_TYPE_VECTOR_FIELD 2
struct Attractor {
mat4 transform;
vec3 extents; //exents or radius
uint type;
uint texture_index; //texture index for vector field
float strength;
float attenuation;
float directionality;
};
#define MAX_COLLIDERS 32
#define COLLIDER_TYPE_SPHERE 0
#define COLLIDER_TYPE_BOX 1
#define COLLIDER_TYPE_SDF 2
#define COLLIDER_TYPE_HEIGHT_FIELD 3
struct Collider {
mat4 transform;
vec3 extents; //exents or radius
uint type;
uint texture_index; //texture index for vector field
float scale;
uint pad[2];
};
struct FrameParams {
bool emitting;
float system_phase;
float prev_system_phase;
uint cycle;
float explosiveness;
float randomness;
float time;
float delta;
uint random_seed;
uint attractor_count;
uint collider_count;
float particle_size;
mat4 emission_transform;
Attractor attractors[MAX_ATTRACTORS];
Collider colliders[MAX_COLLIDERS];
};
layout(set = 1, binding = 0, std430) restrict buffer FrameHistory {
FrameParams data[];
}
frame_history;
struct ParticleData {
mat4 xform;
vec3 velocity;
bool is_active;
vec4 color;
vec4 custom;
};
layout(set = 1, binding = 1, std430) restrict buffer Particles {
ParticleData data[];
}
particles;
#define EMISSION_FLAG_HAS_POSITION 1
#define EMISSION_FLAG_HAS_ROTATION_SCALE 2
#define EMISSION_FLAG_HAS_VELOCITY 4
#define EMISSION_FLAG_HAS_COLOR 8
#define EMISSION_FLAG_HAS_CUSTOM 16
struct ParticleEmission {
mat4 xform;
vec3 velocity;
uint flags;
vec4 color;
vec4 custom;
};
layout(set = 1, binding = 2, std430) restrict buffer SourceEmission {
int particle_count;
uint pad0;
uint pad1;
uint pad2;
ParticleEmission data[];
}
src_particles;
layout(set = 1, binding = 3, std430) restrict buffer DestEmission {
int particle_count;
int particle_max;
uint pad1;
uint pad2;
ParticleEmission data[];
}
dst_particles;
/* SET 2: COLLIDER/ATTRACTOR TEXTURES */
#define MAX_3D_TEXTURES 7
layout(set = 2, binding = 0) uniform texture3D sdf_vec_textures[MAX_3D_TEXTURES];
layout(set = 2, binding = 1) uniform texture2D height_field_texture;
/* SET 3: MATERIAL */
#ifdef USE_MATERIAL_UNIFORMS
layout(set = 3, binding = 0, std140) uniform MaterialUniforms{
/* clang-format off */
MATERIAL_UNIFORMS
/* clang-format on */
} material;
#endif
layout(push_constant, binding = 0, std430) uniform Params {
float lifetime;
bool clear;
uint total_particles;
uint trail_size;
bool use_fractional_delta;
bool sub_emitter_mode;
bool can_emit;
uint pad;
}
params;
uint hash(uint x) {
x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
x = (x >> uint(16)) ^ x;
return x;
}
bool emit_particle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom, uint p_flags) {
if (!params.can_emit) {
return false;
}
bool valid = false;
int dst_index = atomicAdd(dst_particles.particle_count, 1);
if (dst_index >= dst_particles.particle_max) {
atomicAdd(dst_particles.particle_count, -1);
return false;
}
dst_particles.data[dst_index].xform = p_xform;
dst_particles.data[dst_index].velocity = p_velocity;
dst_particles.data[dst_index].color = p_color;
dst_particles.data[dst_index].custom = p_custom;
dst_particles.data[dst_index].flags = p_flags;
return true;
}
/* clang-format off */
COMPUTE_SHADER_GLOBALS
/* clang-format on */
void main() {
uint particle = gl_GlobalInvocationID.x;
if (particle >= params.total_particles * params.trail_size) {
return; //discard
}
uint index = particle / params.trail_size;
uint frame = (particle % params.trail_size);
#define FRAME frame_history.data[frame]
#define PARTICLE particles.data[particle]
bool apply_forces = true;
bool apply_velocity = true;
float local_delta = FRAME.delta;
float mass = 1.0;
bool restart = false;
bool restart_position = false;
bool restart_rotation_scale = false;
bool restart_velocity = false;
bool restart_color = false;
bool restart_custom = false;
if (params.clear) {
PARTICLE.color = vec4(1.0);
PARTICLE.custom = vec4(0.0);
PARTICLE.velocity = vec3(0.0);
PARTICLE.is_active = false;
PARTICLE.xform = mat4(
vec4(1.0, 0.0, 0.0, 0.0),
vec4(0.0, 1.0, 0.0, 0.0),
vec4(0.0, 0.0, 1.0, 0.0),
vec4(0.0, 0.0, 0.0, 1.0));
}
bool collided = false;
vec3 collision_normal = vec3(0.0);
float collision_depth = 0.0;
vec3 attractor_force = vec3(0.0);
#if !defined(DISABLE_VELOCITY)
if (PARTICLE.is_active) {
PARTICLE.xform[3].xyz += PARTICLE.velocity * local_delta;
}
#endif
/* Process physics if active */
if (PARTICLE.is_active) {
for (uint i = 0; i < FRAME.attractor_count; i++) {
vec3 dir;
float amount;
vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.attractors[i].transform[3].xyz;
vec3 local_pos = rel_vec * mat3(FRAME.attractors[i].transform);
switch (FRAME.attractors[i].type) {
case ATTRACTOR_TYPE_SPHERE: {
dir = normalize(rel_vec);
float d = length(local_pos) / FRAME.attractors[i].extents.x;
if (d > 1.0) {
continue;
}
amount = max(0.0, 1.0 - d);
} break;
case ATTRACTOR_TYPE_BOX: {
dir = normalize(rel_vec);
vec3 abs_pos = abs(local_pos / FRAME.attractors[i].extents);
float d = max(abs_pos.x, max(abs_pos.y, abs_pos.z));
if (d > 1.0) {
continue;
}
amount = max(0.0, 1.0 - d);
} break;
case ATTRACTOR_TYPE_VECTOR_FIELD: {
vec3 uvw_pos = (local_pos / FRAME.attractors[i].extents) * 2.0 - 1.0;
if (any(lessThan(uvw_pos, vec3(0.0))) || any(greaterThan(uvw_pos, vec3(1.0)))) {
continue;
}
vec3 s = texture(sampler3D(sdf_vec_textures[FRAME.attractors[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).xyz;
dir = mat3(FRAME.attractors[i].transform) * normalize(s); //revert direction
amount = length(s);
} break;
}
amount = pow(amount, FRAME.attractors[i].attenuation);
dir = normalize(mix(dir, FRAME.attractors[i].transform[2].xyz, FRAME.attractors[i].directionality));
attractor_force -= amount * dir * FRAME.attractors[i].strength;
}
float particle_size = FRAME.particle_size;
#ifdef USE_COLLISON_SCALE
particle_size *= dot(vec3(length(PARTICLE.xform[0].xyz), length(PARTICLE.xform[1].xyz), length(PARTICLE.xform[2].xyz)), vec3(0.33333333333));
#endif
for (uint i = 0; i < FRAME.collider_count; i++) {
vec3 normal;
float depth;
bool col = false;
vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.colliders[i].transform[3].xyz;
vec3 local_pos = rel_vec * mat3(FRAME.colliders[i].transform);
switch (FRAME.colliders[i].type) {
case COLLIDER_TYPE_SPHERE: {
float d = length(rel_vec) - (particle_size + FRAME.colliders[i].extents.x);
if (d < 0.0) {
col = true;
depth = -d;
normal = normalize(rel_vec);
}
} break;
case COLLIDER_TYPE_BOX: {
vec3 abs_pos = abs(local_pos);
vec3 sgn_pos = sign(local_pos);
if (any(greaterThan(abs_pos, FRAME.colliders[i].extents))) {
//point outside box
vec3 closest = min(abs_pos, FRAME.colliders[i].extents);
vec3 rel = abs_pos - closest;
depth = length(rel) - particle_size;
if (depth < 0.0) {
col = true;
normal = mat3(FRAME.colliders[i].transform) * (normalize(rel) * sgn_pos);
depth = -depth;
}
} else {
//point inside box
vec3 axis_len = FRAME.colliders[i].extents - abs_pos;
// there has to be a faster way to do this?
if (all(lessThan(axis_len.xx, axis_len.yz))) {
normal = vec3(1, 0, 0);
} else if (all(lessThan(axis_len.yy, axis_len.xz))) {
normal = vec3(0, 1, 0);
} else {
normal = vec3(0, 0, 1);
}
col = true;
depth = dot(normal * axis_len, vec3(1)) + particle_size;
normal = mat3(FRAME.colliders[i].transform) * (normal * sgn_pos);
}
} break;
case COLLIDER_TYPE_SDF: {
vec3 apos = abs(local_pos);
float extra_dist = 0.0;
if (any(greaterThan(apos, FRAME.colliders[i].extents))) { //outside
vec3 mpos = min(apos, FRAME.colliders[i].extents);
extra_dist = distance(mpos, apos);
}
if (extra_dist > particle_size) {
continue;
}
vec3 uvw_pos = (local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5;
float s = texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).r;
s *= FRAME.colliders[i].scale;
s += extra_dist;
if (s < particle_size) {
col = true;
depth = particle_size - s;
const float EPSILON = 0.001;
normal = mat3(FRAME.colliders[i].transform) *
normalize(
vec3(
texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(EPSILON, 0.0, 0.0)).r,
texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, EPSILON, 0.0)).r,
texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, 0.0, EPSILON)).r));
}
} break;
case COLLIDER_TYPE_HEIGHT_FIELD: {
vec3 local_pos_bottom = local_pos;
local_pos_bottom.y -= particle_size;
if (any(greaterThan(abs(local_pos_bottom), FRAME.colliders[i].extents))) {
continue;
}
const float DELTA = 1.0 / 8192.0;
vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5;
float y = 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz).r;
if (y > uvw_pos.y) {
//inside heightfield
vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents;
normal = normalize(cross(pos1 - pos2, pos1 - pos3));
float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5).y;
col = true;
depth = dot(normal, pos1) - dot(normal, local_pos_bottom);
}
} break;
}
if (col) {
if (!collided) {
collided = true;
collision_normal = normal;
collision_depth = depth;
} else {
vec3 c = collision_normal * collision_depth;
c += normal * max(0.0, depth - dot(normal, c));
collision_normal = normalize(c);
collision_depth = length(c);
}
}
}
}
if (params.sub_emitter_mode) {
if (!PARTICLE.is_active) {
int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
if (src_index >= 0) {
PARTICLE.is_active = true;
restart = true;
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) {
PARTICLE.xform[3] = src_particles.data[src_index].xform[3];
} else {
PARTICLE.xform[3] = vec4(0, 0, 0, 1);
restart_position = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) {
PARTICLE.xform[0] = src_particles.data[src_index].xform[0];
PARTICLE.xform[1] = src_particles.data[src_index].xform[1];
PARTICLE.xform[2] = src_particles.data[src_index].xform[2];
} else {
PARTICLE.xform[0] = vec4(1, 0, 0, 0);
PARTICLE.xform[1] = vec4(0, 1, 0, 0);
PARTICLE.xform[2] = vec4(0, 0, 1, 0);
restart_rotation_scale = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) {
PARTICLE.velocity = src_particles.data[src_index].velocity;
} else {
PARTICLE.velocity = vec3(0);
restart_velocity = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) {
PARTICLE.color = src_particles.data[src_index].color;
} else {
PARTICLE.color = vec4(1);
restart_color = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) {
PARTICLE.custom = src_particles.data[src_index].custom;
} else {
PARTICLE.custom = vec4(0);
restart_custom = true;
}
}
}
} else if (FRAME.emitting) {
float restart_phase = float(index) / float(params.total_particles);
if (FRAME.randomness > 0.0) {
uint seed = FRAME.cycle;
if (restart_phase >= FRAME.system_phase) {
seed -= uint(1);
}
seed *= uint(params.total_particles);
seed += uint(index);
float random = float(hash(seed) % uint(65536)) / 65536.0;
restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
}
restart_phase *= (1.0 - FRAME.explosiveness);
if (FRAME.system_phase > FRAME.prev_system_phase) {
// restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
}
}
} else if (FRAME.delta > 0.0) {
if (restart_phase >= FRAME.prev_system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
}
} else if (restart_phase < FRAME.system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
}
}
}
uint current_cycle = FRAME.cycle;
if (FRAME.system_phase < restart_phase) {
current_cycle -= uint(1);
}
uint particle_number = current_cycle * uint(params.total_particles) + particle;
if (restart) {
PARTICLE.is_active = FRAME.emitting;
restart_position = true;
restart_rotation_scale = true;
restart_velocity = true;
restart_color = true;
restart_custom = true;
}
}
if (PARTICLE.is_active) {
/* clang-format off */
COMPUTE_SHADER_CODE
/* clang-format on */
}
}

View File

@@ -0,0 +1,82 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
struct ParticleData {
mat4 xform;
vec3 velocity;
bool is_active;
vec4 color;
vec4 custom;
};
layout(set = 0, binding = 1, std430) restrict readonly buffer Particles {
ParticleData data[];
}
particles;
layout(set = 0, binding = 2, std430) restrict writeonly buffer Transforms {
vec4 data[];
}
instances;
#ifdef USE_SORT_BUFFER
layout(set = 1, binding = 0, std430) restrict buffer SortBuffer {
vec2 data[];
}
sort_buffer;
#endif // USE_SORT_BUFFER
layout(push_constant, binding = 0, std430) uniform Params {
vec3 sort_direction;
uint total_particles;
}
params;
void main() {
#ifdef MODE_FILL_SORT_BUFFER
uint particle = gl_GlobalInvocationID.x;
if (particle >= params.total_particles) {
return; //discard
}
sort_buffer.data[particle].x = dot(params.sort_direction, particles.data[particle].xform[3].xyz);
sort_buffer.data[particle].y = float(particle);
#endif
#ifdef MODE_FILL_INSTANCES
uint particle = gl_GlobalInvocationID.x;
uint write_offset = gl_GlobalInvocationID.x * (3 + 1 + 1); //xform + color + custom
if (particle >= params.total_particles) {
return; //discard
}
#ifdef USE_SORT_BUFFER
particle = uint(sort_buffer.data[particle].y); //use index from sort buffer
#endif
mat4 txform;
if (particles.data[particle].is_active) {
txform = transpose(particles.data[particle].xform);
} else {
txform = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); //zero scale, becomes invisible
}
instances.data[write_offset + 0] = txform[0];
instances.data[write_offset + 1] = txform[1];
instances.data[write_offset + 2] = txform[2];
instances.data[write_offset + 3] = particles.data[particle].color;
instances.data[write_offset + 4] = particles.data[particle].custom;
#endif
}

View File

@@ -0,0 +1,110 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#ifdef MODE_RESOLVE_GI
layout(set = 0, binding = 0) uniform sampler2DMS source_depth;
layout(set = 0, binding = 1) uniform sampler2DMS source_normal_roughness;
layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_depth;
layout(rgba8, set = 1, binding = 1) uniform restrict writeonly image2D dest_normal_roughness;
#ifdef GIPROBE_RESOLVE
layout(set = 2, binding = 0) uniform usampler2DMS source_giprobe;
layout(rg8ui, set = 3, binding = 0) uniform restrict writeonly uimage2D dest_giprobe;
#endif
#endif
layout(push_constant, binding = 16, std430) uniform Params {
ivec2 screen_size;
int sample_count;
uint pad;
}
params;
void main() {
// Pixel being shaded
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(pos, params.screen_size))) { //too large, do nothing
return;
}
#ifdef MODE_RESOLVE_GI
float best_depth = 1e20;
vec4 best_normal_roughness = vec4(0.0);
#ifdef GIPROBE_RESOLVE
uvec2 best_giprobe;
#endif
#if 0
for(int i=0;i<params.sample_count;i++) {
float depth = texelFetch(source_depth,pos,i).r;
if (depth < best_depth) { //use the depth closest to camera
best_depth = depth;
best_normal_roughness = texelFetch(source_normal_roughness,pos,i);
#ifdef GIPROBE_RESOLVE
best_giprobe = texelFetch(source_giprobe,pos,i).rg;
#endif
}
}
#else
float depths[16];
int depth_indices[16];
int depth_amount[16];
int depth_count = 0;
for (int i = 0; i < params.sample_count; i++) {
float depth = texelFetch(source_depth, pos, i).r;
int depth_index = -1;
for (int j = 0; j < depth_count; j++) {
if (abs(depths[j] - depth) < 0.000001) {
depth_index = j;
break;
}
}
if (depth_index == -1) {
depths[depth_count] = depth;
depth_indices[depth_count] = i;
depth_amount[depth_count] = 1;
depth_count += 1;
} else {
depth_amount[depth_index] += 1;
}
}
int depth_least = 0xFFFF;
int best_index = 0;
for (int j = 0; j < depth_count; j++) {
if (depth_amount[j] < depth_least) {
best_index = depth_indices[j];
depth_least = depth_amount[j];
}
}
best_depth = texelFetch(source_depth, pos, best_index).r;
best_normal_roughness = texelFetch(source_normal_roughness, pos, best_index);
#ifdef GIPROBE_RESOLVE
best_giprobe = texelFetch(source_giprobe, pos, best_index).rg;
#endif
#endif
imageStore(dest_depth, pos, vec4(best_depth));
imageStore(dest_normal_roughness, pos, vec4(best_normal_roughness));
#ifdef GIPROBE_RESOLVE
imageStore(dest_giprobe, pos, uvec4(best_giprobe, 0, 0));
#endif
#endif
}

View File

@@ -0,0 +1,70 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source_normal;
layout(r8, set = 1, binding = 0) uniform restrict writeonly image2D dest_roughness;
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 screen_size;
float curve;
uint pad;
}
params;
#define HALF_PI 1.5707963267948966
void main() {
// Pixel being shaded
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThan(pos, params.screen_size))) { //too large, do nothing
return;
}
vec3 normal_accum = vec3(0.0);
float accum = 0.0;
for (int i = 0; i <= 1; i++) {
for (int j = 0; j <= 1; j++) {
normal_accum += normalize(texelFetch(source_normal, pos + ivec2(i, j), 0).xyz * 2.0 - 1.0);
accum += 1.0;
}
}
normal_accum /= accum;
float r = length(normal_accum);
float limit;
if (r < 1.0) {
float threshold = 0.4;
/*
//Formula from Filament, does not make sense to me.
float r2 = r * r;
float kappa = (3.0f * r - r * r2) / (1.0f - r2);
float variance = 0.25f / kappa;
limit = sqrt(min(2.0f * variance, threshold * threshold));
*/
/*
//Formula based on probability distribution graph
float width = acos(max(0.0,r)); // convert to angle (width)
float roughness = pow(width,1.7)*0.854492; //approximate (crappy) formula to convert to roughness
limit = min(sqrt(roughness), threshold); //convert to perceptual roughness and apply threshold
*/
limit = min(sqrt(pow(acos(max(0.0, r)) / HALF_PI, params.curve)), threshold); //convert to perceptual roughness and apply threshold
//limit = 0.5;
} else {
limit = 0.0;
}
imageStore(dest_roughness, pos, vec4(limit));
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,321 @@
#define M_PI 3.14159265359
#define ROUGHNESS_MAX_LOD 5
#define MAX_GI_PROBES 8
#include "cluster_data_inc.glsl"
layout(push_constant, binding = 0, std430) uniform DrawCall {
uint instance_index;
uint pad; //16 bits minimum size
vec2 bake_uv2_offset; //used for bake to uv2, ignored otherwise
}
draw_call;
/* Set 0 Scene data that never changes, ever */
#define SAMPLER_NEAREST_CLAMP 0
#define SAMPLER_LINEAR_CLAMP 1
#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
#define SAMPLER_NEAREST_REPEAT 6
#define SAMPLER_LINEAR_REPEAT 7
#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
layout(set = 0, binding = 1) uniform sampler material_samplers[12];
layout(set = 0, binding = 2) uniform sampler shadow_sampler;
#define SDFGI_MAX_CASCADES 8
layout(set = 0, binding = 3, std140) uniform SceneData {
mat4 projection_matrix;
mat4 inv_projection_matrix;
mat4 camera_matrix;
mat4 inv_camera_matrix;
vec2 viewport_size;
vec2 screen_pixel_size;
//use vec4s because std140 doesnt play nice with vec2s, z and w are wasted
vec4 directional_penumbra_shadow_kernel[32];
vec4 directional_soft_shadow_kernel[32];
vec4 penumbra_shadow_kernel[32];
vec4 soft_shadow_kernel[32];
uint directional_penumbra_shadow_samples;
uint directional_soft_shadow_samples;
uint penumbra_shadow_samples;
uint soft_shadow_samples;
vec4 ambient_light_color_energy;
float ambient_color_sky_mix;
bool use_ambient_light;
bool use_ambient_cubemap;
bool use_reflection_cubemap;
mat3 radiance_inverse_xform;
vec2 shadow_atlas_pixel_size;
vec2 directional_shadow_pixel_size;
uint directional_light_count;
float dual_paraboloid_side;
float z_far;
float z_near;
bool ssao_enabled;
float ssao_light_affect;
float ssao_ao_affect;
bool roughness_limiter_enabled;
float roughness_limiter_amount;
float roughness_limiter_limit;
uvec2 roughness_limiter_pad;
vec4 ao_color;
mat4 sdf_to_bounds;
ivec3 sdf_offset;
bool material_uv2_mode;
ivec3 sdf_size;
bool gi_upscale_for_msaa;
bool volumetric_fog_enabled;
float volumetric_fog_inv_length;
float volumetric_fog_detail_spread;
uint volumetric_fog_pad;
bool fog_enabled;
float fog_density;
float fog_height;
float fog_height_density;
vec3 fog_light_color;
float fog_sun_scatter;
float fog_aerial_perspective;
float time;
float reflection_multiplier; // one normally, zero when rendering reflections
bool pancake_shadows;
}
scene_data;
#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 6)
#define INSTANCE_FLAGS_USE_SDFGI (1 << 7)
#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 8)
#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 9)
#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 10)
#define INSTANCE_FLAGS_USE_GIPROBE (1 << 11)
#define INSTANCE_FLAGS_MULTIMESH (1 << 12)
#define INSTANCE_FLAGS_MULTIMESH_FORMAT_2D (1 << 13)
#define INSTANCE_FLAGS_MULTIMESH_HAS_COLOR (1 << 14)
#define INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA (1 << 15)
#define INSTANCE_FLAGS_MULTIMESH_STRIDE_SHIFT 16
//3 bits of stride
#define INSTANCE_FLAGS_MULTIMESH_STRIDE_MASK 0x7
#define INSTANCE_FLAGS_SKELETON (1 << 19)
struct InstanceData {
mat4 transform;
mat4 normal_transform;
uint flags;
uint instance_uniforms_ofs; //base offset in global buffer for instance variables
uint gi_offset; //GI information when using lightmapping (VCT or lightmap index)
uint layer_mask;
vec4 lightmap_uv_scale;
};
layout(set = 0, binding = 4, std430) restrict readonly buffer Instances {
InstanceData data[];
}
instances;
layout(set = 0, binding = 5, std430) restrict readonly buffer Lights {
LightData data[];
}
lights;
layout(set = 0, binding = 6) buffer restrict readonly ReflectionProbeData {
ReflectionData data[];
}
reflections;
layout(set = 0, binding = 7, std140) uniform DirectionalLights {
DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
}
directional_lights;
#define LIGHTMAP_FLAG_USE_DIRECTION 1
#define LIGHTMAP_FLAG_USE_SPECULAR_DIRECTION 2
struct Lightmap {
mat3 normal_xform;
};
layout(set = 0, binding = 10, std140) restrict readonly buffer Lightmaps {
Lightmap data[];
}
lightmaps;
layout(set = 0, binding = 11) uniform texture2DArray lightmap_textures[MAX_LIGHTMAP_TEXTURES];
struct LightmapCapture {
vec4 sh[9];
};
layout(set = 0, binding = 12, std140) restrict readonly buffer LightmapCaptures {
LightmapCapture data[];
}
lightmap_captures;
layout(set = 0, binding = 13) uniform texture2D decal_atlas;
layout(set = 0, binding = 14) uniform texture2D decal_atlas_srgb;
layout(set = 0, binding = 15, std430) restrict readonly buffer Decals {
DecalData data[];
}
decals;
layout(set = 0, binding = 16) uniform utexture3D cluster_texture;
layout(set = 0, binding = 17, std430) restrict readonly buffer ClusterData {
uint indices[];
}
cluster_data;
layout(set = 0, binding = 18) uniform texture2D directional_shadow_atlas;
layout(set = 0, binding = 19, std430) restrict readonly buffer GlobalVariableData {
vec4 data[];
}
global_variables;
struct SDFGIProbeCascadeData {
vec3 position;
float to_probe;
ivec3 probe_world_offset;
float to_cell; // 1/bounds * grid_size
};
layout(set = 0, binding = 20, std140) uniform SDFGI {
vec3 grid_size;
uint max_cascades;
bool use_occlusion;
int probe_axis_size;
float probe_to_uvw;
float normal_bias;
vec3 lightprobe_tex_pixel_size;
float energy;
vec3 lightprobe_uv_offset;
float y_mult;
vec3 occlusion_clamp;
uint pad3;
vec3 occlusion_renormalize;
uint pad4;
vec3 cascade_probe_size;
uint pad5;
SDFGIProbeCascadeData cascades[SDFGI_MAX_CASCADES];
}
sdfgi;
// decal atlas
/* Set 1, Radiance */
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
layout(set = 1, binding = 0) uniform textureCubeArray radiance_cubemap;
#else
layout(set = 1, binding = 0) uniform textureCube radiance_cubemap;
#endif
/* Set 2, Reflection and Shadow Atlases (view dependent) */
layout(set = 2, binding = 0) uniform textureCubeArray reflection_atlas;
layout(set = 2, binding = 1) uniform texture2D shadow_atlas;
layout(set = 2, binding = 2) uniform texture3D gi_probe_textures[MAX_GI_PROBES];
/* Set 3, Render Buffers */
#ifdef MODE_RENDER_SDF
layout(r16ui, set = 3, binding = 0) uniform restrict writeonly uimage3D albedo_volume_grid;
layout(r32ui, set = 3, binding = 1) uniform restrict writeonly uimage3D emission_grid;
layout(r32ui, set = 3, binding = 2) uniform restrict writeonly uimage3D emission_aniso_grid;
layout(r32ui, set = 3, binding = 3) uniform restrict uimage3D geom_facing_grid;
//still need to be present for shaders that use it, so remap them to something
#define depth_buffer shadow_atlas
#define color_buffer shadow_atlas
#define normal_roughness_buffer shadow_atlas
#else
layout(set = 3, binding = 0) uniform texture2D depth_buffer;
layout(set = 3, binding = 1) uniform texture2D color_buffer;
layout(set = 3, binding = 2) uniform texture2D normal_roughness_buffer;
layout(set = 3, binding = 4) uniform texture2D ao_buffer;
layout(set = 3, binding = 5) uniform texture2D ambient_buffer;
layout(set = 3, binding = 6) uniform texture2D reflection_buffer;
layout(set = 3, binding = 7) uniform texture2DArray sdfgi_lightprobe_texture;
layout(set = 3, binding = 8) uniform texture3D sdfgi_occlusion_cascades;
struct GIProbeData {
mat4 xform;
vec3 bounds;
float dynamic_range;
float bias;
float normal_bias;
bool blend_ambient;
uint texture_slot;
float anisotropy_strength;
float ambient_occlusion;
float ambient_occlusion_size;
uint mipmaps;
};
layout(set = 3, binding = 9, std140) uniform GIProbes {
GIProbeData data[MAX_GI_PROBES];
}
gi_probes;
layout(set = 3, binding = 10) uniform texture3D volumetric_fog_texture;
#endif
/* Set 4 Skeleton & Instancing (Multimesh) */
layout(set = 4, binding = 0, std430) restrict readonly buffer Transforms {
vec4 data[];
}
transforms;
/* Set 5 User Material */

View File

@@ -0,0 +1,246 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(rgba16f, set = 0, binding = 0) uniform restrict readonly image2D source_diffuse;
layout(r32f, set = 0, binding = 1) uniform restrict readonly image2D source_depth;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D ssr_image;
#ifdef MODE_ROUGH
layout(r8, set = 1, binding = 1) uniform restrict writeonly image2D blur_radius_image;
#endif
layout(rgba8, set = 2, binding = 0) uniform restrict readonly image2D source_normal_roughness;
layout(set = 3, binding = 0) uniform sampler2D source_metallic;
layout(push_constant, binding = 2, std430) uniform Params {
vec4 proj_info;
ivec2 screen_size;
float camera_z_near;
float camera_z_far;
int num_steps;
float depth_tolerance;
float distance_fade;
float curve_fade_in;
bool orthogonal;
float filter_mipmap_levels;
bool use_half_res;
uint metallic_mask;
mat4 projection;
}
params;
vec2 view_to_screen(vec3 view_pos, out float w) {
vec4 projected = params.projection * vec4(view_pos, 1.0);
projected.xyz /= projected.w;
projected.xy = projected.xy * 0.5 + 0.5;
w = projected.w;
return projected.xy;
}
#define M_PI 3.14159265359
vec3 reconstructCSPosition(vec2 S, float z) {
if (params.orthogonal) {
return vec3((S.xy * params.proj_info.xy + params.proj_info.zw), z);
} else {
return vec3((S.xy * params.proj_info.xy + params.proj_info.zw) * z, z);
}
}
void main() {
// Pixel being shaded
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(ssC, params.screen_size))) { //too large, do nothing
return;
}
vec2 pixel_size = 1.0 / vec2(params.screen_size);
vec2 uv = vec2(ssC) * pixel_size;
uv += pixel_size * 0.5;
float base_depth = imageLoad(source_depth, ssC).r;
// World space point being shaded
vec3 vertex = reconstructCSPosition(uv * vec2(params.screen_size), base_depth);
vec4 normal_roughness = imageLoad(source_normal_roughness, ssC);
vec3 normal = normal_roughness.xyz * 2.0 - 1.0;
normal = normalize(normal);
normal.y = -normal.y; //because this code reads flipped
vec3 view_dir = normalize(vertex);
vec3 ray_dir = normalize(reflect(view_dir, normal));
if (dot(ray_dir, normal) < 0.001) {
imageStore(ssr_image, ssC, vec4(0.0));
return;
}
//ray_dir = normalize(view_dir - normal * dot(normal,view_dir) * 2.0);
//ray_dir = normalize(vec3(1.0, 1.0, -1.0));
////////////////
// make ray length and clip it against the near plane (don't want to trace beyond visible)
float ray_len = (vertex.z + ray_dir.z * params.camera_z_far) > -params.camera_z_near ? (-params.camera_z_near - vertex.z) / ray_dir.z : params.camera_z_far;
vec3 ray_end = vertex + ray_dir * ray_len;
float w_begin;
vec2 vp_line_begin = view_to_screen(vertex, w_begin);
float w_end;
vec2 vp_line_end = view_to_screen(ray_end, w_end);
vec2 vp_line_dir = vp_line_end - vp_line_begin;
// we need to interpolate w along the ray, to generate perspective correct reflections
w_begin = 1.0 / w_begin;
w_end = 1.0 / w_end;
float z_begin = vertex.z * w_begin;
float z_end = ray_end.z * w_end;
vec2 line_begin = vp_line_begin / pixel_size;
vec2 line_dir = vp_line_dir / pixel_size;
float z_dir = z_end - z_begin;
float w_dir = w_end - w_begin;
// clip the line to the viewport edges
float scale_max_x = min(1.0, 0.99 * (1.0 - vp_line_begin.x) / max(1e-5, vp_line_dir.x));
float scale_max_y = min(1.0, 0.99 * (1.0 - vp_line_begin.y) / max(1e-5, vp_line_dir.y));
float scale_min_x = min(1.0, 0.99 * vp_line_begin.x / max(1e-5, -vp_line_dir.x));
float scale_min_y = min(1.0, 0.99 * vp_line_begin.y / max(1e-5, -vp_line_dir.y));
float line_clip = min(scale_max_x, scale_max_y) * min(scale_min_x, scale_min_y);
line_dir *= line_clip;
z_dir *= line_clip;
w_dir *= line_clip;
// clip z and w advance to line advance
vec2 line_advance = normalize(line_dir); // down to pixel
float step_size = length(line_advance) / length(line_dir);
float z_advance = z_dir * step_size; // adapt z advance to line advance
float w_advance = w_dir * step_size; // adapt w advance to line advance
// make line advance faster if direction is closer to pixel edges (this avoids sampling the same pixel twice)
float advance_angle_adj = 1.0 / max(abs(line_advance.x), abs(line_advance.y));
line_advance *= advance_angle_adj; // adapt z advance to line advance
z_advance *= advance_angle_adj;
w_advance *= advance_angle_adj;
vec2 pos = line_begin;
float z = z_begin;
float w = w_begin;
float z_from = z / w;
float z_to = z_from;
float depth;
vec2 prev_pos = pos;
bool found = false;
float steps_taken = 0.0;
for (int i = 0; i < params.num_steps; i++) {
pos += line_advance;
z += z_advance;
w += w_advance;
// convert to linear depth
depth = imageLoad(source_depth, ivec2(pos - 0.5)).r;
z_from = z_to;
z_to = z / w;
if (depth > z_to) {
// if depth was surpassed
if (depth <= max(z_to, z_from) + params.depth_tolerance && -depth < params.camera_z_far) {
// check the depth tolerance and far clip
// check that normal is valid
found = true;
}
break;
}
steps_taken += 1.0;
prev_pos = pos;
}
if (found) {
float margin_blend = 1.0;
vec2 margin = vec2((params.screen_size.x + params.screen_size.y) * 0.5 * 0.05); // make a uniform margin
if (any(bvec4(lessThan(pos, -margin), greaterThan(pos, params.screen_size + margin)))) {
// clip outside screen + margin
imageStore(ssr_image, ssC, vec4(0.0));
return;
}
{
//blend fading out towards external margin
vec2 margin_grad = mix(pos - params.screen_size, -pos, lessThan(pos, vec2(0.0)));
margin_blend = 1.0 - smoothstep(0.0, margin.x, max(margin_grad.x, margin_grad.y));
//margin_blend = 1.0;
}
vec2 final_pos;
float grad;
grad = steps_taken / float(params.num_steps);
float initial_fade = params.curve_fade_in == 0.0 ? 1.0 : pow(clamp(grad, 0.0, 1.0), params.curve_fade_in);
float fade = pow(clamp(1.0 - grad, 0.0, 1.0), params.distance_fade) * initial_fade;
final_pos = pos;
vec4 final_color;
#ifdef MODE_ROUGH
// if roughness is enabled, do screen space cone tracing
float blur_radius = 0.0;
float roughness = normal_roughness.w;
if (roughness > 0.001) {
float cone_angle = min(roughness, 0.999) * M_PI * 0.5;
float cone_len = length(final_pos - line_begin);
float op_len = 2.0 * tan(cone_angle) * cone_len; // opposite side of iso triangle
{
// fit to sphere inside cone (sphere ends at end of cone), something like this:
// ___
// \O/
// V
//
// as it avoids bleeding from beyond the reflection as much as possible. As a plus
// it also makes the rough reflection more elongated.
float a = op_len;
float h = cone_len;
float a2 = a * a;
float fh2 = 4.0f * h * h;
blur_radius = (a * (sqrt(a2 + fh2) - a)) / (4.0f * h);
}
}
final_color = imageLoad(source_diffuse, ivec2((final_pos - 0.5) * pixel_size));
imageStore(blur_radius_image, ssC, vec4(blur_radius / 255.0)); //stored in r8
#endif
final_color = vec4(imageLoad(source_diffuse, ivec2(final_pos - 0.5)).rgb, fade * margin_blend);
//change blend by metallic
vec4 metallic_mask = unpackUnorm4x8(params.metallic_mask);
final_color.a *= dot(metallic_mask, texelFetch(source_metallic, ssC << 1, 0));
imageStore(ssr_image, ssC, final_color);
} else {
#ifdef MODE_ROUGH
imageStore(blur_radius_image, ssC, vec4(0.0));
#endif
imageStore(ssr_image, ssC, vec4(0.0));
}
}

View File

@@ -0,0 +1,154 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(rgba16f, set = 0, binding = 0) uniform restrict readonly image2D source_ssr;
layout(r8, set = 0, binding = 1) uniform restrict readonly image2D source_radius;
layout(rgba8, set = 1, binding = 0) uniform restrict readonly image2D source_normal;
layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly image2D dest_ssr;
#ifndef VERTICAL_PASS
layout(r8, set = 2, binding = 1) uniform restrict writeonly image2D dest_radius;
#endif
layout(r32f, set = 3, binding = 0) uniform restrict readonly image2D source_depth;
layout(push_constant, binding = 2, std430) uniform Params {
vec4 proj_info;
bool orthogonal;
float edge_tolerance;
int increment;
uint pad;
ivec2 screen_size;
bool vertical;
uint steps;
}
params;
#define GAUSS_TABLE_SIZE 15
const float gauss_table[GAUSS_TABLE_SIZE + 1] = float[](
0.1847392078702266,
0.16595854345772326,
0.12031364177766891,
0.07038755277896766,
0.03322925565155569,
0.012657819729901945,
0.0038903040680094217,
0.0009646503390864025,
0.00019297087402915717,
0.000031139936308099136,
0.000004053309048174758,
4.255228059965837e-7,
3.602517634249573e-8,
2.4592560765896795e-9,
1.3534945386863618e-10,
0.0 //one more for interpolation
);
float gauss_weight(float p_val) {
float idxf;
float c = modf(max(0.0, p_val * float(GAUSS_TABLE_SIZE)), idxf);
int idx = int(idxf);
if (idx >= GAUSS_TABLE_SIZE + 1) {
return 0.0;
}
return mix(gauss_table[idx], gauss_table[idx + 1], c);
}
#define M_PI 3.14159265359
vec3 reconstructCSPosition(vec2 S, float z) {
if (params.orthogonal) {
return vec3((S.xy * params.proj_info.xy + params.proj_info.zw), z);
} else {
return vec3((S.xy * params.proj_info.xy + params.proj_info.zw) * z, z);
}
}
void do_filter(inout vec4 accum, inout float accum_radius, inout float divisor, ivec2 texcoord, ivec2 increment, vec3 p_pos, vec3 normal, float p_limit_radius) {
for (int i = 1; i < params.steps; i++) {
float d = float(i * params.increment);
ivec2 tc = texcoord + increment * i;
float depth = imageLoad(source_depth, tc).r;
vec3 view_pos = reconstructCSPosition(vec2(tc) + 0.5, depth);
vec3 view_normal = normalize(imageLoad(source_normal, tc).rgb * 2.0 - 1.0);
view_normal.y = -view_normal.y;
float r = imageLoad(source_radius, tc).r;
float radius = round(r * 255.0);
float angle_n = 1.0 - abs(dot(normal, view_normal));
if (angle_n > params.edge_tolerance) {
break;
}
float angle = abs(dot(normal, normalize(view_pos - p_pos)));
if (angle > params.edge_tolerance) {
break;
}
if (d < radius) {
float w = gauss_weight(d / radius);
accum += imageLoad(source_ssr, tc) * w;
#ifndef VERTICAL_PASS
accum_radius += r * w;
#endif
divisor += w;
}
}
}
void main() {
// Pixel being shaded
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(ssC, params.screen_size))) { //too large, do nothing
return;
}
float base_contrib = gauss_table[0];
vec4 accum = imageLoad(source_ssr, ssC);
float accum_radius = imageLoad(source_radius, ssC).r;
float radius = accum_radius * 255.0;
float divisor = gauss_table[0];
accum *= divisor;
accum_radius *= divisor;
#ifdef VERTICAL_PASS
ivec2 direction = ivec2(0, params.increment);
#else
ivec2 direction = ivec2(params.increment, 0);
#endif
float depth = imageLoad(source_depth, ssC).r;
vec3 pos = reconstructCSPosition(vec2(ssC) + 0.5, depth);
vec3 normal = imageLoad(source_normal, ssC).xyz * 2.0 - 1.0;
normal = normalize(normal);
normal.y = -normal.y;
do_filter(accum, accum_radius, divisor, ssC, direction, pos, normal, radius);
do_filter(accum, accum_radius, divisor, ssC, -direction, pos, normal, radius);
if (divisor > 0.0) {
accum /= divisor;
accum_radius /= divisor;
} else {
accum = vec4(0.0);
accum_radius = 0.0;
}
imageStore(dest_ssr, ssC, accum);
#ifndef VERTICAL_PASS
imageStore(dest_radius, ssC, vec4(accum_radius));
#endif
}

View File

@@ -0,0 +1,87 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source_ssr;
layout(set = 1, binding = 0) uniform sampler2D source_depth;
layout(set = 1, binding = 1) uniform sampler2D source_normal;
layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly image2D dest_ssr;
layout(r32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_depth;
layout(rgba8, set = 3, binding = 1) uniform restrict writeonly image2D dest_normal;
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 screen_size;
float camera_z_near;
float camera_z_far;
bool orthogonal;
bool filtered;
uint pad[2];
}
params;
void main() {
// Pixel being shaded
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(ssC, params.screen_size))) { //too large, do nothing
return;
}
//do not filter, SSR will generate arctifacts if this is done
float divisor = 0.0;
vec4 color;
float depth;
vec3 normal;
if (params.filtered) {
color = vec4(0.0);
depth = 0.0;
normal = vec3(0.0);
for (int i = 0; i < 4; i++) {
ivec2 ofs = ssC << 1;
if (bool(i & 1)) {
ofs.x += 1;
}
if (bool(i & 2)) {
ofs.y += 1;
}
color += texelFetch(source_ssr, ofs, 0);
float d = texelFetch(source_depth, ofs, 0).r;
normal += texelFetch(source_normal, ofs, 0).xyz * 2.0 - 1.0;
d = d * 2.0 - 1.0;
if (params.orthogonal) {
d = ((d + (params.camera_z_far + params.camera_z_near) / (params.camera_z_far - params.camera_z_near)) * (params.camera_z_far - params.camera_z_near)) / 2.0;
} else {
d = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - d * (params.camera_z_far - params.camera_z_near));
}
depth += -d;
}
color /= 4.0;
depth /= 4.0;
normal = normalize(normal / 4.0) * 0.5 + 0.5;
} else {
color = texelFetch(source_ssr, ssC << 1, 0);
depth = texelFetch(source_depth, ssC << 1, 0).r;
normal = texelFetch(source_normal, ssC << 1, 0).xyz;
depth = depth * 2.0 - 1.0;
if (params.orthogonal) {
depth = ((depth + (params.camera_z_far + params.camera_z_near) / (params.camera_z_far - params.camera_z_near)) * (params.camera_z_far - params.camera_z_near)) / 2.0;
} else {
depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - depth * (params.camera_z_far - params.camera_z_near));
}
depth = -depth;
}
imageStore(dest_ssr, ssC, color);
imageStore(dest_depth, ssC, vec4(depth));
imageStore(dest_normal, ssC, vec4(normal, 0.0));
}

View File

@@ -0,0 +1,275 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#define MAX_CASCADES 8
layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES];
layout(set = 0, binding = 2) uniform texture3D light_cascades[MAX_CASCADES];
layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[MAX_CASCADES];
layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[MAX_CASCADES];
layout(set = 0, binding = 5) uniform texture3D occlusion_texture;
layout(set = 0, binding = 8) uniform sampler linear_sampler;
struct CascadeData {
vec3 offset; //offset of (0,0,0) in world coordinates
float to_cell; // 1/bounds * grid_size
ivec3 probe_world_offset;
uint pad;
};
layout(set = 0, binding = 9, std140) uniform Cascades {
CascadeData data[MAX_CASCADES];
}
cascades;
layout(rgba16f, set = 0, binding = 10) uniform restrict writeonly image2D screen_buffer;
layout(set = 0, binding = 11) uniform texture2DArray lightprobe_texture;
layout(push_constant, binding = 0, std430) uniform Params {
vec3 grid_size;
uint max_cascades;
ivec2 screen_size;
bool use_occlusion;
float y_mult;
vec3 cam_extent;
int probe_axis_size;
mat4 cam_transform;
}
params;
vec3 linear_to_srgb(vec3 color) {
//if going to srgb, clamp from 0 to 1.
color = clamp(color, vec3(0.0), vec3(1.0));
const vec3 a = vec3(0.055f);
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
}
vec2 octahedron_wrap(vec2 v) {
vec2 signVal;
signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
return (1.0 - abs(v.yx)) * signVal;
}
vec2 octahedron_encode(vec3 n) {
// https://twitter.com/Stubbesaurus/status/937994790553227264
n /= (abs(n.x) + abs(n.y) + abs(n.z));
n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
n.xy = n.xy * 0.5 + 0.5;
return n.xy;
}
void main() {
// Pixel being shaded
ivec2 screen_pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(screen_pos, params.screen_size))) { //too large, do nothing
return;
}
vec3 ray_pos;
vec3 ray_dir;
{
ray_pos = params.cam_transform[3].xyz;
ray_dir.xy = params.cam_extent.xy * ((vec2(screen_pos) / vec2(params.screen_size)) * 2.0 - 1.0);
ray_dir.z = params.cam_extent.z;
ray_dir = normalize(mat3(params.cam_transform) * ray_dir);
}
ray_pos.y *= params.y_mult;
ray_dir.y *= params.y_mult;
ray_dir = normalize(ray_dir);
vec3 pos_to_uvw = 1.0 / params.grid_size;
vec3 light = vec3(0.0);
float blend = 0.0;
#if 1
vec3 inv_dir = 1.0 / ray_dir;
float rough = 0.5;
bool hit = false;
for (uint i = 0; i < params.max_cascades; i++) {
//convert to local bounds
vec3 pos = ray_pos - cascades.data[i].offset;
pos *= cascades.data[i].to_cell;
// Should never happen for debug, since we start mostly at the bounds center,
// but add anyway.
//if (any(lessThan(pos,vec3(0.0))) || any(greaterThanEqual(pos,params.grid_size))) {
// continue; //already past bounds for this cascade, goto next
//}
//find maximum advance distance (until reaching bounds)
vec3 t0 = -pos * inv_dir;
vec3 t1 = (params.grid_size - pos) * inv_dir;
vec3 tmax = max(t0, t1);
float max_advance = min(tmax.x, min(tmax.y, tmax.z));
float advance = 0.0;
vec3 uvw;
hit = false;
while (advance < max_advance) {
//read how much to advance from SDF
uvw = (pos + ray_dir * advance) * pos_to_uvw;
float distance = texture(sampler3D(sdf_cascades[i], linear_sampler), uvw).r * 255.0 - 1.7;
if (distance < 0.001) {
//consider hit
hit = true;
break;
}
advance += distance;
}
if (!hit) {
pos += ray_dir * min(advance, max_advance);
pos /= cascades.data[i].to_cell;
pos += cascades.data[i].offset;
ray_pos = pos;
continue;
}
//compute albedo, emission and normal at hit point
const float EPSILON = 0.001;
vec3 hit_normal = normalize(vec3(
texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(EPSILON, 0.0, 0.0)).r,
texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(0.0, EPSILON, 0.0)).r,
texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(0.0, 0.0, EPSILON)).r));
vec3 hit_light = texture(sampler3D(light_cascades[i], linear_sampler), uvw).rgb;
vec4 aniso0 = texture(sampler3D(aniso0_cascades[i], linear_sampler), uvw);
vec3 hit_aniso0 = aniso0.rgb;
vec3 hit_aniso1 = vec3(aniso0.a, texture(sampler3D(aniso1_cascades[i], linear_sampler), uvw).rg);
hit_light *= (dot(max(vec3(0.0), (hit_normal * hit_aniso0)), vec3(1.0)) + dot(max(vec3(0.0), (-hit_normal * hit_aniso1)), vec3(1.0)));
if (blend > 0.0) {
light = mix(light, hit_light, blend);
blend = 0.0;
} else {
light = hit_light;
//process blend
float blend_from = (float(params.probe_axis_size - 1) / 2.0) - 2.5;
float blend_to = blend_from + 2.0;
vec3 cam_pos = params.cam_transform[3].xyz - cascades.data[i].offset;
cam_pos *= cascades.data[i].to_cell;
pos += ray_dir * min(advance, max_advance);
vec3 inner_pos = pos - cam_pos;
inner_pos = inner_pos * float(params.probe_axis_size - 1) / params.grid_size.x;
float len = length(inner_pos);
inner_pos = abs(normalize(inner_pos));
len *= max(inner_pos.x, max(inner_pos.y, inner_pos.z));
if (len >= blend_from) {
blend = smoothstep(blend_from, blend_to, len);
pos /= cascades.data[i].to_cell;
pos += cascades.data[i].offset;
ray_pos = pos;
hit = false; //continue trace for blend
continue;
}
}
break;
}
light = mix(light, vec3(0.0), blend);
#else
vec3 inv_dir = 1.0 / ray_dir;
bool hit = false;
vec4 light_accum = vec4(0.0);
float blend_size = (params.grid_size.x / float(params.probe_axis_size - 1)) * 0.5;
float radius_sizes[MAX_CASCADES];
for (uint i = 0; i < params.max_cascades; i++) {
radius_sizes[i] = (1.0 / cascades.data[i].to_cell) * (params.grid_size.x * 0.5 - blend_size);
}
float max_distance = radius_sizes[params.max_cascades - 1];
float advance = 0;
while (advance < max_distance) {
for (uint i = 0; i < params.max_cascades; i++) {
if (advance < radius_sizes[i]) {
vec3 pos = (ray_pos + ray_dir * advance) - cascades.data[i].offset;
pos *= cascades.data[i].to_cell * pos_to_uvw;
float distance = texture(sampler3D(sdf_cascades[i], linear_sampler), pos).r * 255.0 - 1.0;
vec4 hit_light = vec4(0.0);
if (distance < 1.0) {
hit_light.a = max(0.0, 1.0 - distance);
hit_light.rgb = texture(sampler3D(light_cascades[i], linear_sampler), pos).rgb;
hit_light.rgb *= hit_light.a;
}
distance /= cascades.data[i].to_cell;
if (i < (params.max_cascades - 1)) {
pos = (ray_pos + ray_dir * advance) - cascades.data[i + 1].offset;
pos *= cascades.data[i + 1].to_cell * pos_to_uvw;
float distance2 = texture(sampler3D(sdf_cascades[i + 1], linear_sampler), pos).r * 255.0 - 1.0;
vec4 hit_light2 = vec4(0.0);
if (distance2 < 1.0) {
hit_light2.a = max(0.0, 1.0 - distance2);
hit_light2.rgb = texture(sampler3D(light_cascades[i + 1], linear_sampler), pos).rgb;
hit_light2.rgb *= hit_light2.a;
}
float prev_radius = i == 0 ? 0.0 : radius_sizes[i - 1];
float blend = (advance - prev_radius) / (radius_sizes[i] - prev_radius);
distance2 /= cascades.data[i + 1].to_cell;
hit_light = mix(hit_light, hit_light2, blend);
distance = mix(distance, distance2, blend);
}
light_accum += hit_light;
advance += distance;
break;
}
}
if (light_accum.a > 0.98) {
break;
}
}
light = light_accum.rgb / light_accum.a;
#endif
imageStore(screen_buffer, screen_pos, vec4(linear_to_srgb(light), 1.0));
}

View File

@@ -0,0 +1,231 @@
#[vertex]
#version 450
VERSION_DEFINES
#define MAX_CASCADES 8
layout(push_constant, binding = 0, std430) uniform Params {
mat4 projection;
uint band_power;
uint sections_in_band;
uint band_mask;
float section_arc;
vec3 grid_size;
uint cascade;
uint pad;
float y_mult;
uint probe_debug_index;
int probe_axis_size;
}
params;
// http://in4k.untergrund.net/html_articles/hugi_27_-_coding_corner_polaris_sphere_tessellation_101.htm
vec3 get_sphere_vertex(uint p_vertex_id) {
float x_angle = float(p_vertex_id & 1u) + (p_vertex_id >> params.band_power);
float y_angle =
float((p_vertex_id & params.band_mask) >> 1) + ((p_vertex_id >> params.band_power) * params.sections_in_band);
x_angle *= params.section_arc * 0.5f; // remember - 180AA x rot not 360
y_angle *= -params.section_arc;
vec3 point = vec3(sin(x_angle) * sin(y_angle), cos(x_angle), sin(x_angle) * cos(y_angle));
return point;
}
#ifdef MODE_PROBES
layout(location = 0) out vec3 normal_interp;
layout(location = 1) out flat uint probe_index;
#endif
#ifdef MODE_VISIBILITY
layout(location = 0) out float visibility;
#endif
struct CascadeData {
vec3 offset; //offset of (0,0,0) in world coordinates
float to_cell; // 1/bounds * grid_size
ivec3 probe_world_offset;
uint pad;
};
layout(set = 0, binding = 1, std140) uniform Cascades {
CascadeData data[MAX_CASCADES];
}
cascades;
layout(set = 0, binding = 4) uniform texture3D occlusion_texture;
layout(set = 0, binding = 3) uniform sampler linear_sampler;
void main() {
#ifdef MODE_PROBES
probe_index = gl_InstanceIndex;
normal_interp = get_sphere_vertex(gl_VertexIndex);
vec3 vertex = normal_interp * 0.2;
float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
ivec3 probe_cell;
probe_cell.x = int(probe_index % params.probe_axis_size);
probe_cell.y = int(probe_index / (params.probe_axis_size * params.probe_axis_size));
probe_cell.z = int((probe_index / params.probe_axis_size) % params.probe_axis_size);
vertex += (cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size) / vec3(1.0, params.y_mult, 1.0);
gl_Position = params.projection * vec4(vertex, 1.0);
#endif
#ifdef MODE_VISIBILITY
int probe_index = int(params.probe_debug_index);
vec3 vertex = get_sphere_vertex(gl_VertexIndex) * 0.01;
float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
ivec3 probe_cell;
probe_cell.x = int(probe_index % params.probe_axis_size);
probe_cell.y = int((probe_index % (params.probe_axis_size * params.probe_axis_size)) / params.probe_axis_size);
probe_cell.z = int(probe_index / (params.probe_axis_size * params.probe_axis_size));
vertex += (cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size) / vec3(1.0, params.y_mult, 1.0);
int probe_voxels = int(params.grid_size.x) / int(params.probe_axis_size - 1);
int occluder_index = int(gl_InstanceIndex);
int diameter = probe_voxels * 2;
ivec3 occluder_pos;
occluder_pos.x = int(occluder_index % diameter);
occluder_pos.y = int(occluder_index / (diameter * diameter));
occluder_pos.z = int((occluder_index / diameter) % diameter);
float cell_size = 1.0 / cascades.data[params.cascade].to_cell;
ivec3 occluder_offset = occluder_pos - ivec3(diameter / 2);
vertex += ((vec3(occluder_offset) + vec3(0.5)) * cell_size) / vec3(1.0, params.y_mult, 1.0);
ivec3 global_cell = probe_cell + cascades.data[params.cascade].probe_world_offset;
uint occlusion_layer = 0;
if ((global_cell.x & 1) != 0) {
occlusion_layer |= 1;
}
if ((global_cell.y & 1) != 0) {
occlusion_layer |= 2;
}
if ((global_cell.z & 1) != 0) {
occlusion_layer |= 4;
}
ivec3 tex_pos = probe_cell * probe_voxels + occluder_offset;
const vec4 layer_axis[4] = vec4[](
vec4(1, 0, 0, 0),
vec4(0, 1, 0, 0),
vec4(0, 0, 1, 0),
vec4(0, 0, 0, 1));
tex_pos.z += int(params.cascade) * int(params.grid_size);
if (occlusion_layer >= 4) {
tex_pos.x += int(params.grid_size.x);
occlusion_layer &= 3;
}
visibility = dot(texelFetch(sampler3D(occlusion_texture, linear_sampler), tex_pos, 0), layer_axis[occlusion_layer]);
gl_Position = params.projection * vec4(vertex, 1.0);
#endif
}
#[fragment]
#version 450
VERSION_DEFINES
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 2) uniform texture2DArray lightprobe_texture;
layout(set = 0, binding = 3) uniform sampler linear_sampler;
layout(push_constant, binding = 0, std430) uniform Params {
mat4 projection;
uint band_power;
uint sections_in_band;
uint band_mask;
float section_arc;
vec3 grid_size;
uint cascade;
uint pad;
float y_mult;
uint probe_debug_index;
int probe_axis_size;
}
params;
#ifdef MODE_PROBES
layout(location = 0) in vec3 normal_interp;
layout(location = 1) in flat uint probe_index;
#endif
#ifdef MODE_VISIBILITY
layout(location = 0) in float visibility;
#endif
vec2 octahedron_wrap(vec2 v) {
vec2 signVal;
signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
return (1.0 - abs(v.yx)) * signVal;
}
vec2 octahedron_encode(vec3 n) {
// https://twitter.com/Stubbesaurus/status/937994790553227264
n /= (abs(n.x) + abs(n.y) + abs(n.z));
n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
n.xy = n.xy * 0.5 + 0.5;
return n.xy;
}
void main() {
#ifdef MODE_PROBES
ivec3 tex_pos;
tex_pos.x = int(probe_index) % params.probe_axis_size; //x
tex_pos.y = int(probe_index) / (params.probe_axis_size * params.probe_axis_size);
tex_pos.x += params.probe_axis_size * ((int(probe_index) / params.probe_axis_size) % params.probe_axis_size); //z
tex_pos.z = int(params.cascade);
vec3 tex_pos_ofs = vec3(octahedron_encode(normal_interp) * float(OCT_SIZE), 0.0);
vec3 tex_posf = vec3(vec2(tex_pos.xy * (OCT_SIZE + 2) + ivec2(1)), float(tex_pos.z)) + tex_pos_ofs;
tex_posf.xy /= vec2(ivec2(params.probe_axis_size * params.probe_axis_size * (OCT_SIZE + 2), params.probe_axis_size * (OCT_SIZE + 2)));
vec4 indirect_light = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), tex_posf, 0.0);
frag_color = indirect_light;
#endif
#ifdef MODE_VISIBILITY
frag_color = vec4(vec3(1, visibility, visibility), 1.0);
#endif
}

View File

@@ -0,0 +1,472 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
#define MAX_CASCADES 8
layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES];
layout(set = 0, binding = 2) uniform sampler linear_sampler;
layout(set = 0, binding = 3, std430) restrict readonly buffer DispatchData {
uint x;
uint y;
uint z;
uint total_count;
}
dispatch_data;
struct ProcessVoxel {
uint position; //xyz 7 bit packed, extra 11 bits for neigbours
uint albedo; //rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neibhbours
uint light; //rgbe8985 encoded total saved light, extra 2 bits for neighbours
uint light_aniso; //55555 light anisotropy, extra 2 bits for neighbours
//total neighbours: 26
};
#ifdef MODE_PROCESS_STATIC
layout(set = 0, binding = 4, std430) restrict buffer ProcessVoxels {
#else
layout(set = 0, binding = 4, std430) restrict buffer readonly ProcessVoxels {
#endif
ProcessVoxel data[];
}
process_voxels;
layout(r32ui, set = 0, binding = 5) uniform restrict uimage3D dst_light;
layout(rgba8, set = 0, binding = 6) uniform restrict image3D dst_aniso0;
layout(rg8, set = 0, binding = 7) uniform restrict image3D dst_aniso1;
struct CascadeData {
vec3 offset; //offset of (0,0,0) in world coordinates
float to_cell; // 1/bounds * grid_size
ivec3 probe_world_offset;
uint pad;
};
layout(set = 0, binding = 8, std140) uniform Cascades {
CascadeData data[MAX_CASCADES];
}
cascades;
#define LIGHT_TYPE_DIRECTIONAL 0
#define LIGHT_TYPE_OMNI 1
#define LIGHT_TYPE_SPOT 2
struct Light {
vec3 color;
float energy;
vec3 direction;
bool has_shadow;
vec3 position;
float attenuation;
uint type;
float spot_angle;
float spot_attenuation;
float radius;
vec4 shadow_color;
};
layout(set = 0, binding = 9, std140) buffer restrict readonly Lights {
Light data[];
}
lights;
layout(set = 0, binding = 10) uniform texture2DArray lightprobe_texture;
layout(push_constant, binding = 0, std430) uniform Params {
vec3 grid_size;
uint max_cascades;
uint cascade;
uint light_count;
uint process_offset;
uint process_increment;
int probe_axis_size;
bool multibounce;
float y_mult;
uint pad;
}
params;
vec2 octahedron_wrap(vec2 v) {
vec2 signVal;
signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
return (1.0 - abs(v.yx)) * signVal;
}
vec2 octahedron_encode(vec3 n) {
// https://twitter.com/Stubbesaurus/status/937994790553227264
n /= (abs(n.x) + abs(n.y) + abs(n.z));
n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
n.xy = n.xy * 0.5 + 0.5;
return n.xy;
}
void main() {
uint voxel_index = uint(gl_GlobalInvocationID.x);
//used for skipping voxels every N frames
voxel_index = params.process_offset + voxel_index * params.process_increment;
if (voxel_index >= dispatch_data.total_count) {
return;
}
uint voxel_position = process_voxels.data[voxel_index].position;
//keep for storing to texture
ivec3 positioni = ivec3((uvec3(voxel_position, voxel_position, voxel_position) >> uvec3(0, 7, 14)) & uvec3(0x7F));
vec3 position = vec3(positioni) + vec3(0.5);
position /= cascades.data[params.cascade].to_cell;
position += cascades.data[params.cascade].offset;
uint voxel_albedo = process_voxels.data[voxel_index].albedo;
vec3 albedo = vec3(uvec3(voxel_albedo >> 10, voxel_albedo >> 5, voxel_albedo) & uvec3(0x1F)) / float(0x1F);
vec3 light_accum[6];
uint valid_aniso = (voxel_albedo >> 15) & 0x3F;
{
uint rgbe = process_voxels.data[voxel_index].light;
//read rgbe8985
float r = float((rgbe & 0xff) << 1);
float g = float((rgbe >> 8) & 0x1ff);
float b = float(((rgbe >> 17) & 0xff) << 1);
float e = float((rgbe >> 25) & 0x1F);
float m = pow(2.0, e - 15.0 - 9.0);
vec3 l = vec3(r, g, b) * m;
uint aniso = process_voxels.data[voxel_index].light_aniso;
for (uint i = 0; i < 6; i++) {
float strength = ((aniso >> (i * 5)) & 0x1F) / float(0x1F);
light_accum[i] = l * strength;
}
}
const vec3 aniso_dir[6] = vec3[](
vec3(1, 0, 0),
vec3(0, 1, 0),
vec3(0, 0, 1),
vec3(-1, 0, 0),
vec3(0, -1, 0),
vec3(0, 0, -1));
// Raytrace light
vec3 pos_to_uvw = 1.0 / params.grid_size;
vec3 uvw_ofs = pos_to_uvw * 0.5;
for (uint i = 0; i < params.light_count; i++) {
float attenuation = 1.0;
vec3 direction;
float light_distance = 1e20;
switch (lights.data[i].type) {
case LIGHT_TYPE_DIRECTIONAL: {
direction = -lights.data[i].direction;
} break;
case LIGHT_TYPE_OMNI: {
vec3 rel_vec = lights.data[i].position - position;
direction = normalize(rel_vec);
light_distance = length(rel_vec);
rel_vec.y /= params.y_mult;
attenuation = pow(clamp(1.0 - length(rel_vec) / lights.data[i].radius, 0.0, 1.0), lights.data[i].attenuation);
} break;
case LIGHT_TYPE_SPOT: {
vec3 rel_vec = lights.data[i].position - position;
direction = normalize(rel_vec);
light_distance = length(rel_vec);
rel_vec.y /= params.y_mult;
attenuation = pow(clamp(1.0 - length(rel_vec) / lights.data[i].radius, 0.0, 1.0), lights.data[i].attenuation);
float angle = acos(dot(normalize(rel_vec), -lights.data[i].direction));
if (angle > lights.data[i].spot_angle) {
attenuation = 0.0;
} else {
float d = clamp(angle / lights.data[i].spot_angle, 0, 1);
attenuation *= pow(1.0 - d, lights.data[i].spot_attenuation);
}
} break;
}
if (attenuation < 0.001) {
continue;
}
bool hit = false;
vec3 ray_pos = position;
vec3 ray_dir = direction;
vec3 inv_dir = 1.0 / ray_dir;
//this is how to properly bias outgoing rays
float cell_size = 1.0 / cascades.data[params.cascade].to_cell;
ray_pos += sign(direction) * cell_size * 0.48; // go almost to the box edge but remain inside
ray_pos += ray_dir * 0.4 * cell_size; //apply a small bias from there
for (uint j = params.cascade; j < params.max_cascades; j++) {
//convert to local bounds
vec3 pos = ray_pos - cascades.data[j].offset;
pos *= cascades.data[j].to_cell;
float local_distance = light_distance * cascades.data[j].to_cell;
if (any(lessThan(pos, vec3(0.0))) || any(greaterThanEqual(pos, params.grid_size))) {
continue; //already past bounds for this cascade, goto next
}
//find maximum advance distance (until reaching bounds)
vec3 t0 = -pos * inv_dir;
vec3 t1 = (params.grid_size - pos) * inv_dir;
vec3 tmax = max(t0, t1);
float max_advance = min(tmax.x, min(tmax.y, tmax.z));
max_advance = min(local_distance, max_advance);
float advance = 0.0;
float occlusion = 1.0;
while (advance < max_advance) {
//read how much to advance from SDF
vec3 uvw = (pos + ray_dir * advance) * pos_to_uvw;
float distance = texture(sampler3D(sdf_cascades[j], linear_sampler), uvw).r * 255.0 - 1.0;
if (distance < 0.001) {
//consider hit
hit = true;
break;
}
occlusion = min(occlusion, distance);
advance += distance;
}
if (hit) {
attenuation *= occlusion;
break;
}
if (advance >= local_distance) {
break; //past light distance, abandon search
}
//change ray origin to collision with bounds
pos += ray_dir * max_advance;
pos /= cascades.data[j].to_cell;
pos += cascades.data[j].offset;
light_distance -= max_advance / cascades.data[j].to_cell;
ray_pos = pos;
}
if (!hit) {
vec3 light = albedo * lights.data[i].color.rgb * lights.data[i].energy * attenuation;
for (int j = 0; j < 6; j++) {
if (bool(valid_aniso & (1 << j))) {
light_accum[j] += max(0.0, dot(aniso_dir[j], direction)) * light;
}
}
}
}
// Add indirect light
if (params.multibounce) {
vec3 pos = (vec3(positioni) + vec3(0.5)) * float(params.probe_axis_size - 1) / params.grid_size;
ivec3 probe_base_pos = ivec3(pos);
vec4 probe_accum[6] = vec4[](vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0));
float weight_accum[6] = float[](0, 0, 0, 0, 0, 0);
ivec3 tex_pos = ivec3(probe_base_pos.xy, int(params.cascade));
tex_pos.x += probe_base_pos.z * int(params.probe_axis_size);
tex_pos.xy = tex_pos.xy * (OCT_SIZE + 2) + ivec2(1);
vec3 base_tex_posf = vec3(tex_pos);
vec2 tex_pixel_size = 1.0 / vec2(ivec2((OCT_SIZE + 2) * params.probe_axis_size * params.probe_axis_size, (OCT_SIZE + 2) * params.probe_axis_size));
vec3 probe_uv_offset = (ivec3(OCT_SIZE + 2, OCT_SIZE + 2, (OCT_SIZE + 2) * params.probe_axis_size)) * tex_pixel_size.xyx;
for (uint j = 0; j < 8; j++) {
ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1);
ivec3 probe_posi = probe_base_pos;
probe_posi += offset;
// Compute weight
vec3 probe_pos = vec3(probe_posi);
vec3 probe_to_pos = pos - probe_pos;
vec3 probe_dir = normalize(-probe_to_pos);
// Compute lightprobe texture position
vec3 trilinear = vec3(1.0) - abs(probe_to_pos);
for (uint k = 0; k < 6; k++) {
if (bool(valid_aniso & (1 << k))) {
vec3 n = aniso_dir[k];
float weight = trilinear.x * trilinear.y * trilinear.z * max(0.005, dot(n, probe_dir));
vec3 tex_posf = base_tex_posf + vec3(octahedron_encode(n) * float(OCT_SIZE), 0.0);
tex_posf.xy *= tex_pixel_size;
vec3 pos_uvw = tex_posf;
pos_uvw.xy += vec2(offset.xy) * probe_uv_offset.xy;
pos_uvw.x += float(offset.z) * probe_uv_offset.z;
vec4 indirect_light = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0);
probe_accum[k] += indirect_light * weight;
weight_accum[k] += weight;
}
}
}
for (uint k = 0; k < 6; k++) {
if (weight_accum[k] > 0.0) {
light_accum[k] += probe_accum[k].rgb * albedo / weight_accum[k];
}
}
}
// Store the light in the light texture
float lumas[6];
vec3 light_total = vec3(0);
for (int i = 0; i < 6; i++) {
light_total += light_accum[i];
lumas[i] = max(light_accum[i].r, max(light_accum[i].g, light_accum[i].b));
}
float luma_total = max(light_total.r, max(light_total.g, light_total.b));
uint light_total_rgbe;
{
//compress to RGBE9995 to save space
const float pow2to9 = 512.0f;
const float B = 15.0f;
const float N = 9.0f;
const float LN2 = 0.6931471805599453094172321215;
float cRed = clamp(light_total.r, 0.0, 65408.0);
float cGreen = clamp(light_total.g, 0.0, 65408.0);
float cBlue = clamp(light_total.b, 0.0, 65408.0);
float cMax = max(cRed, max(cGreen, cBlue));
float expp = max(-B - 1.0f, floor(log(cMax) / LN2)) + 1.0f + B;
float sMax = floor((cMax / pow(2.0f, expp - B - N)) + 0.5f);
float exps = expp + 1.0f;
if (0.0 <= sMax && sMax < pow2to9) {
exps = expp;
}
float sRed = floor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
float sGreen = floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
float sBlue = floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
#ifdef MODE_PROCESS_STATIC
//since its self-save, use RGBE8985
light_total_rgbe = ((uint(sRed) & 0x1FF) >> 1) | ((uint(sGreen) & 0x1FF) << 8) | (((uint(sBlue) & 0x1FF) >> 1) << 17) | ((uint(exps) & 0x1F) << 25);
#else
light_total_rgbe = (uint(sRed) & 0x1FF) | ((uint(sGreen) & 0x1FF) << 9) | ((uint(sBlue) & 0x1FF) << 18) | ((uint(exps) & 0x1F) << 27);
#endif
}
#ifdef MODE_PROCESS_DYNAMIC
vec4 aniso0;
aniso0.r = lumas[0] / luma_total;
aniso0.g = lumas[1] / luma_total;
aniso0.b = lumas[2] / luma_total;
aniso0.a = lumas[3] / luma_total;
vec2 aniso1;
aniso1.r = lumas[4] / luma_total;
aniso1.g = lumas[5] / luma_total;
//save to 3D textures
imageStore(dst_aniso0, positioni, aniso0);
imageStore(dst_aniso1, positioni, vec4(aniso1, 0.0, 0.0));
imageStore(dst_light, positioni, uvec4(light_total_rgbe));
//also fill neighbours, so light interpolation during the indirect pass works
//recover the neighbour list from the leftover bits
uint neighbours = (voxel_albedo >> 21) | ((voxel_position >> 21) << 11) | ((process_voxels.data[voxel_index].light >> 30) << 22) | ((process_voxels.data[voxel_index].light_aniso >> 30) << 24);
const uint max_neighbours = 26;
const ivec3 neighbour_positions[max_neighbours] = ivec3[](
ivec3(-1, -1, -1),
ivec3(-1, -1, 0),
ivec3(-1, -1, 1),
ivec3(-1, 0, -1),
ivec3(-1, 0, 0),
ivec3(-1, 0, 1),
ivec3(-1, 1, -1),
ivec3(-1, 1, 0),
ivec3(-1, 1, 1),
ivec3(0, -1, -1),
ivec3(0, -1, 0),
ivec3(0, -1, 1),
ivec3(0, 0, -1),
ivec3(0, 0, 1),
ivec3(0, 1, -1),
ivec3(0, 1, 0),
ivec3(0, 1, 1),
ivec3(1, -1, -1),
ivec3(1, -1, 0),
ivec3(1, -1, 1),
ivec3(1, 0, -1),
ivec3(1, 0, 0),
ivec3(1, 0, 1),
ivec3(1, 1, -1),
ivec3(1, 1, 0),
ivec3(1, 1, 1));
for (uint i = 0; i < max_neighbours; i++) {
if (bool(neighbours & (1 << i))) {
ivec3 neighbour_pos = positioni + neighbour_positions[i];
imageStore(dst_light, neighbour_pos, uvec4(light_total_rgbe));
imageStore(dst_aniso0, neighbour_pos, aniso0);
imageStore(dst_aniso1, neighbour_pos, vec4(aniso1, 0.0, 0.0));
}
}
#endif
#ifdef MODE_PROCESS_STATIC
//save back the anisotropic
uint light = process_voxels.data[voxel_index].light & (3 << 30);
light |= light_total_rgbe;
process_voxels.data[voxel_index].light = light; //replace
uint light_aniso = process_voxels.data[voxel_index].light_aniso & (3 << 30);
for (int i = 0; i < 6; i++) {
light_aniso |= min(31, uint((lumas[i] / luma_total) * 31.0)) << (i * 5);
}
process_voxels.data[voxel_index].light_aniso = light_aniso;
#endif
}

View File

@@ -0,0 +1,182 @@
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = OCT_RES, local_size_y = OCT_RES, local_size_z = 1) in;
/* clang-format on */
#define MAX_CASCADES 8
layout(rgba16f, set = 0, binding = 1) uniform restrict image2DArray irradiance_texture;
layout(rg16f, set = 0, binding = 2) uniform restrict image2DArray depth_texture;
ayout(rgba32ui, set = 0, binding = 3) uniform restrict uimage2DArray irradiance_history_texture;
layout(rg32ui, set = 0, binding = 4) uniform restrict uimage2DArray depth_history_texture;
struct CascadeData {
vec3 offset; //offset of (0,0,0) in world coordinates
float to_cell; // 1/bounds * grid_size
};
layout(set = 0, binding = 5, std140) uniform Cascades {
CascadeData data[MAX_CASCADES];
}
cascades;
#define DEPTH_HISTORY_BITS 24
#define IRRADIANCE_HISTORY_BITS 16
layout(push_constant, binding = 0, std430) uniform Params {
vec3 grid_size;
uint max_cascades;
uint probe_axis_size;
uint cascade;
uint history_size;
uint pad0;
ivec3 scroll; //scroll in probes
uint pad1;
}
params;
void main() {
ivec2 local = ivec2(gl_LocalInvocationID.xy);
ivec2 probe = ivec2(gl_WorkGroupID.xy);
ivec3 probe_cell;
probe_cell.x = probe.x % int(params.probe_axis_size);
probe_cell.y = probe.y;
probe_cell.z = probe.x / int(params.probe_axis_size);
#ifdef MODE_SCROLL_BEGIN
ivec3 read_cell = probe_cell - params.scroll;
uint src_layer = (params.history_size + 1) * params.cascade;
uint dst_layer = (params.history_size + 1) * params.max_cascades;
for (uint i = 0; i <= params.history_size; i++) {
ivec3 write_pos = ivec3(probe * OCT_RES + local, int(i));
if (any(lessThan(read_pos, ivec3(0))) || any(greaterThanEqual(read_pos, ivec3(params.probe_axis_size)))) {
// nowhere to read from for scrolling, try finding the value from upper probes
#ifdef MODE_IRRADIANCE
imageStore(irradiance_history_texture, write_pos, uvec4(0));
#endif
#ifdef MODE_DEPTH
imageStore(depth_history_texture, write_pos, uvec4(0));
#endif
} else {
ivec3 read_pos;
read_pos.xy = read_cell.xy;
read_pos.x += read_cell.z * params.probe_axis_size;
read_pos.xy = read_pos.xy * OCT_RES + local;
read_pos.z = int(i);
#ifdef MODE_IRRADIANCE
uvec4 value = imageLoad(irradiance_history_texture, read_pos);
imageStore(irradiance_history_texture, write_pos, value);
#endif
#ifdef MODE_DEPTH
uvec2 value = imageLoad(depth_history_texture, read_pos);
imageStore(depth_history_texture, write_pos, value);
#endif
}
}
#endif // MODE_SCROLL_BEGIN
#ifdef MODE_SCROLL_END
uint src_layer = (params.history_size + 1) * params.max_cascades;
uint dst_layer = (params.history_size + 1) * params.cascade;
for (uint i = 0; i <= params.history_size; i++) {
ivec3 pos = ivec3(probe * OCT_RES + local, int(i));
#ifdef MODE_IRRADIANCE
uvec4 value = imageLoad(irradiance_history_texture, read_pos);
imageStore(irradiance_history_texture, write_pos, value);
#endif
#ifdef MODE_DEPTH
uvec2 value = imageLoad(depth_history_texture, read_pos);
imageStore(depth_history_texture, write_pos, value);
#endif
}
#endif //MODE_SCROLL_END
#ifdef MODE_STORE
uint src_layer = (params.history_size + 1) * params.cascade + params.history_size;
ivec3 read_pos = ivec3(probe * OCT_RES + local, int(src_layer));
ivec3 write_pos = ivec3(probe * (OCT_RES + 2) + ivec2(1), int(params.cascade));
ivec3 copy_to[4] = ivec3[](write_pos, ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2));
#ifdef MODE_IRRADIANCE
uvec4 average = imageLoad(irradiance_history_texture, read_pos);
vec4 light_accum = vec4(average / params.history_size) / float(1 << IRRADIANCE_HISTORY_BITS);
#endif
#ifdef MODE_DEPTH
uvec2 value = imageLoad(depth_history_texture, read_pos);
vec2 depth_accum = vec4(average / params.history_size) / float(1 << IRRADIANCE_HISTORY_BITS);
float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
float max_depth = length(params.grid_size / cascades.data[params.max_cascades - 1].to_cell);
max_depth /= probe_cell_size;
depth_value = (vec2(average / params.history_size) / float(1 << DEPTH_HISTORY_BITS)) * vec2(max_depth, max_depth * max_depth);
#endif
/* Fill the border if required */
if (local == ivec2(0, 0)) {
copy_to[1] = texture_pos + ivec3(OCT_RES - 1, -1, 0);
copy_to[2] = texture_pos + ivec3(-1, OCT_RES - 1, 0);
copy_to[3] = texture_pos + ivec3(OCT_RES, OCT_RES, 0);
} else if (local == ivec2(OCT_RES - 1, 0)) {
copy_to[1] = texture_pos + ivec3(0, -1, 0);
copy_to[2] = texture_pos + ivec3(OCT_RES, OCT_RES - 1, 0);
copy_to[3] = texture_pos + ivec3(-1, OCT_RES, 0);
} else if (local == ivec2(0, OCT_RES - 1)) {
copy_to[1] = texture_pos + ivec3(-1, 0, 0);
copy_to[2] = texture_pos + ivec3(OCT_RES - 1, OCT_RES, 0);
copy_to[3] = texture_pos + ivec3(OCT_RES, -1, 0);
} else if (local == ivec2(OCT_RES - 1, OCT_RES - 1)) {
copy_to[1] = texture_pos + ivec3(0, OCT_RES, 0);
copy_to[2] = texture_pos + ivec3(OCT_RES, 0, 0);
copy_to[3] = texture_pos + ivec3(-1, -1, 0);
} else if (local.y == 0) {
copy_to[1] = texture_pos + ivec3(OCT_RES - local.x - 1, local.y - 1, 0);
} else if (local.x == 0) {
copy_to[1] = texture_pos + ivec3(local.x - 1, OCT_RES - local.y - 1, 0);
} else if (local.y == OCT_RES - 1) {
copy_to[1] = texture_pos + ivec3(OCT_RES - local.x - 1, local.y + 1, 0);
} else if (local.x == OCT_RES - 1) {
copy_to[1] = texture_pos + ivec3(local.x + 1, OCT_RES - local.y - 1, 0);
}
for (int i = 0; i < 4; i++) {
if (copy_to[i] == ivec3(-2, -2, -2)) {
continue;
}
#ifdef MODE_IRRADIANCE
imageStore(irradiance_texture, copy_to[i], light_accum);
#endif
#ifdef MODE_DEPTH
imageStore(depth_texture, copy_to[i], vec4(depth_value, 0.0, 0.0));
#endif
}
#endif // MODE_STORE
}

View File

@@ -0,0 +1,617 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#define MAX_CASCADES 8
layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES];
layout(set = 0, binding = 2) uniform texture3D light_cascades[MAX_CASCADES];
layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[MAX_CASCADES];
layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[MAX_CASCADES];
layout(set = 0, binding = 6) uniform sampler linear_sampler;
struct CascadeData {
vec3 offset; //offset of (0,0,0) in world coordinates
float to_cell; // 1/bounds * grid_size
ivec3 probe_world_offset;
uint pad;
};
layout(set = 0, binding = 7, std140) uniform Cascades {
CascadeData data[MAX_CASCADES];
}
cascades;
layout(r32ui, set = 0, binding = 8) uniform restrict uimage2DArray lightprobe_texture_data;
layout(rgba16i, set = 0, binding = 9) uniform restrict iimage2DArray lightprobe_history_texture;
layout(rgba32i, set = 0, binding = 10) uniform restrict iimage2D lightprobe_average_texture;
//used for scrolling
layout(rgba16i, set = 0, binding = 11) uniform restrict iimage2DArray lightprobe_history_scroll_texture;
layout(rgba32i, set = 0, binding = 12) uniform restrict iimage2D lightprobe_average_scroll_texture;
layout(rgba32i, set = 0, binding = 13) uniform restrict iimage2D lightprobe_average_parent_texture;
layout(rgba16f, set = 0, binding = 14) uniform restrict writeonly image2DArray lightprobe_ambient_texture;
layout(set = 1, binding = 0) uniform textureCube sky_irradiance;
layout(set = 1, binding = 1) uniform sampler linear_sampler_mipmaps;
#define HISTORY_BITS 10
#define SKY_MODE_DISABLED 0
#define SKY_MODE_COLOR 1
#define SKY_MODE_SKY 2
layout(push_constant, binding = 0, std430) uniform Params {
vec3 grid_size;
uint max_cascades;
uint probe_axis_size;
uint cascade;
uint history_index;
uint history_size;
uint ray_count;
float ray_bias;
ivec2 image_size;
ivec3 world_offset;
uint sky_mode;
ivec3 scroll;
float sky_energy;
vec3 sky_color;
float y_mult;
bool store_ambient_texture;
uint pad[3];
}
params;
const float PI = 3.14159265f;
const float GOLDEN_ANGLE = PI * (3.0 - sqrt(5.0));
vec3 vogel_hemisphere(uint p_index, uint p_count, float p_offset) {
float r = sqrt(float(p_index) + 0.5f) / sqrt(float(p_count));
float theta = float(p_index) * GOLDEN_ANGLE + p_offset;
float y = cos(r * PI * 0.5);
float l = sin(r * PI * 0.5);
return vec3(l * cos(theta), l * sin(theta), y * (float(p_index & 1) * 2.0 - 1.0));
}
uvec3 hash3(uvec3 x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
float hashf3(vec3 co) {
return fract(sin(dot(co, vec3(12.9898, 78.233, 137.13451))) * 43758.5453);
}
vec3 octahedron_encode(vec2 f) {
// https://twitter.com/Stubbesaurus/status/937994790553227264
f = f * 2.0 - 1.0;
vec3 n = vec3(f.x, f.y, 1.0f - abs(f.x) - abs(f.y));
float t = clamp(-n.z, 0.0, 1.0);
n.x += n.x >= 0 ? -t : t;
n.y += n.y >= 0 ? -t : t;
return normalize(n);
}
uint rgbe_encode(vec3 color) {
const float pow2to9 = 512.0f;
const float B = 15.0f;
const float N = 9.0f;
const float LN2 = 0.6931471805599453094172321215;
float cRed = clamp(color.r, 0.0, 65408.0);
float cGreen = clamp(color.g, 0.0, 65408.0);
float cBlue = clamp(color.b, 0.0, 65408.0);
float cMax = max(cRed, max(cGreen, cBlue));
float expp = max(-B - 1.0f, floor(log(cMax) / LN2)) + 1.0f + B;
float sMax = floor((cMax / pow(2.0f, expp - B - N)) + 0.5f);
float exps = expp + 1.0f;
if (0.0 <= sMax && sMax < pow2to9) {
exps = expp;
}
float sRed = floor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
float sGreen = floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
float sBlue = floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
return (uint(sRed) & 0x1FF) | ((uint(sGreen) & 0x1FF) << 9) | ((uint(sBlue) & 0x1FF) << 18) | ((uint(exps) & 0x1F) << 27);
}
void main() {
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(pos, params.image_size))) { //too large, do nothing
return;
}
#ifdef MODE_PROCESS
float probe_cell_size = float(params.grid_size.x / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
ivec3 probe_cell;
probe_cell.x = pos.x % int(params.probe_axis_size);
probe_cell.y = pos.y;
probe_cell.z = pos.x / int(params.probe_axis_size);
vec3 probe_pos = cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size;
vec3 pos_to_uvw = 1.0 / params.grid_size;
vec4 probe_sh_accum[SH_SIZE] = vec4[](
vec4(0.0),
vec4(0.0),
vec4(0.0),
vec4(0.0),
vec4(0.0),
vec4(0.0),
vec4(0.0),
vec4(0.0),
vec4(0.0)
#if (SH_SIZE == 16)
,
vec4(0.0),
vec4(0.0),
vec4(0.0),
vec4(0.0),
vec4(0.0),
vec4(0.0),
vec4(0.0)
#endif
);
// quickly ensure each probe has a different "offset" for the vogel function, based on integer world position
uvec3 h3 = hash3(uvec3(params.world_offset + probe_cell));
float offset = hashf3(vec3(h3 & uvec3(0xFFFFF)));
//for a more homogeneous hemisphere, alternate based on history frames
uint ray_offset = params.history_index;
uint ray_mult = params.history_size;
uint ray_total = ray_mult * params.ray_count;
for (uint i = 0; i < params.ray_count; i++) {
vec3 ray_dir = vogel_hemisphere(ray_offset + i * ray_mult, ray_total, offset);
ray_dir.y *= params.y_mult;
ray_dir = normalize(ray_dir);
//needs to be visible
vec3 ray_pos = probe_pos;
vec3 inv_dir = 1.0 / ray_dir;
bool hit = false;
vec3 hit_normal;
vec3 hit_light;
vec3 hit_aniso0;
vec3 hit_aniso1;
float bias = params.ray_bias;
vec3 abs_ray_dir = abs(ray_dir);
ray_pos += ray_dir * 1.0 / max(abs_ray_dir.x, max(abs_ray_dir.y, abs_ray_dir.z)) * bias / cascades.data[params.cascade].to_cell;
for (uint j = params.cascade; j < params.max_cascades; j++) {
//convert to local bounds
vec3 pos = ray_pos - cascades.data[j].offset;
pos *= cascades.data[j].to_cell;
if (any(lessThan(pos, vec3(0.0))) || any(greaterThanEqual(pos, params.grid_size))) {
continue; //already past bounds for this cascade, goto next
}
//find maximum advance distance (until reaching bounds)
vec3 t0 = -pos * inv_dir;
vec3 t1 = (params.grid_size - pos) * inv_dir;
vec3 tmax = max(t0, t1);
float max_advance = min(tmax.x, min(tmax.y, tmax.z));
float advance = 0.0;
vec3 uvw;
while (advance < max_advance) {
//read how much to advance from SDF
uvw = (pos + ray_dir * advance) * pos_to_uvw;
float distance = texture(sampler3D(sdf_cascades[j], linear_sampler), uvw).r * 255.0 - 1.0;
if (distance < 0.001) {
//consider hit
hit = true;
break;
}
advance += distance;
}
if (hit) {
const float EPSILON = 0.001;
hit_normal = normalize(vec3(
texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(EPSILON, 0.0, 0.0)).r,
texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(0.0, EPSILON, 0.0)).r,
texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(0.0, 0.0, EPSILON)).r));
hit_light = texture(sampler3D(light_cascades[j], linear_sampler), uvw).rgb;
vec4 aniso0 = texture(sampler3D(aniso0_cascades[j], linear_sampler), uvw);
hit_aniso0 = aniso0.rgb;
hit_aniso1 = vec3(aniso0.a, texture(sampler3D(aniso1_cascades[j], linear_sampler), uvw).rg);
break;
}
//change ray origin to collision with bounds
pos += ray_dir * max_advance;
pos /= cascades.data[j].to_cell;
pos += cascades.data[j].offset;
ray_pos = pos;
}
vec4 light;
if (hit) {
//one liner magic
light.rgb = hit_light * (dot(max(vec3(0.0), (hit_normal * hit_aniso0)), vec3(1.0)) + dot(max(vec3(0.0), (-hit_normal * hit_aniso1)), vec3(1.0)));
light.a = 1.0;
} else if (params.sky_mode == SKY_MODE_SKY) {
light.rgb = textureLod(samplerCube(sky_irradiance, linear_sampler_mipmaps), ray_dir, 2.0).rgb; //use second mipmap because we dont usually throw a lot of rays, so this compensates
light.rgb *= params.sky_energy;
light.a = 0.0;
} else if (params.sky_mode == SKY_MODE_COLOR) {
light.rgb = params.sky_color;
light.rgb *= params.sky_energy;
light.a = 0.0;
} else {
light = vec4(0, 0, 0, 0);
}
vec3 ray_dir2 = ray_dir * ray_dir;
float c[SH_SIZE] = float[](
0.282095, //l0
0.488603 * ray_dir.y, //l1n1
0.488603 * ray_dir.z, //l1n0
0.488603 * ray_dir.x, //l1p1
1.092548 * ray_dir.x * ray_dir.y, //l2n2
1.092548 * ray_dir.y * ray_dir.z, //l2n1
0.315392 * (3.0 * ray_dir2.z - 1.0), //l20
1.092548 * ray_dir.x * ray_dir.z, //l2p1
0.546274 * (ray_dir2.x - ray_dir2.y) //l2p2
#if (SH_SIZE == 16)
,
0.590043 * ray_dir.y * (3.0f * ray_dir2.x - ray_dir2.y),
2.890611 * ray_dir.y * ray_dir.x * ray_dir.z,
0.646360 * ray_dir.y * (-1.0f + 5.0f * ray_dir2.z),
0.373176 * (5.0f * ray_dir2.z * ray_dir.z - 3.0f * ray_dir.z),
0.457045 * ray_dir.x * (-1.0f + 5.0f * ray_dir2.z),
1.445305 * (ray_dir2.x - ray_dir2.y) * ray_dir.z,
0.590043 * ray_dir.x * (ray_dir2.x - 3.0f * ray_dir2.y)
#endif
);
for (uint j = 0; j < SH_SIZE; j++) {
probe_sh_accum[j] += light * c[j];
}
}
for (uint i = 0; i < SH_SIZE; i++) {
// store in history texture
ivec3 prev_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(params.history_index));
ivec2 average_pos = prev_pos.xy;
vec4 value = probe_sh_accum[i] * 4.0 / float(params.ray_count);
ivec4 ivalue = clamp(ivec4(value * float(1 << HISTORY_BITS)), -32768, 32767); //clamp to 16 bits, so higher values don't break average
ivec4 prev_value = imageLoad(lightprobe_history_texture, prev_pos);
ivec4 average = imageLoad(lightprobe_average_texture, average_pos);
average -= prev_value;
average += ivalue;
imageStore(lightprobe_history_texture, prev_pos, ivalue);
imageStore(lightprobe_average_texture, average_pos, average);
if (params.store_ambient_texture && i == 0) {
ivec3 ambient_pos = ivec3(pos, int(params.cascade));
vec4 ambient_light = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS);
ambient_light *= 0.88622; // SHL0
imageStore(lightprobe_ambient_texture, ambient_pos, ambient_light);
}
}
#endif // MODE PROCESS
#ifdef MODE_STORE
// converting to octahedral in this step is required because
// octahedral is much faster to read from the screen than spherical harmonics,
// despite the very slight quality loss
ivec2 sh_pos = (pos / OCT_SIZE) * ivec2(1, SH_SIZE);
ivec2 oct_pos = (pos / OCT_SIZE) * (OCT_SIZE + 2) + ivec2(1);
ivec2 local_pos = pos % OCT_SIZE;
//fill the spherical harmonic
vec4 sh[SH_SIZE];
for (uint i = 0; i < SH_SIZE; i++) {
// store in history texture
ivec2 average_pos = sh_pos + ivec2(0, i);
ivec4 average = imageLoad(lightprobe_average_texture, average_pos);
sh[i] = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS);
}
//compute the octahedral normal for this texel
vec3 normal = octahedron_encode(vec2(local_pos) / float(OCT_SIZE));
/*
// read the spherical harmonic
const float c1 = 0.429043;
const float c2 = 0.511664;
const float c3 = 0.743125;
const float c4 = 0.886227;
const float c5 = 0.247708;
vec4 light = (c1 * sh[8] * (normal.x * normal.x - normal.y * normal.y) +
c3 * sh[6] * normal.z * normal.z +
c4 * sh[0] -
c5 * sh[6] +
2.0 * c1 * sh[4] * normal.x * normal.y +
2.0 * c1 * sh[7] * normal.x * normal.z +
2.0 * c1 * sh[5] * normal.y * normal.z +
2.0 * c2 * sh[3] * normal.x +
2.0 * c2 * sh[1] * normal.y +
2.0 * c2 * sh[2] * normal.z);
*/
vec3 normal2 = normal * normal;
float c[SH_SIZE] = float[](
0.282095, //l0
0.488603 * normal.y, //l1n1
0.488603 * normal.z, //l1n0
0.488603 * normal.x, //l1p1
1.092548 * normal.x * normal.y, //l2n2
1.092548 * normal.y * normal.z, //l2n1
0.315392 * (3.0 * normal2.z - 1.0), //l20
1.092548 * normal.x * normal.z, //l2p1
0.546274 * (normal2.x - normal2.y) //l2p2
#if (SH_SIZE == 16)
,
0.590043 * normal.y * (3.0f * normal2.x - normal2.y),
2.890611 * normal.y * normal.x * normal.z,
0.646360 * normal.y * (-1.0f + 5.0f * normal2.z),
0.373176 * (5.0f * normal2.z * normal.z - 3.0f * normal.z),
0.457045 * normal.x * (-1.0f + 5.0f * normal2.z),
1.445305 * (normal2.x - normal2.y) * normal.z,
0.590043 * normal.x * (normal2.x - 3.0f * normal2.y)
#endif
);
const float l_mult[SH_SIZE] = float[](
1.0,
2.0 / 3.0,
2.0 / 3.0,
2.0 / 3.0,
1.0 / 4.0,
1.0 / 4.0,
1.0 / 4.0,
1.0 / 4.0,
1.0 / 4.0
#if (SH_SIZE == 16)
, // l4 does not contribute to irradiance
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
#endif
);
vec3 irradiance = vec3(0.0);
vec3 radiance = vec3(0.0);
for (uint i = 0; i < SH_SIZE; i++) {
vec3 m = sh[i].rgb * c[i] * 4.0;
irradiance += m * l_mult[i];
radiance += m;
}
//encode RGBE9995 for the final texture
uint irradiance_rgbe = rgbe_encode(irradiance);
uint radiance_rgbe = rgbe_encode(radiance);
//store in octahedral map
ivec3 texture_pos = ivec3(oct_pos, int(params.cascade));
ivec3 copy_to[4] = ivec3[](ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2));
copy_to[0] = texture_pos + ivec3(local_pos, 0);
if (local_pos == ivec2(0, 0)) {
copy_to[1] = texture_pos + ivec3(OCT_SIZE - 1, -1, 0);
copy_to[2] = texture_pos + ivec3(-1, OCT_SIZE - 1, 0);
copy_to[3] = texture_pos + ivec3(OCT_SIZE, OCT_SIZE, 0);
} else if (local_pos == ivec2(OCT_SIZE - 1, 0)) {
copy_to[1] = texture_pos + ivec3(0, -1, 0);
copy_to[2] = texture_pos + ivec3(OCT_SIZE, OCT_SIZE - 1, 0);
copy_to[3] = texture_pos + ivec3(-1, OCT_SIZE, 0);
} else if (local_pos == ivec2(0, OCT_SIZE - 1)) {
copy_to[1] = texture_pos + ivec3(-1, 0, 0);
copy_to[2] = texture_pos + ivec3(OCT_SIZE - 1, OCT_SIZE, 0);
copy_to[3] = texture_pos + ivec3(OCT_SIZE, -1, 0);
} else if (local_pos == ivec2(OCT_SIZE - 1, OCT_SIZE - 1)) {
copy_to[1] = texture_pos + ivec3(0, OCT_SIZE, 0);
copy_to[2] = texture_pos + ivec3(OCT_SIZE, 0, 0);
copy_to[3] = texture_pos + ivec3(-1, -1, 0);
} else if (local_pos.y == 0) {
copy_to[1] = texture_pos + ivec3(OCT_SIZE - local_pos.x - 1, local_pos.y - 1, 0);
} else if (local_pos.x == 0) {
copy_to[1] = texture_pos + ivec3(local_pos.x - 1, OCT_SIZE - local_pos.y - 1, 0);
} else if (local_pos.y == OCT_SIZE - 1) {
copy_to[1] = texture_pos + ivec3(OCT_SIZE - local_pos.x - 1, local_pos.y + 1, 0);
} else if (local_pos.x == OCT_SIZE - 1) {
copy_to[1] = texture_pos + ivec3(local_pos.x + 1, OCT_SIZE - local_pos.y - 1, 0);
}
for (int i = 0; i < 4; i++) {
if (copy_to[i] == ivec3(-2, -2, -2)) {
continue;
}
imageStore(lightprobe_texture_data, copy_to[i], uvec4(irradiance_rgbe));
imageStore(lightprobe_texture_data, copy_to[i] + ivec3(0, 0, int(params.max_cascades)), uvec4(radiance_rgbe));
}
#endif
#ifdef MODE_SCROLL
ivec3 probe_cell;
probe_cell.x = pos.x % int(params.probe_axis_size);
probe_cell.y = pos.y;
probe_cell.z = pos.x / int(params.probe_axis_size);
ivec3 read_probe = probe_cell - params.scroll;
if (all(greaterThanEqual(read_probe, ivec3(0))) && all(lessThan(read_probe, ivec3(params.probe_axis_size)))) {
// can scroll
ivec2 tex_pos;
tex_pos = read_probe.xy;
tex_pos.x += read_probe.z * int(params.probe_axis_size);
//scroll
for (uint j = 0; j < params.history_size; j++) {
for (int i = 0; i < SH_SIZE; i++) {
// copy from history texture
ivec3 src_pos = ivec3(tex_pos.x, tex_pos.y * SH_SIZE + i, int(j));
ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j));
ivec4 value = imageLoad(lightprobe_history_texture, src_pos);
imageStore(lightprobe_history_scroll_texture, dst_pos, value);
}
}
for (int i = 0; i < SH_SIZE; i++) {
// copy from average texture
ivec2 src_pos = ivec2(tex_pos.x, tex_pos.y * SH_SIZE + i);
ivec2 dst_pos = ivec2(pos.x, pos.y * SH_SIZE + i);
ivec4 value = imageLoad(lightprobe_average_texture, src_pos);
imageStore(lightprobe_average_scroll_texture, dst_pos, value);
}
} else if (params.cascade < params.max_cascades - 1) {
//can't scroll, must look for position in parent cascade
//to global coords
float probe_cell_size = float(params.grid_size.x / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
vec3 probe_pos = cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size;
//to parent local coords
probe_pos -= cascades.data[params.cascade + 1].offset;
probe_pos *= cascades.data[params.cascade + 1].to_cell;
probe_pos = probe_pos * float(params.probe_axis_size - 1) / float(params.grid_size.x);
ivec3 probe_posi = ivec3(probe_pos);
//add up all light, no need to use occlusion here, since occlusion will do its work afterwards
vec4 average_light[SH_SIZE] = vec4[](vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0)
#if (SH_SIZE == 16)
,
vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0)
#endif
);
float total_weight = 0.0;
for (int i = 0; i < 8; i++) {
ivec3 offset = probe_posi + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1));
vec3 trilinear = vec3(1.0) - abs(probe_pos - vec3(offset));
float weight = trilinear.x * trilinear.y * trilinear.z;
ivec2 tex_pos;
tex_pos = offset.xy;
tex_pos.x += offset.z * int(params.probe_axis_size);
for (int j = 0; j < SH_SIZE; j++) {
// copy from history texture
ivec2 src_pos = ivec2(tex_pos.x, tex_pos.y * SH_SIZE + j);
ivec4 average = imageLoad(lightprobe_average_parent_texture, src_pos);
vec4 value = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS);
average_light[j] += value * weight;
}
total_weight += weight;
}
if (total_weight > 0.0) {
total_weight = 1.0 / total_weight;
}
//store the averaged values everywhere
for (int i = 0; i < SH_SIZE; i++) {
ivec4 ivalue = clamp(ivec4(average_light[i] * total_weight * float(1 << HISTORY_BITS)), ivec4(-32768), ivec4(32767)); //clamp to 16 bits, so higher values don't break average
// copy from history texture
ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, 0);
for (uint j = 0; j < params.history_size; j++) {
dst_pos.z = int(j);
imageStore(lightprobe_history_scroll_texture, dst_pos, ivalue);
}
ivalue *= int(params.history_size); //average needs to have all history added up
imageStore(lightprobe_average_scroll_texture, dst_pos.xy, ivalue);
}
} else {
// clear and let it re-raytrace, only for the last cascade, which happens very un-often
//scroll
for (uint j = 0; j < params.history_size; j++) {
for (int i = 0; i < SH_SIZE; i++) {
// copy from history texture
ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j));
imageStore(lightprobe_history_scroll_texture, dst_pos, ivec4(0));
}
}
for (int i = 0; i < SH_SIZE; i++) {
// copy from average texture
ivec2 dst_pos = ivec2(pos.x, pos.y * SH_SIZE + i);
imageStore(lightprobe_average_scroll_texture, dst_pos, ivec4(0));
}
}
#endif
#ifdef MODE_SCROLL_STORE
//do not update probe texture, as these will be updated later
for (uint j = 0; j < params.history_size; j++) {
for (int i = 0; i < SH_SIZE; i++) {
// copy from history texture
ivec3 spos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j));
ivec4 value = imageLoad(lightprobe_history_scroll_texture, spos);
imageStore(lightprobe_history_texture, spos, value);
}
}
for (int i = 0; i < SH_SIZE; i++) {
// copy from average texture
ivec2 spos = ivec2(pos.x, pos.y * SH_SIZE + i);
ivec4 average = imageLoad(lightprobe_average_scroll_texture, spos);
imageStore(lightprobe_average_texture, spos, average);
}
#endif
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,105 @@
#[compute]
#version 450
VERSION_DEFINES
#define BLOCK_SIZE 8
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
#ifdef MODE_REDUCE
shared float tmp_data[BLOCK_SIZE * BLOCK_SIZE];
const uint swizzle_table[BLOCK_SIZE] = uint[](0, 4, 2, 6, 1, 5, 3, 7);
const uint unswizzle_table[BLOCK_SIZE] = uint[](0, 0, 0, 1, 0, 2, 1, 3);
#endif
layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D source_depth;
layout(r32f, set = 0, binding = 1) uniform restrict writeonly image2D dst_depth;
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 source_size;
ivec2 source_offset;
uint min_size;
uint gaussian_kernel_version;
ivec2 filter_dir;
}
params;
void main() {
#ifdef MODE_REDUCE
uvec2 pos = gl_LocalInvocationID.xy;
ivec2 image_offset = params.source_offset;
ivec2 image_pos = image_offset + ivec2(gl_GlobalInvocationID.xy);
uint dst_t = swizzle_table[pos.y] * BLOCK_SIZE + swizzle_table[pos.x];
tmp_data[dst_t] = imageLoad(source_depth, min(image_pos, params.source_size - ivec2(1))).r;
ivec2 image_size = params.source_size;
uint t = pos.y * BLOCK_SIZE + pos.x;
//neighbours
uint size = BLOCK_SIZE;
do {
groupMemoryBarrier();
barrier();
size >>= 1;
image_size >>= 1;
image_offset >>= 1;
if (all(lessThan(pos, uvec2(size)))) {
uint nx = t + size;
uint ny = t + (BLOCK_SIZE * size);
uint nxy = ny + size;
tmp_data[t] += tmp_data[nx];
tmp_data[t] += tmp_data[ny];
tmp_data[t] += tmp_data[nxy];
tmp_data[t] /= 4.0;
}
} while (size > params.min_size);
if (all(lessThan(pos, uvec2(size)))) {
image_pos = ivec2(unswizzle_table[size + pos.x], unswizzle_table[size + pos.y]);
image_pos += image_offset + ivec2(gl_WorkGroupID.xy) * int(size);
image_size = max(ivec2(1), image_size); //in case image size became 0
if (all(lessThan(image_pos, uvec2(image_size)))) {
imageStore(dst_depth, image_pos, vec4(tmp_data[t]));
}
}
#endif
#ifdef MODE_FILTER
ivec2 image_pos = params.source_offset + ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(image_pos, params.source_size))) {
return;
}
ivec2 clamp_min = ivec2(params.source_offset);
ivec2 clamp_max = ivec2(params.source_size) - 1;
//gaussian kernel, size 9, sigma 4
const int kernel_size = 9;
const float gaussian_kernel[kernel_size * 3] = float[](
0.000229, 0.005977, 0.060598, 0.241732, 0.382928, 0.241732, 0.060598, 0.005977, 0.000229,
0.028532, 0.067234, 0.124009, 0.179044, 0.20236, 0.179044, 0.124009, 0.067234, 0.028532,
0.081812, 0.101701, 0.118804, 0.130417, 0.134535, 0.130417, 0.118804, 0.101701, 0.081812);
float accum = 0.0;
for (int i = 0; i < kernel_size; i++) {
ivec2 ofs = clamp(image_pos + params.filter_dir * (i - kernel_size / 2), clamp_min, clamp_max);
accum += imageLoad(source_depth, ofs).r * gaussian_kernel[params.gaussian_kernel_version + i];
}
imageStore(dst_depth, image_pos, vec4(accum));
#endif
}

View File

@@ -0,0 +1,250 @@
#[vertex]
#version 450
VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
layout(push_constant, binding = 1, std430) uniform Params {
mat3 orientation;
vec4 proj;
vec4 position_multiplier;
float time;
}
params;
void main() {
vec2 base_arr[4] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(1.0, -1.0));
uv_interp = base_arr[gl_VertexIndex];
gl_Position = vec4(uv_interp, 1.0, 1.0);
}
#[fragment]
#version 450
VERSION_DEFINES
#define M_PI 3.14159265359
layout(location = 0) in vec2 uv_interp;
layout(push_constant, binding = 1, std430) uniform Params {
mat3 orientation;
vec4 proj;
vec4 position_multiplier;
float time; //TODO consider adding vec2 screen res, and float radiance size
}
params;
#define SAMPLER_NEAREST_CLAMP 0
#define SAMPLER_LINEAR_CLAMP 1
#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
#define SAMPLER_NEAREST_REPEAT 6
#define SAMPLER_LINEAR_REPEAT 7
#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
layout(set = 0, binding = 0) uniform sampler material_samplers[12];
layout(set = 0, binding = 1, std430) restrict readonly buffer GlobalVariableData {
vec4 data[];
}
global_variables;
layout(set = 0, binding = 2, std140) uniform SceneData {
bool volumetric_fog_enabled;
float volumetric_fog_inv_length;
float volumetric_fog_detail_spread;
float fog_aerial_perspective;
vec3 fog_light_color;
float fog_sun_scatter;
bool fog_enabled;
float fog_density;
float z_far;
uint directional_light_count;
}
scene_data;
struct DirectionalLightData {
vec4 direction_energy;
vec4 color_size;
bool enabled;
};
layout(set = 0, binding = 3, std140) uniform DirectionalLights {
DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
}
directional_lights;
#ifdef USE_MATERIAL_UNIFORMS
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
/* clang-format off */
MATERIAL_UNIFORMS
/* clang-format on */
} material;
#endif
layout(set = 2, binding = 0) uniform textureCube radiance;
#ifdef USE_CUBEMAP_PASS
layout(set = 2, binding = 1) uniform textureCube half_res;
layout(set = 2, binding = 2) uniform textureCube quarter_res;
#else
layout(set = 2, binding = 1) uniform texture2D half_res;
layout(set = 2, binding = 2) uniform texture2D quarter_res;
#endif
layout(set = 3, binding = 0) uniform texture3D volumetric_fog_texture;
#ifdef USE_CUBEMAP_PASS
#define AT_CUBEMAP_PASS true
#else
#define AT_CUBEMAP_PASS false
#endif
#ifdef USE_HALF_RES_PASS
#define AT_HALF_RES_PASS true
#else
#define AT_HALF_RES_PASS false
#endif
#ifdef USE_QUARTER_RES_PASS
#define AT_QUARTER_RES_PASS true
#else
#define AT_QUARTER_RES_PASS false
#endif
/* clang-format off */
FRAGMENT_SHADER_GLOBALS
/* clang-format on */
layout(location = 0) out vec4 frag_color;
vec4 volumetric_fog_process(vec2 screen_uv) {
vec3 fog_pos = vec3(screen_uv, 1.0);
return texture(sampler3D(volumetric_fog_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), fog_pos);
}
vec4 fog_process(vec3 view, vec3 sky_color) {
vec3 fog_color = mix(scene_data.fog_light_color, sky_color, scene_data.fog_aerial_perspective);
if (scene_data.fog_sun_scatter > 0.001) {
vec4 sun_scatter = vec4(0.0);
float sun_total = 0.0;
for (uint i = 0; i < scene_data.directional_light_count; i++) {
vec3 light_color = directional_lights.data[i].color_size.xyz * directional_lights.data[i].direction_energy.w;
float light_amount = pow(max(dot(view, directional_lights.data[i].direction_energy.xyz), 0.0), 8.0);
fog_color += light_color * light_amount * scene_data.fog_sun_scatter;
}
}
float fog_amount = clamp(1.0 - exp(-scene_data.z_far * scene_data.fog_density), 0.0, 1.0);
return vec4(fog_color, fog_amount);
}
void main() {
vec3 cube_normal;
cube_normal.z = -1.0;
cube_normal.x = (cube_normal.z * (-uv_interp.x - params.proj.x)) / params.proj.y;
cube_normal.y = -(cube_normal.z * (-uv_interp.y - params.proj.z)) / params.proj.w;
cube_normal = mat3(params.orientation) * cube_normal;
cube_normal.z = -cube_normal.z;
cube_normal = normalize(cube_normal);
vec2 uv = uv_interp * 0.5 + 0.5;
vec2 panorama_coords = vec2(atan(cube_normal.x, cube_normal.z), acos(cube_normal.y));
if (panorama_coords.x < 0.0) {
panorama_coords.x += M_PI * 2.0;
}
panorama_coords /= vec2(M_PI * 2.0, M_PI);
vec3 color = vec3(0.0, 0.0, 0.0);
float alpha = 1.0; // Only available to subpasses
vec4 half_res_color = vec4(1.0);
vec4 quarter_res_color = vec4(1.0);
vec4 custom_fog = vec4(0.0);
#ifdef USE_CUBEMAP_PASS
vec3 inverted_cube_normal = cube_normal;
inverted_cube_normal.z *= -1.0;
#ifdef USES_HALF_RES_COLOR
half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal);
#endif
#ifdef USES_QUARTER_RES_COLOR
quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal);
#endif
#else
#ifdef USES_HALF_RES_COLOR
half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0);
#endif
#ifdef USES_QUARTER_RES_COLOR
quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0);
#endif
#endif
// unused, just here to make our compiler happy, make sure we don't execute any light code the user adds in..
#ifndef REALLYINCLUDETHIS
{
/* clang-format off */
LIGHT_SHADER_CODE
/* clang-format on */
}
#endif
{
/* clang-format off */
FRAGMENT_SHADER_CODE
/* clang-format on */
}
frag_color.rgb = color * params.position_multiplier.w;
frag_color.a = alpha;
#if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS)
// Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky.
if (scene_data.fog_enabled) {
vec4 fog = fog_process(cube_normal, frag_color.rgb);
frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a);
}
if (scene_data.volumetric_fog_enabled) {
vec4 fog = volumetric_fog_process(uv);
frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a);
}
if (custom_fog.a > 0.0) {
frag_color.rgb = mix(frag_color.rgb, custom_fog.rgb, custom_fog.a);
}
#endif // DISABLE_FOG
// Blending is disabled for Sky, so alpha doesn't blend
// alpha is used for subsurface scattering so make sure it doesn't get applied to Sky
if (!AT_CUBEMAP_PASS && !AT_HALF_RES_PASS && !AT_QUARTER_RES_PASS) {
frag_color.a = 0.0;
}
}

View File

@@ -0,0 +1,203 @@
#[compute]
#version 450
VERSION_DEFINES
// Original version here:
// https://github.com/GPUOpen-LibrariesAndSDKs/GPUParticles11/blob/master/gpuparticles11/src/Shaders
//
// Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#define SORT_SIZE 512
#define NUM_THREADS (SORT_SIZE / 2)
#define INVERSION (16 * 2 + 8 * 3)
#define ITERATIONS 1
layout(local_size_x = NUM_THREADS, local_size_y = 1, local_size_z = 1) in;
#ifndef MODE_SORT_STEP
shared vec2 g_LDS[SORT_SIZE];
#endif
layout(set = 1, binding = 0, std430) restrict buffer SortBuffer {
vec2 data[];
}
sort_buffer;
layout(push_constant, binding = 0, std430) uniform Params {
uint total_elements;
uint pad[3];
ivec4 job_params;
}
params;
void main() {
#ifdef MODE_SORT_BLOCK
uvec3 Gid = gl_WorkGroupID;
uvec3 DTid = gl_GlobalInvocationID;
uvec3 GTid = gl_LocalInvocationID;
uint GI = gl_LocalInvocationIndex;
int GlobalBaseIndex = int((Gid.x * SORT_SIZE) + GTid.x);
int LocalBaseIndex = int(GI);
int numElementsInThreadGroup = int(min(SORT_SIZE, params.total_elements - (Gid.x * SORT_SIZE)));
// Load shared data
int i;
for (i = 0; i < 2 * ITERATIONS; ++i) {
if (GI + i * NUM_THREADS < numElementsInThreadGroup)
g_LDS[LocalBaseIndex + i * NUM_THREADS] = sort_buffer.data[GlobalBaseIndex + i * NUM_THREADS];
}
groupMemoryBarrier();
barrier();
// Bitonic sort
for (int nMergeSize = 2; nMergeSize <= SORT_SIZE; nMergeSize = nMergeSize * 2) {
for (int nMergeSubSize = nMergeSize >> 1; nMergeSubSize > 0; nMergeSubSize = nMergeSubSize >> 1) {
for (i = 0; i < ITERATIONS; ++i) {
int tmp_index = int(GI + NUM_THREADS * i);
int index_low = tmp_index & (nMergeSubSize - 1);
int index_high = 2 * (tmp_index - index_low);
int index = index_high + index_low;
int nSwapElem = nMergeSubSize == nMergeSize >> 1 ? index_high + (2 * nMergeSubSize - 1) - index_low : index_high + nMergeSubSize + index_low;
if (nSwapElem < numElementsInThreadGroup) {
vec2 a = g_LDS[index];
vec2 b = g_LDS[nSwapElem];
if (a.x > b.x) {
g_LDS[index] = b;
g_LDS[nSwapElem] = a;
}
}
groupMemoryBarrier();
barrier();
}
}
}
// Store shared data
for (i = 0; i < 2 * ITERATIONS; ++i) {
if (GI + i * NUM_THREADS < numElementsInThreadGroup) {
sort_buffer.data[GlobalBaseIndex + i * NUM_THREADS] = g_LDS[LocalBaseIndex + i * NUM_THREADS];
}
}
#endif
#ifdef MODE_SORT_STEP
uvec3 Gid = gl_WorkGroupID;
uvec3 GTid = gl_LocalInvocationID;
ivec4 tgp;
tgp.x = int(Gid.x) * 256;
tgp.y = 0;
tgp.z = int(params.total_elements);
tgp.w = min(512, max(0, tgp.z - int(Gid.x) * 512));
uint localID = int(tgp.x) + GTid.x; // calculate threadID within this sortable-array
uint index_low = localID & (params.job_params.x - 1);
uint index_high = 2 * (localID - index_low);
uint index = tgp.y + index_high + index_low;
uint nSwapElem = tgp.y + index_high + params.job_params.y + params.job_params.z * index_low;
if (nSwapElem < tgp.y + tgp.z) {
vec2 a = sort_buffer.data[index];
vec2 b = sort_buffer.data[nSwapElem];
if (a.x > b.x) {
sort_buffer.data[index] = b;
sort_buffer.data[nSwapElem] = a;
}
}
#endif
#ifdef MODE_SORT_INNER
uvec3 Gid = gl_WorkGroupID;
uvec3 DTid = gl_GlobalInvocationID;
uvec3 GTid = gl_LocalInvocationID;
uint GI = gl_LocalInvocationIndex;
ivec4 tgp;
tgp.x = int(Gid.x * 256);
tgp.y = 0;
tgp.z = int(params.total_elements.x);
tgp.w = int(min(512, max(0, params.total_elements - Gid.x * 512)));
int GlobalBaseIndex = int(tgp.y + tgp.x * 2 + GTid.x);
int LocalBaseIndex = int(GI);
int i;
// Load shared data
for (i = 0; i < 2; ++i) {
if (GI + i * NUM_THREADS < tgp.w)
g_LDS[LocalBaseIndex + i * NUM_THREADS] = sort_buffer.data[GlobalBaseIndex + i * NUM_THREADS];
}
groupMemoryBarrier();
barrier();
// sort threadgroup shared memory
for (int nMergeSubSize = SORT_SIZE >> 1; nMergeSubSize > 0; nMergeSubSize = nMergeSubSize >> 1) {
int tmp_index = int(GI);
int index_low = tmp_index & (nMergeSubSize - 1);
int index_high = 2 * (tmp_index - index_low);
int index = index_high + index_low;
int nSwapElem = index_high + nMergeSubSize + index_low;
if (nSwapElem < tgp.w) {
vec2 a = g_LDS[index];
vec2 b = g_LDS[nSwapElem];
if (a.x > b.x) {
g_LDS[index] = b;
g_LDS[nSwapElem] = a;
}
}
groupMemoryBarrier();
barrier();
}
// Store shared data
for (i = 0; i < 2; ++i) {
if (GI + i * NUM_THREADS < tgp.w) {
sort_buffer.data[GlobalBaseIndex + i * NUM_THREADS] = g_LDS[LocalBaseIndex + i * NUM_THREADS];
}
}
#endif
}

View File

@@ -0,0 +1,53 @@
#[vertex]
#version 450
VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
void main() {
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
uv_interp = base_arr[gl_VertexIndex];
gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
}
#[fragment]
#version 450
VERSION_DEFINES
layout(location = 0) in vec2 uv_interp;
layout(set = 0, binding = 0) uniform sampler2D specular;
#ifdef MODE_SSR
layout(set = 1, binding = 0) uniform sampler2D ssr;
#endif
#ifdef MODE_MERGE
layout(set = 2, binding = 0) uniform sampler2D diffuse;
#endif
layout(location = 0) out vec4 frag_color;
void main() {
frag_color.rgb = texture(specular, uv_interp).rgb;
frag_color.a = 0.0;
#ifdef MODE_SSR
vec4 ssr_color = texture(ssr, uv_interp);
frag_color.rgb = mix(frag_color.rgb, ssr_color.rgb, ssr_color.a);
#endif
#ifdef MODE_MERGE
frag_color += texture(diffuse, uv_interp);
#endif
//added using additive blend
}

View File

@@ -0,0 +1,249 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#define TWO_PI 6.283185307179586476925286766559
#ifdef SSAO_QUALITY_HIGH
#define NUM_SAMPLES (20)
#endif
#ifdef SSAO_QUALITY_ULTRA
#define NUM_SAMPLES (48)
#endif
#ifdef SSAO_QUALITY_LOW
#define NUM_SAMPLES (8)
#endif
#if !defined(SSAO_QUALITY_LOW) && !defined(SSAO_QUALITY_HIGH) && !defined(SSAO_QUALITY_ULTRA)
#define NUM_SAMPLES (12)
#endif
// If using depth mip levels, the log of the maximum pixel offset before we need to switch to a lower
// miplevel to maintain reasonable spatial locality in the cache
// If this number is too small (< 3), too many taps will land in the same pixel, and we'll get bad variance that manifests as flashing.
// If it is too high (> 5), we'll get bad performance because we're not using the MIP levels effectively
#define LOG_MAX_OFFSET (3)
// This must be less than or equal to the MAX_MIP_LEVEL defined in SSAO.cpp
#define MAX_MIP_LEVEL (4)
// This is the number of turns around the circle that the spiral pattern makes. This should be prime to prevent
// taps from lining up. This particular choice was tuned for NUM_SAMPLES == 9
const int ROTATIONS[] = int[](
1, 1, 2, 3, 2, 5, 2, 3, 2,
3, 3, 5, 5, 3, 4, 7, 5, 5, 7,
9, 8, 5, 5, 7, 7, 7, 8, 5, 8,
11, 12, 7, 10, 13, 8, 11, 8, 7, 14,
11, 11, 13, 12, 13, 19, 17, 13, 11, 18,
19, 11, 11, 14, 17, 21, 15, 16, 17, 18,
13, 17, 11, 17, 19, 18, 25, 18, 19, 19,
29, 21, 19, 27, 31, 29, 21, 18, 17, 29,
31, 31, 23, 18, 25, 26, 25, 23, 19, 34,
19, 27, 21, 25, 39, 29, 17, 21, 27);
//#define NUM_SPIRAL_TURNS (7)
const int NUM_SPIRAL_TURNS = ROTATIONS[NUM_SAMPLES - 1];
layout(set = 0, binding = 0) uniform sampler2D source_depth_mipmaps;
layout(r8, set = 1, binding = 0) uniform restrict writeonly image2D dest_image;
#ifndef USE_HALF_SIZE
layout(set = 2, binding = 0) uniform sampler2D source_depth;
#endif
layout(set = 3, binding = 0) uniform sampler2D source_normal;
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 screen_size;
float z_far;
float z_near;
bool orthogonal;
float intensity_div_r6;
float radius;
float bias;
vec4 proj_info;
vec2 pixel_size;
float proj_scale;
uint pad;
}
params;
vec3 reconstructCSPosition(vec2 S, float z) {
if (params.orthogonal) {
return vec3((S.xy * params.proj_info.xy + params.proj_info.zw), z);
} else {
return vec3((S.xy * params.proj_info.xy + params.proj_info.zw) * z, z);
}
}
vec3 getPosition(ivec2 ssP) {
vec3 P;
#ifdef USE_HALF_SIZE
P.z = texelFetch(source_depth_mipmaps, ssP, 0).r;
P.z = -P.z;
#else
P.z = texelFetch(source_depth, ssP, 0).r;
P.z = P.z * 2.0 - 1.0;
if (params.orthogonal) {
P.z = ((P.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
P.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - P.z * (params.z_far - params.z_near));
}
P.z = -P.z;
#endif
// Offset to pixel center
P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z);
return P;
}
/** Returns a unit vector and a screen-space radius for the tap on a unit disk (the caller should scale by the actual disk radius) */
vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR) {
// Radius relative to ssR
float alpha = (float(sampleNumber) + 0.5) * (1.0 / float(NUM_SAMPLES));
float angle = alpha * (float(NUM_SPIRAL_TURNS) * 6.28) + spinAngle;
ssR = alpha;
return vec2(cos(angle), sin(angle));
}
/** Read the camera-space position of the point at screen-space pixel ssP + unitOffset * ssR. Assumes length(unitOffset) == 1 */
vec3 getOffsetPosition(ivec2 ssP, float ssR) {
// Derivation:
// mipLevel = floor(log(ssR / MAX_OFFSET));
int mipLevel = clamp(int(floor(log2(ssR))) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL);
vec3 P;
// We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map.
// Manually clamp to the texture size because texelFetch bypasses the texture unit
ivec2 mipP = clamp(ssP >> mipLevel, ivec2(0), (params.screen_size >> mipLevel) - ivec2(1));
#ifdef USE_HALF_SIZE
P.z = texelFetch(source_depth_mipmaps, mipP, mipLevel).r;
P.z = -P.z;
#else
if (mipLevel < 1) {
//read from depth buffer
P.z = texelFetch(source_depth, mipP, 0).r;
P.z = P.z * 2.0 - 1.0;
if (params.orthogonal) {
P.z = ((P.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
P.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - P.z * (params.z_far - params.z_near));
}
P.z = -P.z;
} else {
//read from mipmaps
P.z = texelFetch(source_depth_mipmaps, mipP, mipLevel - 1).r;
P.z = -P.z;
}
#endif
// Offset to pixel center
P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z);
return P;
}
/** Compute the occlusion due to sample with index \a i about the pixel at \a ssC that corresponds
to camera-space point \a C with unit normal \a n_C, using maximum screen-space sampling radius \a ssDiskRadius
Note that units of H() in the HPG12 paper are meters, not
unitless. The whole falloff/sampling function is therefore
unitless. In this implementation, we factor out (9 / radius).
Four versions of the falloff function are implemented below
*/
float sampleAO(in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in float p_radius, in int tapIndex, in float randomPatternRotationAngle) {
// Offset on the unit disk, spun for this pixel
float ssR;
vec2 unitOffset = tapLocation(tapIndex, randomPatternRotationAngle, ssR);
ssR *= ssDiskRadius;
ivec2 ssP = ivec2(ssR * unitOffset) + ssC;
if (any(lessThan(ssP, ivec2(0))) || any(greaterThanEqual(ssP, params.screen_size))) {
return 0.0;
}
// The occluding point in camera space
vec3 Q = getOffsetPosition(ssP, ssR);
vec3 v = Q - C;
float vv = dot(v, v);
float vn = dot(v, n_C);
const float epsilon = 0.01;
float radius2 = p_radius * p_radius;
// A: From the HPG12 paper
// Note large epsilon to avoid overdarkening within cracks
//return float(vv < radius2) * max((vn - bias) / (epsilon + vv), 0.0) * radius2 * 0.6;
// B: Smoother transition to zero (lowers contrast, smoothing out corners). [Recommended]
float f = max(radius2 - vv, 0.0);
return f * f * f * max((vn - params.bias) / (epsilon + vv), 0.0);
// C: Medium contrast (which looks better at high radii), no division. Note that the
// contribution still falls off with radius^2, but we've adjusted the rate in a way that is
// more computationally efficient and happens to be aesthetically pleasing.
// return 4.0 * max(1.0 - vv * invRadius2, 0.0) * max(vn - bias, 0.0);
// D: Low contrast, no division operation
// return 2.0 * float(vv < radius * radius) * max(vn - bias, 0.0);
}
void main() {
// Pixel being shaded
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(ssC, params.screen_size))) { //too large, do nothing
return;
}
// World space point being shaded
vec3 C = getPosition(ssC);
#ifdef USE_HALF_SIZE
vec3 n_C = texelFetch(source_normal, ssC << 1, 0).xyz * 2.0 - 1.0;
#else
vec3 n_C = texelFetch(source_normal, ssC, 0).xyz * 2.0 - 1.0;
#endif
n_C = normalize(n_C);
n_C.y = -n_C.y; //because this code reads flipped
// Hash function used in the HPG12 AlchemyAO paper
float randomPatternRotationAngle = mod(float((3 * ssC.x ^ ssC.y + ssC.x * ssC.y) * 10), TWO_PI);
// Reconstruct normals from positions. These will lead to 1-pixel black lines
// at depth discontinuities, however the blur will wipe those out so they are not visible
// in the final image.
// Choose the screen-space sample radius
// proportional to the projected area of the sphere
float ssDiskRadius = -params.proj_scale * params.radius;
if (!params.orthogonal) {
ssDiskRadius = -params.proj_scale * params.radius / C.z;
}
float sum = 0.0;
for (int i = 0; i < NUM_SAMPLES; ++i) {
sum += sampleAO(ssC, C, n_C, ssDiskRadius, params.radius, i, randomPatternRotationAngle);
}
float A = max(0.0, 1.0 - sum * params.intensity_div_r6 * (5.0 / float(NUM_SAMPLES)));
imageStore(dest_image, ssC, vec4(A));
}

View File

@@ -0,0 +1,153 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source_ssao;
layout(set = 1, binding = 0) uniform sampler2D source_depth;
#ifdef MODE_UPSCALE
layout(set = 2, binding = 0) uniform sampler2D source_depth_mipmaps;
#endif
layout(r8, set = 3, binding = 0) uniform restrict writeonly image2D dest_image;
//////////////////////////////////////////////////////////////////////////////////////////////
// Tunable Parameters:
layout(push_constant, binding = 1, std430) uniform Params {
float edge_sharpness; /** Increase to make depth edges crisper. Decrease to reduce flicker. */
int filter_scale;
float z_far;
float z_near;
bool orthogonal;
uint pad0;
uint pad1;
uint pad2;
ivec2 axis; /** (1, 0) or (0, 1) */
ivec2 screen_size;
}
params;
/** Filter radius in pixels. This will be multiplied by SCALE. */
#define R (4)
//////////////////////////////////////////////////////////////////////////////////////////////
// Gaussian coefficients
const float gaussian[R + 1] =
//float[](0.356642, 0.239400, 0.072410, 0.009869);
//float[](0.398943, 0.241971, 0.053991, 0.004432, 0.000134); // stddev = 1.0
float[](0.153170, 0.144893, 0.122649, 0.092902, 0.062970); // stddev = 2.0
//float[](0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108); // stddev = 3.0
void main() {
// Pixel being shaded
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(ssC, params.screen_size))) { //too large, do nothing
return;
}
#ifdef MODE_UPSCALE
//closest one should be the same pixel, but check nearby just in case
float depth = texelFetch(source_depth, ssC, 0).r;
depth = depth * 2.0 - 1.0;
if (params.orthogonal) {
depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
}
vec2 pixel_size = 1.0 / vec2(params.screen_size);
vec2 closest_uv = vec2(ssC) * pixel_size + pixel_size * 0.5;
vec2 from_uv = closest_uv;
vec2 ps2 = pixel_size; // * 2.0;
float closest_depth = abs(textureLod(source_depth_mipmaps, closest_uv, 0.0).r - depth);
vec2 offsets[4] = vec2[](vec2(ps2.x, 0), vec2(-ps2.x, 0), vec2(0, ps2.y), vec2(0, -ps2.y));
for (int i = 0; i < 4; i++) {
vec2 neighbour = from_uv + offsets[i];
float neighbour_depth = abs(textureLod(source_depth_mipmaps, neighbour, 0.0).r - depth);
if (neighbour_depth < closest_depth) {
closest_uv = neighbour;
closest_depth = neighbour_depth;
}
}
float visibility = textureLod(source_ssao, closest_uv, 0.0).r;
imageStore(dest_image, ssC, vec4(visibility));
#else
float depth = texelFetch(source_depth, ssC, 0).r;
#ifdef MODE_FULL_SIZE
depth = depth * 2.0 - 1.0;
if (params.orthogonal) {
depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
}
#endif
float depth_divide = 1.0 / params.z_far;
//depth *= depth_divide;
/*
if (depth > params.z_far * 0.999) {
discard; //skybox
}
*/
float sum = texelFetch(source_ssao, ssC, 0).r;
// Base weight for depth falloff. Increase this for more blurriness,
// decrease it for better edge discrimination
float BASE = gaussian[0];
float totalWeight = BASE;
sum *= totalWeight;
ivec2 clamp_limit = params.screen_size - ivec2(1);
for (int r = -R; r <= R; ++r) {
// We already handled the zero case above. This loop should be unrolled and the static branch optimized out,
// so the IF statement has no runtime cost
if (r != 0) {
ivec2 ppos = ssC + params.axis * (r * params.filter_scale);
float value = texelFetch(source_ssao, clamp(ppos, ivec2(0), clamp_limit), 0).r;
ivec2 rpos = clamp(ppos, ivec2(0), clamp_limit);
float temp_depth = texelFetch(source_depth, rpos, 0).r;
#ifdef MODE_FULL_SIZE
temp_depth = temp_depth * 2.0 - 1.0;
if (params.orthogonal) {
temp_depth = ((temp_depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
temp_depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - temp_depth * (params.z_far - params.z_near));
}
//temp_depth *= depth_divide;
#endif
// spatial domain: offset gaussian tap
float weight = 0.3 + gaussian[abs(r)];
//weight *= max(0.0, dot(temp_normal, normal));
// range domain (the "bilateral" weight). As depth difference increases, decrease weight.
weight *= max(0.0, 1.0 - params.edge_sharpness * abs(temp_depth - depth));
sum += value * weight;
totalWeight += weight;
}
}
const float epsilon = 0.0001;
float visibility = sum / (totalWeight + epsilon);
imageStore(dest_image, ssC, vec4(visibility));
#endif
}

View File

@@ -0,0 +1,45 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(push_constant, binding = 1, std430) uniform Params {
vec2 pixel_size;
float z_far;
float z_near;
ivec2 source_size;
bool orthogonal;
uint pad;
}
params;
#ifdef MINIFY_START
layout(set = 0, binding = 0) uniform sampler2D source_texture;
#else
layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D source_image;
#endif
layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_image;
void main() {
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThan(pos, params.source_size >> 1))) { //too large, do nothing
return;
}
#ifdef MINIFY_START
float depth = texelFetch(source_texture, pos << 1, 0).r * 2.0 - 1.0;
if (params.orthogonal) {
depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
}
#else
float depth = imageLoad(source_image, pos << 1).r;
#endif
imageStore(dest_image, pos, vec4(depth));
}

View File

@@ -0,0 +1,189 @@
#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#ifdef USE_25_SAMPLES
const int kernel_size = 13;
const vec2 kernel[kernel_size] = vec2[](
vec2(0.530605, 0.0),
vec2(0.0211412, 0.0208333),
vec2(0.0402784, 0.0833333),
vec2(0.0493588, 0.1875),
vec2(0.0410172, 0.333333),
vec2(0.0263642, 0.520833),
vec2(0.017924, 0.75),
vec2(0.0128496, 1.02083),
vec2(0.0094389, 1.33333),
vec2(0.00700976, 1.6875),
vec2(0.00500364, 2.08333),
vec2(0.00333804, 2.52083),
vec2(0.000973794, 3.0));
const vec4 skin_kernel[kernel_size] = vec4[](
vec4(0.530605, 0.613514, 0.739601, 0),
vec4(0.0211412, 0.0459286, 0.0378196, 0.0208333),
vec4(0.0402784, 0.0657244, 0.04631, 0.0833333),
vec4(0.0493588, 0.0367726, 0.0219485, 0.1875),
vec4(0.0410172, 0.0199899, 0.0118481, 0.333333),
vec4(0.0263642, 0.0119715, 0.00684598, 0.520833),
vec4(0.017924, 0.00711691, 0.00347194, 0.75),
vec4(0.0128496, 0.00356329, 0.00132016, 1.02083),
vec4(0.0094389, 0.00139119, 0.000416598, 1.33333),
vec4(0.00700976, 0.00049366, 0.000151938, 1.6875),
vec4(0.00500364, 0.00020094, 5.28848e-005, 2.08333),
vec4(0.00333804, 7.85443e-005, 1.2945e-005, 2.52083),
vec4(0.000973794, 1.11862e-005, 9.43437e-007, 3));
#endif //USE_25_SAMPLES
#ifdef USE_17_SAMPLES
const int kernel_size = 9;
const vec2 kernel[kernel_size] = vec2[](
vec2(0.536343, 0.0),
vec2(0.0324462, 0.03125),
vec2(0.0582416, 0.125),
vec2(0.0571056, 0.28125),
vec2(0.0347317, 0.5),
vec2(0.0216301, 0.78125),
vec2(0.0144609, 1.125),
vec2(0.0100386, 1.53125),
vec2(0.00317394, 2.0));
const vec4 skin_kernel[kernel_size] = vec4[](
vec4(0.536343, 0.624624, 0.748867, 0),
vec4(0.0324462, 0.0656718, 0.0532821, 0.03125),
vec4(0.0582416, 0.0659959, 0.0411329, 0.125),
vec4(0.0571056, 0.0287432, 0.0172844, 0.28125),
vec4(0.0347317, 0.0151085, 0.00871983, 0.5),
vec4(0.0216301, 0.00794618, 0.00376991, 0.78125),
vec4(0.0144609, 0.00317269, 0.00106399, 1.125),
vec4(0.0100386, 0.000914679, 0.000275702, 1.53125),
vec4(0.00317394, 0.000134823, 3.77269e-005, 2));
#endif //USE_17_SAMPLES
#ifdef USE_11_SAMPLES
const int kernel_size = 6;
const vec2 kernel[kernel_size] = vec2[](
vec2(0.560479, 0.0),
vec2(0.0771802, 0.08),
vec2(0.0821904, 0.32),
vec2(0.03639, 0.72),
vec2(0.0192831, 1.28),
vec2(0.00471691, 2.0));
const vec4 skin_kernel[kernel_size] = vec4[](
vec4(0.560479, 0.669086, 0.784728, 0),
vec4(0.0771802, 0.113491, 0.0793803, 0.08),
vec4(0.0821904, 0.0358608, 0.0209261, 0.32),
vec4(0.03639, 0.0130999, 0.00643685, 0.72),
vec4(0.0192831, 0.00282018, 0.00084214, 1.28),
vec4(0.00471691, 0.000184771, 5.07565e-005, 2));
#endif //USE_11_SAMPLES
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 screen_size;
float camera_z_far;
float camera_z_near;
bool vertical;
bool orthogonal;
float unit_size;
float scale;
float depth_scale;
uint pad[3];
}
params;
layout(set = 0, binding = 0) uniform sampler2D source_image;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D dest_image;
layout(set = 2, binding = 0) uniform sampler2D source_depth;
void do_filter(inout vec3 color_accum, inout vec3 divisor, vec2 uv, vec2 step, bool p_skin) {
// Accumulate the other samples:
for (int i = 1; i < kernel_size; i++) {
// Fetch color and depth for current sample:
vec2 offset = uv + kernel[i].y * step;
vec4 color = texture(source_image, offset);
if (abs(color.a) < 0.001) {
break; //mix no more
}
vec3 w;
if (p_skin) {
//skin
w = skin_kernel[i].rgb;
} else {
w = vec3(kernel[i].x);
}
color_accum += color.rgb * w;
divisor += w;
}
}
void main() {
// Pixel being shaded
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(ssC, params.screen_size))) { //too large, do nothing
return;
}
vec2 uv = (vec2(ssC) + 0.5) / vec2(params.screen_size);
// Fetch color of current pixel:
vec4 base_color = texture(source_image, uv);
float strength = abs(base_color.a);
if (strength > 0.0) {
vec2 dir = params.vertical ? vec2(0.0, 1.0) : vec2(1.0, 0.0);
// Fetch linear depth of current pixel:
float depth = texture(source_depth, uv).r * 2.0 - 1.0;
float depth_scale;
if (params.orthogonal) {
depth = ((depth + (params.camera_z_far + params.camera_z_near) / (params.camera_z_far - params.camera_z_near)) * (params.camera_z_far - params.camera_z_near)) / 2.0;
depth_scale = params.unit_size; //remember depth is negative by default in OpenGL
} else {
depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - depth * (params.camera_z_far - params.camera_z_near));
depth_scale = params.unit_size / depth; //remember depth is negative by default in OpenGL
}
float scale = mix(params.scale, depth_scale, params.depth_scale);
// Calculate the final step to fetch the surrounding pixels:
vec2 step = scale * dir;
step *= strength;
step /= 3.0;
// Accumulate the center sample:
vec3 divisor;
bool skin = bool(base_color.a < 0.0);
if (skin) {
//skin
divisor = skin_kernel[0].rgb;
} else {
divisor = vec3(kernel[0].x);
}
vec3 color = base_color.rgb * divisor;
do_filter(color, divisor, uv, step, skin);
do_filter(color, divisor, uv, -step, skin);
base_color.rgb = color / divisor;
}
imageStore(dest_image, ssC, base_color);
}

View File

@@ -0,0 +1,386 @@
#[vertex]
#version 450
VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
void main() {
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
uv_interp = base_arr[gl_VertexIndex];
gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
}
#[fragment]
#version 450
VERSION_DEFINES
layout(location = 0) in vec2 uv_interp;
layout(set = 0, binding = 0) uniform sampler2D source_color;
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
layout(set = 2, binding = 0) uniform sampler2D source_glow;
#ifdef USE_1D_LUT
layout(set = 3, binding = 0) uniform sampler2D source_color_correction;
#else
layout(set = 3, binding = 0) uniform sampler3D source_color_correction;
#endif
layout(push_constant, binding = 1, std430) uniform Params {
vec3 bcs;
bool use_bcs;
bool use_glow;
bool use_auto_exposure;
bool use_color_correction;
uint tonemapper;
uvec2 glow_texture_size;
float glow_intensity;
uint pad3;
uint glow_mode;
float glow_levels[7];
float exposure;
float white;
float auto_exposure_grey;
uint pad2;
vec2 pixel_size;
bool use_fxaa;
bool use_debanding;
}
params;
layout(location = 0) out vec4 frag_color;
#ifdef USE_GLOW_FILTER_BICUBIC
// w0, w1, w2, and w3 are the four cubic B-spline basis functions
float w0(float a) {
return (1.0f / 6.0f) * (a * (a * (-a + 3.0f) - 3.0f) + 1.0f);
}
float w1(float a) {
return (1.0f / 6.0f) * (a * a * (3.0f * a - 6.0f) + 4.0f);
}
float w2(float a) {
return (1.0f / 6.0f) * (a * (a * (-3.0f * a + 3.0f) + 3.0f) + 1.0f);
}
float w3(float a) {
return (1.0f / 6.0f) * (a * a * a);
}
// g0 and g1 are the two amplitude functions
float g0(float a) {
return w0(a) + w1(a);
}
float g1(float a) {
return w2(a) + w3(a);
}
// h0 and h1 are the two offset functions
float h0(float a) {
return -1.0f + w1(a) / (w0(a) + w1(a));
}
float h1(float a) {
return 1.0f + w3(a) / (w2(a) + w3(a));
}
vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) {
float lod = float(p_lod);
vec2 tex_size = vec2(params.glow_texture_size >> p_lod);
vec2 pixel_size = vec2(1.0f) / tex_size;
uv = uv * tex_size + vec2(0.5f);
vec2 iuv = floor(uv);
vec2 fuv = fract(uv);
float g0x = g0(fuv.x);
float g1x = g1(fuv.x);
float h0x = h0(fuv.x);
float h1x = h1(fuv.x);
float h0y = h0(fuv.y);
float h1y = h1(fuv.y);
vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5f)) * pixel_size;
vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5f)) * pixel_size;
vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size;
vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size;
return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) +
(g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod)));
}
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod)
#else
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod))
#endif
vec3 tonemap_filmic(vec3 color, float white) {
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
// also useful to scale the input to the range that the tonemapper is designed for (some require very high input values)
// has no effect on the curve's general shape or visual properties
const float exposure_bias = 2.0f;
const float A = 0.22f * exposure_bias * exposure_bias; // bias baked into constants for performance
const float B = 0.30f * exposure_bias;
const float C = 0.10f;
const float D = 0.20f;
const float E = 0.01f;
const float F = 0.30f;
vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
float white_tonemapped = ((white * (A * white + C * B) + D * E) / (white * (A * white + B) + D * F)) - E / F;
return color_tonemapped / white_tonemapped;
}
vec3 tonemap_aces(vec3 color, float white) {
const float exposure_bias = 0.85f;
const float A = 2.51f * exposure_bias * exposure_bias;
const float B = 0.03f * exposure_bias;
const float C = 2.43f * exposure_bias * exposure_bias;
const float D = 0.59f * exposure_bias;
const float E = 0.14f;
vec3 color_tonemapped = (color * (A * color + B)) / (color * (C * color + D) + E);
float white_tonemapped = (white * (A * white + B)) / (white * (C * white + D) + E);
return color_tonemapped / white_tonemapped;
}
vec3 tonemap_reinhard(vec3 color, float white) {
// Ensure color values are positive.
// They can be negative in the case of negative lights, which leads to undesired behavior.
color = max(vec3(0.0), color);
return (white * color + color) / (color * white + white);
}
vec3 linear_to_srgb(vec3 color) {
//if going to srgb, clamp from 0 to 1.
color = clamp(color, vec3(0.0), vec3(1.0));
const vec3 a = vec3(0.055f);
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
}
#define TONEMAPPER_LINEAR 0
#define TONEMAPPER_REINHARD 1
#define TONEMAPPER_FILMIC 2
#define TONEMAPPER_ACES 3
vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always outputs clamped [0;1] color
if (params.tonemapper == TONEMAPPER_LINEAR) {
return color;
} else if (params.tonemapper == TONEMAPPER_REINHARD) {
return tonemap_reinhard(color, white);
} else if (params.tonemapper == TONEMAPPER_FILMIC) {
return tonemap_filmic(color, white);
} else { //aces
return tonemap_aces(color, white);
}
}
vec3 gather_glow(sampler2D tex, vec2 uv) { // sample all selected glow levels
vec3 glow = vec3(0.0f);
if (params.glow_levels[0] > 0.0001) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 0).rgb * params.glow_levels[0];
}
if (params.glow_levels[1] > 0.0001) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 1).rgb * params.glow_levels[1];
}
if (params.glow_levels[2] > 0.0001) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 2).rgb * params.glow_levels[2];
}
if (params.glow_levels[3] > 0.0001) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 3).rgb * params.glow_levels[3];
}
if (params.glow_levels[4] > 0.0001) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 4).rgb * params.glow_levels[4];
}
if (params.glow_levels[5] > 0.0001) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 5).rgb * params.glow_levels[5];
}
if (params.glow_levels[6] > 0.0001) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 6).rgb * params.glow_levels[6];
}
return glow;
}
#define GLOW_MODE_ADD 0
#define GLOW_MODE_SCREEN 1
#define GLOW_MODE_SOFTLIGHT 2
#define GLOW_MODE_REPLACE 3
#define GLOW_MODE_MIX 4
vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blending mode
if (params.glow_mode == GLOW_MODE_ADD) {
return color + glow;
} else if (params.glow_mode == GLOW_MODE_SCREEN) {
//need color clamping
return max((color + glow) - (color * glow), vec3(0.0));
} else if (params.glow_mode == GLOW_MODE_SOFTLIGHT) {
//need color clamping
glow = glow * vec3(0.5f) + vec3(0.5f);
color.r = (glow.r <= 0.5f) ? (color.r - (1.0f - 2.0f * glow.r) * color.r * (1.0f - color.r)) : (((glow.r > 0.5f) && (color.r <= 0.25f)) ? (color.r + (2.0f * glow.r - 1.0f) * (4.0f * color.r * (4.0f * color.r + 1.0f) * (color.r - 1.0f) + 7.0f * color.r)) : (color.r + (2.0f * glow.r - 1.0f) * (sqrt(color.r) - color.r)));
color.g = (glow.g <= 0.5f) ? (color.g - (1.0f - 2.0f * glow.g) * color.g * (1.0f - color.g)) : (((glow.g > 0.5f) && (color.g <= 0.25f)) ? (color.g + (2.0f * glow.g - 1.0f) * (4.0f * color.g * (4.0f * color.g + 1.0f) * (color.g - 1.0f) + 7.0f * color.g)) : (color.g + (2.0f * glow.g - 1.0f) * (sqrt(color.g) - color.g)));
color.b = (glow.b <= 0.5f) ? (color.b - (1.0f - 2.0f * glow.b) * color.b * (1.0f - color.b)) : (((glow.b > 0.5f) && (color.b <= 0.25f)) ? (color.b + (2.0f * glow.b - 1.0f) * (4.0f * color.b * (4.0f * color.b + 1.0f) * (color.b - 1.0f) + 7.0f * color.b)) : (color.b + (2.0f * glow.b - 1.0f) * (sqrt(color.b) - color.b)));
return color;
} else { //replace
return glow;
}
}
vec3 apply_bcs(vec3 color, vec3 bcs) {
color = mix(vec3(0.0f), color, bcs.x);
color = mix(vec3(0.5f), color, bcs.y);
color = mix(vec3(dot(vec3(1.0f), color) * 0.33333f), color, bcs.z);
return color;
}
#ifdef USE_1D_LUT
vec3 apply_color_correction(vec3 color) {
color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r;
color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g;
color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b;
return color;
}
#else
vec3 apply_color_correction(vec3 color) {
return textureLod(source_color_correction, color, 0.0).rgb;
}
#endif
vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) {
const float FXAA_REDUCE_MIN = (1.0 / 128.0);
const float FXAA_REDUCE_MUL = (1.0 / 8.0);
const float FXAA_SPAN_MAX = 8.0;
vec3 rgbNW = textureLod(source_color, uv_interp + vec2(-1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure;
vec3 rgbNE = textureLod(source_color, uv_interp + vec2(1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure;
vec3 rgbSW = textureLod(source_color, uv_interp + vec2(-1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure;
vec3 rgbSE = textureLod(source_color, uv_interp + vec2(1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure;
vec3 rgbM = color;
vec3 luma = vec3(0.299, 0.587, 0.114);
float lumaNW = dot(rgbNW, luma);
float lumaNE = dot(rgbNE, luma);
float lumaSW = dot(rgbSW, luma);
float lumaSE = dot(rgbSE, luma);
float lumaM = dot(rgbM, luma);
float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
vec2 dir;
dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) *
(0.25 * FXAA_REDUCE_MUL),
FXAA_REDUCE_MIN);
float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),
max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
dir * rcpDirMin)) *
params.pixel_size;
vec3 rgbA = 0.5 * exposure * (textureLod(source_color, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source_color, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz);
vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source_color, uv_interp + dir * 0.5, 0.0).xyz);
float lumaB = dot(rgbB, luma);
if ((lumaB < lumaMin) || (lumaB > lumaMax)) {
return rgbA;
} else {
return rgbB;
}
}
// From http://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf
// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom)
// NOTE: `frag_coord` is in pixels (i.e. not normalized UV).
vec3 screen_space_dither(vec2 frag_coord) {
// Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR.
vec3 dither = vec3(dot(vec2(171.0, 231.0), frag_coord));
dither.rgb = fract(dither.rgb / vec3(103.0, 71.0, 97.0));
// Subtract 0.5 to avoid slightly brightening the whole viewport.
return (dither.rgb - 0.5) / 255.0;
}
void main() {
vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb;
// Exposure
float exposure = params.exposure;
if (params.use_auto_exposure) {
exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.auto_exposure_grey);
}
color *= exposure;
// Early Tonemap & SRGB Conversion
if (params.use_glow && params.glow_mode == GLOW_MODE_MIX) {
vec3 glow = gather_glow(source_glow, uv_interp);
color.rgb = mix(color.rgb, glow, params.glow_intensity);
}
if (params.use_fxaa) {
color = do_fxaa(color, exposure, uv_interp);
}
if (params.use_debanding) {
// For best results, debanding should be done before tonemapping.
// Otherwise, we're adding noise to an already-quantized image.
color += screen_space_dither(gl_FragCoord.xy);
}
color = apply_tonemapping(color, params.white);
color = linear_to_srgb(color); // regular linear -> SRGB conversion
// Glow
if (params.use_glow && params.glow_mode != GLOW_MODE_MIX) {
vec3 glow = gather_glow(source_glow, uv_interp) * params.glow_intensity;
// high dynamic range -> SRGB
glow = apply_tonemapping(glow, params.white);
glow = linear_to_srgb(glow);
color = apply_glow(color, glow);
}
// Additional effects
if (params.use_bcs) {
color = apply_bcs(color, params.bcs);
}
if (params.use_color_correction) {
color = apply_color_correction(color);
}
frag_color = vec4(color, 1.0f);
}

View File

@@ -0,0 +1,530 @@
#[compute]
#version 450
VERSION_DEFINES
#if defined(MODE_FOG) || defined(MODE_FILTER)
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#endif
#if defined(MODE_DENSITY)
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
#endif
#include "cluster_data_inc.glsl"
#define M_PI 3.14159265359
layout(set = 0, binding = 1) uniform texture2D shadow_atlas;
layout(set = 0, binding = 2) uniform texture2D directional_shadow_atlas;
layout(set = 0, binding = 3, std430) restrict readonly buffer Lights {
LightData data[];
}
lights;
layout(set = 0, binding = 4, std140) uniform DirectionalLights {
DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
}
directional_lights;
layout(set = 0, binding = 5) uniform utexture3D cluster_texture;
layout(set = 0, binding = 6, std430) restrict readonly buffer ClusterData {
uint indices[];
}
cluster_data;
layout(set = 0, binding = 7) uniform sampler linear_sampler;
#ifdef MODE_DENSITY
layout(rgba16f, set = 0, binding = 8) uniform restrict writeonly image3D density_map;
layout(rgba16f, set = 0, binding = 9) uniform restrict readonly image3D fog_map; //unused
#endif
#ifdef MODE_FOG
layout(rgba16f, set = 0, binding = 8) uniform restrict readonly image3D density_map;
layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image3D fog_map;
#endif
#ifdef MODE_FILTER
layout(rgba16f, set = 0, binding = 8) uniform restrict readonly image3D source_map;
layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image3D dest_map;
#endif
layout(set = 0, binding = 10) uniform sampler shadow_sampler;
#define MAX_GI_PROBES 8
struct GIProbeData {
mat4 xform;
vec3 bounds;
float dynamic_range;
float bias;
float normal_bias;
bool blend_ambient;
uint texture_slot;
float anisotropy_strength;
float ambient_occlusion;
float ambient_occlusion_size;
uint mipmaps;
};
layout(set = 0, binding = 11, std140) uniform GIProbes {
GIProbeData data[MAX_GI_PROBES];
}
gi_probes;
layout(set = 0, binding = 12) uniform texture3D gi_probe_textures[MAX_GI_PROBES];
layout(set = 0, binding = 13) uniform sampler linear_sampler_with_mipmaps;
#ifdef ENABLE_SDFGI
// SDFGI Integration on set 1
#define SDFGI_MAX_CASCADES 8
struct SDFGIProbeCascadeData {
vec3 position;
float to_probe;
ivec3 probe_world_offset;
float to_cell; // 1/bounds * grid_size
};
layout(set = 1, binding = 0, std140) uniform SDFGI {
vec3 grid_size;
uint max_cascades;
bool use_occlusion;
int probe_axis_size;
float probe_to_uvw;
float normal_bias;
vec3 lightprobe_tex_pixel_size;
float energy;
vec3 lightprobe_uv_offset;
float y_mult;
vec3 occlusion_clamp;
uint pad3;
vec3 occlusion_renormalize;
uint pad4;
vec3 cascade_probe_size;
uint pad5;
SDFGIProbeCascadeData cascades[SDFGI_MAX_CASCADES];
}
sdfgi;
layout(set = 1, binding = 1) uniform texture2DArray sdfgi_ambient_texture;
layout(set = 1, binding = 2) uniform texture3D sdfgi_occlusion_texture;
#endif //SDFGI
layout(push_constant, binding = 0, std430) uniform Params {
vec2 fog_frustum_size_begin;
vec2 fog_frustum_size_end;
float fog_frustum_end;
float z_near;
float z_far;
int filter_axis;
ivec3 fog_volume_size;
uint directional_light_count;
vec3 light_color;
float base_density;
float detail_spread;
float gi_inject;
uint max_gi_probes;
uint pad;
mat3x4 cam_rotation;
}
params;
float get_depth_at_pos(float cell_depth_size, int z) {
float d = float(z) * cell_depth_size + cell_depth_size * 0.5; //center of voxels
d = pow(d, params.detail_spread);
return params.fog_frustum_end * d;
}
vec3 hash3f(uvec3 x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return vec3(x & 0xFFFFF) / vec3(float(0xFFFFF));
}
void main() {
vec3 fog_cell_size = 1.0 / vec3(params.fog_volume_size);
#ifdef MODE_DENSITY
ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);
if (any(greaterThanEqual(pos, params.fog_volume_size))) {
return; //do not compute
}
vec3 posf = vec3(pos);
//posf += mix(vec3(0.0),vec3(1.0),0.3) * hash3f(uvec3(pos)) * 2.0 - 1.0;
vec3 fog_unit_pos = posf * fog_cell_size + fog_cell_size * 0.5; //center of voxels
fog_unit_pos.z = pow(fog_unit_pos.z, params.detail_spread);
vec3 view_pos;
view_pos.xy = (fog_unit_pos.xy * 2.0 - 1.0) * mix(params.fog_frustum_size_begin, params.fog_frustum_size_end, vec2(fog_unit_pos.z));
view_pos.z = -params.fog_frustum_end * fog_unit_pos.z;
view_pos.y = -view_pos.y;
vec3 total_light = params.light_color;
float total_density = params.base_density;
float cell_depth_size = abs(view_pos.z - get_depth_at_pos(fog_cell_size.z, pos.z + 1));
//compute directional lights
for (uint i = 0; i < params.directional_light_count; i++) {
vec3 shadow_attenuation = vec3(1.0);
if (directional_lights.data[i].shadow_enabled) {
float depth_z = -view_pos.z;
vec4 pssm_coord;
vec3 shadow_color = directional_lights.data[i].shadow_color1.rgb;
vec3 light_dir = directional_lights.data[i].direction;
vec4 v = vec4(view_pos, 1.0);
float z_range;
if (depth_z < directional_lights.data[i].shadow_split_offsets.x) {
pssm_coord = (directional_lights.data[i].shadow_matrix1 * v);
pssm_coord /= pssm_coord.w;
z_range = directional_lights.data[i].shadow_z_range.x;
} else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) {
pssm_coord = (directional_lights.data[i].shadow_matrix2 * v);
pssm_coord /= pssm_coord.w;
z_range = directional_lights.data[i].shadow_z_range.y;
} else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) {
pssm_coord = (directional_lights.data[i].shadow_matrix3 * v);
pssm_coord /= pssm_coord.w;
z_range = directional_lights.data[i].shadow_z_range.z;
} else {
pssm_coord = (directional_lights.data[i].shadow_matrix4 * v);
pssm_coord /= pssm_coord.w;
z_range = directional_lights.data[i].shadow_z_range.w;
}
float depth = texture(sampler2D(directional_shadow_atlas, linear_sampler), pssm_coord.xy).r;
float shadow = exp(min(0.0, (depth - pssm_coord.z)) * z_range * directional_lights.data[i].shadow_volumetric_fog_fade);
/*
//float shadow = textureProj(sampler2DShadow(directional_shadow_atlas,shadow_sampler),pssm_coord);
float shadow = 0.0;
for(float xi=-1;xi<=1;xi++) {
for(float yi=-1;yi<=1;yi++) {
vec2 ofs = vec2(xi,yi) * 1.5 * params.directional_shadow_pixel_size;
shadow += textureProj(sampler2DShadow(directional_shadow_atlas,shadow_sampler),pssm_coord + vec4(ofs,0.0,0.0));
}
}
shadow /= 3.0 * 3.0;
*/
shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, view_pos.z)); //done with negative values for performance
shadow_attenuation = mix(shadow_color, vec3(1.0), shadow);
}
total_light += shadow_attenuation * directional_lights.data[i].color * directional_lights.data[i].energy / M_PI;
}
//compute lights from cluster
vec3 cluster_pos;
cluster_pos.xy = fog_unit_pos.xy;
cluster_pos.z = clamp((abs(view_pos.z) - params.z_near) / (params.z_far - params.z_near), 0.0, 1.0);
uvec4 cluster_cell = texture(usampler3D(cluster_texture, linear_sampler), cluster_pos);
uint omni_light_count = cluster_cell.x >> CLUSTER_COUNTER_SHIFT;
uint omni_light_pointer = cluster_cell.x & CLUSTER_POINTER_MASK;
for (uint i = 0; i < omni_light_count; i++) {
uint light_index = cluster_data.indices[omni_light_pointer + i];
vec3 light_pos = lights.data[i].position;
float d = distance(lights.data[i].position, view_pos) * lights.data[i].inv_radius;
vec3 shadow_attenuation = vec3(1.0);
if (d < 1.0) {
vec2 attenuation_energy = unpackHalf2x16(lights.data[i].attenuation_energy);
vec4 color_specular = unpackUnorm4x8(lights.data[i].color_specular);
float attenuation = pow(max(1.0 - d, 0.0), attenuation_energy.x);
vec3 light = attenuation_energy.y * color_specular.rgb / M_PI;
vec4 shadow_color_enabled = unpackUnorm4x8(lights.data[i].shadow_color_enabled);
if (shadow_color_enabled.a > 0.5) {
//has shadow
vec4 v = vec4(view_pos, 1.0);
vec4 splane = (lights.data[i].shadow_matrix * v);
float shadow_len = length(splane.xyz); //need to remember shadow len from here
splane.xyz = normalize(splane.xyz);
vec4 clamp_rect = lights.data[i].atlas_rect;
if (splane.z >= 0.0) {
splane.z += 1.0;
clamp_rect.y += clamp_rect.w;
} else {
splane.z = 1.0 - splane.z;
}
splane.xy /= splane.z;
splane.xy = splane.xy * 0.5 + 0.5;
splane.z = shadow_len * lights.data[i].inv_radius;
splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw;
splane.w = 1.0; //needed? i think it should be 1 already
float depth = texture(sampler2D(shadow_atlas, linear_sampler), splane.xy).r;
float shadow = exp(min(0.0, (depth - splane.z)) / lights.data[i].inv_radius * lights.data[i].shadow_volumetric_fog_fade);
shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow);
}
total_light += light * attenuation * shadow_attenuation;
}
}
uint spot_light_count = cluster_cell.y >> CLUSTER_COUNTER_SHIFT;
uint spot_light_pointer = cluster_cell.y & CLUSTER_POINTER_MASK;
for (uint i = 0; i < spot_light_count; i++) {
uint light_index = cluster_data.indices[spot_light_pointer + i];
vec3 light_pos = lights.data[i].position;
vec3 light_rel_vec = lights.data[i].position - view_pos;
float d = length(light_rel_vec) * lights.data[i].inv_radius;
vec3 shadow_attenuation = vec3(1.0);
if (d < 1.0) {
vec2 attenuation_energy = unpackHalf2x16(lights.data[i].attenuation_energy);
vec4 color_specular = unpackUnorm4x8(lights.data[i].color_specular);
float attenuation = pow(max(1.0 - d, 0.0), attenuation_energy.x);
vec3 spot_dir = lights.data[i].direction;
vec2 spot_att_angle = unpackHalf2x16(lights.data[i].cone_attenuation_angle);
float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_att_angle.y);
float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_att_angle.y));
attenuation *= 1.0 - pow(spot_rim, spot_att_angle.x);
vec3 light = attenuation_energy.y * color_specular.rgb / M_PI;
vec4 shadow_color_enabled = unpackUnorm4x8(lights.data[i].shadow_color_enabled);
if (shadow_color_enabled.a > 0.5) {
//has shadow
vec4 v = vec4(view_pos, 1.0);
vec4 splane = (lights.data[i].shadow_matrix * v);
splane /= splane.w;
float depth = texture(sampler2D(shadow_atlas, linear_sampler), splane.xy).r;
float shadow = exp(min(0.0, (depth - splane.z)) / lights.data[i].inv_radius * lights.data[i].shadow_volumetric_fog_fade);
shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow);
}
total_light += light * attenuation * shadow_attenuation;
}
}
vec3 world_pos = mat3(params.cam_rotation) * view_pos;
for (uint i = 0; i < params.max_gi_probes; i++) {
vec3 position = (gi_probes.data[i].xform * vec4(world_pos, 1.0)).xyz;
//this causes corrupted pixels, i have no idea why..
if (all(bvec2(all(greaterThanEqual(position, vec3(0.0))), all(lessThan(position, gi_probes.data[i].bounds))))) {
position /= gi_probes.data[i].bounds;
vec4 light = vec4(0.0);
for (uint j = 0; j < gi_probes.data[i].mipmaps; j++) {
vec4 slight = textureLod(sampler3D(gi_probe_textures[i], linear_sampler_with_mipmaps), position, float(j));
float a = (1.0 - light.a);
light += a * slight;
}
light.rgb *= gi_probes.data[i].dynamic_range * params.gi_inject;
total_light += light.rgb;
}
}
//sdfgi
#ifdef ENABLE_SDFGI
{
float blend = -1.0;
vec3 ambient_total = vec3(0.0);
for (uint i = 0; i < sdfgi.max_cascades; i++) {
vec3 cascade_pos = (world_pos - sdfgi.cascades[i].position) * sdfgi.cascades[i].to_probe;
if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, sdfgi.cascade_probe_size))) {
continue; //skip cascade
}
vec3 base_pos = floor(cascade_pos);
ivec3 probe_base_pos = ivec3(base_pos);
vec4 ambient_accum = vec4(0.0);
ivec3 tex_pos = ivec3(probe_base_pos.xy, int(i));
tex_pos.x += probe_base_pos.z * sdfgi.probe_axis_size;
for (uint j = 0; j < 8; j++) {
ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1);
ivec3 probe_posi = probe_base_pos;
probe_posi += offset;
// Compute weight
vec3 probe_pos = vec3(probe_posi);
vec3 probe_to_pos = cascade_pos - probe_pos;
vec3 trilinear = vec3(1.0) - abs(probe_to_pos);
float weight = trilinear.x * trilinear.y * trilinear.z;
// Compute lightprobe occlusion
if (sdfgi.use_occlusion) {
ivec3 occ_indexv = abs((sdfgi.cascades[i].probe_world_offset + probe_posi) & ivec3(1, 1, 1)) * ivec3(1, 2, 4);
vec4 occ_mask = mix(vec4(0.0), vec4(1.0), equal(ivec4(occ_indexv.x | occ_indexv.y), ivec4(0, 1, 2, 3)));
vec3 occ_pos = clamp(cascade_pos, probe_pos - sdfgi.occlusion_clamp, probe_pos + sdfgi.occlusion_clamp) * sdfgi.probe_to_uvw;
occ_pos.z += float(i);
if (occ_indexv.z != 0) { //z bit is on, means index is >=4, so make it switch to the other half of textures
occ_pos.x += 1.0;
}
occ_pos *= sdfgi.occlusion_renormalize;
float occlusion = dot(textureLod(sampler3D(sdfgi_occlusion_texture, linear_sampler), occ_pos, 0.0), occ_mask);
weight *= max(occlusion, 0.01);
}
// Compute ambient texture position
ivec3 uvw = tex_pos;
uvw.xy += offset.xy;
uvw.x += offset.z * sdfgi.probe_axis_size;
vec3 ambient = texelFetch(sampler2DArray(sdfgi_ambient_texture, linear_sampler), uvw, 0).rgb;
ambient_accum.rgb += ambient * weight;
ambient_accum.a += weight;
}
if (ambient_accum.a > 0) {
ambient_accum.rgb /= ambient_accum.a;
}
ambient_total = ambient_accum.rgb;
break;
}
total_light += ambient_total * params.gi_inject;
}
#endif
imageStore(density_map, pos, vec4(total_light, total_density));
#endif
#ifdef MODE_FOG
ivec3 pos = ivec3(gl_GlobalInvocationID.xy, 0);
if (any(greaterThanEqual(pos, params.fog_volume_size))) {
return; //do not compute
}
vec4 fog_accum = vec4(0.0);
float prev_z = 0.0;
float t = 1.0;
for (int i = 0; i < params.fog_volume_size.z; i++) {
//compute fog position
ivec3 fog_pos = pos + ivec3(0, 0, i);
//get fog value
vec4 fog = imageLoad(density_map, fog_pos);
//get depth at cell pos
float z = get_depth_at_pos(fog_cell_size.z, i);
//get distance from previous pos
float d = abs(prev_z - z);
//compute exinction based on beer's
float extinction = t * exp(-d * fog.a);
//compute alpha based on different of extinctions
float alpha = t - extinction;
//update extinction
t = extinction;
fog_accum += vec4(fog.rgb * alpha, alpha);
prev_z = z;
vec4 fog_value;
if (fog_accum.a > 0.0) {
fog_value = vec4(fog_accum.rgb / fog_accum.a, 1.0 - t);
} else {
fog_value = vec4(0.0);
}
imageStore(fog_map, fog_pos, fog_value);
}
#endif
#ifdef MODE_FILTER
ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);
const float gauss[7] = float[](0.071303, 0.131514, 0.189879, 0.214607, 0.189879, 0.131514, 0.071303);
const ivec3 filter_dir[3] = ivec3[](ivec3(1, 0, 0), ivec3(0, 1, 0), ivec3(0, 0, 1));
ivec3 offset = filter_dir[params.filter_axis];
vec4 accum = vec4(0.0);
for (int i = -3; i <= 3; i++) {
accum += imageLoad(source_map, clamp(pos + offset * i, ivec3(0), params.fog_volume_size - ivec3(1))) * gauss[i + 3];
}
imageStore(dest_map, pos, accum);
#endif
}