feat: godot-engine-source-4.3-stable
This commit is contained in:
parent
c59a7dcade
commit
7125d019b5
11149 changed files with 5070401 additions and 0 deletions
22
engine/servers/rendering/renderer_rd/shaders/SCsub
Normal file
22
engine/servers/rendering/renderer_rd/shaders/SCsub
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
if "RD_GLSL" in env["BUILDERS"]:
|
||||
# find all include files
|
||||
gl_include_files = [str(f) for f in Glob("*_inc.glsl")]
|
||||
|
||||
# find all shader code(all glsl files excluding our include files)
|
||||
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
|
||||
|
||||
# make sure we recompile shaders if include files change
|
||||
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#glsl_builders.py"])
|
||||
|
||||
# compile shaders
|
||||
for glsl_file in glsl_files:
|
||||
env.RD_GLSL(glsl_file)
|
||||
|
||||
SConscript("effects/SCsub")
|
||||
SConscript("environment/SCsub")
|
||||
SConscript("forward_clustered/SCsub")
|
||||
SConscript("forward_mobile/SCsub")
|
||||
108
engine/servers/rendering/renderer_rd/shaders/blit.glsl
Normal file
108
engine/servers/rendering/renderer_rd/shaders/blit.glsl
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std140) uniform Pos {
|
||||
vec4 src_rect;
|
||||
vec4 dst_rect;
|
||||
|
||||
vec2 eye_center;
|
||||
float k1;
|
||||
float k2;
|
||||
|
||||
float upscale;
|
||||
float aspect_ratio;
|
||||
uint layer;
|
||||
uint pad1;
|
||||
}
|
||||
data;
|
||||
|
||||
layout(location = 0) out vec2 uv;
|
||||
|
||||
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 = data.src_rect.xy + base_arr[gl_VertexIndex] * data.src_rect.zw;
|
||||
vec2 vtx = data.dst_rect.xy + base_arr[gl_VertexIndex] * data.dst_rect.zw;
|
||||
gl_Position = vec4(vtx * 2.0 - 1.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std140) uniform Pos {
|
||||
vec4 src_rect;
|
||||
vec4 dst_rect;
|
||||
|
||||
vec2 eye_center;
|
||||
float k1;
|
||||
float k2;
|
||||
|
||||
float upscale;
|
||||
float aspect_ratio;
|
||||
uint layer;
|
||||
bool convert_to_srgb;
|
||||
}
|
||||
data;
|
||||
|
||||
layout(location = 0) in vec2 uv;
|
||||
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
#ifdef USE_LAYER
|
||||
layout(binding = 0) uniform sampler2DArray src_rt;
|
||||
#else
|
||||
layout(binding = 0) uniform sampler2D src_rt;
|
||||
#endif
|
||||
|
||||
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() {
|
||||
#ifdef APPLY_LENS_DISTORTION
|
||||
vec2 coords = uv * 2.0 - 1.0;
|
||||
vec2 offset = coords - data.eye_center;
|
||||
|
||||
// take aspect ratio into account
|
||||
offset.y /= data.aspect_ratio;
|
||||
|
||||
// distort
|
||||
vec2 offset_sq = offset * offset;
|
||||
float radius_sq = offset_sq.x + offset_sq.y;
|
||||
float radius_s4 = radius_sq * radius_sq;
|
||||
float distortion_scale = 1.0 + (data.k1 * radius_sq) + (data.k2 * radius_s4);
|
||||
offset *= distortion_scale;
|
||||
|
||||
// reapply aspect ratio
|
||||
offset.y *= data.aspect_ratio;
|
||||
|
||||
// add our eye center back in
|
||||
coords = offset + data.eye_center;
|
||||
coords /= data.upscale;
|
||||
|
||||
// and check our color
|
||||
if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) {
|
||||
color = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
} else {
|
||||
// layer is always used here
|
||||
coords = (coords + vec2(1.0)) / vec2(2.0);
|
||||
color = texture(src_rt, vec3(coords, data.layer));
|
||||
}
|
||||
#elif defined(USE_LAYER)
|
||||
color = texture(src_rt, vec3(uv, data.layer));
|
||||
#else
|
||||
color = texture(src_rt, uv);
|
||||
#endif
|
||||
|
||||
if (data.convert_to_srgb) {
|
||||
color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
|
||||
}
|
||||
}
|
||||
744
engine/servers/rendering/renderer_rd/shaders/canvas.glsl
Normal file
744
engine/servers/rendering/renderer_rd/shaders/canvas.glsl
Normal file
|
|
@ -0,0 +1,744 @@
|
|||
#[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;
|
||||
|
||||
#if defined(CUSTOM0_USED)
|
||||
layout(location = 6) in vec4 custom0_attrib;
|
||||
#endif
|
||||
|
||||
#if defined(CUSTOM1_USED)
|
||||
layout(location = 7) in vec4 custom1_attrib;
|
||||
#endif
|
||||
|
||||
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 MATERIAL_UNIFORMS_USED
|
||||
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
|
||||
|
||||
#MATERIAL_UNIFORMS
|
||||
|
||||
} material;
|
||||
#endif
|
||||
|
||||
#GLOBALS
|
||||
|
||||
#ifdef USE_ATTRIBUTES
|
||||
vec3 srgb_to_linear(vec3 color) {
|
||||
return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045)));
|
||||
}
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
vec4 instance_custom = vec4(0.0);
|
||||
#if defined(CUSTOM0_USED)
|
||||
vec4 custom0 = vec4(0.0);
|
||||
#endif
|
||||
#if defined(CUSTOM1_USED)
|
||||
vec4 custom1 = vec4(0.0);
|
||||
#endif
|
||||
|
||||
#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;
|
||||
if (bool(draw_data.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR)) {
|
||||
color.rgb = srgb_to_linear(color.rgb);
|
||||
}
|
||||
color *= draw_data.modulation;
|
||||
vec2 uv = uv_attrib;
|
||||
|
||||
#if defined(CUSTOM0_USED)
|
||||
custom0 = custom0_attrib;
|
||||
#endif
|
||||
|
||||
#if defined(CUSTOM1_USED)
|
||||
custom1 = custom1_attrib;
|
||||
#endif
|
||||
|
||||
uvec4 bones = bone_attrib;
|
||||
vec4 bone_weights = weight_attrib;
|
||||
#else // !USE_ATTRIBUTES
|
||||
|
||||
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 // USE_ATTRIBUTES
|
||||
|
||||
mat4 model_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));
|
||||
|
||||
#define FLAGS_INSTANCING_MASK 0x7F
|
||||
#define FLAGS_INSTANCING_HAS_COLORS (1 << 7)
|
||||
#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 8)
|
||||
|
||||
uint instancing = draw_data.flags & FLAGS_INSTANCING_MASK;
|
||||
|
||||
#ifdef USE_ATTRIBUTES
|
||||
if (instancing > 1) {
|
||||
// trails
|
||||
|
||||
uint stride = 2 + 1 + 1; //particles always uses this format
|
||||
|
||||
uint trail_size = instancing;
|
||||
|
||||
uint offset = trail_size * stride * gl_InstanceIndex;
|
||||
|
||||
vec4 pcolor;
|
||||
vec2 new_vertex;
|
||||
{
|
||||
uint boffset = offset + bone_attrib.x * stride;
|
||||
new_vertex = (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.x;
|
||||
pcolor = transforms.data[boffset + 2] * weight_attrib.x;
|
||||
}
|
||||
if (weight_attrib.y > 0.001) {
|
||||
uint boffset = offset + bone_attrib.y * stride;
|
||||
new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.y;
|
||||
pcolor += transforms.data[boffset + 2] * weight_attrib.y;
|
||||
}
|
||||
if (weight_attrib.z > 0.001) {
|
||||
uint boffset = offset + bone_attrib.z * stride;
|
||||
new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.z;
|
||||
pcolor += transforms.data[boffset + 2] * weight_attrib.z;
|
||||
}
|
||||
if (weight_attrib.w > 0.001) {
|
||||
uint boffset = offset + bone_attrib.w * stride;
|
||||
new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.w;
|
||||
pcolor += transforms.data[boffset + 2] * weight_attrib.w;
|
||||
}
|
||||
|
||||
instance_custom = transforms.data[offset + 3];
|
||||
|
||||
vertex = new_vertex;
|
||||
color *= pcolor;
|
||||
} else
|
||||
#endif // USE_ATTRIBUTES
|
||||
{
|
||||
if (instancing == 1) {
|
||||
uint stride = 2;
|
||||
{
|
||||
if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_COLORS)) {
|
||||
stride += 1;
|
||||
}
|
||||
if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
|
||||
stride += 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint offset = stride * gl_InstanceIndex;
|
||||
|
||||
mat4 matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
|
||||
offset += 2;
|
||||
|
||||
if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_COLORS)) {
|
||||
color *= transforms.data[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
|
||||
instance_custom = transforms.data[offset];
|
||||
}
|
||||
|
||||
matrix = transpose(matrix);
|
||||
model_matrix = model_matrix * matrix;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_POINT_SIZE
|
||||
float point_size = 1.0;
|
||||
#endif
|
||||
|
||||
#ifdef USE_WORLD_VERTEX_COORDS
|
||||
vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
|
||||
#endif
|
||||
{
|
||||
#CODE : VERTEX
|
||||
}
|
||||
|
||||
#ifdef USE_NINEPATCH
|
||||
pixel_size_interp = abs(draw_data.dst_rect.zw) * vertex_base;
|
||||
#endif
|
||||
|
||||
#if !defined(SKIP_TRANSFORM_USED) && !defined(USE_WORLD_VERTEX_COORDS)
|
||||
vertex = (model_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;
|
||||
}
|
||||
|
||||
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 MATERIAL_UNIFORMS_USED
|
||||
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
|
||||
|
||||
#MATERIAL_UNIFORMS
|
||||
|
||||
} 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, SAMPLER_LINEAR_CLAMP), uv).r;
|
||||
d *= SDF_MAX_LENGTH;
|
||||
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, SAMPLER_LINEAR_CLAMP), uv + vec2(EPSILON, 0.0)).r - texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv - vec2(EPSILON, 0.0)).r,
|
||||
texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv + vec2(0.0, EPSILON)).r - texture(sampler2D(sdf_texture, 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;
|
||||
}
|
||||
|
||||
#GLOBALS
|
||||
|
||||
#ifdef LIGHT_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);
|
||||
vec3 light_direction = vec3(0.0);
|
||||
|
||||
if (is_directional) {
|
||||
light_direction = normalize(mix(vec3(light_position.xy, 0.0), vec3(0, 0, 1), light_position.z));
|
||||
light_position = vec3(0.0);
|
||||
} else {
|
||||
light_direction = normalize(light_position - light_vertex);
|
||||
}
|
||||
|
||||
#CODE : LIGHT
|
||||
|
||||
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_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_CODE_USED
|
||||
shadow_color.rgb *= 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
|
||||
|
||||
float msdf_median(float r, float g, float b, float a) {
|
||||
return min(max(min(r, g), min(max(r, g), b)), a);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#ifndef USE_PRIMITIVE
|
||||
if (bool(draw_data.flags & FLAGS_USE_MSDF)) {
|
||||
float px_range = draw_data.ninepatch_margins.x;
|
||||
float outline_thickness = draw_data.ninepatch_margins.y;
|
||||
//float reserved1 = draw_data.ninepatch_margins.z;
|
||||
//float reserved2 = draw_data.ninepatch_margins.w;
|
||||
|
||||
vec4 msdf_sample = texture(sampler2D(color_texture, texture_sampler), uv);
|
||||
vec2 msdf_size = vec2(textureSize(sampler2D(color_texture, texture_sampler), 0));
|
||||
vec2 dest_size = vec2(1.0) / fwidth(uv);
|
||||
float px_size = max(0.5 * dot((vec2(px_range) / msdf_size), dest_size), 1.0);
|
||||
float d = msdf_median(msdf_sample.r, msdf_sample.g, msdf_sample.b, msdf_sample.a) - 0.5;
|
||||
|
||||
if (outline_thickness > 0) {
|
||||
float cr = clamp(outline_thickness, 0.0, px_range / 2) / px_range;
|
||||
float a = clamp((d + cr) * px_size, 0.0, 1.0);
|
||||
color.a = a * color.a;
|
||||
} else {
|
||||
float a = clamp(d * px_size + 0.5, 0.0, 1.0);
|
||||
color.a = a * color.a;
|
||||
}
|
||||
} else if (bool(draw_data.flags & FLAGS_USE_LCD)) {
|
||||
vec4 lcd_sample = texture(sampler2D(color_texture, texture_sampler), uv);
|
||||
if (lcd_sample.a == 1.0) {
|
||||
color.rgb = lcd_sample.rgb * color.a;
|
||||
} else {
|
||||
color = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#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);
|
||||
if (bool(draw_data.flags & FLAGS_TRANSPOSE_RECT)) {
|
||||
normal.xy = normal.yx;
|
||||
}
|
||||
if (bool(draw_data.flags & FLAGS_FLIP_H)) {
|
||||
normal.x = -normal.x;
|
||||
}
|
||||
if (bool(draw_data.flags & FLAGS_FLIP_V)) {
|
||||
normal.y = -normal.y;
|
||||
}
|
||||
normal.z = sqrt(max(0.0, 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_map_depth = 1.0;
|
||||
|
||||
#if defined(NORMAL_MAP_USED)
|
||||
vec3 normal_map = vec3(0.0, 0.0, 1.0);
|
||||
normal_used = true;
|
||||
#endif
|
||||
|
||||
#CODE : FRAGMENT
|
||||
|
||||
#if defined(NORMAL_MAP_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_map_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);
|
||||
}
|
||||
|
||||
vec4 base_color = color;
|
||||
|
||||
#ifdef MODE_LIGHT_ONLY
|
||||
float light_only_alpha = 0.0;
|
||||
#elif !defined(MODE_UNSHADED)
|
||||
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_CODE_USED
|
||||
|
||||
vec4 shadow_modulate = vec4(1.0);
|
||||
light_color = light_compute(light_vertex, vec3(direction, light_array.data[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, 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.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
|
||||
} else {
|
||||
light_color.rgb *= base_color.rgb;
|
||||
}
|
||||
#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_CODE_USED
|
||||
,
|
||||
shadow_modulate.rgb
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
light_blend_compute(light_base, light_color, color.rgb);
|
||||
#ifdef MODE_LIGHT_ONLY
|
||||
light_only_alpha += light_color.a;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Positional Lights
|
||||
|
||||
for (uint i = 0; i < MAX_LIGHTS_PER_ITEM; i++) {
|
||||
if (i >= light_count) {
|
||||
break;
|
||||
}
|
||||
uint light_base = draw_data.lights[i >> 2];
|
||||
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;
|
||||
|
||||
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
|
||||
continue;
|
||||
}
|
||||
|
||||
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_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, uv, base_color, 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);
|
||||
|
||||
light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
|
||||
} else {
|
||||
light_color.rgb *= base_color.rgb;
|
||||
}
|
||||
#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.
|
||||
|
||||
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_CODE_USED
|
||||
,
|
||||
shadow_modulate.rgb
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
light_blend_compute(light_base, light_color, color.rgb);
|
||||
#ifdef MODE_LIGHT_ONLY
|
||||
light_only_alpha += light_color.a;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MODE_LIGHT_ONLY
|
||||
color.a *= light_only_alpha;
|
||||
#endif
|
||||
|
||||
frag_color = color;
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(location = 0) in highp vec3 vertex;
|
||||
|
||||
layout(push_constant, 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, 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
|
||||
}
|
||||
179
engine/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl
Normal file
179
engine/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
#[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_snorm, 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, 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(ivec2(-32767), 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;
|
||||
int found = 0;
|
||||
int solid_found = 0;
|
||||
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;
|
||||
}
|
||||
solid_found++;
|
||||
}
|
||||
found++;
|
||||
}
|
||||
}
|
||||
|
||||
if (solid_found == found) {
|
||||
//mark solid only if all are solid
|
||||
rel = ivec2(-32767);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
bool solid = rel.x < 0;
|
||||
|
||||
if (solid) {
|
||||
rel = -rel - ivec2(1);
|
||||
}
|
||||
|
||||
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;
|
||||
bool src_solid = src_rel.x < 0;
|
||||
if (src_solid) {
|
||||
src_rel = -src_rel - ivec2(1);
|
||||
}
|
||||
|
||||
if (src_solid != solid) {
|
||||
src_rel = ivec2(src_pos << params.shift); //point to itself if of different type
|
||||
}
|
||||
|
||||
float src_dist = length(vec2(src_rel - center));
|
||||
if (src_dist < dist) {
|
||||
dist = src_dist;
|
||||
rel = src_rel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (solid) {
|
||||
rel = -rel - ivec2(1);
|
||||
}
|
||||
|
||||
imageStore(dst_process, pos, ivec4(rel, 0, 0));
|
||||
#endif
|
||||
|
||||
#ifdef MODE_STORE
|
||||
|
||||
ivec2 rel = imageLoad(src_process, pos).xy;
|
||||
|
||||
bool solid = rel.x < 0;
|
||||
|
||||
if (solid) {
|
||||
rel = -rel - ivec2(1);
|
||||
}
|
||||
|
||||
float d = length(vec2(rel - pos));
|
||||
|
||||
if (solid) {
|
||||
d = -d;
|
||||
}
|
||||
|
||||
d /= SDF_MAX_LENGTH;
|
||||
d = clamp(d, -1.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;
|
||||
|
||||
bool solid = rel.x < 0;
|
||||
|
||||
if (solid) {
|
||||
rel = -rel - ivec2(1);
|
||||
}
|
||||
|
||||
float d = length(vec2(rel - center));
|
||||
|
||||
if (solid) {
|
||||
d = -d;
|
||||
}
|
||||
d /= SDF_MAX_LENGTH;
|
||||
d = clamp(d, -1.0, 1.0);
|
||||
imageStore(dst_sdf, pos, vec4(d));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
|
||||
#define MAX_LIGHTS_PER_ITEM 16
|
||||
|
||||
#define M_PI 3.14159265359
|
||||
|
||||
#define SDF_MAX_LENGTH 16384.0
|
||||
|
||||
//1 means enabled, 2+ means trails in use
|
||||
#define FLAGS_INSTANCING_MASK 0x7F
|
||||
#define FLAGS_INSTANCING_HAS_COLORS (1 << 7)
|
||||
#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 8)
|
||||
|
||||
#define FLAGS_CLIP_RECT_UV (1 << 9)
|
||||
#define FLAGS_TRANSPOSE_RECT (1 << 10)
|
||||
#define FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR (1 << 11)
|
||||
#define FLAGS_NINEPACH_DRAW_CENTER (1 << 12)
|
||||
|
||||
#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 FLAGS_USE_MSDF (1 << 28)
|
||||
#define FLAGS_USE_LCD (1 << 29)
|
||||
|
||||
#define FLAGS_FLIP_H (1 << 30)
|
||||
#define FLAGS_FLIP_V (1 << 31)
|
||||
|
||||
// Push Constant
|
||||
|
||||
layout(push_constant, 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 color_buffer;
|
||||
layout(set = 0, binding = 7) uniform texture2D sdf_texture;
|
||||
|
||||
#include "samplers_inc.glsl"
|
||||
|
||||
layout(set = 0, binding = 9, std430) restrict readonly buffer GlobalShaderUniformData {
|
||||
vec4 data[];
|
||||
}
|
||||
global_shader_uniforms;
|
||||
|
||||
/* 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;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#define CLUSTER_COUNTER_SHIFT 20
|
||||
#define CLUSTER_POINTER_MASK ((1 << CLUSTER_COUNTER_SHIFT) - 1)
|
||||
#define CLUSTER_COUNTER_MASK 0xfff
|
||||
115
engine/servers/rendering/renderer_rd/shaders/cluster_debug.glsl
Normal file
115
engine/servers/rendering/renderer_rd/shaders/cluster_debug.glsl
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
const vec3 usage_gradient[33] = vec3[]( // 1 (none) + 32
|
||||
vec3(0.14, 0.17, 0.23),
|
||||
vec3(0.24, 0.44, 0.83),
|
||||
vec3(0.23, 0.57, 0.84),
|
||||
vec3(0.22, 0.71, 0.84),
|
||||
vec3(0.22, 0.85, 0.83),
|
||||
vec3(0.21, 0.85, 0.72),
|
||||
vec3(0.21, 0.85, 0.57),
|
||||
vec3(0.20, 0.85, 0.42),
|
||||
vec3(0.20, 0.85, 0.27),
|
||||
vec3(0.27, 0.86, 0.19),
|
||||
vec3(0.51, 0.85, 0.19),
|
||||
vec3(0.57, 0.86, 0.19),
|
||||
vec3(0.62, 0.85, 0.19),
|
||||
vec3(0.67, 0.86, 0.20),
|
||||
vec3(0.73, 0.85, 0.20),
|
||||
vec3(0.78, 0.85, 0.20),
|
||||
vec3(0.83, 0.85, 0.20),
|
||||
vec3(0.85, 0.82, 0.20),
|
||||
vec3(0.85, 0.76, 0.20),
|
||||
vec3(0.85, 0.81, 0.20),
|
||||
vec3(0.85, 0.65, 0.20),
|
||||
vec3(0.84, 0.60, 0.21),
|
||||
vec3(0.84, 0.56, 0.21),
|
||||
vec3(0.84, 0.51, 0.21),
|
||||
vec3(0.84, 0.46, 0.21),
|
||||
vec3(0.84, 0.41, 0.21),
|
||||
vec3(0.84, 0.36, 0.21),
|
||||
vec3(0.84, 0.31, 0.21),
|
||||
vec3(0.84, 0.27, 0.21),
|
||||
vec3(0.83, 0.22, 0.22),
|
||||
vec3(0.83, 0.22, 0.27),
|
||||
vec3(0.83, 0.22, 0.32),
|
||||
vec3(1.00, 0.63, 0.70));
|
||||
layout(push_constant, std430) uniform Params {
|
||||
uvec2 screen_size;
|
||||
uvec2 cluster_screen_size;
|
||||
|
||||
uint cluster_shift;
|
||||
uint cluster_type;
|
||||
float z_near;
|
||||
float z_far;
|
||||
|
||||
bool orthogonal;
|
||||
uint max_cluster_element_count_div_32;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(set = 0, binding = 1, std430) buffer restrict readonly ClusterData {
|
||||
uint data[];
|
||||
}
|
||||
cluster_data;
|
||||
|
||||
layout(rgba16f, set = 0, binding = 2) uniform restrict writeonly image2D screen_buffer;
|
||||
layout(set = 0, binding = 3) uniform texture2D depth_buffer;
|
||||
layout(set = 0, binding = 4) uniform sampler depth_buffer_sampler;
|
||||
|
||||
void main() {
|
||||
uvec2 screen_pos = gl_GlobalInvocationID.xy;
|
||||
if (any(greaterThanEqual(screen_pos, params.screen_size))) {
|
||||
return;
|
||||
}
|
||||
|
||||
uvec2 cluster_pos = screen_pos >> params.cluster_shift;
|
||||
|
||||
uint offset = cluster_pos.y * params.cluster_screen_size.x + cluster_pos.x;
|
||||
offset += params.cluster_screen_size.x * params.cluster_screen_size.y * params.cluster_type;
|
||||
offset *= (params.max_cluster_element_count_div_32 + 32);
|
||||
|
||||
//depth buffers generally can't be accessed via image API
|
||||
float depth = texelFetch(sampler2D(depth_buffer, depth_buffer_sampler), ivec2(screen_pos), 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));
|
||||
}
|
||||
depth /= params.z_far;
|
||||
|
||||
uint slice = uint(clamp(floor(depth * 32.0), 0.0, 31.0));
|
||||
uint slice_minmax = cluster_data.data[offset + params.max_cluster_element_count_div_32 + slice];
|
||||
uint item_min = slice_minmax & 0xFFFF;
|
||||
uint item_max = slice_minmax >> 16;
|
||||
|
||||
uint item_count = 0;
|
||||
for (uint i = 0; i < params.max_cluster_element_count_div_32; i++) {
|
||||
uint slice_bits = cluster_data.data[offset + i];
|
||||
while (slice_bits != 0) {
|
||||
uint bit = findLSB(slice_bits);
|
||||
uint item = i * 32 + bit;
|
||||
if ((item >= item_min && item < item_max)) {
|
||||
item_count++;
|
||||
}
|
||||
slice_bits &= ~(1 << bit);
|
||||
}
|
||||
}
|
||||
|
||||
item_count = min(item_count, 32);
|
||||
|
||||
vec3 color = usage_gradient[item_count];
|
||||
|
||||
color = mix(color * 1.2, color * 0.3, float(slice) / 31.0);
|
||||
|
||||
imageStore(screen_buffer, ivec2(screen_pos), vec4(color, 1.0));
|
||||
}
|
||||
187
engine/servers/rendering/renderer_rd/shaders/cluster_render.glsl
Normal file
187
engine/servers/rendering/renderer_rd/shaders/cluster_render.glsl
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(location = 0) in vec3 vertex_attrib;
|
||||
|
||||
layout(location = 0) out float depth_interp;
|
||||
layout(location = 1) out flat uint element_index;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
uint base_index;
|
||||
uint pad0;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(set = 0, binding = 1, std140) uniform State {
|
||||
mat4 projection;
|
||||
|
||||
float inv_z_far;
|
||||
uint screen_to_clusters_shift; // shift to obtain coordinates in block indices
|
||||
uint cluster_screen_width; //
|
||||
uint cluster_data_size; // how much data for a single cluster takes
|
||||
|
||||
uint cluster_depth_offset;
|
||||
uint pad0;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
}
|
||||
state;
|
||||
|
||||
struct RenderElement {
|
||||
uint type; //0-4
|
||||
bool touches_near;
|
||||
bool touches_far;
|
||||
uint original_index;
|
||||
mat3x4 transform_inv;
|
||||
vec3 scale;
|
||||
uint pad;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 2, std430) buffer restrict readonly RenderElements {
|
||||
RenderElement data[];
|
||||
}
|
||||
render_elements;
|
||||
|
||||
void main() {
|
||||
element_index = params.base_index + gl_InstanceIndex;
|
||||
|
||||
vec3 vertex = vertex_attrib;
|
||||
vertex *= render_elements.data[element_index].scale;
|
||||
|
||||
vertex = vec4(vertex, 1.0) * render_elements.data[element_index].transform_inv;
|
||||
depth_interp = -vertex.z;
|
||||
|
||||
gl_Position = state.projection * vec4(vertex, 1.0);
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
#ifndef MOLTENVK_USED // Metal will corrupt GPU state otherwise
|
||||
#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic) && defined(has_GL_KHR_shader_subgroup_vote)
|
||||
|
||||
#extension GL_KHR_shader_subgroup_ballot : enable
|
||||
#extension GL_KHR_shader_subgroup_arithmetic : enable
|
||||
#extension GL_KHR_shader_subgroup_vote : enable
|
||||
|
||||
#define USE_SUBGROUPS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
layout(location = 0) in float depth_interp;
|
||||
layout(location = 1) in flat uint element_index;
|
||||
|
||||
layout(set = 0, binding = 1, std140) uniform State {
|
||||
mat4 projection;
|
||||
float inv_z_far;
|
||||
uint screen_to_clusters_shift; // shift to obtain coordinates in block indices
|
||||
uint cluster_screen_width; //
|
||||
uint cluster_data_size; // how much data for a single cluster takes
|
||||
uint cluster_depth_offset;
|
||||
uint pad0;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
}
|
||||
state;
|
||||
|
||||
//cluster data is layout linearly, each cell contains the follow information:
|
||||
// - list of bits for every element to mark as used, so (max_elem_count/32)*4 uints
|
||||
// - a uint for each element to mark the depth bits used when rendering (0-31)
|
||||
|
||||
layout(set = 0, binding = 3, std430) buffer restrict ClusterRender {
|
||||
uint data[];
|
||||
}
|
||||
cluster_render;
|
||||
|
||||
#ifdef USE_ATTACHMENT
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
//convert from screen to cluster
|
||||
uvec2 cluster = uvec2(gl_FragCoord.xy) >> state.screen_to_clusters_shift;
|
||||
|
||||
//get linear cluster offset from screen poss
|
||||
uint cluster_offset = cluster.x + state.cluster_screen_width * cluster.y;
|
||||
//multiply by data size to position at the beginning of the element list for this cluster
|
||||
cluster_offset *= state.cluster_data_size;
|
||||
|
||||
//find the current element in the list and plot the bit to mark it as used
|
||||
uint usage_write_offset = cluster_offset + (element_index >> 5);
|
||||
uint usage_write_bit = 1 << (element_index & 0x1F);
|
||||
|
||||
uint aux = 0;
|
||||
|
||||
#ifdef USE_SUBGROUPS
|
||||
|
||||
uint cluster_thread_group_index;
|
||||
|
||||
if (!gl_HelperInvocation) {
|
||||
//https://advances.realtimerendering.com/s2017/2017_Sig_Improved_Culling_final.pdf
|
||||
|
||||
uvec4 mask;
|
||||
|
||||
while (true) {
|
||||
// find the cluster offset of the first active thread
|
||||
// threads that did break; go inactive and no longer count
|
||||
uint first = subgroupBroadcastFirst(cluster_offset);
|
||||
// update the mask for thread that match this cluster
|
||||
mask = subgroupBallot(first == cluster_offset);
|
||||
if (first == cluster_offset) {
|
||||
// This thread belongs to the group of threads that match this offset,
|
||||
// so exit the loop.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cluster_thread_group_index = subgroupBallotExclusiveBitCount(mask);
|
||||
|
||||
if (cluster_thread_group_index == 0) {
|
||||
aux = atomicOr(cluster_render.data[usage_write_offset], usage_write_bit);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// MoltenVK/Metal fails to compile shaders using gl_HelperInvocation for some GPUs
|
||||
#ifndef MOLTENVK_USED
|
||||
if (!gl_HelperInvocation)
|
||||
#endif
|
||||
{
|
||||
aux = atomicOr(cluster_render.data[usage_write_offset], usage_write_bit);
|
||||
}
|
||||
#endif
|
||||
//find the current element in the depth usage list and mark the current depth as used
|
||||
float unit_depth = depth_interp * state.inv_z_far;
|
||||
|
||||
uint z_bit = clamp(uint(floor(unit_depth * 32.0)), 0, 31);
|
||||
|
||||
uint z_write_offset = cluster_offset + state.cluster_depth_offset + element_index;
|
||||
uint z_write_bit = 1 << z_bit;
|
||||
|
||||
#ifdef USE_SUBGROUPS
|
||||
if (!gl_HelperInvocation) {
|
||||
z_write_bit = subgroupOr(z_write_bit); //merge all Zs
|
||||
if (cluster_thread_group_index == 0) {
|
||||
aux = atomicOr(cluster_render.data[z_write_offset], z_write_bit);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// MoltenVK/Metal fails to compile shaders using gl_HelperInvocation for some GPUs
|
||||
#ifndef MOLTENVK_USED
|
||||
if (!gl_HelperInvocation)
|
||||
#endif
|
||||
{
|
||||
aux = atomicOr(cluster_render.data[z_write_offset], z_write_bit);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_ATTACHMENT
|
||||
frag_color = vec4(float(aux));
|
||||
#endif
|
||||
}
|
||||
119
engine/servers/rendering/renderer_rd/shaders/cluster_store.glsl
Normal file
119
engine/servers/rendering/renderer_rd/shaders/cluster_store.glsl
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
uint cluster_render_data_size; // how much data for a single cluster takes
|
||||
uint max_render_element_count_div_32; //divided by 32
|
||||
uvec2 cluster_screen_size;
|
||||
uint render_element_count_div_32; //divided by 32
|
||||
|
||||
uint max_cluster_element_count_div_32; //divided by 32
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(set = 0, binding = 1, std430) buffer restrict readonly ClusterRender {
|
||||
uint data[];
|
||||
}
|
||||
cluster_render;
|
||||
|
||||
layout(set = 0, binding = 2, std430) buffer restrict ClusterStore {
|
||||
uint data[];
|
||||
}
|
||||
cluster_store;
|
||||
|
||||
struct RenderElement {
|
||||
uint type; //0-4
|
||||
bool touches_near;
|
||||
bool touches_far;
|
||||
uint original_index;
|
||||
mat3x4 transform_inv;
|
||||
vec3 scale;
|
||||
uint pad;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 3, std430) buffer restrict readonly RenderElements {
|
||||
RenderElement data[];
|
||||
}
|
||||
render_elements;
|
||||
|
||||
void main() {
|
||||
uvec2 pos = gl_GlobalInvocationID.xy;
|
||||
if (any(greaterThanEqual(pos, params.cluster_screen_size))) {
|
||||
return;
|
||||
}
|
||||
|
||||
//counter for each type of render_element
|
||||
|
||||
//base offset for this cluster
|
||||
uint base_offset = (pos.x + params.cluster_screen_size.x * pos.y);
|
||||
uint src_offset = base_offset * params.cluster_render_data_size;
|
||||
|
||||
uint render_element_offset = 0;
|
||||
|
||||
//check all render_elements and see which one was written to
|
||||
while (render_element_offset < params.render_element_count_div_32) {
|
||||
uint bits = cluster_render.data[src_offset + render_element_offset];
|
||||
while (bits != 0) {
|
||||
//if bits exist, check the render_element
|
||||
uint index_bit = findLSB(bits);
|
||||
uint index = render_element_offset * 32 + index_bit;
|
||||
uint type = render_elements.data[index].type;
|
||||
|
||||
uint z_range_offset = src_offset + params.max_render_element_count_div_32 + index;
|
||||
uint z_range = cluster_render.data[z_range_offset];
|
||||
|
||||
//if object was written, z was written, but check just in case
|
||||
if (z_range != 0) { //should always be > 0
|
||||
|
||||
uint from_z = findLSB(z_range);
|
||||
uint to_z = findMSB(z_range) + 1;
|
||||
|
||||
if (render_elements.data[index].touches_near) {
|
||||
from_z = 0;
|
||||
}
|
||||
|
||||
if (render_elements.data[index].touches_far) {
|
||||
to_z = 32;
|
||||
}
|
||||
|
||||
// find cluster offset in the buffer used for indexing in the renderer
|
||||
uint dst_offset = (base_offset + type * (params.cluster_screen_size.x * params.cluster_screen_size.y)) * (params.max_cluster_element_count_div_32 + 32);
|
||||
|
||||
uint orig_index = render_elements.data[index].original_index;
|
||||
//store this index in the Z slices by setting the relevant bit
|
||||
for (uint i = from_z; i < to_z; i++) {
|
||||
uint slice_ofs = dst_offset + params.max_cluster_element_count_div_32 + i;
|
||||
|
||||
uint minmax = cluster_store.data[slice_ofs];
|
||||
|
||||
if (minmax == 0) {
|
||||
minmax = 0xFFFF; //min 0, max 0xFFFF
|
||||
}
|
||||
|
||||
uint elem_min = min(orig_index, minmax & 0xFFFF);
|
||||
uint elem_max = max(orig_index + 1, minmax >> 16); //always store plus one, so zero means range is empty when not written to
|
||||
|
||||
minmax = elem_min | (elem_max << 16);
|
||||
cluster_store.data[slice_ofs] = minmax;
|
||||
}
|
||||
|
||||
uint store_word = orig_index >> 5;
|
||||
uint store_bit = orig_index & 0x1F;
|
||||
|
||||
//store the actual render_element index at the end, so the rendering code can reference it
|
||||
cluster_store.data[dst_offset + store_word] |= 1 << store_bit;
|
||||
}
|
||||
|
||||
bits &= ~(1 << index_bit); //clear the bit to continue iterating
|
||||
}
|
||||
|
||||
render_element_offset++;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
struct DecalData {
|
||||
highp mat4 xform; //to decal transform
|
||||
highp vec3 inv_extents;
|
||||
mediump float albedo_mix;
|
||||
highp vec4 albedo_rect;
|
||||
highp vec4 normal_rect;
|
||||
highp vec4 orm_rect;
|
||||
highp vec4 emission_rect;
|
||||
highp vec4 modulate;
|
||||
mediump float emission_energy;
|
||||
uint mask;
|
||||
mediump float upper_fade;
|
||||
mediump float lower_fade;
|
||||
mediump mat3x4 normal_xform;
|
||||
mediump vec3 normal;
|
||||
mediump float normal_fade;
|
||||
};
|
||||
19
engine/servers/rendering/renderer_rd/shaders/effects/SCsub
Normal file
19
engine/servers/rendering/renderer_rd/shaders/effects/SCsub
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
if "RD_GLSL" in env["BUILDERS"]:
|
||||
# find all include files
|
||||
gl_include_files = [str(f) for f in Glob("*_inc.glsl")] + [str(f) for f in Glob("../*_inc.glsl")]
|
||||
|
||||
# find all shader code(all glsl files excluding our include files)
|
||||
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
|
||||
|
||||
# make sure we recompile shaders if include files change
|
||||
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#glsl_builders.py"])
|
||||
|
||||
# compile shaders
|
||||
for glsl_file in glsl_files:
|
||||
env.RD_GLSL(glsl_file)
|
||||
|
||||
SConscript("fsr2/SCsub")
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "blur_raster_inc.glsl"
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "blur_raster_inc.glsl"
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
|
||||
#ifdef GLOW_USE_AUTO_EXPOSURE
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
|
||||
#endif
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
// We do not apply our color scale for our mobile renderer here, we'll leave our colors at half brightness and apply scale in the tonemap raster.
|
||||
|
||||
#ifdef MODE_MIPMAP
|
||||
|
||||
vec2 pix_size = blur.pixel_size;
|
||||
vec4 color = texture(source_color, uv_interp + vec2(-0.5, -0.5) * pix_size);
|
||||
color += texture(source_color, uv_interp + vec2(0.5, -0.5) * pix_size);
|
||||
color += texture(source_color, uv_interp + vec2(0.5, 0.5) * pix_size);
|
||||
color += texture(source_color, uv_interp + vec2(-0.5, 0.5) * pix_size);
|
||||
frag_color = color / 4.0;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_GAUSSIAN_BLUR
|
||||
|
||||
// For Gaussian Blur we use 13 taps in a single pass instead of 12 taps over 2 passes.
|
||||
// This minimizes the number of times we change framebuffers which is very important for mobile.
|
||||
// Source: http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
|
||||
vec4 A = texture(source_color, uv_interp + blur.pixel_size * vec2(-1.0, -1.0));
|
||||
vec4 B = texture(source_color, uv_interp + blur.pixel_size * vec2(0.0, -1.0));
|
||||
vec4 C = texture(source_color, uv_interp + blur.pixel_size * vec2(1.0, -1.0));
|
||||
vec4 D = texture(source_color, uv_interp + blur.pixel_size * vec2(-0.5, -0.5));
|
||||
vec4 E = texture(source_color, uv_interp + blur.pixel_size * vec2(0.5, -0.5));
|
||||
vec4 F = texture(source_color, uv_interp + blur.pixel_size * vec2(-1.0, 0.0));
|
||||
vec4 G = texture(source_color, uv_interp);
|
||||
vec4 H = texture(source_color, uv_interp + blur.pixel_size * vec2(1.0, 0.0));
|
||||
vec4 I = texture(source_color, uv_interp + blur.pixel_size * vec2(-0.5, 0.5));
|
||||
vec4 J = texture(source_color, uv_interp + blur.pixel_size * vec2(0.5, 0.5));
|
||||
vec4 K = texture(source_color, uv_interp + blur.pixel_size * vec2(-1.0, 1.0));
|
||||
vec4 L = texture(source_color, uv_interp + blur.pixel_size * vec2(0.0, 1.0));
|
||||
vec4 M = texture(source_color, uv_interp + blur.pixel_size * vec2(1.0, 1.0));
|
||||
|
||||
float base_weight = 0.5 / 4.0;
|
||||
float lesser_weight = 0.125 / 4.0;
|
||||
|
||||
frag_color = (D + E + I + J) * base_weight;
|
||||
frag_color += (A + B + G + F) * lesser_weight;
|
||||
frag_color += (B + C + H + G) * lesser_weight;
|
||||
frag_color += (F + G + L + K) * lesser_weight;
|
||||
frag_color += (G + H + M + L) * lesser_weight;
|
||||
#endif
|
||||
|
||||
#ifdef MODE_GAUSSIAN_GLOW
|
||||
|
||||
//Glow uses larger sigma 1 for a more rounded blur effect
|
||||
|
||||
#define GLOW_ADD(m_ofs, m_mult) \
|
||||
{ \
|
||||
vec2 ofs = uv_interp + m_ofs * pix_size; \
|
||||
vec4 c = texture(source_color, ofs) * m_mult; \
|
||||
if (any(lessThan(ofs, vec2(0.0))) || any(greaterThan(ofs, vec2(1.0)))) { \
|
||||
c *= 0.0; \
|
||||
} \
|
||||
color += c; \
|
||||
}
|
||||
|
||||
if (bool(blur.flags & FLAG_HORIZONTAL)) {
|
||||
vec2 pix_size = blur.pixel_size;
|
||||
pix_size *= 0.5; //reading from larger buffer, so use more samples
|
||||
|
||||
vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.174938;
|
||||
GLOW_ADD(vec2(1.0, 0.0), 0.165569);
|
||||
GLOW_ADD(vec2(2.0, 0.0), 0.140367);
|
||||
GLOW_ADD(vec2(3.0, 0.0), 0.106595);
|
||||
GLOW_ADD(vec2(-1.0, 0.0), 0.165569);
|
||||
GLOW_ADD(vec2(-2.0, 0.0), 0.140367);
|
||||
GLOW_ADD(vec2(-3.0, 0.0), 0.106595);
|
||||
|
||||
// only do this in the horizontal pass, if we also do this in the vertical pass we're doubling up.
|
||||
color *= blur.glow_strength;
|
||||
|
||||
frag_color = color;
|
||||
} else {
|
||||
vec2 pix_size = blur.pixel_size;
|
||||
vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.288713;
|
||||
GLOW_ADD(vec2(0.0, 1.0), 0.233062);
|
||||
GLOW_ADD(vec2(0.0, 2.0), 0.122581);
|
||||
GLOW_ADD(vec2(0.0, -1.0), 0.233062);
|
||||
GLOW_ADD(vec2(0.0, -2.0), 0.122581);
|
||||
|
||||
frag_color = color;
|
||||
}
|
||||
|
||||
#undef GLOW_ADD
|
||||
|
||||
if (bool(blur.flags & FLAG_GLOW_FIRST_PASS)) {
|
||||
// In the first pass bring back to correct color range else we're applying the wrong threshold
|
||||
// in subsequent passes we can use it as is as we'd just be undoing it right after.
|
||||
frag_color *= blur.luminance_multiplier;
|
||||
|
||||
#ifdef GLOW_USE_AUTO_EXPOSURE
|
||||
|
||||
frag_color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / blur.glow_auto_exposure_scale;
|
||||
#endif
|
||||
frag_color *= blur.glow_exposure;
|
||||
|
||||
float luminance = max(frag_color.r, max(frag_color.g, frag_color.b));
|
||||
float feedback = max(smoothstep(blur.glow_hdr_threshold, blur.glow_hdr_threshold + blur.glow_hdr_scale, luminance), blur.glow_bloom);
|
||||
|
||||
frag_color = min(frag_color * feedback, vec4(blur.glow_luminance_cap)) / blur.luminance_multiplier;
|
||||
}
|
||||
|
||||
#endif // MODE_GAUSSIAN_GLOW
|
||||
|
||||
#ifdef MODE_COPY
|
||||
vec4 color = textureLod(source_color, uv_interp, 0.0);
|
||||
frag_color = color;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#define FLAG_HORIZONTAL (1 << 0)
|
||||
#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 1)
|
||||
#define FLAG_GLOW_FIRST_PASS (1 << 2)
|
||||
|
||||
layout(push_constant, std430) uniform Blur {
|
||||
vec2 pixel_size; // 08 - 08
|
||||
uint flags; // 04 - 12
|
||||
uint pad; // 04 - 16
|
||||
|
||||
// Glow.
|
||||
float glow_strength; // 04 - 20
|
||||
float glow_bloom; // 04 - 24
|
||||
float glow_hdr_threshold; // 04 - 28
|
||||
float glow_hdr_scale; // 04 - 32
|
||||
|
||||
float glow_exposure; // 04 - 36
|
||||
float glow_white; // 04 - 40
|
||||
float glow_luminance_cap; // 04 - 44
|
||||
float glow_auto_exposure_scale; // 04 - 48
|
||||
|
||||
float luminance_multiplier; // 04 - 52
|
||||
float res1; // 04 - 56
|
||||
float res2; // 04 - 60
|
||||
float res3; // 04 - 64
|
||||
}
|
||||
blur;
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
#[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
|
||||
|
||||
#include "bokeh_dof_inc.glsl"
|
||||
|
||||
#ifdef MODE_GEN_BLUR_SIZE
|
||||
|
||||
float get_depth_at_pos(vec2 uv) {
|
||||
float depth = textureLod(source_depth, uv, 0.0).x * 2.0 - 1.0;
|
||||
if (params.orthogonal) {
|
||||
depth = -(depth * (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) {
|
||||
if (params.use_physical_near) {
|
||||
// Physically-based.
|
||||
float d = abs(params.blur_near_begin - depth);
|
||||
return -(d / (params.blur_near_begin - d)) * params.blur_size_near - DEPTH_GAP; // Near blur is negative.
|
||||
} else {
|
||||
// Non-physically-based.
|
||||
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) {
|
||||
if (params.use_physical_far) {
|
||||
// Physically-based.
|
||||
float d = abs(params.blur_far_begin - depth);
|
||||
return (d / (params.blur_far_begin + d)) * params.blur_size_far + DEPTH_GAP;
|
||||
} else {
|
||||
// Non-physically-based.
|
||||
return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#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 initial_blur = color.a;
|
||||
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 > initial_blur) {
|
||||
sample_size = clamp(sample_size, 0.0, abs(initial_blur) * 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
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
layout(push_constant, 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;
|
||||
bool use_physical_near;
|
||||
bool use_physical_far;
|
||||
|
||||
float blur_size_near;
|
||||
float blur_size_far;
|
||||
uint pad[2];
|
||||
}
|
||||
params;
|
||||
|
||||
//used to work around downsampling filter
|
||||
#define DEPTH_GAP 0.0
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "bokeh_dof_inc.glsl"
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
// old code, ARM driver bug on Mali-GXXx GPUs and Vulkan API 1.3.xxx
|
||||
// https://github.com/godotengine/godot/pull/92817#issuecomment-2168625982
|
||||
//vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
//gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
//uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
|
||||
vec2 vertex_base;
|
||||
if (gl_VertexIndex == 0) {
|
||||
vertex_base = vec2(-1.0, -1.0);
|
||||
} else if (gl_VertexIndex == 1) {
|
||||
vertex_base = vec2(-1.0, 3.0);
|
||||
} else {
|
||||
vertex_base = vec2(3.0, -1.0);
|
||||
}
|
||||
gl_Position = vec4(vertex_base, 0.0, 1.0);
|
||||
uv_interp = clamp(vertex_base, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "bokeh_dof_inc.glsl"
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
#ifdef MODE_GEN_BLUR_SIZE
|
||||
layout(location = 0) out float weight;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_depth;
|
||||
#else
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
#ifdef OUTPUT_WEIGHT
|
||||
layout(location = 1) out float weight;
|
||||
#endif
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_weight;
|
||||
#ifdef MODE_COMPOSITE_BOKEH
|
||||
layout(set = 2, binding = 0) uniform sampler2D original_weight;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//DOF
|
||||
// Bokeh single pass implementation based on https://tuxedolabs.blogspot.com/2018/05/bokeh-depth-of-field-in-single-pass.html
|
||||
|
||||
#ifdef MODE_GEN_BLUR_SIZE
|
||||
|
||||
float get_depth_at_pos(vec2 uv) {
|
||||
float depth = textureLod(source_depth, uv, 0.0).x * 2.0 - 1.0;
|
||||
if (params.orthogonal) {
|
||||
depth = -(depth * (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) {
|
||||
if (params.use_physical_near) {
|
||||
// Physically-based.
|
||||
float d = abs(params.blur_near_begin - depth);
|
||||
return -(d / (params.blur_near_begin - d)) * params.blur_size_near - DEPTH_GAP; // Near blur is negative.
|
||||
} else {
|
||||
// Non-physically-based.
|
||||
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) {
|
||||
if (params.use_physical_far) {
|
||||
// Physically-based.
|
||||
float d = abs(params.blur_far_begin - depth);
|
||||
return (d / (params.blur_far_begin + d)) * params.blur_size_far + DEPTH_GAP;
|
||||
} else {
|
||||
// Non-physically-based.
|
||||
return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#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(source_color, uv);
|
||||
color.a = texture(source_weight, uv).r;
|
||||
|
||||
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(source_color, suv);
|
||||
sample_color.a = texture(source_weight, suv).r;
|
||||
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() {
|
||||
vec2 pixel_size = 1.0 / vec2(params.size);
|
||||
vec2 uv = uv_interp;
|
||||
|
||||
#ifdef MODE_GEN_BLUR_SIZE
|
||||
uv += pixel_size * 0.5;
|
||||
float center_depth = get_depth_at_pos(uv);
|
||||
weight = get_blur_size(center_depth);
|
||||
#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
|
||||
}
|
||||
|
||||
float alpha = texture(source_color, uv).a; // retain this
|
||||
vec2 dir = (params.second_pass ? vec2(0.0, 1.0) : vec2(1.0, 0.0));
|
||||
|
||||
vec4 color = weighted_filter_dir(dir, uv, pixel_size);
|
||||
|
||||
frag_color = color;
|
||||
frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
|
||||
#ifdef OUTPUT_WEIGHT
|
||||
weight = color.a;
|
||||
#endif
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
float alpha = texture(source_color, uv).a; // retain this
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
frag_color = color;
|
||||
frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
|
||||
#ifdef OUTPUT_WEIGHT
|
||||
weight = color.a;
|
||||
#endif
|
||||
|
||||
#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(source_color, uv);
|
||||
float alpha = color.a; // retain this
|
||||
color.a = texture(source_weight, uv).r;
|
||||
|
||||
vec4 color_accum = color;
|
||||
float accum = 1.0;
|
||||
|
||||
float radius = params.blur_scale;
|
||||
for (float ang = 0.0; radius < params.blur_size; ang += GOLDEN_ANGLE) {
|
||||
vec2 uv_adj = uv + vec2(cos(ang), sin(ang)) * pixel_size * radius;
|
||||
|
||||
vec4 sample_color = texture(source_color, uv_adj);
|
||||
sample_color.a = texture(source_weight, uv_adj).r;
|
||||
|
||||
float limit = abs(sample_color.a);
|
||||
if (sample_color.a > color.a) {
|
||||
limit = clamp(limit, 0.0, abs(color.a) * 2.0);
|
||||
}
|
||||
|
||||
limit -= DEPTH_GAP;
|
||||
|
||||
float m = smoothstep(radius - 0.5, radius + 0.5, limit);
|
||||
color_accum += mix(color_accum / accum, sample_color, m);
|
||||
accum += 1.0;
|
||||
|
||||
radius += params.blur_scale / radius;
|
||||
}
|
||||
|
||||
color_accum = color_accum / accum;
|
||||
|
||||
frag_color.rgb = color_accum.rgb;
|
||||
frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
|
||||
#ifdef OUTPUT_WEIGHT
|
||||
weight = color_accum.a;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_COMPOSITE_BOKEH
|
||||
frag_color.rgb = texture(source_color, uv).rgb;
|
||||
|
||||
float center_weigth = texture(source_weight, uv).r;
|
||||
float sample_weight = texture(original_weight, uv).r;
|
||||
|
||||
float mix_amount;
|
||||
if (sample_weight < center_weigth) {
|
||||
mix_amount = min(1.0, max(0.0, max(abs(center_weigth), abs(sample_weight)) - DEPTH_GAP));
|
||||
} else {
|
||||
mix_amount = min(1.0, max(0.0, abs(center_weigth) - DEPTH_GAP));
|
||||
}
|
||||
|
||||
// let alpha blending take care of mixing
|
||||
frag_color.a = mix_amount;
|
||||
#endif
|
||||
}
|
||||
285
engine/servers/rendering/renderer_rd/shaders/effects/copy.glsl
Normal file
285
engine/servers/rendering/renderer_rd/shaders/effects/copy.glsl
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
#[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_ALPHA_TO_ONE (1 << 8)
|
||||
|
||||
layout(push_constant, 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_scale;
|
||||
// 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(rgba16f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
|
||||
#endif
|
||||
|
||||
#ifdef MODE_GAUSSIAN_BLUR
|
||||
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_BLUR // Gaussian blur 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;
|
||||
color = mix(color, vec4(100.0, 100.0, 100.0, 1.0), isinf(color));
|
||||
color = mix(color, vec4(100.0, 100.0, 100.0, 1.0), isnan(color));
|
||||
|
||||
imageStore(dest_buffer, pos + params.target, color);
|
||||
#endif
|
||||
|
||||
#ifdef MODE_GAUSSIAN_BLUR
|
||||
|
||||
// First pass copy texture into 16x16 local memory for every 8x8 thread block
|
||||
vec2 quad_center_uv = clamp(vec2(params.section.xy + 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;
|
||||
|
||||
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);
|
||||
|
||||
#ifdef MODE_GLOW
|
||||
if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) {
|
||||
// Tonemap initial samples to reduce weight of fireflies: https://graphicrants.blogspot.com/2013/12/tone-mapping.html
|
||||
vec3 tonemap_col = vec3(0.299, 0.587, 0.114) / max(params.glow_luminance_cap, 6.0);
|
||||
local_cache[dest_index] /= 1.0 + dot(local_cache[dest_index].rgb, tonemap_col);
|
||||
local_cache[dest_index + 1] /= 1.0 + dot(local_cache[dest_index + 1].rgb, tonemap_col);
|
||||
local_cache[dest_index + 16] /= 1.0 + dot(local_cache[dest_index + 16].rgb, tonemap_col);
|
||||
local_cache[dest_index + 16 + 1] /= 1.0 + dot(local_cache[dest_index + 16 + 1].rgb, tonemap_col);
|
||||
}
|
||||
const float kernel[5] = { 0.2024, 0.1790, 0.1240, 0.0672, 0.0285 };
|
||||
#else
|
||||
// Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect.
|
||||
const float kernel[4] = { 0.214607, 0.189879, 0.131514, 0.071303 };
|
||||
#endif
|
||||
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] * kernel[0];
|
||||
color_top += local_cache[read_index + 1] * kernel[1];
|
||||
color_top += local_cache[read_index + 2] * kernel[2];
|
||||
color_top += local_cache[read_index + 3] * kernel[3];
|
||||
color_top += local_cache[read_index - 1] * kernel[1];
|
||||
color_top += local_cache[read_index - 2] * kernel[2];
|
||||
color_top += local_cache[read_index - 3] * kernel[3];
|
||||
#ifdef MODE_GLOW
|
||||
color_top += local_cache[read_index + 4] * kernel[4];
|
||||
color_top += local_cache[read_index - 4] * kernel[4];
|
||||
#endif // MODE_GLOW
|
||||
|
||||
vec4 color_bottom = vec4(0.0);
|
||||
color_bottom += local_cache[read_index + 16] * kernel[0];
|
||||
color_bottom += local_cache[read_index + 1 + 16] * kernel[1];
|
||||
color_bottom += local_cache[read_index + 2 + 16] * kernel[2];
|
||||
color_bottom += local_cache[read_index + 3 + 16] * kernel[3];
|
||||
color_bottom += local_cache[read_index - 1 + 16] * kernel[1];
|
||||
color_bottom += local_cache[read_index - 2 + 16] * kernel[2];
|
||||
color_bottom += local_cache[read_index - 3 + 16] * kernel[3];
|
||||
#ifdef MODE_GLOW
|
||||
color_bottom += local_cache[read_index + 4 + 16] * kernel[4];
|
||||
color_bottom += local_cache[read_index - 4 + 16] * kernel[4];
|
||||
#endif // MODE_GLOW
|
||||
|
||||
// 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();
|
||||
|
||||
// If destination outside of texture, can stop doing work now
|
||||
if (any(greaterThanEqual(pos, params.section.zw))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Vertical pass
|
||||
uint index = gl_LocalInvocationID.y + gl_LocalInvocationID.x * 16 + 4;
|
||||
vec4 color = vec4(0.0);
|
||||
|
||||
color += temp_cache[index] * kernel[0];
|
||||
color += temp_cache[index + 1] * kernel[1];
|
||||
color += temp_cache[index + 2] * kernel[2];
|
||||
color += temp_cache[index + 3] * kernel[3];
|
||||
color += temp_cache[index - 1] * kernel[1];
|
||||
color += temp_cache[index - 2] * kernel[2];
|
||||
color += temp_cache[index - 3] * kernel[3];
|
||||
#ifdef MODE_GLOW
|
||||
color += temp_cache[index + 4] * kernel[4];
|
||||
color += temp_cache[index - 4] * kernel[4];
|
||||
#endif // MODE_GLOW
|
||||
|
||||
#ifdef MODE_GLOW
|
||||
if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) {
|
||||
// Undo tonemap to restore range: https://graphicrants.blogspot.com/2013/12/tone-mapping.html
|
||||
color /= 1.0 - dot(color.rgb, vec3(0.299, 0.587, 0.114) / max(params.glow_luminance_cap, 6.0));
|
||||
}
|
||||
|
||||
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_scale;
|
||||
#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));
|
||||
}
|
||||
#endif // MODE_GLOW
|
||||
imageStore(dest_buffer, pos + params.target, color);
|
||||
|
||||
#endif // MODE_GAUSSIAN_BLUR
|
||||
|
||||
#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 // MODE_SIMPLE_COPY
|
||||
|
||||
#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 // MODE_SIMPLE_COPY_DEPTH
|
||||
|
||||
#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 // MODE_LINEARIZE_DEPTH_COPY
|
||||
|
||||
#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);
|
||||
if (bool(params.flags & FLAG_FLIP_Y)) {
|
||||
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 // defined(MODE_CUBEMAP_TO_PANORAMA) || defined(MODE_CUBEMAP_ARRAY_TO_PANORAMA)
|
||||
|
||||
#ifdef MODE_SET_COLOR
|
||||
imageStore(dest_buffer, pos + params.target, params.set_color);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#extension GL_EXT_multiview : enable
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#define FLAG_FLIP_Y (1 << 0)
|
||||
#define FLAG_USE_SECTION (1 << 1)
|
||||
#define FLAG_FORCE_LUMINANCE (1 << 2)
|
||||
#define FLAG_ALPHA_TO_ZERO (1 << 3)
|
||||
#define FLAG_SRGB (1 << 4)
|
||||
#define FLAG_ALPHA_TO_ONE (1 << 5)
|
||||
#define FLAG_LINEAR (1 << 6)
|
||||
#define FLAG_NORMAL (1 << 7)
|
||||
#define FLAG_USE_SRC_SECTION (1 << 8)
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(location = 0) out vec3 uv_interp;
|
||||
#else
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
#endif
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec4 section;
|
||||
vec2 pixel_size;
|
||||
float luminance_multiplier;
|
||||
uint flags;
|
||||
|
||||
vec4 color;
|
||||
}
|
||||
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.xy = base_arr[gl_VertexIndex];
|
||||
#ifdef USE_MULTIVIEW
|
||||
uv_interp.z = ViewIndex;
|
||||
#endif
|
||||
vec2 vpos = uv_interp.xy;
|
||||
if (bool(params.flags & FLAG_USE_SECTION)) {
|
||||
vpos = params.section.xy + vpos * params.section.zw;
|
||||
}
|
||||
|
||||
gl_Position = vec4(vpos * 2.0 - 1.0, 0.0, 1.0);
|
||||
|
||||
if (bool(params.flags & FLAG_FLIP_Y)) {
|
||||
uv_interp.y = 1.0 - uv_interp.y;
|
||||
}
|
||||
|
||||
if (bool(params.flags & FLAG_USE_SRC_SECTION)) {
|
||||
uv_interp.xy = params.section.xy + uv_interp.xy * params.section.zw;
|
||||
}
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#extension GL_EXT_multiview : enable
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#define FLAG_FLIP_Y (1 << 0)
|
||||
#define FLAG_USE_SECTION (1 << 1)
|
||||
#define FLAG_FORCE_LUMINANCE (1 << 2)
|
||||
#define FLAG_ALPHA_TO_ZERO (1 << 3)
|
||||
#define FLAG_SRGB (1 << 4)
|
||||
#define FLAG_ALPHA_TO_ONE (1 << 5)
|
||||
#define FLAG_LINEAR (1 << 6)
|
||||
#define FLAG_NORMAL (1 << 7)
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec4 section;
|
||||
vec2 pixel_size;
|
||||
float luminance_multiplier;
|
||||
uint flags;
|
||||
|
||||
vec4 color;
|
||||
}
|
||||
params;
|
||||
|
||||
#ifndef MODE_SET_COLOR
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(location = 0) in vec3 uv_interp;
|
||||
#else
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
#endif
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(set = 0, binding = 0) uniform sampler2DArray source_color;
|
||||
#ifdef MODE_TWO_SOURCES
|
||||
layout(set = 1, binding = 0) uniform sampler2DArray source_depth;
|
||||
layout(location = 1) out float depth;
|
||||
#endif /* MODE_TWO_SOURCES */
|
||||
#else /* USE_MULTIVIEW */
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
#ifdef MODE_TWO_SOURCES
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_color2;
|
||||
#endif /* MODE_TWO_SOURCES */
|
||||
#endif /* USE_MULTIVIEW */
|
||||
#endif /* !SET_COLOR */
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
vec3 srgb_to_linear(vec3 color) {
|
||||
return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045)));
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef MODE_SET_COLOR
|
||||
frag_color = params.color;
|
||||
#else
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec3 uv = uv_interp;
|
||||
#else
|
||||
vec2 uv = uv_interp;
|
||||
#endif
|
||||
|
||||
#ifdef MODE_PANORAMA_TO_DP
|
||||
// Note, multiview and panorama should not be mixed at this time
|
||||
|
||||
//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 /* MODE_PANORAMA_TO_DP */
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec4 color = textureLod(source_color, uv, 0.0);
|
||||
#ifdef MODE_TWO_SOURCES
|
||||
// In multiview our 2nd input will be our depth map
|
||||
depth = textureLod(source_depth, uv, 0.0).r;
|
||||
#endif /* MODE_TWO_SOURCES */
|
||||
|
||||
#else /* USE_MULTIVIEW */
|
||||
vec4 color = textureLod(source_color, uv, 0.0);
|
||||
#ifdef MODE_TWO_SOURCES
|
||||
color += textureLod(source_color2, uv, 0.0);
|
||||
#endif /* MODE_TWO_SOURCES */
|
||||
#endif /* USE_MULTIVIEW */
|
||||
|
||||
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_ZERO)) {
|
||||
color.rgb *= color.a;
|
||||
}
|
||||
if (bool(params.flags & FLAG_SRGB)) {
|
||||
color.rgb = linear_to_srgb(color.rgb);
|
||||
}
|
||||
if (bool(params.flags & FLAG_ALPHA_TO_ONE)) {
|
||||
color.a = 1.0;
|
||||
}
|
||||
if (bool(params.flags & FLAG_LINEAR)) {
|
||||
color.rgb = srgb_to_linear(color.rgb);
|
||||
}
|
||||
if (bool(params.flags & FLAG_NORMAL)) {
|
||||
color.rgb = normalize(color.rgb * 2.0 - 1.0) * 0.5 + 0.5;
|
||||
}
|
||||
|
||||
frag_color = color / params.luminance_multiplier;
|
||||
#endif // MODE_SET_COLOR
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float z_far;
|
||||
float z_near;
|
||||
vec2 texel_size;
|
||||
vec4 screen_rect;
|
||||
}
|
||||
params;
|
||||
|
||||
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];
|
||||
vec2 screen_pos = uv_interp * params.screen_rect.zw + params.screen_rect.xy;
|
||||
gl_Position = vec4(screen_pos * 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 samplerCube source_cube;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float z_far;
|
||||
float z_near;
|
||||
vec2 texel_size;
|
||||
vec4 screen_rect;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
vec2 uv = uv_interp;
|
||||
vec2 texel_size = abs(params.texel_size);
|
||||
|
||||
uv = clamp(uv * (1.0 + 2.0 * texel_size) - texel_size, vec2(0.0), vec2(1.0));
|
||||
|
||||
vec3 normal = vec3(uv * 2.0 - 1.0, 0.0);
|
||||
normal.z = 0.5 * (1.0 - dot(normal.xy, normal.xy)); // z = 1/2 - 1/2 * (x^2 + y^2)
|
||||
normal = normalize(normal);
|
||||
|
||||
normal.y = -normal.y; //needs to be flipped to match projection matrix
|
||||
if (params.texel_size.x >= 0.0) { // Sign is used to encode 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));
|
||||
// linear_depth equal to view space depth
|
||||
depth = (params.z_far - linear_depth * depth_fix) / params.z_far;
|
||||
gl_FragDepth = depth;
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
// 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;
|
||||
|
||||
#include "cubemap_downsampler_inc.glsl"
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
layout(push_constant, std430) uniform Params {
|
||||
uint face_size;
|
||||
uint face_id; // only used in raster shader
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
// 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.
|
||||
|
||||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "cubemap_downsampler_inc.glsl"
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0 * float(params.face_size); // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "cubemap_downsampler_inc.glsl"
|
||||
|
||||
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
// Converted from compute shader which uses absolute coordinates.
|
||||
// Could possibly simplify this
|
||||
float face_size = float(params.face_size);
|
||||
float inv_face_size = 1.0 / face_size;
|
||||
vec2 id = floor(uv_interp);
|
||||
|
||||
float u1 = (id.x * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0;
|
||||
float u0 = (id.x * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0;
|
||||
|
||||
float v0 = (id.y * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0;
|
||||
float v1 = (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 (params.face_id) {
|
||||
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;
|
||||
}
|
||||
frag_color = color;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
// 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.
|
||||
|
||||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
int mip_level;
|
||||
uint face_id;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
int mip_level;
|
||||
uint face_id;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#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() {
|
||||
// determine dir / pos for the texel
|
||||
vec3 dir, adir, frameZ;
|
||||
{
|
||||
vec2 uv;
|
||||
uv.x = uv_interp.x;
|
||||
uv.y = 1.0 - uv_interp.y;
|
||||
uv = uv * 2.0 - 1.0;
|
||||
|
||||
get_dir(dir, uv, params.face_id);
|
||||
frameZ = normalize(dir);
|
||||
|
||||
adir = abs(dir);
|
||||
}
|
||||
|
||||
// determine which texel this is
|
||||
// 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 (params.mip_level < 0) {
|
||||
// return as is
|
||||
frag_color.rgb = textureLod(source_cubemap, frameZ, 0.0).rgb;
|
||||
frag_color.a = 1.0;
|
||||
return;
|
||||
} else if (params.mip_level > 6) {
|
||||
// maximum level
|
||||
mip_level = 6;
|
||||
} else {
|
||||
mip_level = params.mip_level;
|
||||
}
|
||||
|
||||
// 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));
|
||||
// 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;
|
||||
|
||||
frag_color = color;
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
#[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;
|
||||
|
||||
#include "cubemap_roughness_inc.glsl"
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
float solid_angle_texel = 4.0 * M_PI / (6.0 * params.face_size * params.face_size);
|
||||
float roughness2 = params.roughness * params.roughness;
|
||||
float roughness4 = roughness2 * roughness2;
|
||||
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||
mat3 T;
|
||||
T[0] = normalize(cross(UpVector, N));
|
||||
T[1] = cross(N, T[0]);
|
||||
T[2] = N;
|
||||
|
||||
for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) {
|
||||
vec2 xi = Hammersley(sampleNum, params.sample_count);
|
||||
|
||||
vec3 H = T * ImportanceSampleGGX(xi, roughness4);
|
||||
float NdotH = dot(N, H);
|
||||
vec3 L = (2.0 * NdotH * H - N);
|
||||
|
||||
float ndotl = clamp(dot(N, L), 0.0, 1.0);
|
||||
|
||||
if (ndotl > 0.0) {
|
||||
float D = DistributionGGX(NdotH, roughness4);
|
||||
float pdf = D * NdotH / (4.0 * NdotH) + 0.0001;
|
||||
|
||||
float solid_angle_sample = 1.0 / (float(params.sample_count) * pdf + 0.0001);
|
||||
|
||||
float mipLevel = params.roughness == 0.0 ? 0.0 : 0.5 * log2(solid_angle_sample / solid_angle_texel);
|
||||
|
||||
sum.rgb += textureLod(source_cube, L, mipLevel).rgb * ndotl;
|
||||
sum.a += ndotl;
|
||||
}
|
||||
}
|
||||
sum /= sum.a;
|
||||
|
||||
imageStore(dest_cubemap, ivec3(id), vec4(sum.rgb, 1.0));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
#define M_PI 3.14159265359
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
uint face_id;
|
||||
uint sample_count;
|
||||
float roughness;
|
||||
bool use_direct_write;
|
||||
float face_size;
|
||||
}
|
||||
params;
|
||||
|
||||
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 roughness4) {
|
||||
// Compute distribution direction
|
||||
float Phi = 2.0 * M_PI * xi.x;
|
||||
float CosTheta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 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;
|
||||
|
||||
return H;
|
||||
}
|
||||
|
||||
float DistributionGGX(float NdotH, float roughness4) {
|
||||
float NdotH2 = NdotH * NdotH;
|
||||
float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
|
||||
denom = M_PI * denom * denom;
|
||||
|
||||
return roughness4 / denom;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "cubemap_roughness_inc.glsl"
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "cubemap_roughness_inc.glsl"
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
|
||||
layout(set = 0, binding = 0) uniform samplerCube source_cube;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec3 N = texelCoordToVec(uv_interp * 2.0 - 1.0, params.face_id);
|
||||
|
||||
//vec4 color = color_interp;
|
||||
|
||||
if (params.use_direct_write) {
|
||||
frag_color = vec4(texture(source_cube, N).rgb, 1.0);
|
||||
} else {
|
||||
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
float solid_angle_texel = 4.0 * M_PI / (6.0 * params.face_size * params.face_size);
|
||||
float roughness2 = params.roughness * params.roughness;
|
||||
float roughness4 = roughness2 * roughness2;
|
||||
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||
mat3 T;
|
||||
T[0] = normalize(cross(UpVector, N));
|
||||
T[1] = cross(N, T[0]);
|
||||
T[2] = N;
|
||||
|
||||
for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) {
|
||||
vec2 xi = Hammersley(sampleNum, params.sample_count);
|
||||
|
||||
vec3 H = T * ImportanceSampleGGX(xi, roughness4);
|
||||
float NdotH = dot(N, H);
|
||||
vec3 L = (2.0 * NdotH * H - N);
|
||||
|
||||
float ndotl = clamp(dot(N, L), 0.0, 1.0);
|
||||
|
||||
if (ndotl > 0.0) {
|
||||
float D = DistributionGGX(NdotH, roughness4);
|
||||
float pdf = D * NdotH / (4.0 * NdotH) + 0.0001;
|
||||
|
||||
float solid_angle_sample = 1.0 / (float(params.sample_count) * pdf + 0.0001);
|
||||
|
||||
float mipLevel = params.roughness == 0.0 ? 0.0 : 0.5 * log2(solid_angle_sample / solid_angle_texel);
|
||||
|
||||
sum.rgb += textureLod(source_cube, L, mipLevel).rgb * ndotl;
|
||||
sum.a += ndotl;
|
||||
}
|
||||
}
|
||||
sum /= sum.a;
|
||||
|
||||
frag_color = vec4(sum.rgb, 1.0);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
if "RD_GLSL" in env["BUILDERS"]:
|
||||
# find all include files
|
||||
gl_include_files = [str(f) for f in Glob("*_inc.glsl")] + [str(f) for f in Glob("../*_inc.glsl")]
|
||||
|
||||
# Add all FSR2 shader and header files.
|
||||
fsr2_dir = "#thirdparty/amd-fsr2/shaders"
|
||||
gl_include_files += [str(f) for f in Glob(fsr2_dir + "/*.h")]
|
||||
gl_include_files += [str(f) for f in Glob(fsr2_dir + "/*.glsl")]
|
||||
|
||||
# find all shader code(all glsl files excluding our include files)
|
||||
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
|
||||
|
||||
# make sure we recompile shaders if include files change
|
||||
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#glsl_builders.py"])
|
||||
|
||||
# compile shaders
|
||||
for glsl_file in glsl_files:
|
||||
env.RD_GLSL(glsl_file)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "../motion_vector_inc.glsl"
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_accumulate_pass.glsl"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "../motion_vector_inc.glsl"
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_autogen_reactive_pass.glsl"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_compute_luminance_pyramid_pass.glsl"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "../motion_vector_inc.glsl"
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_depth_clip_pass.glsl"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_lock_pass.glsl"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_rcas_pass.glsl"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "../motion_vector_inc.glsl"
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_reconstruct_previous_depth_pass.glsl"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "../motion_vector_inc.glsl"
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_tcr_autogen_pass.glsl"
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
/**************************************************************************/
|
||||
/* fsr_upscale.glsl */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 A_GPU
|
||||
#define A_GLSL
|
||||
|
||||
#ifdef MODE_FSR_UPSCALE_NORMAL
|
||||
|
||||
#define A_HALF
|
||||
|
||||
#endif
|
||||
|
||||
#include "thirdparty/amd-fsr/ffx_a.h"
|
||||
|
||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D fsr_image;
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_image;
|
||||
|
||||
#define FSR_UPSCALE_PASS_TYPE_EASU 0
|
||||
#define FSR_UPSCALE_PASS_TYPE_RCAS 1
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float resolution_width;
|
||||
float resolution_height;
|
||||
float upscaled_width;
|
||||
float upscaled_height;
|
||||
float sharpness;
|
||||
int pass;
|
||||
}
|
||||
params;
|
||||
|
||||
AU4 Const0, Const1, Const2, Const3;
|
||||
|
||||
#ifdef MODE_FSR_UPSCALE_FALLBACK
|
||||
|
||||
#define FSR_EASU_F
|
||||
AF4 FsrEasuRF(AF2 p) {
|
||||
AF4 res = textureGather(source_image, p, 0);
|
||||
return res;
|
||||
}
|
||||
AF4 FsrEasuGF(AF2 p) {
|
||||
AF4 res = textureGather(source_image, p, 1);
|
||||
return res;
|
||||
}
|
||||
AF4 FsrEasuBF(AF2 p) {
|
||||
AF4 res = textureGather(source_image, p, 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
#define FSR_RCAS_F
|
||||
AF4 FsrRcasLoadF(ASU2 p) {
|
||||
return AF4(texelFetch(source_image, ASU2(p), 0));
|
||||
}
|
||||
void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
|
||||
|
||||
#else
|
||||
|
||||
#define FSR_EASU_H
|
||||
AH4 FsrEasuRH(AF2 p) {
|
||||
AH4 res = AH4(textureGather(source_image, p, 0));
|
||||
return res;
|
||||
}
|
||||
AH4 FsrEasuGH(AF2 p) {
|
||||
AH4 res = AH4(textureGather(source_image, p, 1));
|
||||
return res;
|
||||
}
|
||||
AH4 FsrEasuBH(AF2 p) {
|
||||
AH4 res = AH4(textureGather(source_image, p, 2));
|
||||
return res;
|
||||
}
|
||||
|
||||
#define FSR_RCAS_H
|
||||
AH4 FsrRcasLoadH(ASW2 p) {
|
||||
return AH4(texelFetch(source_image, ASU2(p), 0));
|
||||
}
|
||||
void FsrRcasInputH(inout AH1 r, inout AH1 g, inout AH1 b) {}
|
||||
|
||||
#endif
|
||||
|
||||
#include "thirdparty/amd-fsr/ffx_fsr1.h"
|
||||
|
||||
void fsr_easu_pass(AU2 pos) {
|
||||
#ifdef MODE_FSR_UPSCALE_NORMAL
|
||||
|
||||
AH3 Gamma2Color = AH3(0, 0, 0);
|
||||
FsrEasuH(Gamma2Color, pos, Const0, Const1, Const2, Const3);
|
||||
imageStore(fsr_image, ASU2(pos), AH4(Gamma2Color, 1));
|
||||
|
||||
#else
|
||||
|
||||
AF3 Gamma2Color = AF3(0, 0, 0);
|
||||
FsrEasuF(Gamma2Color, pos, Const0, Const1, Const2, Const3);
|
||||
imageStore(fsr_image, ASU2(pos), AF4(Gamma2Color, 1));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void fsr_rcas_pass(AU2 pos) {
|
||||
#ifdef MODE_FSR_UPSCALE_NORMAL
|
||||
|
||||
AH3 Gamma2Color = AH3(0, 0, 0);
|
||||
FsrRcasH(Gamma2Color.r, Gamma2Color.g, Gamma2Color.b, pos, Const0);
|
||||
imageStore(fsr_image, ASU2(pos), AH4(Gamma2Color, 1));
|
||||
|
||||
#else
|
||||
|
||||
AF3 Gamma2Color = AF3(0, 0, 0);
|
||||
FsrRcasF(Gamma2Color.r, Gamma2Color.g, Gamma2Color.b, pos, Const0);
|
||||
imageStore(fsr_image, ASU2(pos), AF4(Gamma2Color, 1));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void fsr_pass(AU2 pos) {
|
||||
if (params.pass == FSR_UPSCALE_PASS_TYPE_EASU) {
|
||||
fsr_easu_pass(pos);
|
||||
} else if (params.pass == FSR_UPSCALE_PASS_TYPE_RCAS) {
|
||||
fsr_rcas_pass(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Clang does not like unused functions. If ffx_a.h is included in the binary, clang will throw a fit and not compile so we must configure FSR in this shader
|
||||
if (params.pass == FSR_UPSCALE_PASS_TYPE_EASU) {
|
||||
FsrEasuCon(Const0, Const1, Const2, Const3, params.resolution_width, params.resolution_height, params.resolution_width, params.resolution_height, params.upscaled_width, params.upscaled_height);
|
||||
} else if (params.pass == FSR_UPSCALE_PASS_TYPE_RCAS) {
|
||||
FsrRcasCon(Const0, params.sharpness);
|
||||
}
|
||||
|
||||
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u);
|
||||
|
||||
fsr_pass(gxy);
|
||||
gxy.x += 8u;
|
||||
fsr_pass(gxy);
|
||||
gxy.y += 8u;
|
||||
fsr_pass(gxy);
|
||||
gxy.x -= 8u;
|
||||
fsr_pass(gxy);
|
||||
}
|
||||
|
|
@ -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, 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));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "luminance_reduce_raster_inc.glsl"
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "luminance_reduce_raster_inc.glsl"
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_exposure;
|
||||
|
||||
#ifdef FINAL_PASS
|
||||
layout(set = 1, binding = 0) uniform sampler2D prev_luminance;
|
||||
#endif
|
||||
|
||||
layout(location = 0) out highp float luminance;
|
||||
|
||||
void main() {
|
||||
ivec2 dest_pos = ivec2(uv_interp * settings.dest_size);
|
||||
ivec2 src_pos = ivec2(uv_interp * settings.source_size);
|
||||
|
||||
ivec2 next_pos = (dest_pos + ivec2(1)) * settings.source_size / settings.dest_size;
|
||||
next_pos = max(next_pos, src_pos + ivec2(1)); //so it at least reads one pixel
|
||||
|
||||
highp vec3 source_color = vec3(0.0);
|
||||
for (int i = src_pos.x; i < next_pos.x; i++) {
|
||||
for (int j = src_pos.y; j < next_pos.y; j++) {
|
||||
source_color += texelFetch(source_exposure, ivec2(i, j), 0).rgb;
|
||||
}
|
||||
}
|
||||
|
||||
source_color /= float((next_pos.x - src_pos.x) * (next_pos.y - src_pos.y));
|
||||
|
||||
#ifdef FIRST_PASS
|
||||
luminance = max(source_color.r, max(source_color.g, source_color.b));
|
||||
|
||||
// This formula should be more "accurate" but gave an overexposed result when testing.
|
||||
// Leaving it here so we can revisit it if we want.
|
||||
// luminance = source_color.r * 0.21 + source_color.g * 0.71 + source_color.b * 0.07;
|
||||
#else
|
||||
luminance = source_color.r;
|
||||
#endif
|
||||
|
||||
#ifdef FINAL_PASS
|
||||
// Obtain our target luminance
|
||||
luminance = clamp(luminance, settings.min_luminance, settings.max_luminance);
|
||||
|
||||
// Now smooth to our transition
|
||||
highp float prev_lum = texelFetch(prev_luminance, ivec2(0, 0), 0).r; //1 pixel previous luminance
|
||||
luminance = prev_lum + (luminance - prev_lum) * clamp(settings.exposure_adjust, 0.0, 1.0);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
layout(push_constant, std430) uniform PushConstant {
|
||||
ivec2 source_size;
|
||||
ivec2 dest_size;
|
||||
|
||||
float exposure_adjust;
|
||||
float min_luminance;
|
||||
float max_luminance;
|
||||
uint pad1;
|
||||
}
|
||||
settings;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
vec2 derive_motion_vector(vec2 uv, float depth, mat4 reprojection_matrix) {
|
||||
vec4 previous_pos_ndc = reprojection_matrix * vec4(uv * 2.0f - 1.0f, depth * 2.0f - 1.0f, 1.0f);
|
||||
return 0.5f + (previous_pos_ndc.xy / previous_pos_ndc.w) * 0.5f - uv;
|
||||
}
|
||||
|
||||
#define FFX_FSR2_OPTION_GODOT_DERIVE_INVALID_MOTION_VECTORS_FUNCTION(i, j, k) derive_motion_vector(i, j, k)
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "motion_vector_inc.glsl"
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_velocity;
|
||||
layout(set = 0, binding = 1) uniform sampler2D source_depth;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
highp mat4 reprojection_matrix;
|
||||
vec2 resolution;
|
||||
bool force_derive_from_depth;
|
||||
}
|
||||
params;
|
||||
|
||||
// Based on distance to line segment from https://www.shadertoy.com/view/3tdSDj
|
||||
|
||||
float line_segment(in vec2 p, in vec2 a, in vec2 b) {
|
||||
vec2 aspect = vec2(params.resolution.x / params.resolution.y, 1.0f);
|
||||
vec2 ba = (b - a) * aspect;
|
||||
vec2 pa = (p - a) * aspect;
|
||||
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0f, 1.0f);
|
||||
return length(pa - h * ba) * (params.resolution.y / 2.0f);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Retrieve motion vector data.
|
||||
float cell_size = 32.0f;
|
||||
float circle_radius = 2.0f;
|
||||
vec3 nan_color = vec3(1.0f, 0.0f, 0.0f);
|
||||
vec3 active_color = vec3(1.0f, 0.8f, 0.1f);
|
||||
vec3 inactive_color = vec3(0.5f, 0.5f, 0.5f);
|
||||
vec2 pos_pixel = uv_interp * params.resolution;
|
||||
vec2 cell_pos_pixel = floor(pos_pixel / cell_size) * cell_size + (cell_size * 0.5f);
|
||||
vec2 cell_pos_uv = cell_pos_pixel / params.resolution;
|
||||
vec2 cell_pos_velocity = textureLod(source_velocity, cell_pos_uv, 0.0f).xy;
|
||||
bool derive_velocity = params.force_derive_from_depth || all(lessThanEqual(cell_pos_velocity, vec2(-1.0f, -1.0f)));
|
||||
if (derive_velocity) {
|
||||
float depth = textureLod(source_depth, cell_pos_uv, 0.0f).x;
|
||||
cell_pos_velocity = derive_motion_vector(cell_pos_uv, depth, params.reprojection_matrix);
|
||||
}
|
||||
|
||||
vec2 cell_pos_previous_uv = cell_pos_uv + cell_pos_velocity;
|
||||
|
||||
// Draw the shapes.
|
||||
float epsilon = 1e-6f;
|
||||
vec2 cell_pos_delta_uv = cell_pos_uv - cell_pos_previous_uv;
|
||||
bool motion_active = length(cell_pos_delta_uv) > epsilon;
|
||||
vec3 color;
|
||||
if (any(isnan(cell_pos_delta_uv))) {
|
||||
color = nan_color;
|
||||
} else if (motion_active) {
|
||||
color = active_color;
|
||||
} else {
|
||||
color = inactive_color;
|
||||
}
|
||||
|
||||
float alpha;
|
||||
if (length(cell_pos_pixel - pos_pixel) <= circle_radius) {
|
||||
// Circle center.
|
||||
alpha = 1.0f;
|
||||
} else if (motion_active) {
|
||||
// Motion vector line.
|
||||
alpha = 1.0f - line_segment(uv_interp, cell_pos_uv, cell_pos_previous_uv);
|
||||
} else {
|
||||
// Ignore pixel.
|
||||
alpha = 0.0f;
|
||||
}
|
||||
|
||||
if (derive_velocity) {
|
||||
color = vec3(1.0f, 1.0f, 1.0f) - color;
|
||||
alpha *= 0.5f;
|
||||
}
|
||||
|
||||
frag_color = vec4(color, alpha);
|
||||
}
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
#ifdef MODE_RESOLVE_DEPTH
|
||||
layout(set = 0, binding = 0) uniform sampler2DMS source_depth;
|
||||
layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_depth;
|
||||
#endif
|
||||
|
||||
#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 VOXEL_GI_RESOLVE
|
||||
layout(set = 2, binding = 0) uniform usampler2DMS source_voxel_gi;
|
||||
layout(rg8ui, set = 3, binding = 0) uniform restrict writeonly uimage2D dest_voxel_gi;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
layout(push_constant, 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_DEPTH
|
||||
|
||||
float depth_avg = 0.0;
|
||||
for (int i = 0; i < params.sample_count; i++) {
|
||||
depth_avg += texelFetch(source_depth, pos, i).r;
|
||||
}
|
||||
depth_avg /= float(params.sample_count);
|
||||
imageStore(dest_depth, pos, vec4(depth_avg));
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_RESOLVE_GI
|
||||
|
||||
float best_depth = 1e20;
|
||||
vec4 best_normal_roughness = vec4(0.0);
|
||||
#ifdef VOXEL_GI_RESOLVE
|
||||
uvec2 best_voxel_gi;
|
||||
#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 VOXEL_GI_RESOLVE
|
||||
best_voxel_gi = texelFetch(source_voxel_gi,pos,i).rg;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#if 1
|
||||
|
||||
vec4 group1;
|
||||
vec4 group2;
|
||||
vec4 group3;
|
||||
vec4 group4;
|
||||
int best_index = 0;
|
||||
|
||||
//2X
|
||||
group1.x = texelFetch(source_depth, pos, 0).r;
|
||||
group1.y = texelFetch(source_depth, pos, 1).r;
|
||||
|
||||
//4X
|
||||
if (params.sample_count >= 4) {
|
||||
group1.z = texelFetch(source_depth, pos, 2).r;
|
||||
group1.w = texelFetch(source_depth, pos, 3).r;
|
||||
}
|
||||
//8X
|
||||
if (params.sample_count >= 8) {
|
||||
group2.x = texelFetch(source_depth, pos, 4).r;
|
||||
group2.y = texelFetch(source_depth, pos, 5).r;
|
||||
group2.z = texelFetch(source_depth, pos, 6).r;
|
||||
group2.w = texelFetch(source_depth, pos, 7).r;
|
||||
}
|
||||
//16X
|
||||
if (params.sample_count >= 16) {
|
||||
group3.x = texelFetch(source_depth, pos, 8).r;
|
||||
group3.y = texelFetch(source_depth, pos, 9).r;
|
||||
group3.z = texelFetch(source_depth, pos, 10).r;
|
||||
group3.w = texelFetch(source_depth, pos, 11).r;
|
||||
|
||||
group4.x = texelFetch(source_depth, pos, 12).r;
|
||||
group4.y = texelFetch(source_depth, pos, 13).r;
|
||||
group4.z = texelFetch(source_depth, pos, 14).r;
|
||||
group4.w = texelFetch(source_depth, pos, 15).r;
|
||||
}
|
||||
|
||||
if (params.sample_count == 2) {
|
||||
best_index = (pos.x & 1) ^ ((pos.y >> 1) & 1); //not much can be done here
|
||||
} else if (params.sample_count == 4) {
|
||||
vec4 freq = vec4(equal(group1, vec4(group1.x)));
|
||||
freq += vec4(equal(group1, vec4(group1.y)));
|
||||
freq += vec4(equal(group1, vec4(group1.z)));
|
||||
freq += vec4(equal(group1, vec4(group1.w)));
|
||||
|
||||
float min_f = freq.x;
|
||||
best_index = 0;
|
||||
if (freq.y < min_f) {
|
||||
best_index = 1;
|
||||
min_f = freq.y;
|
||||
}
|
||||
if (freq.z < min_f) {
|
||||
best_index = 2;
|
||||
min_f = freq.z;
|
||||
}
|
||||
if (freq.w < min_f) {
|
||||
best_index = 3;
|
||||
}
|
||||
} else if (params.sample_count == 8) {
|
||||
vec4 freq0 = vec4(equal(group1, vec4(group1.x)));
|
||||
vec4 freq1 = vec4(equal(group2, vec4(group1.x)));
|
||||
freq0 += vec4(equal(group1, vec4(group1.y)));
|
||||
freq1 += vec4(equal(group2, vec4(group1.y)));
|
||||
freq0 += vec4(equal(group1, vec4(group1.z)));
|
||||
freq1 += vec4(equal(group2, vec4(group1.z)));
|
||||
freq0 += vec4(equal(group1, vec4(group1.w)));
|
||||
freq1 += vec4(equal(group2, vec4(group1.w)));
|
||||
freq0 += vec4(equal(group1, vec4(group2.x)));
|
||||
freq1 += vec4(equal(group2, vec4(group2.x)));
|
||||
freq0 += vec4(equal(group1, vec4(group2.y)));
|
||||
freq1 += vec4(equal(group2, vec4(group2.y)));
|
||||
freq0 += vec4(equal(group1, vec4(group2.z)));
|
||||
freq1 += vec4(equal(group2, vec4(group2.z)));
|
||||
freq0 += vec4(equal(group1, vec4(group2.w)));
|
||||
freq1 += vec4(equal(group2, vec4(group2.w)));
|
||||
|
||||
float min_f0 = freq0.x;
|
||||
int best_index0 = 0;
|
||||
if (freq0.y < min_f0) {
|
||||
best_index0 = 1;
|
||||
min_f0 = freq0.y;
|
||||
}
|
||||
if (freq0.z < min_f0) {
|
||||
best_index0 = 2;
|
||||
min_f0 = freq0.z;
|
||||
}
|
||||
if (freq0.w < min_f0) {
|
||||
best_index0 = 3;
|
||||
min_f0 = freq0.w;
|
||||
}
|
||||
|
||||
float min_f1 = freq1.x;
|
||||
int best_index1 = 4;
|
||||
if (freq1.y < min_f1) {
|
||||
best_index1 = 5;
|
||||
min_f1 = freq1.y;
|
||||
}
|
||||
if (freq1.z < min_f1) {
|
||||
best_index1 = 6;
|
||||
min_f1 = freq1.z;
|
||||
}
|
||||
if (freq1.w < min_f1) {
|
||||
best_index1 = 7;
|
||||
min_f1 = freq1.w;
|
||||
}
|
||||
|
||||
best_index = mix(best_index0, best_index1, min_f0 < min_f1);
|
||||
}
|
||||
|
||||
#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];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
best_depth = texelFetch(source_depth, pos, best_index).r;
|
||||
best_normal_roughness = texelFetch(source_normal_roughness, pos, best_index);
|
||||
#ifdef VOXEL_GI_RESOLVE
|
||||
best_voxel_gi = texelFetch(source_voxel_gi, pos, best_index).rg;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
imageStore(dest_depth, pos, vec4(best_depth));
|
||||
imageStore(dest_normal_roughness, pos, vec4(best_normal_roughness));
|
||||
#ifdef VOXEL_GI_RESOLVE
|
||||
imageStore(dest_voxel_gi, pos, uvec4(best_voxel_gi, 0, 0));
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
@ -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, 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));
|
||||
}
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
#[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, 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 view_index;
|
||||
}
|
||||
params;
|
||||
|
||||
#include "screen_space_reflection_inc.glsl"
|
||||
|
||||
vec2 view_to_screen(vec3 view_pos, out float w) {
|
||||
vec4 projected = scene_data.projection[params.view_index] * 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
|
||||
|
||||
void main() {
|
||||
// Pixel being shaded
|
||||
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
if (any(greaterThanEqual(ssC.xy, params.screen_size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 pixel_size = 1.0 / vec2(params.screen_size);
|
||||
vec2 uv = vec2(ssC.xy) * 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 = normalize(normal_roughness.xyz * 2.0 - 1.0);
|
||||
float roughness = normal_roughness.w;
|
||||
if (roughness > 0.5) {
|
||||
roughness = 1.0 - roughness;
|
||||
}
|
||||
roughness /= (127.0 / 255.0);
|
||||
|
||||
// The roughness cutoff of 0.6 is chosen to match the roughness fadeout from GH-69828.
|
||||
if (roughness > 0.6) {
|
||||
// Do not compute SSR for rough materials to improve performance at the cost of
|
||||
// subtle artifacting.
|
||||
#ifdef MODE_ROUGH
|
||||
imageStore(blur_radius_image, ssC, vec4(0.0));
|
||||
#endif
|
||||
imageStore(ssr_image, ssC, vec4(0.0));
|
||||
return;
|
||||
}
|
||||
|
||||
normal = normalize(normal);
|
||||
normal.y = -normal.y; //because this code reads flipped
|
||||
|
||||
vec3 view_dir;
|
||||
if (sc_multiview) {
|
||||
view_dir = normalize(vertex + scene_data.eye_offset[params.view_index].xyz);
|
||||
} else {
|
||||
view_dir = params.orthogonal ? vec3(0.0, 0.0, -1.0) : 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;
|
||||
}
|
||||
|
||||
////////////////
|
||||
|
||||
// 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 = 1.0 / 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;
|
||||
|
||||
if (ivec2(pos + line_advance - 0.5) == ssC) {
|
||||
// It is possible for rounding to cause our first pixel to check to be the pixel we're reflecting.
|
||||
// Make sure we skip it
|
||||
pos += line_advance;
|
||||
z += z_advance;
|
||||
w += w_advance;
|
||||
}
|
||||
|
||||
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
|
||||
ivec2 test_pos = ivec2(pos - 0.5);
|
||||
depth = imageLoad(source_depth, test_pos).r;
|
||||
if (sc_multiview) {
|
||||
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));
|
||||
depth = -depth;
|
||||
}
|
||||
|
||||
z_from = z_to;
|
||||
z_to = z / w;
|
||||
|
||||
if (depth > z_to) {
|
||||
// Test if our ray is hitting the "right" side of the surface, if not we're likely self reflecting and should skip.
|
||||
vec4 test_normal_roughness = imageLoad(source_normal_roughness, test_pos);
|
||||
vec3 test_normal = test_normal_roughness.xyz * 2.0 - 1.0;
|
||||
test_normal = normalize(test_normal);
|
||||
test_normal.y = -test_normal.y; // Because this code reads flipped.
|
||||
|
||||
if (dot(ray_dir, test_normal) < 0.001) {
|
||||
// if depth was surpassed
|
||||
if (depth <= max(z_to, z_from) + params.depth_tolerance && -depth < params.camera_z_far * 0.95) {
|
||||
// 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 final_pos = pos;
|
||||
|
||||
vec2 margin = vec2((params.screen_size.x + params.screen_size.y) * 0.05); // make a uniform margin
|
||||
if (any(bvec4(lessThan(pos, vec2(0.0, 0.0)), greaterThan(pos, params.screen_size)))) {
|
||||
// clip at the screen edges
|
||||
imageStore(ssr_image, ssC, vec4(0.0));
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
//blend fading out towards inner margin
|
||||
// 0.5 = midpoint of reflection
|
||||
vec2 margin_grad = mix(params.screen_size - pos, pos, lessThan(pos, params.screen_size * 0.5));
|
||||
margin_blend = smoothstep(0.0, margin.x * margin.y, margin_grad.x * margin_grad.y);
|
||||
//margin_blend = 1.0;
|
||||
}
|
||||
|
||||
// Fade In / Fade Out
|
||||
float grad = (steps_taken + 1.0) / 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;
|
||||
|
||||
// Ensure that precision errors do not introduce any fade. Even if it is just slightly below 1.0,
|
||||
// strong specular light can leak through the reflection.
|
||||
if (fade > 0.999) {
|
||||
fade = 1.0;
|
||||
}
|
||||
|
||||
// This is an ad-hoc term to fade out the SSR as roughness increases. Values used
|
||||
// are meant to match the visual appearance of a ReflectionProbe.
|
||||
float roughness_fade = smoothstep(0.4, 0.7, 1.0 - normal_roughness.w);
|
||||
|
||||
// Schlick term.
|
||||
float metallic = texelFetch(source_metallic, ssC << 1, 0).w;
|
||||
|
||||
// F0 is the reflectance of normally incident light (perpendicular to the surface).
|
||||
// Dielectric materials have a widely accepted default value of 0.04. We assume that metals reflect all light, so their F0 is 1.0.
|
||||
float f0 = mix(0.04, 1.0, metallic);
|
||||
float m = clamp(1.0 - dot(normal, -view_dir), 0.0, 1.0);
|
||||
float m2 = m * m;
|
||||
m = m2 * m2 * m; // pow(m,5)
|
||||
float fresnel_term = f0 + (1.0 - f0) * m; // Fresnel Schlick term.
|
||||
|
||||
// The alpha value of final_color controls the blending with specular light in specular_merge.glsl.
|
||||
// Note that the Fresnel term is multiplied with the RGB color instead of being a part of the alpha value.
|
||||
// There is a key difference:
|
||||
// - multiplying a term with RGB darkens the SSR light without introducing/taking away specular light.
|
||||
// - combining a term into the Alpha value introduces specular light at the expense of the SSR light.
|
||||
vec4 final_color = vec4(imageLoad(source_diffuse, ivec2(final_pos - 0.5)).rgb * fresnel_term, fade * margin_blend * roughness_fade);
|
||||
|
||||
imageStore(ssr_image, ssC, final_color);
|
||||
|
||||
#ifdef MODE_ROUGH
|
||||
|
||||
// if roughness is enabled, do screen space cone tracing
|
||||
float blur_radius = 0.0;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
imageStore(blur_radius_image, ssC, vec4(blur_radius / 255.0)); //stored in r8
|
||||
|
||||
#endif // MODE_ROUGH
|
||||
|
||||
} else {
|
||||
#ifdef MODE_ROUGH
|
||||
imageStore(blur_radius_image, ssC, vec4(0.0));
|
||||
#endif
|
||||
imageStore(ssr_image, ssC, vec4(0.0));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
#[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, std430) uniform Params {
|
||||
vec4 proj_info;
|
||||
|
||||
bool orthogonal;
|
||||
float edge_tolerance;
|
||||
int increment;
|
||||
uint view_index;
|
||||
|
||||
ivec2 screen_size;
|
||||
bool vertical;
|
||||
uint steps;
|
||||
}
|
||||
params;
|
||||
|
||||
#include "screen_space_reflection_inc.glsl"
|
||||
|
||||
#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
|
||||
|
||||
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.xy, 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.xy) + 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.xy, direction, pos, normal, radius);
|
||||
do_filter(accum, accum_radius, divisor, ssC.xy, -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
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
layout(constant_id = 0) const bool sc_multiview = false;
|
||||
|
||||
layout(set = 4, binding = 0, std140) uniform SceneData {
|
||||
mat4x4 projection[2];
|
||||
mat4x4 inv_projection[2];
|
||||
vec4 eye_offset[2];
|
||||
}
|
||||
scene_data;
|
||||
|
||||
vec3 reconstructCSPosition(vec2 screen_pos, float z) {
|
||||
if (sc_multiview) {
|
||||
vec4 pos;
|
||||
pos.xy = (2.0 * vec2(screen_pos) / vec2(params.screen_size)) - 1.0;
|
||||
pos.z = z * 2.0 - 1.0;
|
||||
pos.w = 1.0;
|
||||
|
||||
pos = scene_data.inv_projection[params.view_index] * pos;
|
||||
pos.xyz /= pos.w;
|
||||
|
||||
return pos.xyz;
|
||||
} else {
|
||||
if (params.orthogonal) {
|
||||
return vec3(-(screen_pos.xy * params.proj_info.xy + params.proj_info.zw), z);
|
||||
} else {
|
||||
return vec3((screen_pos.xy * params.proj_info.xy + params.proj_info.zw) * z, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
/* Specialization Constants (Toggles) */
|
||||
|
||||
layout(constant_id = 0) const bool sc_multiview = false;
|
||||
|
||||
/* inputs */
|
||||
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, 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.xy, params.screen_size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
//do not filter, SSR will generate artifacts if this is done
|
||||
|
||||
float divisor = 0.0;
|
||||
vec4 color;
|
||||
float depth;
|
||||
vec4 normal;
|
||||
|
||||
if (params.filtered) {
|
||||
color = vec4(0.0);
|
||||
depth = 0.0;
|
||||
normal = vec4(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;
|
||||
vec4 nr = texelFetch(source_normal, ofs, 0);
|
||||
normal.xyz += normalize(nr.xyz * 2.0 - 1.0);
|
||||
float roughness = normal.w;
|
||||
if (roughness > 0.5) {
|
||||
roughness = 1.0 - roughness;
|
||||
}
|
||||
roughness /= (127.0 / 255.0);
|
||||
normal.w += roughness;
|
||||
|
||||
if (sc_multiview) {
|
||||
// we're doing a full unproject so we need the value as is.
|
||||
depth += d;
|
||||
} else {
|
||||
// unproject our Z value so we can use it directly.
|
||||
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.xyz = normalize(normal.xyz / 4.0) * 0.5 + 0.5;
|
||||
normal.w /= 4.0;
|
||||
normal.w = normal.w * (127.0 / 255.0);
|
||||
} else {
|
||||
ivec2 ofs = ssC << 1;
|
||||
|
||||
color = texelFetch(source_ssr, ofs, 0);
|
||||
depth = texelFetch(source_depth, ofs, 0).r;
|
||||
normal = texelFetch(source_normal, ofs, 0);
|
||||
|
||||
if (!sc_multiview) {
|
||||
// unproject our Z value so we can use it directly.
|
||||
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)) / 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, normal);
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
layout(push_constant, std430) uniform Info {
|
||||
mat4 mvp;
|
||||
vec4 color;
|
||||
}
|
||||
info;
|
||||
|
||||
layout(location = 0) in vec3 vertex_attrib;
|
||||
|
||||
void main() {
|
||||
vec4 vertex = info.mvp * vec4(vertex_attrib, 1.0);
|
||||
vertex.xyz /= vertex.w;
|
||||
gl_Position = vec4(vertex.xy, 0.0, 1.0);
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std430) uniform Info {
|
||||
mat4 mvp;
|
||||
vec4 color;
|
||||
}
|
||||
info;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = info.color;
|
||||
}
|
||||
203
engine/servers/rendering/renderer_rd/shaders/effects/sort.glsl
Normal file
203
engine/servers/rendering/renderer_rd/shaders/effects/sort.glsl
Normal 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, 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
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
|
||||
#extension GL_EXT_multiview : enable
|
||||
#endif
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
// !BAS! This needs to become an input once we implement our fallback!
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#else // USE_MULTIVIEW
|
||||
// Set to zero, not supported in non stereo
|
||||
#define ViewIndex 0
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(location = 0) out vec3 uv_interp;
|
||||
#else // USE_MULTIVIEW
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp.xy = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
#ifdef USE_MULTIVIEW
|
||||
uv_interp.z = ViewIndex;
|
||||
#endif
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
|
||||
#extension GL_EXT_multiview : enable
|
||||
#endif
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
// !BAS! This needs to become an input once we implement our fallback!
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#else // USE_MULTIVIEW
|
||||
// Set to zero, not supported in non stereo
|
||||
#define ViewIndex 0
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(location = 0) in vec3 uv_interp;
|
||||
#else // USE_MULTIVIEW
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(set = 0, binding = 0) uniform sampler2DArray specular;
|
||||
#else // USE_MULTIVIEW
|
||||
layout(set = 0, binding = 0) uniform sampler2D specular;
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#ifdef MODE_SSR
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(set = 1, binding = 0) uniform sampler2DArray ssr;
|
||||
#else // USE_MULTIVIEW
|
||||
layout(set = 1, binding = 0) uniform sampler2D ssr;
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_MERGE
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(set = 2, binding = 0) uniform sampler2DArray diffuse;
|
||||
#else // USE_MULTIVIEW
|
||||
layout(set = 2, binding = 0) uniform sampler2D diffuse;
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#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
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) 2016, Intel Corporation
|
||||
// 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.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// File changes (yyyy-mm-dd)
|
||||
// 2016-09-07: filip.strugar@intel.com: first commit
|
||||
// 2020-12-05: clayjohn: convert to Vulkan and Godot
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec2 pixel_size;
|
||||
float z_far;
|
||||
float z_near;
|
||||
bool orthogonal;
|
||||
float radius_sq;
|
||||
uvec2 pad;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_depth;
|
||||
|
||||
layout(r16f, set = 1, binding = 0) uniform restrict writeonly image2DArray dest_image0; //rename
|
||||
#ifdef GENERATE_MIPS
|
||||
layout(r16f, set = 2, binding = 0) uniform restrict writeonly image2DArray dest_image1;
|
||||
layout(r16f, set = 2, binding = 1) uniform restrict writeonly image2DArray dest_image2;
|
||||
layout(r16f, set = 2, binding = 2) uniform restrict writeonly image2DArray dest_image3;
|
||||
#ifdef GENERATE_FULL_MIPS
|
||||
layout(r16f, set = 2, binding = 3) uniform restrict writeonly image2DArray dest_image4;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
vec4 screen_space_to_view_space_depth(vec4 p_depth) {
|
||||
if (params.orthogonal) {
|
||||
vec4 depth = p_depth * 2.0 - 1.0;
|
||||
return -(depth * (params.z_far - params.z_near) - (params.z_far + params.z_near)) / 2.0;
|
||||
}
|
||||
|
||||
float depth_linearize_mul = params.z_near;
|
||||
float depth_linearize_add = params.z_far;
|
||||
|
||||
// Optimized version of "-cameraClipNear / (cameraClipFar - projDepth * (cameraClipFar - cameraClipNear)) * cameraClipFar"
|
||||
|
||||
// Set your depth_linearize_mul and depth_linearize_add to:
|
||||
// depth_linearize_mul = ( cameraClipFar * cameraClipNear) / ( cameraClipFar - cameraClipNear );
|
||||
// depth_linearize_add = cameraClipFar / ( cameraClipFar - cameraClipNear );
|
||||
|
||||
return depth_linearize_mul / (depth_linearize_add - p_depth);
|
||||
}
|
||||
|
||||
float screen_space_to_view_space_depth(float p_depth) {
|
||||
if (params.orthogonal) {
|
||||
float depth = p_depth * 2.0 - 1.0;
|
||||
return -(depth * (params.z_far - params.z_near) - (params.z_far + params.z_near)) / 2.0;
|
||||
}
|
||||
|
||||
float depth_linearize_mul = params.z_near;
|
||||
float depth_linearize_add = params.z_far;
|
||||
|
||||
return depth_linearize_mul / (depth_linearize_add - p_depth);
|
||||
}
|
||||
|
||||
#ifdef GENERATE_MIPS
|
||||
|
||||
shared float depth_buffer[4][8][8];
|
||||
|
||||
float mip_smart_average(vec4 p_depths) {
|
||||
float closest = min(min(p_depths.x, p_depths.y), min(p_depths.z, p_depths.w));
|
||||
float fallof_sq = -1.0f / params.radius_sq;
|
||||
vec4 dists = p_depths - closest.xxxx;
|
||||
vec4 weights = clamp(dists * dists * fallof_sq + 1.0, 0.0, 1.0);
|
||||
return dot(weights, p_depths) / dot(weights, vec4(1.0, 1.0, 1.0, 1.0));
|
||||
}
|
||||
|
||||
void prepare_depths_and_mips(vec4 p_samples, uvec2 p_output_coord, uvec2 p_gtid) {
|
||||
p_samples = screen_space_to_view_space_depth(p_samples);
|
||||
|
||||
depth_buffer[0][p_gtid.x][p_gtid.y] = p_samples.w;
|
||||
depth_buffer[1][p_gtid.x][p_gtid.y] = p_samples.z;
|
||||
depth_buffer[2][p_gtid.x][p_gtid.y] = p_samples.x;
|
||||
depth_buffer[3][p_gtid.x][p_gtid.y] = p_samples.y;
|
||||
|
||||
imageStore(dest_image0, ivec3(p_output_coord.x, p_output_coord.y, 0), vec4(p_samples.w));
|
||||
imageStore(dest_image0, ivec3(p_output_coord.x, p_output_coord.y, 1), vec4(p_samples.z));
|
||||
imageStore(dest_image0, ivec3(p_output_coord.x, p_output_coord.y, 2), vec4(p_samples.x));
|
||||
imageStore(dest_image0, ivec3(p_output_coord.x, p_output_coord.y, 3), vec4(p_samples.y));
|
||||
|
||||
uint depth_array_index = 2 * (p_gtid.y % 2) + (p_gtid.x % 2);
|
||||
uvec2 depth_array_offset = ivec2(p_gtid.x % 2, p_gtid.y % 2);
|
||||
ivec2 buffer_coord = ivec2(p_gtid) - ivec2(depth_array_offset);
|
||||
|
||||
p_output_coord /= 2;
|
||||
groupMemoryBarrier();
|
||||
barrier();
|
||||
|
||||
// if (still_alive) <-- all threads alive here
|
||||
{
|
||||
float sample_00 = depth_buffer[depth_array_index][buffer_coord.x + 0][buffer_coord.y + 0];
|
||||
float sample_01 = depth_buffer[depth_array_index][buffer_coord.x + 0][buffer_coord.y + 1];
|
||||
float sample_10 = depth_buffer[depth_array_index][buffer_coord.x + 1][buffer_coord.y + 0];
|
||||
float sample_11 = depth_buffer[depth_array_index][buffer_coord.x + 1][buffer_coord.y + 1];
|
||||
|
||||
float avg = mip_smart_average(vec4(sample_00, sample_01, sample_10, sample_11));
|
||||
imageStore(dest_image1, ivec3(p_output_coord.x, p_output_coord.y, depth_array_index), vec4(avg));
|
||||
depth_buffer[depth_array_index][buffer_coord.x][buffer_coord.y] = avg;
|
||||
}
|
||||
|
||||
bool still_alive = p_gtid.x % 4 == depth_array_offset.x && p_gtid.y % 4 == depth_array_offset.y;
|
||||
|
||||
p_output_coord /= 2;
|
||||
groupMemoryBarrier();
|
||||
barrier();
|
||||
|
||||
if (still_alive) {
|
||||
float sample_00 = depth_buffer[depth_array_index][buffer_coord.x + 0][buffer_coord.y + 0];
|
||||
float sample_01 = depth_buffer[depth_array_index][buffer_coord.x + 0][buffer_coord.y + 2];
|
||||
float sample_10 = depth_buffer[depth_array_index][buffer_coord.x + 2][buffer_coord.y + 0];
|
||||
float sample_11 = depth_buffer[depth_array_index][buffer_coord.x + 2][buffer_coord.y + 2];
|
||||
|
||||
float avg = mip_smart_average(vec4(sample_00, sample_01, sample_10, sample_11));
|
||||
imageStore(dest_image2, ivec3(p_output_coord.x, p_output_coord.y, depth_array_index), vec4(avg));
|
||||
depth_buffer[depth_array_index][buffer_coord.x][buffer_coord.y] = avg;
|
||||
}
|
||||
|
||||
still_alive = p_gtid.x % 8 == depth_array_offset.x && depth_array_offset.y % 8 == depth_array_offset.y;
|
||||
|
||||
p_output_coord /= 2;
|
||||
groupMemoryBarrier();
|
||||
barrier();
|
||||
|
||||
if (still_alive) {
|
||||
float sample_00 = depth_buffer[depth_array_index][buffer_coord.x + 0][buffer_coord.y + 0];
|
||||
float sample_01 = depth_buffer[depth_array_index][buffer_coord.x + 0][buffer_coord.y + 4];
|
||||
float sample_10 = depth_buffer[depth_array_index][buffer_coord.x + 4][buffer_coord.y + 0];
|
||||
float sample_11 = depth_buffer[depth_array_index][buffer_coord.x + 4][buffer_coord.y + 4];
|
||||
|
||||
float avg = mip_smart_average(vec4(sample_00, sample_01, sample_10, sample_11));
|
||||
imageStore(dest_image3, ivec3(p_output_coord.x, p_output_coord.y, depth_array_index), vec4(avg));
|
||||
#ifndef GENERATE_FULL_MIPS
|
||||
}
|
||||
#else
|
||||
depth_buffer[depth_array_index][buffer_coord.x][buffer_coord.y] = avg;
|
||||
}
|
||||
still_alive = p_gtid.x % 16 == depth_array_offset.x && depth_array_offset.y % 16 == depth_array_offset.y;
|
||||
|
||||
p_output_coord /= 2;
|
||||
|
||||
if (still_alive) {
|
||||
// Use the previous average, not ideal, but still not bad.
|
||||
float sample_00 = depth_buffer[depth_array_index][buffer_coord.x + 0][buffer_coord.y + 0];
|
||||
imageStore(dest_image4, ivec3(p_output_coord.x, p_output_coord.y, depth_array_index), vec4(sample_00));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
#ifndef USE_HALF_BUFFERS
|
||||
void prepare_depths(vec4 p_samples, uvec2 p_tid) {
|
||||
p_samples = screen_space_to_view_space_depth(p_samples);
|
||||
|
||||
imageStore(dest_image0, ivec3(p_tid, 0), vec4(p_samples.w));
|
||||
imageStore(dest_image0, ivec3(p_tid, 1), vec4(p_samples.z));
|
||||
imageStore(dest_image0, ivec3(p_tid, 2), vec4(p_samples.x));
|
||||
imageStore(dest_image0, ivec3(p_tid, 3), vec4(p_samples.y));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
#ifdef USE_HALF_BUFFERS
|
||||
// Half buffers means that we divide depth into two half res buffers (we only capture 1/4 of pixels).
|
||||
#ifdef USE_HALF_SIZE
|
||||
float sample_00 = texelFetch(source_depth, ivec2(4 * gl_GlobalInvocationID.x + 0, 4 * gl_GlobalInvocationID.y + 0), 0).x;
|
||||
float sample_11 = texelFetch(source_depth, ivec2(4 * gl_GlobalInvocationID.x + 2, 4 * gl_GlobalInvocationID.y + 2), 0).x;
|
||||
#else
|
||||
float sample_00 = texelFetch(source_depth, ivec2(2 * gl_GlobalInvocationID.x + 0, 2 * gl_GlobalInvocationID.y + 0), 0).x;
|
||||
float sample_11 = texelFetch(source_depth, ivec2(2 * gl_GlobalInvocationID.x + 1, 2 * gl_GlobalInvocationID.y + 1), 0).x;
|
||||
#endif
|
||||
sample_00 = screen_space_to_view_space_depth(sample_00);
|
||||
sample_11 = screen_space_to_view_space_depth(sample_11);
|
||||
|
||||
imageStore(dest_image0, ivec3(gl_GlobalInvocationID.xy, 0), vec4(sample_00));
|
||||
imageStore(dest_image0, ivec3(gl_GlobalInvocationID.xy, 3), vec4(sample_11));
|
||||
#else //!USE_HALF_BUFFERS
|
||||
#ifdef USE_HALF_SIZE
|
||||
ivec2 depth_buffer_coord = 4 * ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 output_coord = ivec2(gl_GlobalInvocationID);
|
||||
|
||||
vec2 uv = (vec2(depth_buffer_coord) + 0.5f) * params.pixel_size;
|
||||
vec4 samples;
|
||||
samples.x = textureLodOffset(source_depth, uv, 0, ivec2(0, 2)).x;
|
||||
samples.y = textureLodOffset(source_depth, uv, 0, ivec2(2, 2)).x;
|
||||
samples.z = textureLodOffset(source_depth, uv, 0, ivec2(2, 0)).x;
|
||||
samples.w = textureLodOffset(source_depth, uv, 0, ivec2(0, 0)).x;
|
||||
#else
|
||||
ivec2 depth_buffer_coord = 2 * ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 output_coord = ivec2(gl_GlobalInvocationID);
|
||||
|
||||
vec2 uv = (vec2(depth_buffer_coord) + 0.5f) * params.pixel_size;
|
||||
vec4 samples = textureGather(source_depth, uv);
|
||||
#endif //USE_HALF_SIZE
|
||||
#ifdef GENERATE_MIPS
|
||||
prepare_depths_and_mips(samples, output_coord, gl_LocalInvocationID.xy);
|
||||
#else
|
||||
prepare_depths(samples, gl_GlobalInvocationID.xy);
|
||||
#endif
|
||||
#endif //USE_HALF_BUFFERS
|
||||
}
|
||||
478
engine/servers/rendering/renderer_rd/shaders/effects/ssao.glsl
Normal file
478
engine/servers/rendering/renderer_rd/shaders/effects/ssao.glsl
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) 2016, Intel Corporation
|
||||
// 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.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// File changes (yyyy-mm-dd)
|
||||
// 2016-09-07: filip.strugar@intel.com: first commit
|
||||
// 2020-12-05: clayjohn: convert to Vulkan and Godot
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define INTELSSAO_MAIN_DISK_SAMPLE_COUNT (32)
|
||||
const vec4 sample_pattern[INTELSSAO_MAIN_DISK_SAMPLE_COUNT] = {
|
||||
vec4(0.78488064, 0.56661671, 1.500000, -0.126083), vec4(0.26022232, -0.29575172, 1.500000, -1.064030), vec4(0.10459357, 0.08372527, 1.110000, -2.730563), vec4(-0.68286800, 0.04963045, 1.090000, -0.498827),
|
||||
vec4(-0.13570161, -0.64190155, 1.250000, -0.532765), vec4(-0.26193795, -0.08205118, 0.670000, -1.783245), vec4(-0.61177456, 0.66664219, 0.710000, -0.044234), vec4(0.43675563, 0.25119025, 0.610000, -1.167283),
|
||||
vec4(0.07884444, 0.86618668, 0.640000, -0.459002), vec4(-0.12790935, -0.29869005, 0.600000, -1.729424), vec4(-0.04031125, 0.02413622, 0.600000, -4.792042), vec4(0.16201244, -0.52851415, 0.790000, -1.067055),
|
||||
vec4(-0.70991218, 0.47301072, 0.640000, -0.335236), vec4(0.03277707, -0.22349690, 0.600000, -1.982384), vec4(0.68921727, 0.36800742, 0.630000, -0.266718), vec4(0.29251814, 0.37775412, 0.610000, -1.422520),
|
||||
vec4(-0.12224089, 0.96582592, 0.600000, -0.426142), vec4(0.11071457, -0.16131058, 0.600000, -2.165947), vec4(0.46562141, -0.59747696, 0.600000, -0.189760), vec4(-0.51548797, 0.11804193, 0.600000, -1.246800),
|
||||
vec4(0.89141309, -0.42090443, 0.600000, 0.028192), vec4(-0.32402530, -0.01591529, 0.600000, -1.543018), vec4(0.60771245, 0.41635221, 0.600000, -0.605411), vec4(0.02379565, -0.08239821, 0.600000, -3.809046),
|
||||
vec4(0.48951152, -0.23657045, 0.600000, -1.189011), vec4(-0.17611565, -0.81696892, 0.600000, -0.513724), vec4(-0.33930185, -0.20732205, 0.600000, -1.698047), vec4(-0.91974425, 0.05403209, 0.600000, 0.062246),
|
||||
vec4(-0.15064627, -0.14949332, 0.600000, -1.896062), vec4(0.53180975, -0.35210401, 0.600000, -0.758838), vec4(0.41487166, 0.81442589, 0.600000, -0.505648), vec4(-0.24106961, -0.32721516, 0.600000, -1.665244)
|
||||
};
|
||||
|
||||
// these values can be changed (up to SSAO_MAX_TAPS) with no changes required elsewhere; values for 4th and 5th preset are ignored but array needed to avoid compilation errors
|
||||
// the actual number of texture samples is two times this value (each "tap" has two symmetrical depth texture samples)
|
||||
const int num_taps[5] = { 3, 5, 12, 0, 0 };
|
||||
|
||||
#define SSAO_TILT_SAMPLES_ENABLE_AT_QUALITY_PRESET (99) // to disable simply set to 99 or similar
|
||||
#define SSAO_TILT_SAMPLES_AMOUNT (0.4)
|
||||
//
|
||||
#define SSAO_HALOING_REDUCTION_ENABLE_AT_QUALITY_PRESET (1) // to disable simply set to 99 or similar
|
||||
#define SSAO_HALOING_REDUCTION_AMOUNT (0.6) // values from 0.0 - 1.0, 1.0 means max weighting (will cause artifacts, 0.8 is more reasonable)
|
||||
//
|
||||
#define SSAO_NORMAL_BASED_EDGES_ENABLE_AT_QUALITY_PRESET (2) // to disable simply set to 99 or similar
|
||||
#define SSAO_NORMAL_BASED_EDGES_DOT_THRESHOLD (0.5) // use 0-0.1 for super-sharp normal-based edges
|
||||
//
|
||||
#define SSAO_DETAIL_AO_ENABLE_AT_QUALITY_PRESET (1) // whether to use detail; to disable simply set to 99 or similar
|
||||
//
|
||||
#define SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET (2) // !!warning!! the MIP generation on the C++ side will be enabled on quality preset 2 regardless of this value, so if changing here, change the C++ side too
|
||||
#define SSAO_DEPTH_MIPS_GLOBAL_OFFSET (-4.3) // best noise/quality/performance tradeoff, found empirically
|
||||
//
|
||||
// !!warning!! the edge handling is hard-coded to 'disabled' on quality level 0, and enabled above, on the C++ side; while toggling it here will work for
|
||||
// testing purposes, it will not yield performance gains (or correct results)
|
||||
#define SSAO_DEPTH_BASED_EDGES_ENABLE_AT_QUALITY_PRESET (1)
|
||||
//
|
||||
#define SSAO_REDUCE_RADIUS_NEAR_SCREEN_BORDER_ENABLE_AT_QUALITY_PRESET (1)
|
||||
|
||||
#define SSAO_MAX_TAPS 32
|
||||
#define SSAO_ADAPTIVE_TAP_BASE_COUNT 5
|
||||
#define SSAO_ADAPTIVE_TAP_FLEXIBLE_COUNT (SSAO_MAX_TAPS - SSAO_ADAPTIVE_TAP_BASE_COUNT)
|
||||
#define SSAO_DEPTH_MIP_LEVELS 4
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2DArray source_depth_mipmaps;
|
||||
layout(rgba8, set = 0, binding = 1) uniform restrict readonly image2D source_normal;
|
||||
layout(set = 0, binding = 2) uniform Constants { //get into a lower set
|
||||
vec4 rotation_matrices[20];
|
||||
}
|
||||
constants;
|
||||
|
||||
#ifdef ADAPTIVE
|
||||
layout(rg8, set = 1, binding = 0) uniform restrict readonly image2DArray source_ssao;
|
||||
layout(set = 1, binding = 1) uniform sampler2D source_importance;
|
||||
layout(set = 1, binding = 2, std430) buffer Counter {
|
||||
uint sum;
|
||||
}
|
||||
counter;
|
||||
#endif
|
||||
|
||||
layout(rg8, set = 2, binding = 0) uniform restrict writeonly image2D dest_image;
|
||||
|
||||
// This push_constant is full - 128 bytes - if you need to add more data, consider adding to the uniform buffer instead
|
||||
layout(push_constant, std430) uniform Params {
|
||||
ivec2 screen_size;
|
||||
int pass;
|
||||
int quality;
|
||||
|
||||
vec2 half_screen_pixel_size;
|
||||
int size_multiplier;
|
||||
float detail_intensity;
|
||||
|
||||
vec2 NDC_to_view_mul;
|
||||
vec2 NDC_to_view_add;
|
||||
|
||||
vec2 pad2;
|
||||
vec2 half_screen_pixel_size_x025;
|
||||
|
||||
float radius;
|
||||
float intensity;
|
||||
float shadow_power;
|
||||
float shadow_clamp;
|
||||
|
||||
float fade_out_mul;
|
||||
float fade_out_add;
|
||||
float horizon_angle_threshold;
|
||||
float inv_radius_near_limit;
|
||||
|
||||
bool is_orthogonal;
|
||||
float neg_inv_radius;
|
||||
float load_counter_avg_div;
|
||||
float adaptive_sample_limit;
|
||||
|
||||
ivec2 pass_coord_offset;
|
||||
vec2 pass_uv_offset;
|
||||
}
|
||||
params;
|
||||
|
||||
// packing/unpacking for edges; 2 bits per edge mean 4 gradient values (0, 0.33, 0.66, 1) for smoother transitions!
|
||||
float pack_edges(vec4 p_edgesLRTB) {
|
||||
p_edgesLRTB = round(clamp(p_edgesLRTB, 0.0, 1.0) * 3.05);
|
||||
return dot(p_edgesLRTB, vec4(64.0 / 255.0, 16.0 / 255.0, 4.0 / 255.0, 1.0 / 255.0));
|
||||
}
|
||||
|
||||
vec3 NDC_to_view_space(vec2 p_pos, float p_viewspace_depth) {
|
||||
if (params.is_orthogonal) {
|
||||
return vec3((params.NDC_to_view_mul * p_pos.xy + params.NDC_to_view_add), p_viewspace_depth);
|
||||
} else {
|
||||
return vec3((params.NDC_to_view_mul * p_pos.xy + params.NDC_to_view_add) * p_viewspace_depth, p_viewspace_depth);
|
||||
}
|
||||
}
|
||||
|
||||
// calculate effect radius and fit our screen sampling pattern inside it
|
||||
void calculate_radius_parameters(const float p_pix_center_length, const vec2 p_pixel_size_at_center, out float r_lookup_radius, out float r_radius, out float r_fallof_sq) {
|
||||
r_radius = params.radius;
|
||||
|
||||
// when too close, on-screen sampling disk will grow beyond screen size; limit this to avoid closeup temporal artifacts
|
||||
const float too_close_limit = clamp(p_pix_center_length * params.inv_radius_near_limit, 0.0, 1.0) * 0.8 + 0.2;
|
||||
|
||||
r_radius *= too_close_limit;
|
||||
|
||||
// 0.85 is to reduce the radius to allow for more samples on a slope to still stay within influence
|
||||
r_lookup_radius = (0.85 * r_radius) / p_pixel_size_at_center.x;
|
||||
|
||||
// used to calculate falloff (both for AO samples and per-sample weights)
|
||||
r_fallof_sq = -1.0 / (r_radius * r_radius);
|
||||
}
|
||||
|
||||
vec4 calculate_edges(const float p_center_z, const float p_left_z, const float p_right_z, const float p_top_z, const float p_bottom_z) {
|
||||
// slope-sensitive depth-based edge detection
|
||||
vec4 edgesLRTB = vec4(p_left_z, p_right_z, p_top_z, p_bottom_z) - p_center_z;
|
||||
vec4 edgesLRTB_slope_adjusted = edgesLRTB + edgesLRTB.yxwz;
|
||||
edgesLRTB = min(abs(edgesLRTB), abs(edgesLRTB_slope_adjusted));
|
||||
return clamp((1.3 - edgesLRTB / (p_center_z * 0.040)), 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 load_normal(ivec2 p_pos) {
|
||||
vec3 encoded_normal = normalize(imageLoad(source_normal, p_pos).xyz * 2.0 - 1.0);
|
||||
encoded_normal.z = -encoded_normal.z;
|
||||
return encoded_normal;
|
||||
}
|
||||
|
||||
vec3 load_normal(ivec2 p_pos, ivec2 p_offset) {
|
||||
vec3 encoded_normal = normalize(imageLoad(source_normal, p_pos + p_offset).xyz * 2.0 - 1.0);
|
||||
encoded_normal.z = -encoded_normal.z;
|
||||
return encoded_normal;
|
||||
}
|
||||
|
||||
// all vectors in viewspace
|
||||
float calculate_pixel_obscurance(vec3 p_pixel_normal, vec3 p_hit_delta, float p_fallof_sq) {
|
||||
float length_sq = dot(p_hit_delta, p_hit_delta);
|
||||
float NdotD = dot(p_pixel_normal, p_hit_delta) / sqrt(length_sq);
|
||||
|
||||
float falloff_mult = max(0.0, length_sq * p_fallof_sq + 1.0);
|
||||
|
||||
return max(0, NdotD - params.horizon_angle_threshold) * falloff_mult;
|
||||
}
|
||||
|
||||
void SSAO_tap_inner(const int p_quality_level, inout float r_obscurance_sum, inout float r_weight_sum, const vec2 p_sampling_uv, const float p_mip_level, const vec3 p_pix_center_pos, vec3 p_pixel_normal, const float p_fallof_sq, const float p_weight_mod) {
|
||||
// get depth at sample
|
||||
float viewspace_sample_z = textureLod(source_depth_mipmaps, vec3(p_sampling_uv, params.pass), p_mip_level).x;
|
||||
|
||||
// convert to viewspace
|
||||
vec3 hit_pos = NDC_to_view_space(p_sampling_uv.xy, viewspace_sample_z).xyz;
|
||||
vec3 hit_delta = hit_pos - p_pix_center_pos;
|
||||
|
||||
float obscurance = calculate_pixel_obscurance(p_pixel_normal, hit_delta, p_fallof_sq);
|
||||
float weight = 1.0;
|
||||
|
||||
if (p_quality_level >= SSAO_HALOING_REDUCTION_ENABLE_AT_QUALITY_PRESET) {
|
||||
float reduct = max(0, -hit_delta.z);
|
||||
reduct = clamp(reduct * params.neg_inv_radius + 2.0, 0.0, 1.0);
|
||||
weight = SSAO_HALOING_REDUCTION_AMOUNT * reduct + (1.0 - SSAO_HALOING_REDUCTION_AMOUNT);
|
||||
}
|
||||
weight *= p_weight_mod;
|
||||
r_obscurance_sum += obscurance * weight;
|
||||
r_weight_sum += weight;
|
||||
}
|
||||
|
||||
void SSAOTap(const int p_quality_level, inout float r_obscurance_sum, inout float r_weight_sum, const int p_tap_index, const mat2 p_rot_scale, const vec3 p_pix_center_pos, vec3 p_pixel_normal, const vec2 p_normalized_screen_pos, const float p_mip_offset, const float p_fallof_sq, float p_weight_mod, vec2 p_norm_xy, float p_norm_xy_length) {
|
||||
vec2 sample_offset;
|
||||
float sample_pow_2_len;
|
||||
|
||||
// patterns
|
||||
{
|
||||
vec4 new_sample = sample_pattern[p_tap_index];
|
||||
sample_offset = new_sample.xy * p_rot_scale;
|
||||
sample_pow_2_len = new_sample.w; // precalculated, same as: sample_pow_2_len = log2( length( new_sample.xy ) );
|
||||
p_weight_mod *= new_sample.z;
|
||||
}
|
||||
|
||||
// snap to pixel center (more correct obscurance math, avoids artifacts)
|
||||
sample_offset = round(sample_offset);
|
||||
|
||||
// calculate MIP based on the sample distance from the center, similar to as described
|
||||
// in http://graphics.cs.williams.edu/papers/SAOHPG12/.
|
||||
float mip_level = (p_quality_level < SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET) ? (0) : (sample_pow_2_len + p_mip_offset);
|
||||
|
||||
vec2 sampling_uv = sample_offset * params.half_screen_pixel_size + p_normalized_screen_pos;
|
||||
|
||||
SSAO_tap_inner(p_quality_level, r_obscurance_sum, r_weight_sum, sampling_uv, mip_level, p_pix_center_pos, p_pixel_normal, p_fallof_sq, p_weight_mod);
|
||||
|
||||
// for the second tap, just use the mirrored offset
|
||||
vec2 sample_offset_mirrored_uv = -sample_offset;
|
||||
|
||||
// tilt the second set of samples so that the disk is effectively rotated by the normal
|
||||
// effective at removing one set of artifacts, but too expensive for lower quality settings
|
||||
if (p_quality_level >= SSAO_TILT_SAMPLES_ENABLE_AT_QUALITY_PRESET) {
|
||||
float dot_norm = dot(sample_offset_mirrored_uv, p_norm_xy);
|
||||
sample_offset_mirrored_uv -= dot_norm * p_norm_xy_length * p_norm_xy;
|
||||
sample_offset_mirrored_uv = round(sample_offset_mirrored_uv);
|
||||
}
|
||||
|
||||
// snap to pixel center (more correct obscurance math, avoids artifacts)
|
||||
vec2 sampling_mirrored_uv = sample_offset_mirrored_uv * params.half_screen_pixel_size + p_normalized_screen_pos;
|
||||
|
||||
SSAO_tap_inner(p_quality_level, r_obscurance_sum, r_weight_sum, sampling_mirrored_uv, mip_level, p_pix_center_pos, p_pixel_normal, p_fallof_sq, p_weight_mod);
|
||||
}
|
||||
|
||||
void generate_SSAO_shadows_internal(out float r_shadow_term, out vec4 r_edges, out float r_weight, const vec2 p_pos, int p_quality_level, bool p_adaptive_base) {
|
||||
vec2 pos_rounded = trunc(p_pos);
|
||||
uvec2 upos = uvec2(pos_rounded);
|
||||
|
||||
const int number_of_taps = (p_adaptive_base) ? (SSAO_ADAPTIVE_TAP_BASE_COUNT) : (num_taps[p_quality_level]);
|
||||
float pix_z, pix_left_z, pix_top_z, pix_right_z, pix_bottom_z;
|
||||
|
||||
vec4 valuesUL = textureGather(source_depth_mipmaps, vec3(pos_rounded * params.half_screen_pixel_size, params.pass));
|
||||
vec4 valuesBR = textureGather(source_depth_mipmaps, vec3((pos_rounded + vec2(1.0)) * params.half_screen_pixel_size, params.pass));
|
||||
|
||||
// get this pixel's viewspace depth
|
||||
pix_z = valuesUL.y;
|
||||
|
||||
// get left right top bottom neighboring pixels for edge detection (gets compiled out on quality_level == 0)
|
||||
pix_left_z = valuesUL.x;
|
||||
pix_top_z = valuesUL.z;
|
||||
pix_right_z = valuesBR.z;
|
||||
pix_bottom_z = valuesBR.x;
|
||||
|
||||
vec2 normalized_screen_pos = pos_rounded * params.half_screen_pixel_size + params.half_screen_pixel_size_x025;
|
||||
vec3 pix_center_pos = NDC_to_view_space(normalized_screen_pos, pix_z);
|
||||
|
||||
// Load this pixel's viewspace normal
|
||||
uvec2 full_res_coord = upos * 2 * params.size_multiplier + params.pass_coord_offset.xy;
|
||||
vec3 pixel_normal = load_normal(ivec2(full_res_coord));
|
||||
|
||||
const vec2 pixel_size_at_center = NDC_to_view_space(normalized_screen_pos.xy + params.half_screen_pixel_size, pix_center_pos.z).xy - pix_center_pos.xy;
|
||||
|
||||
float pixel_lookup_radius;
|
||||
float fallof_sq;
|
||||
|
||||
// calculate effect radius and fit our screen sampling pattern inside it
|
||||
float viewspace_radius;
|
||||
calculate_radius_parameters(length(pix_center_pos), pixel_size_at_center, pixel_lookup_radius, viewspace_radius, fallof_sq);
|
||||
|
||||
// calculate samples rotation/scaling
|
||||
mat2 rot_scale_matrix;
|
||||
uint pseudo_random_index;
|
||||
|
||||
{
|
||||
vec4 rotation_scale;
|
||||
// reduce effect radius near the screen edges slightly; ideally, one would render a larger depth buffer (5% on each side) instead
|
||||
if (!p_adaptive_base && (p_quality_level >= SSAO_REDUCE_RADIUS_NEAR_SCREEN_BORDER_ENABLE_AT_QUALITY_PRESET)) {
|
||||
float near_screen_border = min(min(normalized_screen_pos.x, 1.0 - normalized_screen_pos.x), min(normalized_screen_pos.y, 1.0 - normalized_screen_pos.y));
|
||||
near_screen_border = clamp(10.0 * near_screen_border + 0.6, 0.0, 1.0);
|
||||
pixel_lookup_radius *= near_screen_border;
|
||||
}
|
||||
|
||||
// load & update pseudo-random rotation matrix
|
||||
pseudo_random_index = uint(pos_rounded.y * 2 + pos_rounded.x) % 5;
|
||||
rotation_scale = constants.rotation_matrices[params.pass * 5 + pseudo_random_index];
|
||||
rot_scale_matrix = mat2(rotation_scale.x * pixel_lookup_radius, rotation_scale.y * pixel_lookup_radius, rotation_scale.z * pixel_lookup_radius, rotation_scale.w * pixel_lookup_radius);
|
||||
}
|
||||
|
||||
// the main obscurance & sample weight storage
|
||||
float obscurance_sum = 0.0;
|
||||
float weight_sum = 0.0;
|
||||
|
||||
// edge mask for between this and left/right/top/bottom neighbor pixels - not used in quality level 0 so initialize to "no edge" (1 is no edge, 0 is edge)
|
||||
vec4 edgesLRTB = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
// Move center pixel slightly towards camera to avoid imprecision artifacts due to using of 16bit depth buffer.
|
||||
pix_center_pos *= 0.99;
|
||||
|
||||
if (!p_adaptive_base && (p_quality_level >= SSAO_DEPTH_BASED_EDGES_ENABLE_AT_QUALITY_PRESET)) {
|
||||
edgesLRTB = calculate_edges(pix_z, pix_left_z, pix_right_z, pix_top_z, pix_bottom_z);
|
||||
}
|
||||
|
||||
// adds a more high definition sharp effect, which gets blurred out (reuses left/right/top/bottom samples that we used for edge detection)
|
||||
if (!p_adaptive_base && (p_quality_level >= SSAO_DETAIL_AO_ENABLE_AT_QUALITY_PRESET)) {
|
||||
// disable in case of quality level 4 (reference)
|
||||
if (p_quality_level != 4) {
|
||||
//approximate neighboring pixels positions (actually just deltas or "positions - pix_center_pos" )
|
||||
vec3 normalized_viewspace_dir = vec3(pix_center_pos.xy / pix_center_pos.zz, 1.0);
|
||||
vec3 pixel_left_delta = vec3(-pixel_size_at_center.x, 0.0, 0.0) + normalized_viewspace_dir * (pix_left_z - pix_center_pos.z);
|
||||
vec3 pixel_right_delta = vec3(+pixel_size_at_center.x, 0.0, 0.0) + normalized_viewspace_dir * (pix_right_z - pix_center_pos.z);
|
||||
vec3 pixel_top_delta = vec3(0.0, -pixel_size_at_center.y, 0.0) + normalized_viewspace_dir * (pix_top_z - pix_center_pos.z);
|
||||
vec3 pixel_bottom_delta = vec3(0.0, +pixel_size_at_center.y, 0.0) + normalized_viewspace_dir * (pix_bottom_z - pix_center_pos.z);
|
||||
|
||||
const float range_reduction = 4.0f; // this is to avoid various artifacts
|
||||
const float modified_fallof_sq = range_reduction * fallof_sq;
|
||||
|
||||
vec4 additional_obscurance;
|
||||
additional_obscurance.x = calculate_pixel_obscurance(pixel_normal, pixel_left_delta, modified_fallof_sq);
|
||||
additional_obscurance.y = calculate_pixel_obscurance(pixel_normal, pixel_right_delta, modified_fallof_sq);
|
||||
additional_obscurance.z = calculate_pixel_obscurance(pixel_normal, pixel_top_delta, modified_fallof_sq);
|
||||
additional_obscurance.w = calculate_pixel_obscurance(pixel_normal, pixel_bottom_delta, modified_fallof_sq);
|
||||
|
||||
obscurance_sum += params.detail_intensity * dot(additional_obscurance, edgesLRTB);
|
||||
}
|
||||
}
|
||||
|
||||
// Sharp normals also create edges - but this adds to the cost as well
|
||||
if (!p_adaptive_base && (p_quality_level >= SSAO_NORMAL_BASED_EDGES_ENABLE_AT_QUALITY_PRESET)) {
|
||||
vec3 neighbour_normal_left = load_normal(ivec2(full_res_coord), ivec2(-2, 0));
|
||||
vec3 neighbour_normal_right = load_normal(ivec2(full_res_coord), ivec2(2, 0));
|
||||
vec3 neighbour_normal_top = load_normal(ivec2(full_res_coord), ivec2(0, -2));
|
||||
vec3 neighbour_normal_bottom = load_normal(ivec2(full_res_coord), ivec2(0, 2));
|
||||
|
||||
const float dot_threshold = SSAO_NORMAL_BASED_EDGES_DOT_THRESHOLD;
|
||||
|
||||
vec4 normal_edgesLRTB;
|
||||
normal_edgesLRTB.x = clamp((dot(pixel_normal, neighbour_normal_left) + dot_threshold), 0.0, 1.0);
|
||||
normal_edgesLRTB.y = clamp((dot(pixel_normal, neighbour_normal_right) + dot_threshold), 0.0, 1.0);
|
||||
normal_edgesLRTB.z = clamp((dot(pixel_normal, neighbour_normal_top) + dot_threshold), 0.0, 1.0);
|
||||
normal_edgesLRTB.w = clamp((dot(pixel_normal, neighbour_normal_bottom) + dot_threshold), 0.0, 1.0);
|
||||
|
||||
edgesLRTB *= normal_edgesLRTB;
|
||||
}
|
||||
|
||||
const float global_mip_offset = SSAO_DEPTH_MIPS_GLOBAL_OFFSET;
|
||||
float mip_offset = (p_quality_level < SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET) ? (0) : (log2(pixel_lookup_radius) + global_mip_offset);
|
||||
|
||||
// Used to tilt the second set of samples so that the disk is effectively rotated by the normal
|
||||
// effective at removing one set of artifacts, but too expensive for lower quality settings
|
||||
vec2 norm_xy = vec2(pixel_normal.x, pixel_normal.y);
|
||||
float norm_xy_length = length(norm_xy);
|
||||
norm_xy /= vec2(norm_xy_length, -norm_xy_length);
|
||||
norm_xy_length *= SSAO_TILT_SAMPLES_AMOUNT;
|
||||
|
||||
// standard, non-adaptive approach
|
||||
if ((p_quality_level != 3) || p_adaptive_base) {
|
||||
for (int i = 0; i < number_of_taps; i++) {
|
||||
SSAOTap(p_quality_level, obscurance_sum, weight_sum, i, rot_scale_matrix, pix_center_pos, pixel_normal, normalized_screen_pos, mip_offset, fallof_sq, 1.0, norm_xy, norm_xy_length);
|
||||
}
|
||||
}
|
||||
#ifdef ADAPTIVE
|
||||
else {
|
||||
// add new ones if needed
|
||||
vec2 full_res_uv = normalized_screen_pos + params.pass_uv_offset.xy;
|
||||
float importance = textureLod(source_importance, full_res_uv, 0.0).x;
|
||||
|
||||
// this is to normalize SSAO_DETAIL_AO_AMOUNT across all pixel regardless of importance
|
||||
obscurance_sum *= (SSAO_ADAPTIVE_TAP_BASE_COUNT / float(SSAO_MAX_TAPS)) + (importance * SSAO_ADAPTIVE_TAP_FLEXIBLE_COUNT / float(SSAO_MAX_TAPS));
|
||||
|
||||
// load existing base values
|
||||
vec2 base_values = imageLoad(source_ssao, ivec3(upos, params.pass)).xy;
|
||||
weight_sum += base_values.y * float(SSAO_ADAPTIVE_TAP_BASE_COUNT * 4.0);
|
||||
obscurance_sum += (base_values.x) * weight_sum;
|
||||
|
||||
// increase importance around edges
|
||||
float edge_count = dot(1.0 - edgesLRTB, vec4(1.0, 1.0, 1.0, 1.0));
|
||||
|
||||
float avg_total_importance = float(counter.sum) * params.load_counter_avg_div;
|
||||
|
||||
float importance_limiter = clamp(params.adaptive_sample_limit / avg_total_importance, 0.0, 1.0);
|
||||
importance *= importance_limiter;
|
||||
|
||||
float additional_sample_count = SSAO_ADAPTIVE_TAP_FLEXIBLE_COUNT * importance;
|
||||
|
||||
const float blend_range = 3.0;
|
||||
const float blend_range_inv = 1.0 / blend_range;
|
||||
|
||||
additional_sample_count += 0.5;
|
||||
uint additional_samples = uint(additional_sample_count);
|
||||
uint additional_samples_to = min(SSAO_MAX_TAPS, additional_samples + SSAO_ADAPTIVE_TAP_BASE_COUNT);
|
||||
|
||||
for (uint i = SSAO_ADAPTIVE_TAP_BASE_COUNT; i < additional_samples_to; i++) {
|
||||
additional_sample_count -= 1.0f;
|
||||
float weight_mod = clamp(additional_sample_count * blend_range_inv, 0.0, 1.0);
|
||||
SSAOTap(p_quality_level, obscurance_sum, weight_sum, int(i), rot_scale_matrix, pix_center_pos, pixel_normal, normalized_screen_pos, mip_offset, fallof_sq, weight_mod, norm_xy, norm_xy_length);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// early out for adaptive base - just output weight (used for the next pass)
|
||||
if (p_adaptive_base) {
|
||||
float obscurance = obscurance_sum / weight_sum;
|
||||
|
||||
r_shadow_term = obscurance;
|
||||
r_edges = vec4(0.0);
|
||||
r_weight = weight_sum;
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate weighted average
|
||||
float obscurance = obscurance_sum / weight_sum;
|
||||
|
||||
// calculate fadeout (1 close, gradient, 0 far)
|
||||
float fade_out = clamp(pix_center_pos.z * params.fade_out_mul + params.fade_out_add, 0.0, 1.0);
|
||||
|
||||
// Reduce the SSAO shadowing if we're on the edge to remove artifacts on edges (we don't care for the lower quality one)
|
||||
if (!p_adaptive_base && (p_quality_level >= SSAO_DEPTH_BASED_EDGES_ENABLE_AT_QUALITY_PRESET)) {
|
||||
// when there's more than 2 opposite edges, start fading out the occlusion to reduce aliasing artifacts
|
||||
float edge_fadeout_factor = clamp((1.0 - edgesLRTB.x - edgesLRTB.y) * 0.35, 0.0, 1.0) + clamp((1.0 - edgesLRTB.z - edgesLRTB.w) * 0.35, 0.0, 1.0);
|
||||
|
||||
fade_out *= clamp(1.0 - edge_fadeout_factor, 0.0, 1.0);
|
||||
}
|
||||
|
||||
// strength
|
||||
obscurance = params.intensity * obscurance;
|
||||
|
||||
// clamp
|
||||
obscurance = min(obscurance, params.shadow_clamp);
|
||||
|
||||
// fadeout
|
||||
obscurance *= fade_out;
|
||||
|
||||
// conceptually switch to occlusion with the meaning being visibility (grows with visibility, occlusion == 1 implies full visibility),
|
||||
// to be in line with what is more commonly used.
|
||||
float occlusion = 1.0 - obscurance;
|
||||
|
||||
// modify the gradient
|
||||
// note: this cannot be moved to a later pass because of loss of precision after storing in the render target
|
||||
occlusion = pow(clamp(occlusion, 0.0, 1.0), params.shadow_power);
|
||||
|
||||
// outputs!
|
||||
r_shadow_term = occlusion; // Our final 'occlusion' term (0 means fully occluded, 1 means fully lit)
|
||||
r_edges = edgesLRTB; // These are used to prevent blurring across edges, 1 means no edge, 0 means edge, 0.5 means half way there, etc.
|
||||
r_weight = weight_sum;
|
||||
}
|
||||
|
||||
void main() {
|
||||
float out_shadow_term;
|
||||
float out_weight;
|
||||
vec4 out_edges;
|
||||
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(ssC, params.screen_size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 uv = vec2(gl_GlobalInvocationID) + vec2(0.5);
|
||||
#ifdef SSAO_BASE
|
||||
generate_SSAO_shadows_internal(out_shadow_term, out_edges, out_weight, uv, params.quality, true);
|
||||
|
||||
imageStore(dest_image, ivec2(gl_GlobalInvocationID.xy), vec4(out_shadow_term, out_weight / (float(SSAO_ADAPTIVE_TAP_BASE_COUNT) * 4.0), 0.0, 0.0));
|
||||
#else
|
||||
generate_SSAO_shadows_internal(out_shadow_term, out_edges, out_weight, uv, params.quality, false); // pass in quality levels
|
||||
if (params.quality == 0) {
|
||||
out_edges = vec4(1.0);
|
||||
}
|
||||
|
||||
imageStore(dest_image, ivec2(gl_GlobalInvocationID.xy), vec4(out_shadow_term, pack_edges(out_edges), 0.0, 0.0));
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) 2016, Intel Corporation
|
||||
// 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.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// File changes (yyyy-mm-dd)
|
||||
// 2016-09-07: filip.strugar@intel.com: first commit
|
||||
// 2020-12-05: clayjohn: convert to Vulkan and Godot
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[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(rg8, set = 1, binding = 0) uniform restrict writeonly image2D dest_image;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float edge_sharpness;
|
||||
float pad;
|
||||
vec2 half_screen_pixel_size;
|
||||
}
|
||||
params;
|
||||
|
||||
vec4 unpack_edges(float p_packed_val) {
|
||||
uint packed_val = uint(p_packed_val * 255.5);
|
||||
vec4 edgesLRTB;
|
||||
edgesLRTB.x = float((packed_val >> 6) & 0x03) / 3.0;
|
||||
edgesLRTB.y = float((packed_val >> 4) & 0x03) / 3.0;
|
||||
edgesLRTB.z = float((packed_val >> 2) & 0x03) / 3.0;
|
||||
edgesLRTB.w = float((packed_val >> 0) & 0x03) / 3.0;
|
||||
|
||||
return clamp(edgesLRTB + params.edge_sharpness, 0.0, 1.0);
|
||||
}
|
||||
|
||||
void add_sample(float p_ssao_value, float p_edge_value, inout float r_sum, inout float r_sum_weight) {
|
||||
float weight = p_edge_value;
|
||||
|
||||
r_sum += (weight * p_ssao_value);
|
||||
r_sum_weight += weight;
|
||||
}
|
||||
|
||||
#ifdef MODE_WIDE
|
||||
vec2 sample_blurred_wide(vec2 p_coord) {
|
||||
vec2 vC = textureLodOffset(source_ssao, vec2(p_coord), 0.0, ivec2(0, 0)).xy;
|
||||
vec2 vL = textureLodOffset(source_ssao, vec2(p_coord), 0.0, ivec2(-2, 0)).xy;
|
||||
vec2 vT = textureLodOffset(source_ssao, vec2(p_coord), 0.0, ivec2(0, -2)).xy;
|
||||
vec2 vR = textureLodOffset(source_ssao, vec2(p_coord), 0.0, ivec2(2, 0)).xy;
|
||||
vec2 vB = textureLodOffset(source_ssao, vec2(p_coord), 0.0, ivec2(0, 2)).xy;
|
||||
|
||||
float packed_edges = vC.y;
|
||||
vec4 edgesLRTB = unpack_edges(packed_edges);
|
||||
edgesLRTB.x *= unpack_edges(vL.y).y;
|
||||
edgesLRTB.z *= unpack_edges(vT.y).w;
|
||||
edgesLRTB.y *= unpack_edges(vR.y).x;
|
||||
edgesLRTB.w *= unpack_edges(vB.y).z;
|
||||
|
||||
float ssao_value = vC.x;
|
||||
float ssao_valueL = vL.x;
|
||||
float ssao_valueT = vT.x;
|
||||
float ssao_valueR = vR.x;
|
||||
float ssao_valueB = vB.x;
|
||||
|
||||
float sum_weight = 0.8f;
|
||||
float sum = ssao_value * sum_weight;
|
||||
|
||||
add_sample(ssao_valueL, edgesLRTB.x, sum, sum_weight);
|
||||
add_sample(ssao_valueR, edgesLRTB.y, sum, sum_weight);
|
||||
add_sample(ssao_valueT, edgesLRTB.z, sum, sum_weight);
|
||||
add_sample(ssao_valueB, edgesLRTB.w, sum, sum_weight);
|
||||
|
||||
float ssao_avg = sum / sum_weight;
|
||||
|
||||
ssao_value = ssao_avg;
|
||||
|
||||
return vec2(ssao_value, packed_edges);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MODE_SMART
|
||||
vec2 sample_blurred(vec3 p_pos, vec2 p_coord) {
|
||||
float packed_edges = texelFetch(source_ssao, ivec2(p_pos.xy), 0).y;
|
||||
vec4 edgesLRTB = unpack_edges(packed_edges);
|
||||
|
||||
vec4 valuesUL = textureGather(source_ssao, vec2(p_coord - params.half_screen_pixel_size * 0.5));
|
||||
vec4 valuesBR = textureGather(source_ssao, vec2(p_coord + params.half_screen_pixel_size * 0.5));
|
||||
|
||||
float ssao_value = valuesUL.y;
|
||||
float ssao_valueL = valuesUL.x;
|
||||
float ssao_valueT = valuesUL.z;
|
||||
float ssao_valueR = valuesBR.z;
|
||||
float ssao_valueB = valuesBR.x;
|
||||
|
||||
float sum_weight = 0.5;
|
||||
float sum = ssao_value * sum_weight;
|
||||
|
||||
add_sample(ssao_valueL, edgesLRTB.x, sum, sum_weight);
|
||||
add_sample(ssao_valueR, edgesLRTB.y, sum, sum_weight);
|
||||
|
||||
add_sample(ssao_valueT, edgesLRTB.z, sum, sum_weight);
|
||||
add_sample(ssao_valueB, edgesLRTB.w, sum, sum_weight);
|
||||
|
||||
float ssao_avg = sum / sum_weight;
|
||||
|
||||
ssao_value = ssao_avg;
|
||||
|
||||
return vec2(ssao_value, packed_edges);
|
||||
}
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
// Pixel being shaded
|
||||
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
#ifdef MODE_NON_SMART
|
||||
|
||||
vec2 half_pixel = params.half_screen_pixel_size * 0.5;
|
||||
|
||||
vec2 uv = (vec2(gl_GlobalInvocationID.xy) + vec2(0.5, 0.5)) * params.half_screen_pixel_size;
|
||||
|
||||
vec2 center = textureLod(source_ssao, vec2(uv), 0.0).xy;
|
||||
|
||||
vec4 vals;
|
||||
vals.x = textureLod(source_ssao, vec2(uv + vec2(-half_pixel.x * 3, -half_pixel.y)), 0.0).x;
|
||||
vals.y = textureLod(source_ssao, vec2(uv + vec2(+half_pixel.x, -half_pixel.y * 3)), 0.0).x;
|
||||
vals.z = textureLod(source_ssao, vec2(uv + vec2(-half_pixel.x, +half_pixel.y * 3)), 0.0).x;
|
||||
vals.w = textureLod(source_ssao, vec2(uv + vec2(+half_pixel.x * 3, +half_pixel.y)), 0.0).x;
|
||||
|
||||
vec2 sampled = vec2(dot(vals, vec4(0.2)) + center.x * 0.2, center.y);
|
||||
|
||||
#else
|
||||
#ifdef MODE_SMART
|
||||
vec2 sampled = sample_blurred(vec3(gl_GlobalInvocationID), (vec2(gl_GlobalInvocationID.xy) + vec2(0.5, 0.5)) * params.half_screen_pixel_size);
|
||||
#else // MODE_WIDE
|
||||
vec2 sampled = sample_blurred_wide((vec2(gl_GlobalInvocationID.xy) + vec2(0.5, 0.5)) * params.half_screen_pixel_size);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
imageStore(dest_image, ivec2(ssC), vec4(sampled, 0.0, 0.0));
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) 2016, Intel Corporation
|
||||
// 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.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// File changes (yyyy-mm-dd)
|
||||
// 2016-09-07: filip.strugar@intel.com: first commit
|
||||
// 2020-12-05: clayjohn: convert to Vulkan and Godot
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
#ifdef GENERATE_MAP
|
||||
layout(set = 0, binding = 0) uniform sampler2DArray source_texture;
|
||||
#else
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_importance;
|
||||
#endif
|
||||
layout(r8, set = 1, binding = 0) uniform restrict writeonly image2D dest_image;
|
||||
|
||||
#ifdef PROCESS_MAPB
|
||||
layout(set = 2, binding = 0, std430) buffer Counter {
|
||||
uint sum;
|
||||
}
|
||||
counter;
|
||||
#endif
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec2 half_screen_pixel_size;
|
||||
float intensity;
|
||||
float power;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
// Pixel being shaded
|
||||
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
#ifdef GENERATE_MAP
|
||||
// importance map stuff
|
||||
uvec2 base_position = ssC * 2;
|
||||
|
||||
vec2 base_uv = (vec2(base_position) + vec2(0.5f, 0.5f)) * params.half_screen_pixel_size;
|
||||
|
||||
float minV = 1.0;
|
||||
float maxV = 0.0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vec4 vals = textureGather(source_texture, vec3(base_uv, i));
|
||||
|
||||
// apply the same modifications that would have been applied in the main shader
|
||||
vals = params.intensity * vals;
|
||||
|
||||
vals = 1 - vals;
|
||||
|
||||
vals = pow(clamp(vals, 0.0, 1.0), vec4(params.power));
|
||||
|
||||
maxV = max(maxV, max(max(vals.x, vals.y), max(vals.z, vals.w)));
|
||||
minV = min(minV, min(min(vals.x, vals.y), min(vals.z, vals.w)));
|
||||
}
|
||||
|
||||
float min_max_diff = maxV - minV;
|
||||
|
||||
imageStore(dest_image, ssC, vec4(pow(clamp(min_max_diff * 2.0, 0.0, 1.0), 0.8)));
|
||||
#endif
|
||||
|
||||
#ifdef PROCESS_MAPA
|
||||
vec2 uv = (vec2(ssC) + 0.5f) * params.half_screen_pixel_size * 2.0;
|
||||
|
||||
float center = textureLod(source_importance, uv, 0.0).x;
|
||||
|
||||
vec2 half_pixel = params.half_screen_pixel_size;
|
||||
|
||||
vec4 vals;
|
||||
vals.x = textureLod(source_importance, uv + vec2(-half_pixel.x * 3, -half_pixel.y), 0.0).x;
|
||||
vals.y = textureLod(source_importance, uv + vec2(+half_pixel.x, -half_pixel.y * 3), 0.0).x;
|
||||
vals.z = textureLod(source_importance, uv + vec2(+half_pixel.x * 3, +half_pixel.y), 0.0).x;
|
||||
vals.w = textureLod(source_importance, uv + vec2(-half_pixel.x, +half_pixel.y * 3), 0.0).x;
|
||||
|
||||
float avg = dot(vals, vec4(0.25, 0.25, 0.25, 0.25));
|
||||
|
||||
imageStore(dest_image, ssC, vec4(avg));
|
||||
#endif
|
||||
|
||||
#ifdef PROCESS_MAPB
|
||||
vec2 uv = (vec2(ssC) + 0.5f) * params.half_screen_pixel_size * 2.0;
|
||||
|
||||
float center = textureLod(source_importance, uv, 0.0).x;
|
||||
|
||||
vec2 half_pixel = params.half_screen_pixel_size;
|
||||
|
||||
vec4 vals;
|
||||
vals.x = textureLod(source_importance, uv + vec2(-half_pixel.x, -half_pixel.y * 3), 0.0).x;
|
||||
vals.y = textureLod(source_importance, uv + vec2(+half_pixel.x * 3, -half_pixel.y), 0.0).x;
|
||||
vals.z = textureLod(source_importance, uv + vec2(+half_pixel.x, +half_pixel.y * 3), 0.0).x;
|
||||
vals.w = textureLod(source_importance, uv + vec2(-half_pixel.x * 3, +half_pixel.y), 0.0).x;
|
||||
|
||||
float avg = dot(vals, vec4(0.25, 0.25, 0.25, 0.25));
|
||||
|
||||
imageStore(dest_image, ssC, vec4(avg));
|
||||
|
||||
// sum the average; to avoid overflowing we assume max AO resolution is not bigger than 16384x16384; so quarter res (used here) will be 4096x4096, which leaves us with 8 bits per pixel
|
||||
uint sum = uint(clamp(avg, 0.0, 1.0) * 255.0 + 0.5);
|
||||
|
||||
// save every 9th to avoid InterlockedAdd congestion - since we're blurring, this is good enough; compensated by multiplying load_counter_avg_div by 9
|
||||
if (((ssC.x % 3) + (ssC.y % 3)) == 0) {
|
||||
atomicAdd(counter.sum, sum);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) 2016, Intel Corporation
|
||||
// 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.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// File changes (yyyy-mm-dd)
|
||||
// 2016-09-07: filip.strugar@intel.com: first commit
|
||||
// 2020-12-05: clayjohn: convert to Vulkan and Godot
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(rgba8, set = 0, binding = 0) uniform restrict writeonly image2D dest_image;
|
||||
layout(set = 1, binding = 0) uniform sampler2DArray source_texture;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float inv_sharpness;
|
||||
uint size_modifier;
|
||||
vec2 pixel_size;
|
||||
}
|
||||
params;
|
||||
|
||||
vec4 unpack_edges(float p_packed_val) {
|
||||
uint packed_val = uint(p_packed_val * 255.5);
|
||||
vec4 edgesLRTB;
|
||||
edgesLRTB.x = float((packed_val >> 6) & 0x03) / 3.0;
|
||||
edgesLRTB.y = float((packed_val >> 4) & 0x03) / 3.0;
|
||||
edgesLRTB.z = float((packed_val >> 2) & 0x03) / 3.0;
|
||||
edgesLRTB.w = float((packed_val >> 0) & 0x03) / 3.0;
|
||||
|
||||
return clamp(edgesLRTB + params.inv_sharpness, 0.0, 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(ssC, ivec2(1.0 / params.pixel_size)))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MODE_SMART
|
||||
float ao;
|
||||
uvec2 pix_pos = uvec2(gl_GlobalInvocationID.xy);
|
||||
vec2 uv = (gl_GlobalInvocationID.xy + vec2(0.5)) * params.pixel_size;
|
||||
|
||||
// calculate index in the four deinterleaved source array texture
|
||||
int mx = int(pix_pos.x % 2);
|
||||
int my = int(pix_pos.y % 2);
|
||||
int index_center = mx + my * 2; // center index
|
||||
int index_horizontal = (1 - mx) + my * 2; // neighboring, horizontal
|
||||
int index_vertical = mx + (1 - my) * 2; // neighboring, vertical
|
||||
int index_diagonal = (1 - mx) + (1 - my) * 2; // diagonal
|
||||
|
||||
vec2 center_val = texelFetch(source_texture, ivec3(pix_pos / uvec2(params.size_modifier), index_center), 0).xy;
|
||||
|
||||
ao = center_val.x;
|
||||
|
||||
vec4 edgesLRTB = unpack_edges(center_val.y);
|
||||
|
||||
// convert index shifts to sampling offsets
|
||||
float fmx = float(mx);
|
||||
float fmy = float(my);
|
||||
|
||||
// in case of an edge, push sampling offsets away from the edge (towards pixel center)
|
||||
float fmxe = (edgesLRTB.y - edgesLRTB.x);
|
||||
float fmye = (edgesLRTB.w - edgesLRTB.z);
|
||||
|
||||
// calculate final sampling offsets and sample using bilinear filter
|
||||
vec2 uv_horizontal = (gl_GlobalInvocationID.xy + vec2(0.5) + vec2(fmx + fmxe - 0.5, 0.5 - fmy)) * params.pixel_size;
|
||||
float ao_horizontal = textureLod(source_texture, vec3(uv_horizontal, index_horizontal), 0.0).x;
|
||||
vec2 uv_vertical = (gl_GlobalInvocationID.xy + vec2(0.5) + vec2(0.5 - fmx, fmy - 0.5 + fmye)) * params.pixel_size;
|
||||
float ao_vertical = textureLod(source_texture, vec3(uv_vertical, index_vertical), 0.0).x;
|
||||
vec2 uv_diagonal = (gl_GlobalInvocationID.xy + vec2(0.5) + vec2(fmx - 0.5 + fmxe, fmy - 0.5 + fmye)) * params.pixel_size;
|
||||
float ao_diagonal = textureLod(source_texture, vec3(uv_diagonal, index_diagonal), 0.0).x;
|
||||
|
||||
// reduce weight for samples near edge - if the edge is on both sides, weight goes to 0
|
||||
vec4 blendWeights;
|
||||
blendWeights.x = 1.0;
|
||||
blendWeights.y = (edgesLRTB.x + edgesLRTB.y) * 0.5;
|
||||
blendWeights.z = (edgesLRTB.z + edgesLRTB.w) * 0.5;
|
||||
blendWeights.w = (blendWeights.y + blendWeights.z) * 0.5;
|
||||
|
||||
// calculate weighted average
|
||||
float blendWeightsSum = dot(blendWeights, vec4(1.0, 1.0, 1.0, 1.0));
|
||||
ao = dot(vec4(ao, ao_horizontal, ao_vertical, ao_diagonal), blendWeights) / blendWeightsSum;
|
||||
|
||||
imageStore(dest_image, ivec2(gl_GlobalInvocationID.xy), vec4(ao));
|
||||
#else // !MODE_SMART
|
||||
|
||||
vec2 uv = (gl_GlobalInvocationID.xy + vec2(0.5)) * params.pixel_size;
|
||||
#ifdef MODE_HALF
|
||||
float a = textureLod(source_texture, vec3(uv, 0), 0.0).x;
|
||||
float d = textureLod(source_texture, vec3(uv, 3), 0.0).x;
|
||||
float avg = (a + d) * 0.5;
|
||||
|
||||
#else
|
||||
float a = textureLod(source_texture, vec3(uv, 0), 0.0).x;
|
||||
float b = textureLod(source_texture, vec3(uv, 1), 0.0).x;
|
||||
float c = textureLod(source_texture, vec3(uv, 2), 0.0).x;
|
||||
float d = textureLod(source_texture, vec3(uv, 3), 0.0).x;
|
||||
float avg = (a + b + c + d) * 0.25;
|
||||
|
||||
#endif
|
||||
imageStore(dest_image, ivec2(gl_GlobalInvocationID.xy), vec4(avg));
|
||||
#endif
|
||||
}
|
||||
439
engine/servers/rendering/renderer_rd/shaders/effects/ssil.glsl
Normal file
439
engine/servers/rendering/renderer_rd/shaders/effects/ssil.glsl
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) 2016, Intel Corporation
|
||||
// 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.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// File changes (yyyy-mm-dd)
|
||||
// 2016-09-07: filip.strugar@intel.com: first commit
|
||||
// 2020-12-05: clayjohn: convert to Vulkan and Godot
|
||||
// 2021-05-27: clayjohn: convert SSAO to SSIL
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define SSIL_MAIN_DISK_SAMPLE_COUNT (32)
|
||||
const vec4 sample_pattern[SSIL_MAIN_DISK_SAMPLE_COUNT] = {
|
||||
vec4(0.78488064, 0.56661671, 1.500000, -0.126083), vec4(0.26022232, -0.29575172, 1.500000, -1.064030), vec4(0.10459357, 0.08372527, 1.110000, -2.730563), vec4(-0.68286800, 0.04963045, 1.090000, -0.498827),
|
||||
vec4(-0.13570161, -0.64190155, 1.250000, -0.532765), vec4(-0.26193795, -0.08205118, 0.670000, -1.783245), vec4(-0.61177456, 0.66664219, 0.710000, -0.044234), vec4(0.43675563, 0.25119025, 0.610000, -1.167283),
|
||||
vec4(0.07884444, 0.86618668, 0.640000, -0.459002), vec4(-0.12790935, -0.29869005, 0.600000, -1.729424), vec4(-0.04031125, 0.02413622, 0.600000, -4.792042), vec4(0.16201244, -0.52851415, 0.790000, -1.067055),
|
||||
vec4(-0.70991218, 0.47301072, 0.640000, -0.335236), vec4(0.03277707, -0.22349690, 0.600000, -1.982384), vec4(0.68921727, 0.36800742, 0.630000, -0.266718), vec4(0.29251814, 0.37775412, 0.610000, -1.422520),
|
||||
vec4(-0.12224089, 0.96582592, 0.600000, -0.426142), vec4(0.11071457, -0.16131058, 0.600000, -2.165947), vec4(0.46562141, -0.59747696, 0.600000, -0.189760), vec4(-0.51548797, 0.11804193, 0.600000, -1.246800),
|
||||
vec4(0.89141309, -0.42090443, 0.600000, 0.028192), vec4(-0.32402530, -0.01591529, 0.600000, -1.543018), vec4(0.60771245, 0.41635221, 0.600000, -0.605411), vec4(0.02379565, -0.08239821, 0.600000, -3.809046),
|
||||
vec4(0.48951152, -0.23657045, 0.600000, -1.189011), vec4(-0.17611565, -0.81696892, 0.600000, -0.513724), vec4(-0.33930185, -0.20732205, 0.600000, -1.698047), vec4(-0.91974425, 0.05403209, 0.600000, 0.062246),
|
||||
vec4(-0.15064627, -0.14949332, 0.600000, -1.896062), vec4(0.53180975, -0.35210401, 0.600000, -0.758838), vec4(0.41487166, 0.81442589, 0.600000, -0.505648), vec4(-0.24106961, -0.32721516, 0.600000, -1.665244)
|
||||
};
|
||||
|
||||
// these values can be changed (up to SSIL_MAX_TAPS) with no changes required elsewhere; values for 4th and 5th preset are ignored but array needed to avoid compilation errors
|
||||
// the actual number of texture samples is two times this value (each "tap" has two symmetrical depth texture samples)
|
||||
const int num_taps[5] = { 3, 5, 12, 0, 0 };
|
||||
|
||||
#define SSIL_TILT_SAMPLES_ENABLE_AT_QUALITY_PRESET (99) // to disable simply set to 99 or similar
|
||||
#define SSIL_TILT_SAMPLES_AMOUNT (0.4)
|
||||
//
|
||||
#define SSIL_HALOING_REDUCTION_ENABLE_AT_QUALITY_PRESET (1) // to disable simply set to 99 or similar
|
||||
#define SSIL_HALOING_REDUCTION_AMOUNT (0.8) // values from 0.0 - 1.0, 1.0 means max weighting (will cause artifacts, 0.8 is more reasonable)
|
||||
//
|
||||
#define SSIL_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET (2)
|
||||
#define SSIL_DEPTH_MIPS_GLOBAL_OFFSET (-4.3) // best noise/quality/performance tradeoff, found empirically
|
||||
//
|
||||
// !!warning!! the edge handling is hard-coded to 'disabled' on quality level 0, and enabled above, on the C++ side; while toggling it here will work for
|
||||
// testing purposes, it will not yield performance gains (or correct results)
|
||||
#define SSIL_DEPTH_BASED_EDGES_ENABLE_AT_QUALITY_PRESET (1)
|
||||
//
|
||||
#define SSIL_REDUCE_RADIUS_NEAR_SCREEN_BORDER_ENABLE_AT_QUALITY_PRESET (1)
|
||||
|
||||
#define SSIL_MAX_TAPS 32
|
||||
#define SSIL_ADAPTIVE_TAP_BASE_COUNT 5
|
||||
#define SSIL_ADAPTIVE_TAP_FLEXIBLE_COUNT (SSIL_MAX_TAPS - SSIL_ADAPTIVE_TAP_BASE_COUNT)
|
||||
#define SSIL_DEPTH_MIP_LEVELS 4
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2DArray source_depth_mipmaps;
|
||||
layout(rgba8, set = 0, binding = 1) uniform restrict readonly image2D source_normal;
|
||||
layout(set = 0, binding = 2) uniform Constants { //get into a lower set
|
||||
vec4 rotation_matrices[20];
|
||||
}
|
||||
constants;
|
||||
|
||||
#ifdef ADAPTIVE
|
||||
layout(rgba16, set = 1, binding = 0) uniform restrict readonly image2DArray source_ssil;
|
||||
layout(set = 1, binding = 1) uniform sampler2D source_importance;
|
||||
layout(set = 1, binding = 2, std430) buffer Counter {
|
||||
uint sum;
|
||||
}
|
||||
counter;
|
||||
#endif
|
||||
|
||||
layout(rgba16, set = 2, binding = 0) uniform restrict writeonly image2D dest_image;
|
||||
layout(r8, set = 2, binding = 1) uniform image2D edges_weights_image;
|
||||
|
||||
layout(set = 3, binding = 0) uniform sampler2D last_frame;
|
||||
layout(set = 3, binding = 1) uniform ProjectionConstants {
|
||||
mat4 reprojection;
|
||||
}
|
||||
projection_constants;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
ivec2 screen_size;
|
||||
int pass;
|
||||
int quality;
|
||||
|
||||
vec2 half_screen_pixel_size;
|
||||
vec2 half_screen_pixel_size_x025;
|
||||
|
||||
vec2 NDC_to_view_mul;
|
||||
vec2 NDC_to_view_add;
|
||||
|
||||
vec2 pad2;
|
||||
float z_near;
|
||||
float z_far;
|
||||
|
||||
float radius;
|
||||
float intensity;
|
||||
int size_multiplier;
|
||||
int pad;
|
||||
|
||||
float fade_out_mul;
|
||||
float fade_out_add;
|
||||
float normal_rejection_amount;
|
||||
float inv_radius_near_limit;
|
||||
|
||||
bool is_orthogonal;
|
||||
float neg_inv_radius;
|
||||
float load_counter_avg_div;
|
||||
float adaptive_sample_limit;
|
||||
|
||||
ivec2 pass_coord_offset;
|
||||
vec2 pass_uv_offset;
|
||||
}
|
||||
params;
|
||||
|
||||
float pack_edges(vec4 p_edgesLRTB) {
|
||||
p_edgesLRTB = round(clamp(p_edgesLRTB, 0.0, 1.0) * 3.05);
|
||||
return dot(p_edgesLRTB, vec4(64.0 / 255.0, 16.0 / 255.0, 4.0 / 255.0, 1.0 / 255.0));
|
||||
}
|
||||
|
||||
vec3 NDC_to_view_space(vec2 p_pos, float p_viewspace_depth) {
|
||||
if (params.is_orthogonal) {
|
||||
return vec3((params.NDC_to_view_mul * p_pos.xy + params.NDC_to_view_add), p_viewspace_depth);
|
||||
} else {
|
||||
return vec3((params.NDC_to_view_mul * p_pos.xy + params.NDC_to_view_add) * p_viewspace_depth, p_viewspace_depth);
|
||||
}
|
||||
}
|
||||
|
||||
// calculate effect radius and fit our screen sampling pattern inside it
|
||||
void calculate_radius_parameters(const float p_pix_center_length, const vec2 p_pixel_size_at_center, out float r_lookup_radius, out float r_radius, out float r_fallof_sq) {
|
||||
r_radius = params.radius;
|
||||
|
||||
// when too close, on-screen sampling disk will grow beyond screen size; limit this to avoid closeup temporal artifacts
|
||||
const float too_close_limit = clamp(p_pix_center_length * params.inv_radius_near_limit, 0.0, 1.0) * 0.8 + 0.2;
|
||||
|
||||
r_radius *= too_close_limit;
|
||||
|
||||
// 0.85 is to reduce the radius to allow for more samples on a slope to still stay within influence
|
||||
r_lookup_radius = (0.85 * r_radius) / p_pixel_size_at_center.x;
|
||||
|
||||
// used to calculate falloff (both for AO samples and per-sample weights)
|
||||
r_fallof_sq = -1.0 / (r_radius * r_radius);
|
||||
}
|
||||
|
||||
vec4 calculate_edges(const float p_center_z, const float p_left_z, const float p_right_z, const float p_top_z, const float p_bottom_z) {
|
||||
// slope-sensitive depth-based edge detection
|
||||
vec4 edgesLRTB = vec4(p_left_z, p_right_z, p_top_z, p_bottom_z) - p_center_z;
|
||||
vec4 edgesLRTB_slope_adjusted = edgesLRTB + edgesLRTB.yxwz;
|
||||
edgesLRTB = min(abs(edgesLRTB), abs(edgesLRTB_slope_adjusted));
|
||||
return clamp((1.3 - edgesLRTB / (p_center_z * 0.040)), 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 load_normal(ivec2 p_pos) {
|
||||
vec3 encoded_normal = normalize(imageLoad(source_normal, p_pos).xyz * 2.0 - 1.0);
|
||||
encoded_normal.z = -encoded_normal.z;
|
||||
return encoded_normal;
|
||||
}
|
||||
|
||||
vec3 load_normal(ivec2 p_pos, ivec2 p_offset) {
|
||||
vec3 encoded_normal = normalize(imageLoad(source_normal, p_pos + p_offset).xyz * 2.0 - 1.0);
|
||||
encoded_normal.z = -encoded_normal.z;
|
||||
return encoded_normal;
|
||||
}
|
||||
|
||||
// all vectors in viewspace
|
||||
float calculate_pixel_obscurance(vec3 p_pixel_normal, vec3 p_hit_delta, float p_fallof_sq) {
|
||||
float length_sq = dot(p_hit_delta, p_hit_delta);
|
||||
float NdotD = dot(p_pixel_normal, p_hit_delta) / sqrt(length_sq);
|
||||
|
||||
float falloff_mult = max(0.0, length_sq * p_fallof_sq + 1.0);
|
||||
|
||||
return max(0, NdotD - 0.05) * falloff_mult;
|
||||
}
|
||||
|
||||
void SSIL_tap_inner(const int p_quality_level, inout vec3 r_color_sum, inout float r_obscurance_sum, inout float r_weight_sum, const vec2 p_sampling_uv, const float p_mip_level, const vec3 p_pix_center_pos, vec3 p_pixel_normal, const float p_fallof_sq, const float p_weight_mod) {
|
||||
// get depth at sample
|
||||
float viewspace_sample_z = textureLod(source_depth_mipmaps, vec3(p_sampling_uv, params.pass), p_mip_level).x;
|
||||
vec3 sample_normal = load_normal(ivec2(p_sampling_uv * vec2(params.screen_size)));
|
||||
|
||||
// convert to viewspace
|
||||
vec3 hit_pos = NDC_to_view_space(p_sampling_uv.xy, viewspace_sample_z);
|
||||
vec3 hit_delta = hit_pos - p_pix_center_pos;
|
||||
|
||||
float obscurance = calculate_pixel_obscurance(p_pixel_normal, hit_delta, p_fallof_sq);
|
||||
float weight = 1.0;
|
||||
|
||||
if (p_quality_level >= SSIL_HALOING_REDUCTION_ENABLE_AT_QUALITY_PRESET) {
|
||||
float reduct = max(0, -hit_delta.z);
|
||||
reduct = clamp(reduct * params.neg_inv_radius + 2.0, 0.0, 1.0);
|
||||
weight = SSIL_HALOING_REDUCTION_AMOUNT * reduct + (1.0 - SSIL_HALOING_REDUCTION_AMOUNT);
|
||||
}
|
||||
|
||||
// Translate sampling_uv to last screen's coordinates
|
||||
const vec4 sample_pos = projection_constants.reprojection * vec4(p_sampling_uv * 2.0 - 1.0, (viewspace_sample_z - params.z_near) / (params.z_far - params.z_near) * 2.0 - 1.0, 1.0);
|
||||
vec2 reprojected_sampling_uv = (sample_pos.xy / sample_pos.w) * 0.5 + 0.5;
|
||||
|
||||
weight *= p_weight_mod;
|
||||
|
||||
r_obscurance_sum += obscurance * weight;
|
||||
|
||||
vec3 sample_color = textureLod(last_frame, reprojected_sampling_uv, 5.0).rgb;
|
||||
// Reduce impact of fireflies by tonemapping before averaging: http://graphicrants.blogspot.com/2013/12/tone-mapping.html
|
||||
sample_color /= (1.0 + dot(sample_color, vec3(0.299, 0.587, 0.114)));
|
||||
r_color_sum += sample_color * obscurance * weight * mix(1.0, smoothstep(0.0, 0.1, -dot(sample_normal, normalize(hit_delta))), params.normal_rejection_amount);
|
||||
r_weight_sum += weight;
|
||||
}
|
||||
|
||||
void SSILTap(const int p_quality_level, inout vec3 r_color_sum, inout float r_obscurance_sum, inout float r_weight_sum, const int p_tap_index, const mat2 p_rot_scale, const vec3 p_pix_center_pos, vec3 p_pixel_normal, const vec2 p_normalized_screen_pos, const float p_mip_offset, const float p_fallof_sq, float p_weight_mod, vec2 p_norm_xy, float p_norm_xy_length) {
|
||||
vec2 sample_offset;
|
||||
float sample_pow_2_len;
|
||||
|
||||
// patterns
|
||||
{
|
||||
vec4 new_sample = sample_pattern[p_tap_index];
|
||||
sample_offset = new_sample.xy * p_rot_scale;
|
||||
sample_pow_2_len = new_sample.w; // precalculated, same as: sample_pow_2_len = log2( length( new_sample.xy ) );
|
||||
p_weight_mod *= new_sample.z;
|
||||
}
|
||||
|
||||
// snap to pixel center (more correct obscurance math, avoids artifacts)
|
||||
sample_offset = round(sample_offset);
|
||||
|
||||
// calculate MIP based on the sample distance from the center, similar to as described
|
||||
// in http://graphics.cs.williams.edu/papers/SAOHPG12/.
|
||||
float mip_level = (p_quality_level < SSIL_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET) ? (0) : (sample_pow_2_len + p_mip_offset);
|
||||
|
||||
vec2 sampling_uv = sample_offset * params.half_screen_pixel_size + p_normalized_screen_pos;
|
||||
|
||||
SSIL_tap_inner(p_quality_level, r_color_sum, r_obscurance_sum, r_weight_sum, sampling_uv, mip_level, p_pix_center_pos, p_pixel_normal, p_fallof_sq, p_weight_mod);
|
||||
|
||||
// for the second tap, just use the mirrored offset
|
||||
vec2 sample_offset_mirrored_uv = -sample_offset;
|
||||
|
||||
// tilt the second set of samples so that the disk is effectively rotated by the normal
|
||||
// effective at removing one set of artifacts, but too expensive for lower quality settings
|
||||
if (p_quality_level >= SSIL_TILT_SAMPLES_ENABLE_AT_QUALITY_PRESET) {
|
||||
float dot_norm = dot(sample_offset_mirrored_uv, p_norm_xy);
|
||||
sample_offset_mirrored_uv -= dot_norm * p_norm_xy_length * p_norm_xy;
|
||||
sample_offset_mirrored_uv = round(sample_offset_mirrored_uv);
|
||||
}
|
||||
|
||||
// snap to pixel center (more correct obscurance math, avoids artifacts)
|
||||
vec2 sampling_mirrored_uv = sample_offset_mirrored_uv * params.half_screen_pixel_size + p_normalized_screen_pos;
|
||||
|
||||
SSIL_tap_inner(p_quality_level, r_color_sum, r_obscurance_sum, r_weight_sum, sampling_mirrored_uv, mip_level, p_pix_center_pos, p_pixel_normal, p_fallof_sq, p_weight_mod);
|
||||
}
|
||||
|
||||
void generate_SSIL(out vec3 r_color, out vec4 r_edges, out float r_obscurance, out float r_weight, const vec2 p_pos, int p_quality_level, bool p_adaptive_base) {
|
||||
vec2 pos_rounded = trunc(p_pos);
|
||||
uvec2 upos = uvec2(pos_rounded);
|
||||
|
||||
const int number_of_taps = (p_adaptive_base) ? (SSIL_ADAPTIVE_TAP_BASE_COUNT) : (num_taps[p_quality_level]);
|
||||
float pix_z, pix_left_z, pix_top_z, pix_right_z, pix_bottom_z;
|
||||
|
||||
vec4 valuesUL = textureGather(source_depth_mipmaps, vec3(pos_rounded * params.half_screen_pixel_size, params.pass));
|
||||
vec4 valuesBR = textureGather(source_depth_mipmaps, vec3((pos_rounded + vec2(1.0)) * params.half_screen_pixel_size, params.pass));
|
||||
|
||||
// get this pixel's viewspace depth
|
||||
pix_z = valuesUL.y;
|
||||
|
||||
// get left right top bottom neighboring pixels for edge detection (gets compiled out on quality_level == 0)
|
||||
pix_left_z = valuesUL.x;
|
||||
pix_top_z = valuesUL.z;
|
||||
pix_right_z = valuesBR.z;
|
||||
pix_bottom_z = valuesBR.x;
|
||||
|
||||
vec2 normalized_screen_pos = pos_rounded * params.half_screen_pixel_size + params.half_screen_pixel_size_x025;
|
||||
vec3 pix_center_pos = NDC_to_view_space(normalized_screen_pos, pix_z);
|
||||
|
||||
// Load this pixel's viewspace normal
|
||||
uvec2 full_res_coord = upos * 2 * params.size_multiplier + params.pass_coord_offset.xy;
|
||||
vec3 pixel_normal = load_normal(ivec2(full_res_coord));
|
||||
|
||||
const vec2 pixel_size_at_center = NDC_to_view_space(normalized_screen_pos.xy + params.half_screen_pixel_size, pix_center_pos.z).xy - pix_center_pos.xy;
|
||||
|
||||
float pixel_lookup_radius;
|
||||
float fallof_sq;
|
||||
|
||||
// calculate effect radius and fit our screen sampling pattern inside it
|
||||
float viewspace_radius;
|
||||
calculate_radius_parameters(length(pix_center_pos), pixel_size_at_center, pixel_lookup_radius, viewspace_radius, fallof_sq);
|
||||
|
||||
// calculate samples rotation/scaling
|
||||
mat2 rot_scale_matrix;
|
||||
uint pseudo_random_index;
|
||||
|
||||
{
|
||||
vec4 rotation_scale;
|
||||
// reduce effect radius near the screen edges slightly; ideally, one would render a larger depth buffer (5% on each side) instead
|
||||
if (!p_adaptive_base && (p_quality_level >= SSIL_REDUCE_RADIUS_NEAR_SCREEN_BORDER_ENABLE_AT_QUALITY_PRESET)) {
|
||||
float near_screen_border = min(min(normalized_screen_pos.x, 1.0 - normalized_screen_pos.x), min(normalized_screen_pos.y, 1.0 - normalized_screen_pos.y));
|
||||
near_screen_border = clamp(10.0 * near_screen_border + 0.6, 0.0, 1.0);
|
||||
pixel_lookup_radius *= near_screen_border;
|
||||
}
|
||||
|
||||
// load & update pseudo-random rotation matrix
|
||||
pseudo_random_index = uint(pos_rounded.y * 2 + pos_rounded.x) % 5;
|
||||
rotation_scale = constants.rotation_matrices[params.pass * 5 + pseudo_random_index];
|
||||
rot_scale_matrix = mat2(rotation_scale.x * pixel_lookup_radius, rotation_scale.y * pixel_lookup_radius, rotation_scale.z * pixel_lookup_radius, rotation_scale.w * pixel_lookup_radius);
|
||||
}
|
||||
|
||||
// the main obscurance & sample weight storage
|
||||
vec3 color_sum = vec3(0.0);
|
||||
float obscurance_sum = 0.0;
|
||||
float weight_sum = 0.0;
|
||||
|
||||
// edge mask for between this and left/right/top/bottom neighbor pixels - not used in quality level 0 so initialize to "no edge" (1 is no edge, 0 is edge)
|
||||
vec4 edgesLRTB = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
// Move center pixel slightly towards camera to avoid imprecision artifacts due to using of 16bit depth buffer.
|
||||
pix_center_pos *= 0.99;
|
||||
|
||||
if (!p_adaptive_base && (p_quality_level >= SSIL_DEPTH_BASED_EDGES_ENABLE_AT_QUALITY_PRESET)) {
|
||||
edgesLRTB = calculate_edges(pix_z, pix_left_z, pix_right_z, pix_top_z, pix_bottom_z);
|
||||
}
|
||||
|
||||
const float global_mip_offset = SSIL_DEPTH_MIPS_GLOBAL_OFFSET;
|
||||
float mip_offset = (p_quality_level < SSIL_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET) ? (0) : (log2(pixel_lookup_radius) + global_mip_offset);
|
||||
|
||||
// Used to tilt the second set of samples so that the disk is effectively rotated by the normal
|
||||
// effective at removing one set of artifacts, but too expensive for lower quality settings
|
||||
vec2 norm_xy = vec2(pixel_normal.x, pixel_normal.y);
|
||||
float norm_xy_length = length(norm_xy);
|
||||
norm_xy /= vec2(norm_xy_length, -norm_xy_length);
|
||||
norm_xy_length *= SSIL_TILT_SAMPLES_AMOUNT;
|
||||
|
||||
// standard, non-adaptive approach
|
||||
if ((p_quality_level != 3) || p_adaptive_base) {
|
||||
for (int i = 0; i < number_of_taps; i++) {
|
||||
SSILTap(p_quality_level, color_sum, obscurance_sum, weight_sum, i, rot_scale_matrix, pix_center_pos, pixel_normal, normalized_screen_pos, mip_offset, fallof_sq, 1.0, norm_xy, norm_xy_length);
|
||||
}
|
||||
}
|
||||
#ifdef ADAPTIVE
|
||||
else {
|
||||
// add new ones if needed
|
||||
vec2 full_res_uv = normalized_screen_pos + params.pass_uv_offset.xy;
|
||||
float importance = textureLod(source_importance, full_res_uv, 0.0).x;
|
||||
|
||||
//Need to store obscurance from base pass
|
||||
// load existing base values
|
||||
vec4 base_values = imageLoad(source_ssil, ivec3(upos, params.pass));
|
||||
weight_sum += imageLoad(edges_weights_image, ivec2(upos)).r * float(SSIL_ADAPTIVE_TAP_BASE_COUNT * 4.0);
|
||||
color_sum += (base_values.rgb) * weight_sum;
|
||||
obscurance_sum += (base_values.a) * weight_sum;
|
||||
|
||||
// increase importance around edges
|
||||
float edge_count = dot(1.0 - edgesLRTB, vec4(1.0, 1.0, 1.0, 1.0));
|
||||
|
||||
float avg_total_importance = float(counter.sum) * params.load_counter_avg_div;
|
||||
|
||||
float importance_limiter = clamp(params.adaptive_sample_limit / avg_total_importance, 0.0, 1.0);
|
||||
importance *= importance_limiter;
|
||||
|
||||
float additional_sample_count = SSIL_ADAPTIVE_TAP_FLEXIBLE_COUNT * importance;
|
||||
|
||||
const float blend_range = 3.0;
|
||||
const float blend_range_inv = 1.0 / blend_range;
|
||||
|
||||
additional_sample_count += 0.5;
|
||||
uint additional_samples = uint(additional_sample_count);
|
||||
uint additional_samples_to = min(SSIL_MAX_TAPS, additional_samples + SSIL_ADAPTIVE_TAP_BASE_COUNT);
|
||||
|
||||
for (uint i = SSIL_ADAPTIVE_TAP_BASE_COUNT; i < additional_samples_to; i++) {
|
||||
additional_sample_count -= 1.0f;
|
||||
float weight_mod = clamp(additional_sample_count * blend_range_inv, 0.0, 1.0);
|
||||
SSILTap(p_quality_level, color_sum, obscurance_sum, weight_sum, int(i), rot_scale_matrix, pix_center_pos, pixel_normal, normalized_screen_pos, mip_offset, fallof_sq, weight_mod, norm_xy, norm_xy_length);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Early out for adaptive base
|
||||
if (p_adaptive_base) {
|
||||
vec3 color = color_sum / weight_sum;
|
||||
|
||||
r_color = color;
|
||||
r_edges = vec4(0.0);
|
||||
r_obscurance = obscurance_sum / weight_sum;
|
||||
r_weight = weight_sum;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate weighted average
|
||||
vec3 color = color_sum / weight_sum;
|
||||
color /= 1.0 - dot(color, vec3(0.299, 0.587, 0.114));
|
||||
|
||||
// Calculate fadeout (1 close, gradient, 0 far)
|
||||
float fade_out = clamp(pix_center_pos.z * params.fade_out_mul + params.fade_out_add, 0.0, 1.0);
|
||||
|
||||
// Reduce the SSIL if we're on the edge to remove artifacts on edges (we don't care for the lower quality one)
|
||||
if (!p_adaptive_base && (p_quality_level >= SSIL_DEPTH_BASED_EDGES_ENABLE_AT_QUALITY_PRESET)) {
|
||||
// when there's more than 2 opposite edges, start fading out the occlusion to reduce aliasing artifacts
|
||||
float edge_fadeout_factor = clamp((1.0 - edgesLRTB.x - edgesLRTB.y) * 0.35, 0.0, 1.0) + clamp((1.0 - edgesLRTB.z - edgesLRTB.w) * 0.35, 0.0, 1.0);
|
||||
|
||||
fade_out *= clamp(1.0 - edge_fadeout_factor, 0.0, 1.0);
|
||||
}
|
||||
|
||||
color = params.intensity * color;
|
||||
|
||||
color *= fade_out;
|
||||
|
||||
// outputs!
|
||||
r_color = color;
|
||||
r_edges = edgesLRTB; // These are used to prevent blurring across edges, 1 means no edge, 0 means edge, 0.5 means half way there, etc.
|
||||
r_obscurance = clamp((obscurance_sum / weight_sum) * params.intensity, 0.0, 1.0);
|
||||
r_weight = weight_sum;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 out_color;
|
||||
float out_obscurance;
|
||||
float out_weight;
|
||||
vec4 out_edges;
|
||||
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(ssC, params.screen_size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 uv = vec2(gl_GlobalInvocationID) + vec2(0.5);
|
||||
#ifdef SSIL_BASE
|
||||
generate_SSIL(out_color, out_edges, out_obscurance, out_weight, uv, params.quality, true);
|
||||
|
||||
imageStore(dest_image, ssC, vec4(out_color, out_obscurance));
|
||||
imageStore(edges_weights_image, ssC, vec4(out_weight / (float(SSIL_ADAPTIVE_TAP_BASE_COUNT) * 4.0)));
|
||||
#else
|
||||
generate_SSIL(out_color, out_edges, out_obscurance, out_weight, uv, params.quality, false); // pass in quality levels
|
||||
|
||||
imageStore(dest_image, ssC, vec4(out_color, out_obscurance));
|
||||
imageStore(edges_weights_image, ssC, vec4(pack_edges(out_edges)));
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) 2016, Intel Corporation
|
||||
// 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.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// File changes (yyyy-mm-dd)
|
||||
// 2016-09-07: filip.strugar@intel.com: first commit
|
||||
// 2020-12-05: clayjohn: convert to Vulkan and Godot
|
||||
// 2021-05-27: clayjohn: convert SSAO to SSIL
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[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_ssil;
|
||||
|
||||
layout(rgba16, set = 1, binding = 0) uniform restrict writeonly image2D dest_image;
|
||||
|
||||
layout(r8, set = 2, binding = 0) uniform restrict readonly image2D source_edges;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float edge_sharpness;
|
||||
float pad;
|
||||
vec2 half_screen_pixel_size;
|
||||
}
|
||||
params;
|
||||
|
||||
vec4 unpack_edges(float p_packed_val) {
|
||||
uint packed_val = uint(p_packed_val * 255.5);
|
||||
vec4 edgesLRTB;
|
||||
edgesLRTB.x = float((packed_val >> 6) & 0x03) / 3.0;
|
||||
edgesLRTB.y = float((packed_val >> 4) & 0x03) / 3.0;
|
||||
edgesLRTB.z = float((packed_val >> 2) & 0x03) / 3.0;
|
||||
edgesLRTB.w = float((packed_val >> 0) & 0x03) / 3.0;
|
||||
|
||||
return clamp(edgesLRTB + params.edge_sharpness, 0.0, 1.0);
|
||||
}
|
||||
|
||||
void add_sample(vec4 p_ssil_value, float p_edge_value, inout vec4 r_sum, inout float r_sum_weight) {
|
||||
float weight = p_edge_value;
|
||||
|
||||
r_sum += (weight * p_ssil_value);
|
||||
r_sum_weight += weight;
|
||||
}
|
||||
|
||||
#ifdef MODE_WIDE
|
||||
vec4 sample_blurred_wide(ivec2 p_pos, vec2 p_coord) {
|
||||
vec4 ssil_value = textureLodOffset(source_ssil, vec2(p_coord), 0.0, ivec2(0, 0));
|
||||
vec4 ssil_valueL = textureLodOffset(source_ssil, vec2(p_coord), 0.0, ivec2(-2, 0));
|
||||
vec4 ssil_valueT = textureLodOffset(source_ssil, vec2(p_coord), 0.0, ivec2(0, -2));
|
||||
vec4 ssil_valueR = textureLodOffset(source_ssil, vec2(p_coord), 0.0, ivec2(2, 0));
|
||||
vec4 ssil_valueB = textureLodOffset(source_ssil, vec2(p_coord), 0.0, ivec2(0, 2));
|
||||
|
||||
vec4 edgesLRTB = unpack_edges(imageLoad(source_edges, p_pos).r);
|
||||
edgesLRTB.x *= unpack_edges(imageLoad(source_edges, p_pos + ivec2(-2, 0)).r).y;
|
||||
edgesLRTB.z *= unpack_edges(imageLoad(source_edges, p_pos + ivec2(0, -2)).r).w;
|
||||
edgesLRTB.y *= unpack_edges(imageLoad(source_edges, p_pos + ivec2(2, 0)).r).x;
|
||||
edgesLRTB.w *= unpack_edges(imageLoad(source_edges, p_pos + ivec2(0, 2)).r).z;
|
||||
|
||||
float sum_weight = 0.8;
|
||||
vec4 sum = ssil_value * sum_weight;
|
||||
|
||||
add_sample(ssil_valueL, edgesLRTB.x, sum, sum_weight);
|
||||
add_sample(ssil_valueR, edgesLRTB.y, sum, sum_weight);
|
||||
add_sample(ssil_valueT, edgesLRTB.z, sum, sum_weight);
|
||||
add_sample(ssil_valueB, edgesLRTB.w, sum, sum_weight);
|
||||
|
||||
vec4 ssil_avg = sum / sum_weight;
|
||||
|
||||
ssil_value = ssil_avg;
|
||||
|
||||
return ssil_value;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MODE_SMART
|
||||
vec4 sample_blurred(ivec2 p_pos, vec2 p_coord) {
|
||||
vec4 vC = textureLodOffset(source_ssil, vec2(p_coord), 0.0, ivec2(0, 0));
|
||||
vec4 vL = textureLodOffset(source_ssil, vec2(p_coord), 0.0, ivec2(-1, 0));
|
||||
vec4 vT = textureLodOffset(source_ssil, vec2(p_coord), 0.0, ivec2(0, -1));
|
||||
vec4 vR = textureLodOffset(source_ssil, vec2(p_coord), 0.0, ivec2(1, 0));
|
||||
vec4 vB = textureLodOffset(source_ssil, vec2(p_coord), 0.0, ivec2(0, 1));
|
||||
|
||||
float packed_edges = imageLoad(source_edges, p_pos).r;
|
||||
vec4 edgesLRTB = unpack_edges(packed_edges);
|
||||
|
||||
float sum_weight = 0.5;
|
||||
vec4 sum = vC * sum_weight;
|
||||
|
||||
add_sample(vL, edgesLRTB.x, sum, sum_weight);
|
||||
add_sample(vR, edgesLRTB.y, sum, sum_weight);
|
||||
add_sample(vT, edgesLRTB.z, sum, sum_weight);
|
||||
add_sample(vB, edgesLRTB.w, sum, sum_weight);
|
||||
|
||||
vec4 ssil_avg = sum / sum_weight;
|
||||
|
||||
vec4 ssil_value = ssil_avg;
|
||||
|
||||
return ssil_value;
|
||||
}
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
// Pixel being shaded
|
||||
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
#ifdef MODE_NON_SMART
|
||||
|
||||
vec2 half_pixel = params.half_screen_pixel_size * 0.5;
|
||||
|
||||
vec2 uv = (vec2(gl_GlobalInvocationID.xy) + vec2(0.5, 0.5)) * params.half_screen_pixel_size;
|
||||
|
||||
vec4 center = textureLod(source_ssil, uv, 0.0);
|
||||
|
||||
vec4 value = textureLod(source_ssil, vec2(uv + vec2(-half_pixel.x * 3, -half_pixel.y)), 0.0) * 0.2;
|
||||
value += textureLod(source_ssil, vec2(uv + vec2(+half_pixel.x, -half_pixel.y * 3)), 0.0) * 0.2;
|
||||
value += textureLod(source_ssil, vec2(uv + vec2(-half_pixel.x, +half_pixel.y * 3)), 0.0) * 0.2;
|
||||
value += textureLod(source_ssil, vec2(uv + vec2(+half_pixel.x * 3, +half_pixel.y)), 0.0) * 0.2;
|
||||
|
||||
vec4 sampled = value + center * 0.2;
|
||||
|
||||
#else
|
||||
#ifdef MODE_SMART
|
||||
vec4 sampled = sample_blurred(ssC, (vec2(gl_GlobalInvocationID.xy) + vec2(0.5, 0.5)) * params.half_screen_pixel_size);
|
||||
#else // MODE_WIDE
|
||||
vec4 sampled = sample_blurred_wide(ssC, (vec2(gl_GlobalInvocationID.xy) + vec2(0.5, 0.5)) * params.half_screen_pixel_size);
|
||||
#endif
|
||||
#endif // MODE_NON_SMART
|
||||
imageStore(dest_image, ssC, sampled);
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) 2016, Intel Corporation
|
||||
// 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.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// File changes (yyyy-mm-dd)
|
||||
// 2016-09-07: filip.strugar@intel.com: first commit
|
||||
// 2020-12-05: clayjohn: convert to Vulkan and Godot
|
||||
// 2021-05-27: clayjohn: convert SSAO to SSIL
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
#ifdef GENERATE_MAP
|
||||
layout(set = 0, binding = 0) uniform sampler2DArray source_texture;
|
||||
#else
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_importance;
|
||||
#endif
|
||||
layout(r8, set = 1, binding = 0) uniform restrict writeonly image2D dest_image;
|
||||
|
||||
#ifdef PROCESS_MAPB
|
||||
layout(set = 2, binding = 0, std430) buffer Counter {
|
||||
uint sum;
|
||||
}
|
||||
counter;
|
||||
#endif
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec2 half_screen_pixel_size;
|
||||
float intensity;
|
||||
float pad;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
// Pixel being shaded
|
||||
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
#ifdef GENERATE_MAP
|
||||
// importance map stuff
|
||||
uvec2 base_position = ssC * 2;
|
||||
|
||||
float avg = 0.0;
|
||||
float minV = 1.0;
|
||||
float maxV = 0.0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vec3 value_a = texelFetch(source_texture, ivec3(base_position, i), 0).rgb * params.intensity;
|
||||
vec3 value_b = texelFetch(source_texture, ivec3(base_position, i) + ivec3(0, 1, 0), 0).rgb * params.intensity;
|
||||
vec3 value_c = texelFetch(source_texture, ivec3(base_position, i) + ivec3(1, 0, 0), 0).rgb * params.intensity;
|
||||
vec3 value_d = texelFetch(source_texture, ivec3(base_position, i) + ivec3(1, 1, 0), 0).rgb * params.intensity;
|
||||
|
||||
// Calculate luminance (black and white value)
|
||||
float a = dot(value_a, vec3(0.2125, 0.7154, 0.0721));
|
||||
float b = dot(value_b, vec3(0.2125, 0.7154, 0.0721));
|
||||
float c = dot(value_c, vec3(0.2125, 0.7154, 0.0721));
|
||||
float d = dot(value_d, vec3(0.2125, 0.7154, 0.0721));
|
||||
|
||||
maxV = max(maxV, max(max(a, b), max(c, d)));
|
||||
minV = min(minV, min(min(a, b), min(c, d)));
|
||||
}
|
||||
|
||||
float min_max_diff = maxV - minV;
|
||||
|
||||
imageStore(dest_image, ssC, vec4(pow(clamp(min_max_diff * 2.0, 0.0, 1.0), 0.6)));
|
||||
#endif
|
||||
|
||||
#ifdef PROCESS_MAPA
|
||||
vec2 uv = (vec2(ssC) + 0.5) * params.half_screen_pixel_size * 2.0;
|
||||
|
||||
float center = textureLod(source_importance, uv, 0.0).x;
|
||||
|
||||
vec2 half_pixel = params.half_screen_pixel_size;
|
||||
|
||||
vec4 vals;
|
||||
vals.x = textureLod(source_importance, uv + vec2(-half_pixel.x * 3, -half_pixel.y), 0.0).x;
|
||||
vals.y = textureLod(source_importance, uv + vec2(+half_pixel.x, -half_pixel.y * 3), 0.0).x;
|
||||
vals.z = textureLod(source_importance, uv + vec2(+half_pixel.x * 3, +half_pixel.y), 0.0).x;
|
||||
vals.w = textureLod(source_importance, uv + vec2(-half_pixel.x, +half_pixel.y * 3), 0.0).x;
|
||||
|
||||
float avg = dot(vals, vec4(0.25, 0.25, 0.25, 0.25));
|
||||
|
||||
imageStore(dest_image, ssC, vec4(avg));
|
||||
#endif
|
||||
|
||||
#ifdef PROCESS_MAPB
|
||||
vec2 uv = (vec2(ssC) + 0.5f) * params.half_screen_pixel_size * 2.0;
|
||||
|
||||
float center = textureLod(source_importance, uv, 0.0).x;
|
||||
|
||||
vec2 half_pixel = params.half_screen_pixel_size;
|
||||
|
||||
vec4 vals;
|
||||
vals.x = textureLod(source_importance, uv + vec2(-half_pixel.x, -half_pixel.y * 3), 0.0).x;
|
||||
vals.y = textureLod(source_importance, uv + vec2(+half_pixel.x * 3, -half_pixel.y), 0.0).x;
|
||||
vals.z = textureLod(source_importance, uv + vec2(+half_pixel.x, +half_pixel.y * 3), 0.0).x;
|
||||
vals.w = textureLod(source_importance, uv + vec2(-half_pixel.x * 3, +half_pixel.y), 0.0).x;
|
||||
|
||||
float avg = dot(vals, vec4(0.25, 0.25, 0.25, 0.25));
|
||||
|
||||
imageStore(dest_image, ssC, vec4(avg));
|
||||
|
||||
// sum the average; to avoid overflowing we assume max AO resolution is not bigger than 16384x16384; so quarter res (used here) will be 4096x4096, which leaves us with 8 bits per pixel
|
||||
uint sum = uint(clamp(avg, 0.0, 1.0) * 255.0 + 0.5);
|
||||
|
||||
// save every 9th to avoid InterlockedAdd congestion - since we're blurring, this is good enough; compensated by multiplying load_counter_avg_div by 9
|
||||
if (((ssC.x % 3) + (ssC.y % 3)) == 0) {
|
||||
atomicAdd(counter.sum, sum);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) 2016, Intel Corporation
|
||||
// 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.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// File changes (yyyy-mm-dd)
|
||||
// 2016-09-07: filip.strugar@intel.com: first commit
|
||||
// 2020-12-05: clayjohn: convert to Vulkan and Godot
|
||||
// 2021-05-27: clayjohn: convert SSAO to SSIL
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(rgba16, set = 0, binding = 0) uniform restrict writeonly image2D dest_image;
|
||||
layout(set = 1, binding = 0) uniform sampler2DArray source_texture;
|
||||
layout(r8, set = 2, binding = 0) uniform restrict readonly image2DArray source_edges;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float inv_sharpness;
|
||||
uint size_modifier;
|
||||
vec2 pixel_size;
|
||||
}
|
||||
params;
|
||||
|
||||
vec4 unpack_edges(float p_packed_val) {
|
||||
uint packed_val = uint(p_packed_val * 255.5);
|
||||
vec4 edgesLRTB;
|
||||
edgesLRTB.x = float((packed_val >> 6) & 0x03) / 3.0;
|
||||
edgesLRTB.y = float((packed_val >> 4) & 0x03) / 3.0;
|
||||
edgesLRTB.z = float((packed_val >> 2) & 0x03) / 3.0;
|
||||
edgesLRTB.w = float((packed_val >> 0) & 0x03) / 3.0;
|
||||
|
||||
return clamp(edgesLRTB + params.inv_sharpness, 0.0, 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(ssC, ivec2(1.0 / params.pixel_size)))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MODE_SMART
|
||||
uvec2 pix_pos = uvec2(gl_GlobalInvocationID.xy);
|
||||
vec2 uv = (gl_GlobalInvocationID.xy + vec2(0.5)) * params.pixel_size;
|
||||
|
||||
// calculate index in the four deinterleaved source array texture
|
||||
int mx = int(pix_pos.x % 2);
|
||||
int my = int(pix_pos.y % 2);
|
||||
int index_center = mx + my * 2; // center index
|
||||
int index_horizontal = (1 - mx) + my * 2; // neighboring, horizontal
|
||||
int index_vertical = mx + (1 - my) * 2; // neighboring, vertical
|
||||
int index_diagonal = (1 - mx) + (1 - my) * 2; // diagonal
|
||||
|
||||
vec4 color = texelFetch(source_texture, ivec3(pix_pos / uvec2(params.size_modifier), index_center), 0);
|
||||
|
||||
vec4 edgesLRTB = unpack_edges(imageLoad(source_edges, ivec3(pix_pos / uvec2(params.size_modifier), index_center)).r);
|
||||
|
||||
// convert index shifts to sampling offsets
|
||||
float fmx = float(mx);
|
||||
float fmy = float(my);
|
||||
|
||||
// in case of an edge, push sampling offsets away from the edge (towards pixel center)
|
||||
float fmxe = (edgesLRTB.y - edgesLRTB.x);
|
||||
float fmye = (edgesLRTB.w - edgesLRTB.z);
|
||||
|
||||
// calculate final sampling offsets and sample using bilinear filter
|
||||
vec2 uv_horizontal = (gl_GlobalInvocationID.xy + vec2(0.5) + vec2(fmx + fmxe - 0.5, 0.5 - fmy)) * params.pixel_size;
|
||||
vec4 color_horizontal = textureLod(source_texture, vec3(uv_horizontal, index_horizontal), 0.0);
|
||||
vec2 uv_vertical = (gl_GlobalInvocationID.xy + vec2(0.5) + vec2(0.5 - fmx, fmy - 0.5 + fmye)) * params.pixel_size;
|
||||
vec4 color_vertical = textureLod(source_texture, vec3(uv_vertical, index_vertical), 0.0);
|
||||
vec2 uv_diagonal = (gl_GlobalInvocationID.xy + vec2(0.5) + vec2(fmx - 0.5 + fmxe, fmy - 0.5 + fmye)) * params.pixel_size;
|
||||
vec4 color_diagonal = textureLod(source_texture, vec3(uv_diagonal, index_diagonal), 0.0);
|
||||
|
||||
// reduce weight for samples near edge - if the edge is on both sides, weight goes to 0
|
||||
vec4 blendWeights;
|
||||
blendWeights.x = 1.0;
|
||||
blendWeights.y = (edgesLRTB.x + edgesLRTB.y) * 0.5;
|
||||
blendWeights.z = (edgesLRTB.z + edgesLRTB.w) * 0.5;
|
||||
blendWeights.w = (blendWeights.y + blendWeights.z) * 0.5;
|
||||
|
||||
// calculate weighted average
|
||||
float blendWeightsSum = dot(blendWeights, vec4(1.0, 1.0, 1.0, 1.0));
|
||||
color += color_horizontal * blendWeights.y;
|
||||
color += color_vertical * blendWeights.z;
|
||||
color += color_diagonal * blendWeights.w;
|
||||
color /= blendWeightsSum;
|
||||
|
||||
imageStore(dest_image, ivec2(gl_GlobalInvocationID.xy), color);
|
||||
#else // !MODE_SMART
|
||||
|
||||
vec2 uv = (gl_GlobalInvocationID.xy + vec2(0.5)) * params.pixel_size;
|
||||
#ifdef MODE_HALF
|
||||
vec4 a = textureLod(source_texture, vec3(uv, 0), 0.0);
|
||||
vec4 d = textureLod(source_texture, vec3(uv, 3), 0.0);
|
||||
vec4 avg = (a + d) * 0.5;
|
||||
|
||||
#else
|
||||
vec4 a = textureLod(source_texture, vec3(uv, 0), 0.0);
|
||||
vec4 b = textureLod(source_texture, vec3(uv, 1), 0.0);
|
||||
vec4 c = textureLod(source_texture, vec3(uv, 2), 0.0);
|
||||
vec4 d = textureLod(source_texture, vec3(uv, 3), 0.0);
|
||||
vec4 avg = (a + b + c + d) * 0.25;
|
||||
|
||||
#endif
|
||||
imageStore(dest_image, ivec2(gl_GlobalInvocationID.xy), avg);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -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, 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright(c) 2016-2022 Panos Karabelas
|
||||
//
|
||||
// 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.
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// File changes (yyyy-mm-dd)
|
||||
// 2022-05-06: Panos Karabelas: first commit
|
||||
// 2020-12-05: Joan Fons: convert to Vulkan and Godot
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
// Based on Spartan Engine's TAA implementation (without TAA upscale).
|
||||
// <https://github.com/PanosK92/SpartanEngine/blob/a8338d0609b85dc32f3732a5c27fb4463816a3b9/Data/shaders/temporal_antialiasing.hlsl>
|
||||
|
||||
#ifndef MOLTENVK_USED
|
||||
#define USE_SUBGROUPS
|
||||
#endif // MOLTENVK_USED
|
||||
|
||||
#define GROUP_SIZE 8
|
||||
#define FLT_MIN 0.00000001
|
||||
#define FLT_MAX 32767.0
|
||||
#define RPC_9 0.11111111111
|
||||
#define RPC_16 0.0625
|
||||
|
||||
#ifdef USE_SUBGROUPS
|
||||
layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = 1) in;
|
||||
#endif
|
||||
|
||||
layout(rgba16f, set = 0, binding = 0) uniform restrict readonly image2D color_buffer;
|
||||
layout(set = 0, binding = 1) uniform sampler2D depth_buffer;
|
||||
layout(rg16f, set = 0, binding = 2) uniform restrict readonly image2D velocity_buffer;
|
||||
layout(rg16f, set = 0, binding = 3) uniform restrict readonly image2D last_velocity_buffer;
|
||||
layout(set = 0, binding = 4) uniform sampler2D history_buffer;
|
||||
layout(rgba16f, set = 0, binding = 5) uniform restrict writeonly image2D output_buffer;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec2 resolution;
|
||||
float disocclusion_threshold; // 0.1 / max(params.resolution.x, params.resolution.y
|
||||
float disocclusion_scale;
|
||||
}
|
||||
params;
|
||||
|
||||
const ivec2 kOffsets3x3[9] = {
|
||||
ivec2(-1, -1),
|
||||
ivec2(0, -1),
|
||||
ivec2(1, -1),
|
||||
ivec2(-1, 0),
|
||||
ivec2(0, 0),
|
||||
ivec2(1, 0),
|
||||
ivec2(-1, 1),
|
||||
ivec2(0, 1),
|
||||
ivec2(1, 1),
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
THREAD GROUP SHARED MEMORY (LDS)
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
const int kBorderSize = 1;
|
||||
const int kGroupSize = GROUP_SIZE;
|
||||
const int kTileDimension = kGroupSize + kBorderSize * 2;
|
||||
const int kTileDimension2 = kTileDimension * kTileDimension;
|
||||
|
||||
vec3 reinhard(vec3 hdr) {
|
||||
return hdr / (hdr + 1.0);
|
||||
}
|
||||
vec3 reinhard_inverse(vec3 sdr) {
|
||||
return sdr / (1.0 - sdr);
|
||||
}
|
||||
|
||||
float get_depth(ivec2 thread_id) {
|
||||
return texelFetch(depth_buffer, thread_id, 0).r;
|
||||
}
|
||||
|
||||
#ifdef USE_SUBGROUPS
|
||||
shared vec3 tile_color[kTileDimension][kTileDimension];
|
||||
shared float tile_depth[kTileDimension][kTileDimension];
|
||||
|
||||
vec3 load_color(uvec2 group_thread_id) {
|
||||
group_thread_id += kBorderSize;
|
||||
return tile_color[group_thread_id.x][group_thread_id.y];
|
||||
}
|
||||
|
||||
void store_color(uvec2 group_thread_id, vec3 color) {
|
||||
tile_color[group_thread_id.x][group_thread_id.y] = color;
|
||||
}
|
||||
|
||||
float load_depth(uvec2 group_thread_id) {
|
||||
group_thread_id += kBorderSize;
|
||||
return tile_depth[group_thread_id.x][group_thread_id.y];
|
||||
}
|
||||
|
||||
void store_depth(uvec2 group_thread_id, float depth) {
|
||||
tile_depth[group_thread_id.x][group_thread_id.y] = depth;
|
||||
}
|
||||
|
||||
void store_color_depth(uvec2 group_thread_id, ivec2 thread_id) {
|
||||
// out of bounds clamp
|
||||
thread_id = clamp(thread_id, ivec2(0, 0), ivec2(params.resolution) - ivec2(1, 1));
|
||||
|
||||
store_color(group_thread_id, imageLoad(color_buffer, thread_id).rgb);
|
||||
store_depth(group_thread_id, get_depth(thread_id));
|
||||
}
|
||||
|
||||
void populate_group_shared_memory(uvec2 group_id, uint group_index) {
|
||||
// Populate group shared memory
|
||||
ivec2 group_top_left = ivec2(group_id) * kGroupSize - kBorderSize;
|
||||
if (group_index < (kTileDimension2 >> 2)) {
|
||||
ivec2 group_thread_id_1 = ivec2(group_index % kTileDimension, group_index / kTileDimension);
|
||||
ivec2 group_thread_id_2 = ivec2((group_index + (kTileDimension2 >> 2)) % kTileDimension, (group_index + (kTileDimension2 >> 2)) / kTileDimension);
|
||||
ivec2 group_thread_id_3 = ivec2((group_index + (kTileDimension2 >> 1)) % kTileDimension, (group_index + (kTileDimension2 >> 1)) / kTileDimension);
|
||||
ivec2 group_thread_id_4 = ivec2((group_index + kTileDimension2 * 3 / 4) % kTileDimension, (group_index + kTileDimension2 * 3 / 4) / kTileDimension);
|
||||
|
||||
store_color_depth(group_thread_id_1, group_top_left + group_thread_id_1);
|
||||
store_color_depth(group_thread_id_2, group_top_left + group_thread_id_2);
|
||||
store_color_depth(group_thread_id_3, group_top_left + group_thread_id_3);
|
||||
store_color_depth(group_thread_id_4, group_top_left + group_thread_id_4);
|
||||
}
|
||||
|
||||
// Wait for group threads to load store data.
|
||||
groupMemoryBarrier();
|
||||
barrier();
|
||||
}
|
||||
#else
|
||||
vec3 load_color(uvec2 screen_pos) {
|
||||
return imageLoad(color_buffer, ivec2(screen_pos)).rgb;
|
||||
}
|
||||
|
||||
float load_depth(uvec2 screen_pos) {
|
||||
return get_depth(ivec2(screen_pos));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
VELOCITY
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
void depth_test_min(uvec2 pos, inout float min_depth, inout uvec2 min_pos) {
|
||||
float depth = load_depth(pos);
|
||||
|
||||
if (depth < min_depth) {
|
||||
min_depth = depth;
|
||||
min_pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns velocity with closest depth (3x3 neighborhood)
|
||||
void get_closest_pixel_velocity_3x3(in uvec2 group_pos, uvec2 group_top_left, out vec2 velocity) {
|
||||
float min_depth = 1.0;
|
||||
uvec2 min_pos = group_pos;
|
||||
|
||||
depth_test_min(group_pos + kOffsets3x3[0], min_depth, min_pos);
|
||||
depth_test_min(group_pos + kOffsets3x3[1], min_depth, min_pos);
|
||||
depth_test_min(group_pos + kOffsets3x3[2], min_depth, min_pos);
|
||||
depth_test_min(group_pos + kOffsets3x3[3], min_depth, min_pos);
|
||||
depth_test_min(group_pos + kOffsets3x3[4], min_depth, min_pos);
|
||||
depth_test_min(group_pos + kOffsets3x3[5], min_depth, min_pos);
|
||||
depth_test_min(group_pos + kOffsets3x3[6], min_depth, min_pos);
|
||||
depth_test_min(group_pos + kOffsets3x3[7], min_depth, min_pos);
|
||||
depth_test_min(group_pos + kOffsets3x3[8], min_depth, min_pos);
|
||||
|
||||
// Velocity out
|
||||
velocity = imageLoad(velocity_buffer, ivec2(group_top_left + min_pos)).xy;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
HISTORY SAMPLING
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
vec3 sample_catmull_rom_9(sampler2D stex, vec2 uv, vec2 resolution) {
|
||||
// Source: https://gist.github.com/TheRealMJP/c83b8c0f46b63f3a88a5986f4fa982b1
|
||||
// License: https://gist.github.com/TheRealMJP/bc503b0b87b643d3505d41eab8b332ae
|
||||
|
||||
// We're going to sample a 4x4 grid of texels surrounding the target UV coordinate. We'll do this by rounding
|
||||
// down the sample location to get the exact center of our "starting" texel. The starting texel will be at
|
||||
// location [1, 1] in the grid, where [0, 0] is the top left corner.
|
||||
vec2 sample_pos = uv * resolution;
|
||||
vec2 texPos1 = floor(sample_pos - 0.5f) + 0.5f;
|
||||
|
||||
// Compute the fractional offset from our starting texel to our original sample location, which we'll
|
||||
// feed into the Catmull-Rom spline function to get our filter weights.
|
||||
vec2 f = sample_pos - texPos1;
|
||||
|
||||
// Compute the Catmull-Rom weights using the fractional offset that we calculated earlier.
|
||||
// These equations are pre-expanded based on our knowledge of where the texels will be located,
|
||||
// which lets us avoid having to evaluate a piece-wise function.
|
||||
vec2 w0 = f * (-0.5f + f * (1.0f - 0.5f * f));
|
||||
vec2 w1 = 1.0f + f * f * (-2.5f + 1.5f * f);
|
||||
vec2 w2 = f * (0.5f + f * (2.0f - 1.5f * f));
|
||||
vec2 w3 = f * f * (-0.5f + 0.5f * f);
|
||||
|
||||
// Work out weighting factors and sampling offsets that will let us use bilinear filtering to
|
||||
// simultaneously evaluate the middle 2 samples from the 4x4 grid.
|
||||
vec2 w12 = w1 + w2;
|
||||
vec2 offset12 = w2 / (w1 + w2);
|
||||
|
||||
// Compute the final UV coordinates we'll use for sampling the texture
|
||||
vec2 texPos0 = texPos1 - 1.0f;
|
||||
vec2 texPos3 = texPos1 + 2.0f;
|
||||
vec2 texPos12 = texPos1 + offset12;
|
||||
|
||||
texPos0 /= resolution;
|
||||
texPos3 /= resolution;
|
||||
texPos12 /= resolution;
|
||||
|
||||
vec3 result = vec3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
result += textureLod(stex, vec2(texPos0.x, texPos0.y), 0.0).xyz * w0.x * w0.y;
|
||||
result += textureLod(stex, vec2(texPos12.x, texPos0.y), 0.0).xyz * w12.x * w0.y;
|
||||
result += textureLod(stex, vec2(texPos3.x, texPos0.y), 0.0).xyz * w3.x * w0.y;
|
||||
|
||||
result += textureLod(stex, vec2(texPos0.x, texPos12.y), 0.0).xyz * w0.x * w12.y;
|
||||
result += textureLod(stex, vec2(texPos12.x, texPos12.y), 0.0).xyz * w12.x * w12.y;
|
||||
result += textureLod(stex, vec2(texPos3.x, texPos12.y), 0.0).xyz * w3.x * w12.y;
|
||||
|
||||
result += textureLod(stex, vec2(texPos0.x, texPos3.y), 0.0).xyz * w0.x * w3.y;
|
||||
result += textureLod(stex, vec2(texPos12.x, texPos3.y), 0.0).xyz * w12.x * w3.y;
|
||||
result += textureLod(stex, vec2(texPos3.x, texPos3.y), 0.0).xyz * w3.x * w3.y;
|
||||
|
||||
return max(result, 0.0f);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
HISTORY CLIPPING
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
// Based on "Temporal Reprojection Anti-Aliasing" - https://github.com/playdeadgames/temporal
|
||||
vec3 clip_aabb(vec3 aabb_min, vec3 aabb_max, vec3 p, vec3 q) {
|
||||
vec3 r = q - p;
|
||||
vec3 rmax = (aabb_max - p.xyz);
|
||||
vec3 rmin = (aabb_min - p.xyz);
|
||||
|
||||
if (r.x > rmax.x + FLT_MIN)
|
||||
r *= (rmax.x / r.x);
|
||||
if (r.y > rmax.y + FLT_MIN)
|
||||
r *= (rmax.y / r.y);
|
||||
if (r.z > rmax.z + FLT_MIN)
|
||||
r *= (rmax.z / r.z);
|
||||
|
||||
if (r.x < rmin.x - FLT_MIN)
|
||||
r *= (rmin.x / r.x);
|
||||
if (r.y < rmin.y - FLT_MIN)
|
||||
r *= (rmin.y / r.y);
|
||||
if (r.z < rmin.z - FLT_MIN)
|
||||
r *= (rmin.z / r.z);
|
||||
|
||||
return p + r;
|
||||
}
|
||||
|
||||
// Clip history to the neighbourhood of the current sample
|
||||
vec3 clip_history_3x3(uvec2 group_pos, vec3 color_history, vec2 velocity_closest) {
|
||||
// Sample a 3x3 neighbourhood
|
||||
vec3 s1 = load_color(group_pos + kOffsets3x3[0]);
|
||||
vec3 s2 = load_color(group_pos + kOffsets3x3[1]);
|
||||
vec3 s3 = load_color(group_pos + kOffsets3x3[2]);
|
||||
vec3 s4 = load_color(group_pos + kOffsets3x3[3]);
|
||||
vec3 s5 = load_color(group_pos + kOffsets3x3[4]);
|
||||
vec3 s6 = load_color(group_pos + kOffsets3x3[5]);
|
||||
vec3 s7 = load_color(group_pos + kOffsets3x3[6]);
|
||||
vec3 s8 = load_color(group_pos + kOffsets3x3[7]);
|
||||
vec3 s9 = load_color(group_pos + kOffsets3x3[8]);
|
||||
|
||||
// Compute min and max (with an adaptive box size, which greatly reduces ghosting)
|
||||
vec3 color_avg = (s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9) * RPC_9;
|
||||
vec3 color_avg2 = ((s1 * s1) + (s2 * s2) + (s3 * s3) + (s4 * s4) + (s5 * s5) + (s6 * s6) + (s7 * s7) + (s8 * s8) + (s9 * s9)) * RPC_9;
|
||||
float box_size = mix(0.0f, 2.5f, smoothstep(0.02f, 0.0f, length(velocity_closest)));
|
||||
vec3 dev = sqrt(abs(color_avg2 - (color_avg * color_avg))) * box_size;
|
||||
vec3 color_min = color_avg - dev;
|
||||
vec3 color_max = color_avg + dev;
|
||||
|
||||
// Variance clipping
|
||||
vec3 color = clip_aabb(color_min, color_max, clamp(color_avg, color_min, color_max), color_history);
|
||||
|
||||
// Clamp to prevent NaNs
|
||||
color = clamp(color, FLT_MIN, FLT_MAX);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
TAA
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
const vec3 lumCoeff = vec3(0.299f, 0.587f, 0.114f);
|
||||
|
||||
float luminance(vec3 color) {
|
||||
return max(dot(color, lumCoeff), 0.0001f);
|
||||
}
|
||||
|
||||
float get_factor_disocclusion(vec2 uv_reprojected, vec2 velocity) {
|
||||
vec2 velocity_previous = imageLoad(last_velocity_buffer, ivec2(uv_reprojected * params.resolution)).xy;
|
||||
vec2 velocity_texels = velocity * params.resolution;
|
||||
vec2 prev_velocity_texels = velocity_previous * params.resolution;
|
||||
float disocclusion = length(prev_velocity_texels - velocity_texels) - params.disocclusion_threshold;
|
||||
return clamp(disocclusion * params.disocclusion_scale, 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 temporal_antialiasing(uvec2 pos_group_top_left, uvec2 pos_group, uvec2 pos_screen, vec2 uv, sampler2D tex_history) {
|
||||
// Get the velocity of the current pixel
|
||||
vec2 velocity = imageLoad(velocity_buffer, ivec2(pos_screen)).xy;
|
||||
|
||||
// Get reprojected uv
|
||||
vec2 uv_reprojected = uv + velocity;
|
||||
|
||||
// Get input color
|
||||
vec3 color_input = load_color(pos_group);
|
||||
|
||||
// Get history color (catmull-rom reduces a lot of the blurring that you get under motion)
|
||||
vec3 color_history = sample_catmull_rom_9(tex_history, uv_reprojected, params.resolution).rgb;
|
||||
|
||||
// Clip history to the neighbourhood of the current sample (fixes a lot of the ghosting).
|
||||
vec2 velocity_closest = vec2(0.0); // This is best done by using the velocity with the closest depth.
|
||||
get_closest_pixel_velocity_3x3(pos_group, pos_group_top_left, velocity_closest);
|
||||
color_history = clip_history_3x3(pos_group, color_history, velocity_closest);
|
||||
|
||||
// Compute blend factor
|
||||
float blend_factor = RPC_16; // We want to be able to accumulate as many jitter samples as we generated, that is, 16.
|
||||
{
|
||||
// If re-projected UV is out of screen, converge to current color immediatel
|
||||
float factor_screen = any(lessThan(uv_reprojected, vec2(0.0))) || any(greaterThan(uv_reprojected, vec2(1.0))) ? 1.0 : 0.0;
|
||||
|
||||
// Increase blend factor when there is disocclusion (fixes a lot of the remaining ghosting).
|
||||
float factor_disocclusion = get_factor_disocclusion(uv_reprojected, velocity);
|
||||
|
||||
// Add to the blend factor
|
||||
blend_factor = clamp(blend_factor + factor_screen + factor_disocclusion, 0.0, 1.0);
|
||||
}
|
||||
|
||||
// Resolve
|
||||
vec3 color_resolved = vec3(0.0);
|
||||
{
|
||||
// Tonemap
|
||||
color_history = reinhard(color_history);
|
||||
color_input = reinhard(color_input);
|
||||
|
||||
// Reduce flickering
|
||||
float lum_color = luminance(color_input);
|
||||
float lum_history = luminance(color_history);
|
||||
float diff = abs(lum_color - lum_history) / max(lum_color, max(lum_history, 1.001));
|
||||
diff = 1.0 - diff;
|
||||
diff = diff * diff;
|
||||
blend_factor = mix(0.0, blend_factor, diff);
|
||||
|
||||
// Lerp/blend
|
||||
color_resolved = mix(color_history, color_input, blend_factor);
|
||||
|
||||
// Inverse tonemap
|
||||
color_resolved = reinhard_inverse(color_resolved);
|
||||
}
|
||||
|
||||
return color_resolved;
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef USE_SUBGROUPS
|
||||
populate_group_shared_memory(gl_WorkGroupID.xy, gl_LocalInvocationIndex);
|
||||
#endif
|
||||
|
||||
// Out of bounds check
|
||||
if (any(greaterThanEqual(vec2(gl_GlobalInvocationID.xy), params.resolution))) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_SUBGROUPS
|
||||
const uvec2 pos_group = gl_LocalInvocationID.xy;
|
||||
const uvec2 pos_group_top_left = gl_WorkGroupID.xy * kGroupSize - kBorderSize;
|
||||
#else
|
||||
const uvec2 pos_group = gl_GlobalInvocationID.xy;
|
||||
const uvec2 pos_group_top_left = uvec2(0, 0);
|
||||
#endif
|
||||
const uvec2 pos_screen = gl_GlobalInvocationID.xy;
|
||||
const vec2 uv = (gl_GlobalInvocationID.xy + 0.5f) / params.resolution;
|
||||
|
||||
vec3 result = temporal_antialiasing(pos_group_top_left, pos_group, pos_screen, uv, history_buffer);
|
||||
imageStore(output_buffer, ivec2(gl_GlobalInvocationID.xy), vec4(result, 1.0));
|
||||
}
|
||||
|
|
@ -0,0 +1,523 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#extension GL_EXT_multiview : enable
|
||||
#endif
|
||||
#endif
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
|
||||
void main() {
|
||||
// old code, ARM driver bug on Mali-GXXx GPUs and Vulkan API 1.3.xxx
|
||||
// https://github.com/godotengine/godot/pull/92817#issuecomment-2168625982
|
||||
//vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
//gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
//uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
|
||||
vec2 vertex_base;
|
||||
if (gl_VertexIndex == 0) {
|
||||
vertex_base = vec2(-1.0, -1.0);
|
||||
} else if (gl_VertexIndex == 1) {
|
||||
vertex_base = vec2(-1.0, 3.0);
|
||||
} else {
|
||||
vertex_base = vec2(3.0, -1.0);
|
||||
}
|
||||
gl_Position = vec4(vertex_base, 0.0, 1.0);
|
||||
uv_interp = clamp(vertex_base, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#extension GL_EXT_multiview : enable
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
|
||||
#ifdef SUBPASS
|
||||
layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput input_color;
|
||||
#elif defined(USE_MULTIVIEW)
|
||||
layout(set = 0, binding = 0) uniform sampler2DArray source_color;
|
||||
#else
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
#endif
|
||||
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(set = 2, binding = 0) uniform sampler2DArray source_glow;
|
||||
#else
|
||||
layout(set = 2, binding = 0) uniform sampler2D source_glow;
|
||||
#endif
|
||||
layout(set = 2, binding = 1) uniform sampler2D glow_map;
|
||||
|
||||
#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
|
||||
|
||||
#define FLAG_USE_BCS (1 << 0)
|
||||
#define FLAG_USE_GLOW (1 << 1)
|
||||
#define FLAG_USE_AUTO_EXPOSURE (1 << 2)
|
||||
#define FLAG_USE_COLOR_CORRECTION (1 << 3)
|
||||
#define FLAG_USE_FXAA (1 << 4)
|
||||
#define FLAG_USE_DEBANDING (1 << 5)
|
||||
#define FLAG_CONVERT_TO_SRGB (1 << 6)
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec3 bcs;
|
||||
uint flags;
|
||||
|
||||
vec2 pixel_size;
|
||||
uint tonemapper;
|
||||
uint pad;
|
||||
|
||||
uvec2 glow_texture_size;
|
||||
float glow_intensity;
|
||||
float glow_map_strength;
|
||||
|
||||
uint glow_mode;
|
||||
float glow_levels[7];
|
||||
|
||||
float exposure;
|
||||
float white;
|
||||
float auto_exposure_scale;
|
||||
float luminance_multiplier;
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec4 texture2D_bicubic(sampler2DArray 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);
|
||||
|
||||
vec3 p0 = vec3((vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
vec3 p1 = vec3((vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
vec3 p2 = vec3((vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
vec3 p3 = vec3((vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
|
||||
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 // USE_MULTIVIEW
|
||||
|
||||
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)
|
||||
#endif // !USE_MULTIVIEW
|
||||
|
||||
#else // USE_GLOW_FILTER_BICUBIC
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, vec3(m_uv, ViewIndex), float(m_lod))
|
||||
#else // USE_MULTIVIEW
|
||||
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod))
|
||||
#endif // !USE_MULTIVIEW
|
||||
|
||||
#endif // !USE_GLOW_FILTER_BICUBIC
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Adapted from https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
|
||||
// (MIT License).
|
||||
vec3 tonemap_aces(vec3 color, float white) {
|
||||
const float exposure_bias = 1.8f;
|
||||
const float A = 0.0245786f;
|
||||
const float B = 0.000090537f;
|
||||
const float C = 0.983729f;
|
||||
const float D = 0.432951f;
|
||||
const float E = 0.238081f;
|
||||
|
||||
// Exposure bias baked into transform to save shader instructions. Equivalent to `color *= exposure_bias`
|
||||
const mat3 rgb_to_rrt = mat3(
|
||||
vec3(0.59719f * exposure_bias, 0.35458f * exposure_bias, 0.04823f * exposure_bias),
|
||||
vec3(0.07600f * exposure_bias, 0.90834f * exposure_bias, 0.01566f * exposure_bias),
|
||||
vec3(0.02840f * exposure_bias, 0.13383f * exposure_bias, 0.83777f * exposure_bias));
|
||||
|
||||
const mat3 odt_to_rgb = mat3(
|
||||
vec3(1.60475f, -0.53108f, -0.07367f),
|
||||
vec3(-0.10208f, 1.10813f, -0.00605f),
|
||||
vec3(-0.00327f, -0.07276f, 1.07602f));
|
||||
|
||||
color *= rgb_to_rrt;
|
||||
vec3 color_tonemapped = (color * (color + A) - B) / (color * (C * color + D) + E);
|
||||
color_tonemapped *= odt_to_rgb;
|
||||
|
||||
white *= exposure_bias;
|
||||
float white_tonemapped = (white * (white + A) - B) / (white * (C * white + D) + E);
|
||||
|
||||
return color_tonemapped / white_tonemapped;
|
||||
}
|
||||
|
||||
vec3 tonemap_reinhard(vec3 color, float white) {
|
||||
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
|
||||
// Ensure color values passed to tonemappers are positive.
|
||||
// They can be negative in the case of negative lights, which leads to undesired behavior.
|
||||
if (params.tonemapper == TONEMAPPER_LINEAR) {
|
||||
return color;
|
||||
} else if (params.tonemapper == TONEMAPPER_REINHARD) {
|
||||
return tonemap_reinhard(max(vec3(0.0f), color), white);
|
||||
} else if (params.tonemapper == TONEMAPPER_FILMIC) {
|
||||
return tonemap_filmic(max(vec3(0.0f), color), white);
|
||||
} else { // TONEMAPPER_ACES
|
||||
return tonemap_aces(max(vec3(0.0f), color), white);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec3 gather_glow(sampler2DArray tex, vec2 uv) { // sample all selected glow levels, view is added to uv later
|
||||
#else
|
||||
vec3 gather_glow(sampler2D tex, vec2 uv) { // sample all selected glow levels
|
||||
#endif // defined(USE_MULTIVIEW)
|
||||
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) {
|
||||
// Needs color clamping.
|
||||
glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
|
||||
return max((color + glow) - (color * glow), vec3(0.0));
|
||||
} else if (params.glow_mode == GLOW_MODE_SOFTLIGHT) {
|
||||
// Needs color clamping.
|
||||
glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
|
||||
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
|
||||
|
||||
#ifndef SUBPASS
|
||||
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;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec3 rgbNW = textureLod(source_color, vec3(uv_interp + vec2(-0.5, -0.5) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier;
|
||||
vec3 rgbNE = textureLod(source_color, vec3(uv_interp + vec2(0.5, -0.5) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier;
|
||||
vec3 rgbSW = textureLod(source_color, vec3(uv_interp + vec2(-0.5, 0.5) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier;
|
||||
vec3 rgbSE = textureLod(source_color, vec3(uv_interp + vec2(0.5, 0.5) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier;
|
||||
#else
|
||||
vec3 rgbNW = textureLod(source_color, uv_interp + vec2(-0.5, -0.5) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier;
|
||||
vec3 rgbNE = textureLod(source_color, uv_interp + vec2(0.5, -0.5) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier;
|
||||
vec3 rgbSW = textureLod(source_color, uv_interp + vec2(-0.5, 0.5) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier;
|
||||
vec3 rgbSE = textureLod(source_color, uv_interp + vec2(0.5, 0.5) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier;
|
||||
#endif
|
||||
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;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec3 rgbA = 0.5 * exposure * (textureLod(source_color, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz) * params.luminance_multiplier;
|
||||
vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz) * params.luminance_multiplier;
|
||||
#else
|
||||
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) * params.luminance_multiplier;
|
||||
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) * params.luminance_multiplier;
|
||||
#endif
|
||||
|
||||
float lumaB = dot(rgbB, luma);
|
||||
if ((lumaB < lumaMin) || (lumaB > lumaMax)) {
|
||||
return rgbA;
|
||||
} else {
|
||||
return rgbB;
|
||||
}
|
||||
}
|
||||
#endif // !SUBPASS
|
||||
|
||||
// From https://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() {
|
||||
#ifdef SUBPASS
|
||||
// SUBPASS and USE_MULTIVIEW can be combined but in that case we're already reading from the correct layer
|
||||
vec4 color = subpassLoad(input_color);
|
||||
#elif defined(USE_MULTIVIEW)
|
||||
vec4 color = textureLod(source_color, vec3(uv_interp, ViewIndex), 0.0f);
|
||||
#else
|
||||
vec4 color = textureLod(source_color, uv_interp, 0.0f);
|
||||
#endif
|
||||
color.rgb *= params.luminance_multiplier;
|
||||
|
||||
// Exposure
|
||||
|
||||
float exposure = params.exposure;
|
||||
|
||||
#ifndef SUBPASS
|
||||
if (bool(params.flags & FLAG_USE_AUTO_EXPOSURE)) {
|
||||
exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r * params.luminance_multiplier / params.auto_exposure_scale);
|
||||
}
|
||||
#endif
|
||||
|
||||
color.rgb *= exposure;
|
||||
|
||||
// Early Tonemap & SRGB Conversion
|
||||
#ifndef SUBPASS
|
||||
if (bool(params.flags & FLAG_USE_FXAA)) {
|
||||
// FXAA must be performed before glow to preserve the "bleed" effect of glow.
|
||||
color.rgb = do_fxaa(color.rgb, exposure, uv_interp);
|
||||
}
|
||||
|
||||
if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode == GLOW_MODE_MIX) {
|
||||
vec3 glow = gather_glow(source_glow, uv_interp) * params.luminance_multiplier;
|
||||
if (params.glow_map_strength > 0.001) {
|
||||
glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
|
||||
}
|
||||
color.rgb = mix(color.rgb, glow, params.glow_intensity);
|
||||
}
|
||||
#endif
|
||||
|
||||
color.rgb = apply_tonemapping(color.rgb, params.white);
|
||||
|
||||
if (bool(params.flags & FLAG_CONVERT_TO_SRGB)) {
|
||||
color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
|
||||
}
|
||||
#ifndef SUBPASS
|
||||
// Glow
|
||||
if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode != GLOW_MODE_MIX) {
|
||||
vec3 glow = gather_glow(source_glow, uv_interp) * params.glow_intensity * params.luminance_multiplier;
|
||||
if (params.glow_map_strength > 0.001) {
|
||||
glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
|
||||
}
|
||||
|
||||
// high dynamic range -> SRGB
|
||||
glow = apply_tonemapping(glow, params.white);
|
||||
if (bool(params.flags & FLAG_CONVERT_TO_SRGB)) {
|
||||
glow = linear_to_srgb(glow);
|
||||
}
|
||||
|
||||
color.rgb = apply_glow(color.rgb, glow);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Additional effects
|
||||
|
||||
if (bool(params.flags & FLAG_USE_BCS)) {
|
||||
color.rgb = apply_bcs(color.rgb, params.bcs);
|
||||
}
|
||||
|
||||
if (bool(params.flags & FLAG_USE_COLOR_CORRECTION)) {
|
||||
color.rgb = apply_color_correction(color.rgb);
|
||||
}
|
||||
|
||||
if (bool(params.flags & FLAG_USE_DEBANDING)) {
|
||||
// Debanding should be done at the end of tonemapping, but before writing to the LDR buffer.
|
||||
// Otherwise, we're adding noise to an already-quantized image.
|
||||
color.rgb += screen_space_dither(gl_FragCoord.xy);
|
||||
}
|
||||
|
||||
frag_color = color;
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#extension GL_EXT_multiview : enable
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(location = 0) out vec3 uv_interp;
|
||||
#else
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
#endif
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float max_texel_factor;
|
||||
float res1;
|
||||
float res2;
|
||||
float res3;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp.xy = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
#ifdef USE_MULTIVIEW
|
||||
uv_interp.z = ViewIndex;
|
||||
#endif
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#extension GL_EXT_multiview : enable
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(location = 0) in vec3 uv_interp;
|
||||
layout(set = 0, binding = 0) uniform sampler2DArray source_color;
|
||||
#else /* USE_MULTIVIEW */
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
#endif /* USE_MULTIVIEW */
|
||||
|
||||
layout(location = 0) out uint frag_color;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float max_texel_factor;
|
||||
float res1;
|
||||
float res2;
|
||||
float res3;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec3 uv = uv_interp;
|
||||
#else
|
||||
vec2 uv = uv_interp;
|
||||
#endif
|
||||
|
||||
// Input is standardized. R for X, G for Y, 0.0 (0) = 1, 0.33 (85) = 2, 0.66 (170) = 3, 1.0 (255) = 8
|
||||
vec4 color = textureLod(source_color, uv, 0.0);
|
||||
|
||||
// Output image shading rate image for VRS according to VK_KHR_fragment_shading_rate.
|
||||
color.r = clamp(floor(color.r * params.max_texel_factor + 0.1), 0.0, params.max_texel_factor);
|
||||
color.g = clamp(floor(color.g * params.max_texel_factor + 0.1), 0.0, params.max_texel_factor);
|
||||
|
||||
// Note 1x4, 4x1, 1x8, 8x1, 2x8 and 8x2 are not supported:
|
||||
if (color.r < (color.g - 1.0)) {
|
||||
color.r = color.g - 1.0;
|
||||
}
|
||||
if (color.g < (color.r - 1.0)) {
|
||||
color.g = color.r - 1.0;
|
||||
}
|
||||
|
||||
// Encode to frag_color;
|
||||
frag_color = int(color.r + 0.1) << 2;
|
||||
frag_color += int(color.g + 0.1);
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
if "RD_GLSL" in env["BUILDERS"]:
|
||||
# find all include files
|
||||
gl_include_files = [str(f) for f in Glob("*_inc.glsl")] + [str(f) for f in Glob("../*_inc.glsl")]
|
||||
|
||||
# find all shader code(all glsl files excluding our include files)
|
||||
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
|
||||
|
||||
# make sure we recompile shaders if include files change
|
||||
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#glsl_builders.py"])
|
||||
|
||||
# compile shaders
|
||||
for glsl_file in glsl_files:
|
||||
env.RD_GLSL(glsl_file)
|
||||
788
engine/servers/rendering/renderer_rd/shaders/environment/gi.glsl
Normal file
788
engine/servers/rendering/renderer_rd/shaders/environment/gi.glsl
Normal file
|
|
@ -0,0 +1,788 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef SAMPLE_VOXEL_GI_NEAREST
|
||||
#extension GL_EXT_samplerless_texture_functions : enable
|
||||
#endif
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
#define M_PI 3.141592
|
||||
|
||||
/* Specialization Constants (Toggles) */
|
||||
|
||||
layout(constant_id = 0) const bool sc_half_res = false;
|
||||
layout(constant_id = 1) const bool sc_use_full_projection_matrix = false;
|
||||
layout(constant_id = 2) const bool sc_use_vrs = false;
|
||||
|
||||
#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
|
||||
vec3 pad;
|
||||
float exposure_normalization;
|
||||
};
|
||||
|
||||
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 voxel_gi_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_VOXEL_GI_INSTANCES 8
|
||||
|
||||
struct VoxelGIData {
|
||||
mat4 xform; // 64 - 64
|
||||
|
||||
vec3 bounds; // 12 - 76
|
||||
float dynamic_range; // 4 - 80
|
||||
|
||||
float bias; // 4 - 84
|
||||
float normal_bias; // 4 - 88
|
||||
bool blend_ambient; // 4 - 92
|
||||
uint mipmaps; // 4 - 96
|
||||
|
||||
vec3 pad; // 12 - 108
|
||||
float exposure_normalization; // 4 - 112
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 16, std140) uniform VoxelGIs {
|
||||
VoxelGIData data[MAX_VOXEL_GI_INSTANCES];
|
||||
}
|
||||
voxel_gi_instances;
|
||||
|
||||
layout(set = 0, binding = 17) uniform texture3D voxel_gi_textures[MAX_VOXEL_GI_INSTANCES];
|
||||
|
||||
layout(set = 0, binding = 18, std140) uniform SceneData {
|
||||
mat4x4 inv_projection[2];
|
||||
mat4x4 cam_transform;
|
||||
vec4 eye_offset[2];
|
||||
|
||||
ivec2 screen_size;
|
||||
float pad1;
|
||||
float pad2;
|
||||
}
|
||||
scene_data;
|
||||
|
||||
#ifdef USE_VRS
|
||||
layout(r8ui, set = 0, binding = 19) uniform restrict readonly uimage2D vrs_buffer;
|
||||
#endif
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
uint max_voxel_gi_instances;
|
||||
bool high_quality_vct;
|
||||
bool orthogonal;
|
||||
uint view_index;
|
||||
|
||||
vec4 proj_info;
|
||||
|
||||
float z_near;
|
||||
float z_far;
|
||||
float pad2;
|
||||
float pad3;
|
||||
}
|
||||
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) {
|
||||
if (sc_use_full_projection_matrix) {
|
||||
vec4 pos;
|
||||
pos.xy = (2.0 * vec2(screen_pos) / vec2(scene_data.screen_size)) - 1.0;
|
||||
pos.z = texelFetch(sampler2D(depth_buffer, linear_sampler), screen_pos, 0).r * 2.0 - 1.0;
|
||||
pos.w = 1.0;
|
||||
|
||||
pos = scene_data.inv_projection[params.view_index] * pos;
|
||||
|
||||
return pos.xyz / pos.w;
|
||||
} else {
|
||||
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)) / 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 sdfvoxel_gi_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 * sdfgi.cascades[cascade].exposure_normalization, 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 * sdfgi.cascades[cascade].exposure_normalization;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
sdfvoxel_gi_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;
|
||||
sdfvoxel_gi_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 (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
|
||||
uint i = 0;
|
||||
bool found = false;
|
||||
while (true) {
|
||||
if (length(ray_pos) >= max_distance || light_accum.a > 0.99) {
|
||||
break;
|
||||
}
|
||||
if (!found && i >= cascade && length(ray_pos) < radius_sizes[i]) {
|
||||
uint next_i = min(i + 1, sdfgi.max_cascades - 1);
|
||||
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 fdistance = textureLod(sampler3D(sdf_cascades[i], linear_sampler), pos, 0.0).r * 255.0 - 1.1;
|
||||
|
||||
vec4 hit_light = vec4(0.0);
|
||||
if (fdistance < softness) {
|
||||
hit_light.rgb = textureLod(sampler3D(light_cascades[i], linear_sampler), pos, 0.0).rgb;
|
||||
hit_light.rgb *= 0.5; //approximation given value read is actually meant for anisotropy
|
||||
hit_light.a = clamp(1.0 - (fdistance / softness), 0.0, 1.0);
|
||||
hit_light.rgb *= hit_light.a;
|
||||
}
|
||||
|
||||
fdistance /= sdfgi.cascades[i].to_cell;
|
||||
|
||||
if (i < (sdfgi.max_cascades - 1)) {
|
||||
pos = ray_pos - sdfgi.cascades[next_i].position;
|
||||
pos *= sdfgi.cascades[next_i].to_cell * pos_to_uvw;
|
||||
|
||||
float fdistance2 = textureLod(sampler3D(sdf_cascades[next_i], linear_sampler), pos, 0.0).r * 255.0 - 1.1;
|
||||
|
||||
vec4 hit_light2 = vec4(0.0);
|
||||
if (fdistance2 < softness) {
|
||||
hit_light2.rgb = textureLod(sampler3D(light_cascades[next_i], linear_sampler), pos, 0.0).rgb;
|
||||
hit_light2.rgb *= 0.5; //approximation given value read is actually meant for anisotropy
|
||||
hit_light2.a = clamp(1.0 - (fdistance2 / softness), 0.0, 1.0);
|
||||
hit_light2.rgb *= hit_light2.a;
|
||||
}
|
||||
|
||||
float prev_radius = i == 0 ? 0.0 : radius_sizes[max(0, i - 1)];
|
||||
float blend = clamp((length(ray_pos) - prev_radius) / (radius_sizes[i] - prev_radius), 0.0, 1.0);
|
||||
|
||||
fdistance2 /= sdfgi.cascades[next_i].to_cell;
|
||||
|
||||
hit_light = mix(hit_light, hit_light2, blend);
|
||||
fdistance = mix(fdistance, fdistance2, blend);
|
||||
}
|
||||
|
||||
light_accum += hit_light;
|
||||
ray_pos += ray_dir * fdistance;
|
||||
found = true;
|
||||
}
|
||||
i++;
|
||||
if (i == sdfgi.max_cascades) {
|
||||
i = 0;
|
||||
found = false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 voxel_gi_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 = (voxel_gi_instances.data[index].xform * vec4(position, 1.0)).xyz;
|
||||
ref_vec = normalize((voxel_gi_instances.data[index].xform * vec4(ref_vec, 0.0)).xyz);
|
||||
normal = normalize((voxel_gi_instances.data[index].xform * vec4(normal, 0.0)).xyz);
|
||||
|
||||
position += normal * voxel_gi_instances.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, voxel_gi_instances.data[index].bounds))))) {
|
||||
return;
|
||||
}
|
||||
|
||||
mat3 dir_xform = mat3(voxel_gi_instances.data[index].xform) * normal_xform;
|
||||
|
||||
vec3 blendv = abs(position / voxel_gi_instances.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(voxel_gi_instances.data[index].bounds);
|
||||
vec3 cell_size = 1.0 / voxel_gi_instances.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(voxel_gi_textures[index], cell_size, position, dir, cone_angle_tan, max_distance, voxel_gi_instances.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(voxel_gi_textures[index], cell_size, position, dir, max_distance, voxel_gi_instances.data[index].bias);
|
||||
}
|
||||
}
|
||||
|
||||
light.rgb *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization;
|
||||
if (!voxel_gi_instances.data[index].blend_ambient) {
|
||||
light.a = 1.0;
|
||||
}
|
||||
|
||||
out_diff += light * blend;
|
||||
|
||||
//radiance
|
||||
vec4 irr_light = voxel_cone_trace(voxel_gi_textures[index], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, voxel_gi_instances.data[index].bias);
|
||||
irr_light.rgb *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization;
|
||||
if (!voxel_gi_instances.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 process_gi(ivec2 pos, vec3 vertex, inout vec4 ambient_light, inout vec4 reflection_light) {
|
||||
vec4 normal_roughness = fetch_normal_and_roughness(pos);
|
||||
|
||||
vec3 normal = normal_roughness.xyz;
|
||||
|
||||
if (normal.length() > 0.5) {
|
||||
//valid normal, can do GI
|
||||
float roughness = normal_roughness.w;
|
||||
bool dynamic_object = roughness > 0.5;
|
||||
if (dynamic_object) {
|
||||
roughness = 1.0 - roughness;
|
||||
}
|
||||
roughness /= (127.0 / 255.0);
|
||||
vec3 view = -normalize(mat3(scene_data.cam_transform) * (vertex - scene_data.eye_offset[gl_GlobalInvocationID.z].xyz));
|
||||
vertex = mat3(scene_data.cam_transform) * vertex;
|
||||
normal = normalize(mat3(scene_data.cam_transform) * normal);
|
||||
vec3 reflection = normalize(reflect(-view, normal));
|
||||
|
||||
#ifdef USE_SDFGI
|
||||
sdfgi_process(vertex, normal, reflection, roughness, ambient_light, reflection_light);
|
||||
#endif
|
||||
|
||||
#ifdef USE_VOXEL_GI_INSTANCES
|
||||
{
|
||||
#ifdef SAMPLE_VOXEL_GI_NEAREST
|
||||
uvec2 voxel_gi_tex = texelFetch(voxel_gi_buffer, pos, 0).rg;
|
||||
#else
|
||||
uvec2 voxel_gi_tex = texelFetch(usampler2D(voxel_gi_buffer, linear_sampler), pos, 0).rg;
|
||||
#endif
|
||||
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_voxel_gi_instances; i++) {
|
||||
if (any(equal(uvec2(i), voxel_gi_tex))) {
|
||||
voxel_gi_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;
|
||||
}
|
||||
|
||||
#ifdef 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;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
uint vrs_x, vrs_y;
|
||||
#ifdef USE_VRS
|
||||
if (sc_use_vrs) {
|
||||
ivec2 vrs_pos;
|
||||
|
||||
// Currently we use a 16x16 texel, possibly some day make this configurable.
|
||||
if (sc_half_res) {
|
||||
vrs_pos = pos >> 3;
|
||||
} else {
|
||||
vrs_pos = pos >> 4;
|
||||
}
|
||||
|
||||
uint vrs_texel = imageLoad(vrs_buffer, vrs_pos).r;
|
||||
// note, valid values for vrs_x and vrs_y are 1, 2 and 4.
|
||||
vrs_x = 1 << ((vrs_texel >> 2) & 3);
|
||||
vrs_y = 1 << (vrs_texel & 3);
|
||||
|
||||
if (mod(pos.x, vrs_x) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mod(pos.y, vrs_y) != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sc_half_res) {
|
||||
pos <<= 1;
|
||||
}
|
||||
|
||||
if (any(greaterThanEqual(pos, scene_data.screen_size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
vec4 ambient_light = vec4(0.0);
|
||||
vec4 reflection_light = vec4(0.0);
|
||||
|
||||
vec3 vertex = reconstruct_position(pos);
|
||||
vertex.y = -vertex.y;
|
||||
|
||||
process_gi(pos, vertex, ambient_light, reflection_light);
|
||||
|
||||
if (sc_half_res) {
|
||||
pos >>= 1;
|
||||
}
|
||||
|
||||
imageStore(ambient_buffer, pos, ambient_light);
|
||||
imageStore(reflection_buffer, pos, reflection_light);
|
||||
|
||||
#ifdef USE_VRS
|
||||
if (sc_use_vrs) {
|
||||
if (vrs_x > 1) {
|
||||
imageStore(ambient_buffer, pos + ivec2(1, 0), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(1, 0), reflection_light);
|
||||
}
|
||||
|
||||
if (vrs_x > 2) {
|
||||
imageStore(ambient_buffer, pos + ivec2(2, 0), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(2, 0), reflection_light);
|
||||
|
||||
imageStore(ambient_buffer, pos + ivec2(3, 0), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(3, 0), reflection_light);
|
||||
}
|
||||
|
||||
if (vrs_y > 1) {
|
||||
imageStore(ambient_buffer, pos + ivec2(0, 1), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(0, 1), reflection_light);
|
||||
}
|
||||
|
||||
if (vrs_y > 1 && vrs_x > 1) {
|
||||
imageStore(ambient_buffer, pos + ivec2(1, 1), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(1, 1), reflection_light);
|
||||
}
|
||||
|
||||
if (vrs_y > 1 && vrs_x > 2) {
|
||||
imageStore(ambient_buffer, pos + ivec2(2, 1), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(2, 1), reflection_light);
|
||||
|
||||
imageStore(ambient_buffer, pos + ivec2(3, 1), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(3, 1), reflection_light);
|
||||
}
|
||||
|
||||
if (vrs_y > 2) {
|
||||
imageStore(ambient_buffer, pos + ivec2(0, 2), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(0, 2), reflection_light);
|
||||
imageStore(ambient_buffer, pos + ivec2(0, 3), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(0, 3), reflection_light);
|
||||
}
|
||||
|
||||
if (vrs_y > 2 && vrs_x > 1) {
|
||||
imageStore(ambient_buffer, pos + ivec2(1, 2), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(1, 2), reflection_light);
|
||||
imageStore(ambient_buffer, pos + ivec2(1, 3), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(1, 3), reflection_light);
|
||||
}
|
||||
|
||||
if (vrs_y > 2 && vrs_x > 2) {
|
||||
imageStore(ambient_buffer, pos + ivec2(2, 2), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(2, 2), reflection_light);
|
||||
imageStore(ambient_buffer, pos + ivec2(2, 3), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(2, 3), reflection_light);
|
||||
|
||||
imageStore(ambient_buffer, pos + ivec2(3, 2), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(3, 2), reflection_light);
|
||||
imageStore(ambient_buffer, pos + ivec2(3, 3), ambient_light);
|
||||
imageStore(reflection_buffer, pos + ivec2(3, 3), reflection_light);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
#[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;
|
||||
vec4 pad2;
|
||||
};
|
||||
|
||||
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, std430) uniform Params {
|
||||
vec3 grid_size;
|
||||
uint max_cascades;
|
||||
|
||||
ivec2 screen_size;
|
||||
float y_mult;
|
||||
|
||||
float z_near;
|
||||
|
||||
mat3x4 inv_projection;
|
||||
// We pack these more tightly than mat3 and vec3, which will require some reconstruction trickery.
|
||||
float cam_basis[3][3];
|
||||
float cam_origin[3];
|
||||
}
|
||||
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 = vec3(params.cam_origin[0], params.cam_origin[1], params.cam_origin[2]);
|
||||
|
||||
ray_dir.xy = ((vec2(screen_pos) / vec2(params.screen_size)) * 2.0 - 1.0);
|
||||
ray_dir.z = params.z_near;
|
||||
|
||||
ray_dir = (vec4(ray_dir, 1.0) * mat4(params.inv_projection)).xyz;
|
||||
|
||||
mat3 cam_basis;
|
||||
{
|
||||
vec3 c0 = vec3(params.cam_basis[0][0], params.cam_basis[0][1], params.cam_basis[0][2]);
|
||||
vec3 c1 = vec3(params.cam_basis[1][0], params.cam_basis[1][1], params.cam_basis[1][2]);
|
||||
vec3 c2 = vec3(params.cam_basis[2][0], params.cam_basis[2][1], params.cam_basis[2][2]);
|
||||
cam_basis = mat3(c0, c1, c2);
|
||||
}
|
||||
ray_dir = normalize(cam_basis * 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
|
||||
// No interpolation
|
||||
|
||||
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)));
|
||||
|
||||
light = hit_light;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
imageStore(screen_buffer, screen_pos, vec4(linear_to_srgb(light), 1.0));
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
|
||||
#extension GL_EXT_multiview : enable
|
||||
#endif
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
// !BAS! This needs to become an input once we implement our fallback!
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#else // USE_MULTIVIEW
|
||||
// Set to zero, not supported in non stereo
|
||||
#define ViewIndex 0
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define MAX_CASCADES 8
|
||||
#define MAX_VIEWS 2
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
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;
|
||||
|
||||
// https://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;
|
||||
vec4 pad2;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
layout(set = 0, binding = 5, std140) uniform SceneData {
|
||||
mat4 projection[MAX_VIEWS];
|
||||
}
|
||||
scene_data;
|
||||
|
||||
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 = scene_data.projection[ViewIndex] * 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 = scene_data.projection[ViewIndex] * vec4(vertex, 1.0);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
|
||||
#extension GL_EXT_multiview : enable
|
||||
#endif
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
// !BAS! This needs to become an input once we implement our fallback!
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#else // USE_MULTIVIEW
|
||||
// Set to zero, not supported in non stereo
|
||||
#define ViewIndex 0
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define MAX_VIEWS 2
|
||||
|
||||
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, std430) uniform Params {
|
||||
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
|
||||
}
|
||||
|
|
@ -0,0 +1,507 @@
|
|||
#[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 neighbors.
|
||||
uint albedo; // rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neighbors.
|
||||
uint light; // rgbe8985 encoded total saved light, extra 2 bits for neighbors.
|
||||
uint light_aniso; // 55555 light anisotropy, extra 2 bits for neighbors.
|
||||
//total neighbors: 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;
|
||||
vec4 pad2;
|
||||
};
|
||||
|
||||
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 cos_spot_angle;
|
||||
float inv_spot_attenuation;
|
||||
float radius;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 9, std140) buffer restrict readonly Lights {
|
||||
Light data[];
|
||||
}
|
||||
lights;
|
||||
|
||||
layout(set = 0, binding = 10) uniform texture2DArray lightprobe_texture;
|
||||
layout(set = 0, binding = 11) uniform texture3D occlusion_texture;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec3 grid_size;
|
||||
uint max_cascades;
|
||||
|
||||
uint cascade;
|
||||
uint light_count;
|
||||
uint process_offset;
|
||||
uint process_increment;
|
||||
|
||||
int probe_axis_size;
|
||||
float bounce_feedback;
|
||||
float y_mult;
|
||||
bool use_occlusion;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
float get_omni_attenuation(float distance, float inv_range, float decay) {
|
||||
float nd = distance * inv_range;
|
||||
nd *= nd;
|
||||
nd *= nd; // nd^4
|
||||
nd = max(1.0 - nd, 0.0);
|
||||
nd *= nd; // nd^2
|
||||
return nd * pow(max(distance, 0.0001), -decay);
|
||||
}
|
||||
|
||||
void main() {
|
||||
uint voxel_index = uint(gl_GlobalInvocationID.x);
|
||||
|
||||
//used for skipping voxels every N frames
|
||||
if (params.process_increment > 1) {
|
||||
voxel_index *= params.process_increment;
|
||||
voxel_index += params.process_offset;
|
||||
}
|
||||
|
||||
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] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
|
||||
uint valid_aniso = (voxel_albedo >> 15) & 0x3F;
|
||||
|
||||
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));
|
||||
|
||||
// Add indirect light first, in order to save computation resources
|
||||
#ifdef MODE_PROCESS_DYNAMIC
|
||||
if (params.bounce_feedback > 0.001) {
|
||||
vec3 feedback = (params.bounce_feedback < 1.0) ? (albedo * params.bounce_feedback) : mix(albedo, vec3(1.0), params.bounce_feedback - 1.0);
|
||||
vec3 pos = (vec3(positioni) + vec3(0.5)) * float(params.probe_axis_size - 1) / params.grid_size;
|
||||
ivec3 probe_base_pos = ivec3(pos);
|
||||
|
||||
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 = vec3(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, dot(n, probe_dir));
|
||||
|
||||
if (weight > 0.0 && params.use_occlusion) {
|
||||
ivec3 occ_indexv = abs((cascades.data[params.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 = (vec3(positioni) + aniso_dir[k] + vec3(0.5)) / params.grid_size;
|
||||
occ_pos.z += float(params.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 *= vec3(0.5, 1.0, 1.0 / float(params.max_cascades)); //renormalize
|
||||
float occlusion = dot(textureLod(sampler3D(occlusion_texture, linear_sampler), occ_pos, 0.0), occ_mask);
|
||||
|
||||
weight *= occlusion;
|
||||
}
|
||||
|
||||
if (weight > 0.0) {
|
||||
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;
|
||||
vec3 indirect_light = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb;
|
||||
|
||||
light_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] /= weight_accum[k];
|
||||
light_accum[k] *= feedback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = get_omni_attenuation(light_distance, 1.0 / lights.data[i].radius, 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 = get_omni_attenuation(light_distance, 1.0 / lights.data[i].radius, lights.data[i].attenuation);
|
||||
|
||||
float cos_spot_angle = lights.data[i].cos_spot_angle;
|
||||
float cos_angle = dot(-direction, lights.data[i].direction);
|
||||
|
||||
if (cos_angle < cos_spot_angle) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float scos = max(cos_angle, cos_spot_angle);
|
||||
float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - cos_spot_angle));
|
||||
attenuation *= 1.0 - pow(spot_rim, lights.data[i].inv_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 neighbors, so light interpolation during the indirect pass works
|
||||
|
||||
//recover the neighbor list from the leftover bits
|
||||
uint neighbors = (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(neighbors & (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
|
||||
}
|
||||
|
|
@ -0,0 +1,613 @@
|
|||
#[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;
|
||||
vec4 pad2;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
#ifdef USE_CUBEMAP_ARRAY
|
||||
layout(set = 1, binding = 0) uniform textureCubeArray sky_irradiance;
|
||||
#else
|
||||
layout(set = 1, binding = 0) uniform textureCube sky_irradiance;
|
||||
#endif
|
||||
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, 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);
|
||||
}
|
||||
|
||||
struct SH {
|
||||
#if (SH_SIZE == 16)
|
||||
float c[48];
|
||||
#else
|
||||
float c[28];
|
||||
#endif
|
||||
};
|
||||
|
||||
shared SH sh_accum[64]; //8x8
|
||||
|
||||
void main() {
|
||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(pos, params.image_size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
uint probe_index = gl_LocalInvocationID.x + gl_LocalInvocationID.y * 8;
|
||||
|
||||
#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;
|
||||
|
||||
for (uint i = 0; i < SH_SIZE * 3; i++) {
|
||||
sh_accum[probe_index].c[i] = 0.0;
|
||||
}
|
||||
|
||||
// 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;
|
||||
uint hit_cascade;
|
||||
|
||||
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;
|
||||
vec3 uvw;
|
||||
|
||||
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;
|
||||
|
||||
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.05) {
|
||||
//consider hit
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
advance += distance;
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
hit_cascade = j;
|
||||
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) {
|
||||
//avoid reading different texture from different threads
|
||||
for (uint j = params.cascade; j < params.max_cascades; j++) {
|
||||
if (j == hit_cascade) {
|
||||
const float EPSILON = 0.001;
|
||||
vec3 hit_normal = normalize(vec3(
|
||||
texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw - vec3(EPSILON, 0.0, 0.0)).r,
|
||||
texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw - vec3(0.0, EPSILON, 0.0)).r,
|
||||
texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw - vec3(0.0, 0.0, EPSILON)).r));
|
||||
|
||||
vec3 hit_light = texture(sampler3D(light_cascades[hit_cascade], linear_sampler), uvw).rgb;
|
||||
vec4 aniso0 = texture(sampler3D(aniso0_cascades[hit_cascade], linear_sampler), uvw);
|
||||
vec3 hit_aniso0 = aniso0.rgb;
|
||||
vec3 hit_aniso1 = vec3(aniso0.a, texture(sampler3D(aniso1_cascades[hit_cascade], linear_sampler), uvw).rg);
|
||||
|
||||
//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) {
|
||||
#ifdef USE_CUBEMAP_ARRAY
|
||||
light.rgb = textureLod(samplerCubeArray(sky_irradiance, linear_sampler_mipmaps), vec4(ray_dir, 0.0), 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates.
|
||||
#else
|
||||
light.rgb = textureLod(samplerCube(sky_irradiance, linear_sampler_mipmaps), ray_dir, 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates.
|
||||
#endif
|
||||
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;
|
||||
|
||||
#define SH_ACCUM(m_idx, m_value) \
|
||||
{ \
|
||||
vec3 l = light.rgb * (m_value); \
|
||||
sh_accum[probe_index].c[m_idx * 3 + 0] += l.r; \
|
||||
sh_accum[probe_index].c[m_idx * 3 + 1] += l.g; \
|
||||
sh_accum[probe_index].c[m_idx * 3 + 2] += l.b; \
|
||||
}
|
||||
SH_ACCUM(0, 0.282095); //l0
|
||||
SH_ACCUM(1, 0.488603 * ray_dir.y); //l1n1
|
||||
SH_ACCUM(2, 0.488603 * ray_dir.z); //l1n0
|
||||
SH_ACCUM(3, 0.488603 * ray_dir.x); //l1p1
|
||||
SH_ACCUM(4, 1.092548 * ray_dir.x * ray_dir.y); //l2n2
|
||||
SH_ACCUM(5, 1.092548 * ray_dir.y * ray_dir.z); //l2n1
|
||||
SH_ACCUM(6, 0.315392 * (3.0 * ray_dir2.z - 1.0)); //l20
|
||||
SH_ACCUM(7, 1.092548 * ray_dir.x * ray_dir.z); //l2p1
|
||||
SH_ACCUM(8, 0.546274 * (ray_dir2.x - ray_dir2.y)); //l2p2
|
||||
#if (SH_SIZE == 16)
|
||||
SH_ACCUM(9, 0.590043 * ray_dir.y * (3.0f * ray_dir2.x - ray_dir2.y));
|
||||
SH_ACCUM(10, 2.890611 * ray_dir.y * ray_dir.x * ray_dir.z);
|
||||
SH_ACCUM(11, 0.646360 * ray_dir.y * (-1.0f + 5.0f * ray_dir2.z));
|
||||
SH_ACCUM(12, 0.373176 * (5.0f * ray_dir2.z * ray_dir.z - 3.0f * ray_dir.z));
|
||||
SH_ACCUM(13, 0.457045 * ray_dir.x * (-1.0f + 5.0f * ray_dir2.z));
|
||||
SH_ACCUM(14, 1.445305 * (ray_dir2.x - ray_dir2.y) * ray_dir.z);
|
||||
SH_ACCUM(15, 0.590043 * ray_dir.x * (ray_dir2.x - 3.0f * ray_dir2.y));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
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 = vec4(sh_accum[probe_index].c[i * 3 + 0], sh_accum[probe_index].c[i * 3 + 1], sh_accum[probe_index].c[i * 3 + 2], 1.0) * 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;
|
||||
|
||||
//compute the octahedral normal for this texel
|
||||
vec3 normal = octahedron_encode(vec2(local_pos) / float(OCT_SIZE));
|
||||
|
||||
// read the spherical harmonic
|
||||
|
||||
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++) {
|
||||
// store in history texture
|
||||
ivec2 average_pos = sh_pos + ivec2(0, i);
|
||||
ivec4 average = imageLoad(lightprobe_average_texture, average_pos);
|
||||
|
||||
vec4 sh = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS);
|
||||
|
||||
vec3 m = sh.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 cell_to_probe = float(params.grid_size.x / float(params.probe_axis_size - 1));
|
||||
|
||||
float probe_cell_size = cell_to_probe / cascades.data[params.cascade].to_cell;
|
||||
vec3 probe_pos = cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size;
|
||||
|
||||
//to parent local coords
|
||||
float probe_cell_size_next = cell_to_probe / cascades.data[params.cascade + 1].to_cell;
|
||||
probe_pos -= cascades.data[params.cascade + 1].offset;
|
||||
probe_pos /= probe_cell_size_next;
|
||||
|
||||
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 {
|
||||
//scroll at the edge of the highest cascade, just copy what is there,
|
||||
//since its the closest we have anyway
|
||||
|
||||
for (uint j = 0; j < params.history_size; j++) {
|
||||
ivec2 tex_pos;
|
||||
tex_pos = probe_cell.xy;
|
||||
tex_pos.x += probe_cell.z * int(params.probe_axis_size);
|
||||
|
||||
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, dst_pos);
|
||||
imageStore(lightprobe_history_scroll_texture, dst_pos, 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_texture, spos);
|
||||
imageStore(lightprobe_average_scroll_texture, spos, average);
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
|
@ -0,0 +1,290 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define MAX_VIEWS 2
|
||||
|
||||
#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
|
||||
#extension GL_EXT_multiview : enable
|
||||
#endif
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
mat3 orientation;
|
||||
vec4 projection; // only applicable if not multiview
|
||||
vec3 position;
|
||||
float time;
|
||||
vec3 pad;
|
||||
float luminance_multiplier;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -3.0), vec2(-1.0, 1.0), vec2(3.0, 1.0));
|
||||
uv_interp = base_arr[gl_VertexIndex];
|
||||
gl_Position = vec4(uv_interp, 0.0, 1.0);
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#extension GL_EXT_multiview : enable
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
// !BAS! This needs to become an input once we implement our fallback!
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#else // USE_MULTIVIEW
|
||||
// Set to zero, not supported in non stereo
|
||||
#define ViewIndex 0
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#define M_PI 3.14159265359
|
||||
#define MAX_VIEWS 2
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
mat3 orientation;
|
||||
vec4 projection; // only applicable if not multiview
|
||||
vec3 position;
|
||||
float time;
|
||||
vec3 pad;
|
||||
float luminance_multiplier;
|
||||
}
|
||||
params;
|
||||
|
||||
#include "../samplers_inc.glsl"
|
||||
|
||||
layout(set = 0, binding = 1, std430) restrict readonly buffer GlobalShaderUniformData {
|
||||
vec4 data[];
|
||||
}
|
||||
global_shader_uniforms;
|
||||
|
||||
layout(set = 0, binding = 2, std140) uniform SkySceneData {
|
||||
mat4 combined_reprojection[2];
|
||||
mat4 view_inv_projections[2];
|
||||
vec4 view_eye_offsets[2];
|
||||
|
||||
bool volumetric_fog_enabled; // 4 - 4
|
||||
float volumetric_fog_inv_length; // 4 - 8
|
||||
float volumetric_fog_detail_spread; // 4 - 12
|
||||
float volumetric_fog_sky_affect; // 4 - 16
|
||||
|
||||
bool fog_enabled; // 4 - 20
|
||||
float fog_sky_affect; // 4 - 24
|
||||
float fog_density; // 4 - 28
|
||||
float fog_sun_scatter; // 4 - 32
|
||||
|
||||
vec3 fog_light_color; // 12 - 44
|
||||
float fog_aerial_perspective; // 4 - 48
|
||||
|
||||
float z_far; // 4 - 52
|
||||
uint directional_light_count; // 4 - 56
|
||||
uint pad1; // 4 - 60
|
||||
uint pad2; // 4 - 64
|
||||
}
|
||||
sky_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 MATERIAL_UNIFORMS_USED
|
||||
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
|
||||
#MATERIAL_UNIFORMS
|
||||
} 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;
|
||||
#elif defined(USE_MULTIVIEW)
|
||||
layout(set = 2, binding = 1) uniform texture2DArray half_res;
|
||||
layout(set = 2, binding = 2) uniform texture2DArray 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
|
||||
|
||||
#GLOBALS
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
#ifdef USE_DEBANDING
|
||||
// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
|
||||
vec3 interleaved_gradient_noise(vec2 pos) {
|
||||
const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f);
|
||||
float res = fract(magic.z * fract(dot(pos, magic.xy))) * 2.0 - 1.0;
|
||||
return vec3(res, -res, res) / 255.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
vec4 volumetric_fog_process(vec2 screen_uv) {
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec4 reprojected = sky_scene_data.combined_reprojection[ViewIndex] * vec4(screen_uv * 2.0 - 1.0, 0.0, 1.0); // Unproject at the far plane
|
||||
vec3 fog_pos = vec3(reprojected.xy / reprojected.w, 1.0) * 0.5 + 0.5;
|
||||
#else
|
||||
vec3 fog_pos = vec3(screen_uv, 1.0);
|
||||
#endif
|
||||
|
||||
return texture(sampler3D(volumetric_fog_texture, SAMPLER_LINEAR_CLAMP), fog_pos);
|
||||
}
|
||||
|
||||
vec4 fog_process(vec3 view, vec3 sky_color) {
|
||||
vec3 fog_color = mix(sky_scene_data.fog_light_color, sky_color, sky_scene_data.fog_aerial_perspective);
|
||||
|
||||
if (sky_scene_data.fog_sun_scatter > 0.001) {
|
||||
vec4 sun_scatter = vec4(0.0);
|
||||
float sun_total = 0.0;
|
||||
for (uint i = 0; i < sky_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 * sky_scene_data.fog_sun_scatter;
|
||||
}
|
||||
}
|
||||
|
||||
return vec4(fog_color, 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 cube_normal;
|
||||
#ifdef USE_MULTIVIEW
|
||||
// In multiview our projection matrices will contain positional and rotational offsets that we need to properly unproject.
|
||||
vec4 unproject = vec4(uv_interp.x, -uv_interp.y, 0.0, 1.0); // unproject at the far plane
|
||||
vec4 unprojected = sky_scene_data.view_inv_projections[ViewIndex] * unproject;
|
||||
cube_normal = unprojected.xyz / unprojected.w;
|
||||
|
||||
// Unproject will give us the position between the eyes, need to re-offset
|
||||
cube_normal += sky_scene_data.view_eye_offsets[ViewIndex].xyz;
|
||||
#else
|
||||
cube_normal.z = -1.0;
|
||||
cube_normal.x = (cube_normal.z * (-uv_interp.x - params.projection.x)) / params.projection.y;
|
||||
cube_normal.y = -(cube_normal.z * (-uv_interp.y - params.projection.z)) / params.projection.w;
|
||||
#endif
|
||||
cube_normal = mat3(params.orientation) * cube_normal;
|
||||
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
|
||||
|
||||
#ifdef USES_HALF_RES_COLOR
|
||||
half_res_color = texture(samplerCube(half_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal) / params.luminance_multiplier;
|
||||
#endif
|
||||
#ifdef USES_QUARTER_RES_COLOR
|
||||
quarter_res_color = texture(samplerCube(quarter_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal) / params.luminance_multiplier;
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifdef USES_HALF_RES_COLOR
|
||||
#ifdef USE_MULTIVIEW
|
||||
half_res_color = textureLod(sampler2DArray(half_res, SAMPLER_LINEAR_CLAMP), vec3(uv, ViewIndex), 0.0) / params.luminance_multiplier;
|
||||
#else
|
||||
half_res_color = textureLod(sampler2D(half_res, SAMPLER_LINEAR_CLAMP), uv, 0.0) / params.luminance_multiplier;
|
||||
#endif // USE_MULTIVIEW
|
||||
#endif // USES_HALF_RES_COLOR
|
||||
|
||||
#ifdef USES_QUARTER_RES_COLOR
|
||||
#ifdef USE_MULTIVIEW
|
||||
quarter_res_color = textureLod(sampler2DArray(quarter_res, SAMPLER_LINEAR_CLAMP), vec3(uv, ViewIndex), 0.0) / params.luminance_multiplier;
|
||||
#else
|
||||
quarter_res_color = textureLod(sampler2D(quarter_res, SAMPLER_LINEAR_CLAMP), uv, 0.0) / params.luminance_multiplier;
|
||||
#endif // USE_MULTIVIEW
|
||||
#endif // USES_QUARTER_RES_COLOR
|
||||
|
||||
#endif //USE_CUBEMAP_PASS
|
||||
|
||||
{
|
||||
|
||||
#CODE : SKY
|
||||
|
||||
}
|
||||
|
||||
frag_color.rgb = color;
|
||||
frag_color.a = alpha;
|
||||
|
||||
// For mobile renderer we're multiplying by 0.5 as we're using a UNORM buffer.
|
||||
// For both mobile and clustered, we also bake in the exposure value for the environment and camera.
|
||||
frag_color.rgb = frag_color.rgb * params.luminance_multiplier;
|
||||
|
||||
#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 (sky_scene_data.fog_enabled) {
|
||||
vec4 fog = fog_process(cube_normal, frag_color.rgb);
|
||||
frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a * sky_scene_data.fog_sky_affect);
|
||||
}
|
||||
|
||||
if (sky_scene_data.volumetric_fog_enabled) {
|
||||
vec4 fog = volumetric_fog_process(uv);
|
||||
frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a * sky_scene_data.volumetric_fog_sky_affect);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef USE_DEBANDING
|
||||
frag_color.rgb += interleaved_gradient_noise(gl_FragCoord.xy) * params.luminance_multiplier;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
|
||||
|
||||
#define DENSITY_SCALE 1024.0
|
||||
|
||||
#include "../cluster_data_inc.glsl"
|
||||
#include "../light_data_inc.glsl"
|
||||
|
||||
#define M_PI 3.14159265359
|
||||
|
||||
#include "../samplers_inc.glsl"
|
||||
|
||||
layout(set = 0, binding = 2, std430) restrict readonly buffer GlobalShaderUniformData {
|
||||
vec4 data[];
|
||||
}
|
||||
global_shader_uniforms;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec3 position;
|
||||
float pad;
|
||||
|
||||
vec3 size;
|
||||
float pad2;
|
||||
|
||||
ivec3 corner;
|
||||
uint shape;
|
||||
|
||||
mat4 transform;
|
||||
}
|
||||
params;
|
||||
|
||||
#ifdef MOLTENVK_USED
|
||||
layout(set = 1, binding = 1) volatile buffer emissive_only_map_buffer {
|
||||
uint emissive_only_map[];
|
||||
};
|
||||
#else
|
||||
layout(r32ui, set = 1, binding = 1) uniform volatile uimage3D emissive_only_map;
|
||||
#endif
|
||||
|
||||
layout(set = 1, binding = 2, std140) uniform SceneParams {
|
||||
vec2 fog_frustum_size_begin;
|
||||
vec2 fog_frustum_size_end;
|
||||
|
||||
float fog_frustum_end;
|
||||
float z_near; //
|
||||
float z_far; //
|
||||
float time;
|
||||
|
||||
ivec3 fog_volume_size;
|
||||
uint directional_light_count; //
|
||||
|
||||
bool use_temporal_reprojection;
|
||||
uint temporal_frame;
|
||||
float detail_spread;
|
||||
float temporal_blend;
|
||||
|
||||
mat4 to_prev_view;
|
||||
mat4 transform;
|
||||
}
|
||||
scene_params;
|
||||
|
||||
#ifdef MOLTENVK_USED
|
||||
layout(set = 1, binding = 3) volatile buffer density_only_map_buffer {
|
||||
uint density_only_map[];
|
||||
};
|
||||
layout(set = 1, binding = 4) volatile buffer light_only_map_buffer {
|
||||
uint light_only_map[];
|
||||
};
|
||||
#else
|
||||
layout(r32ui, set = 1, binding = 3) uniform volatile uimage3D density_only_map;
|
||||
layout(r32ui, set = 1, binding = 4) uniform volatile uimage3D light_only_map;
|
||||
#endif
|
||||
|
||||
#ifdef MATERIAL_UNIFORMS_USED
|
||||
layout(set = 2, binding = 0, std140) uniform MaterialUniforms{
|
||||
#MATERIAL_UNIFORMS
|
||||
} material;
|
||||
#endif
|
||||
|
||||
#GLOBALS
|
||||
|
||||
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, scene_params.detail_spread);
|
||||
return scene_params.fog_frustum_end * d;
|
||||
}
|
||||
|
||||
#define TEMPORAL_FRAMES 16
|
||||
|
||||
const vec3 halton_map[TEMPORAL_FRAMES] = vec3[](
|
||||
vec3(0.5, 0.33333333, 0.2),
|
||||
vec3(0.25, 0.66666667, 0.4),
|
||||
vec3(0.75, 0.11111111, 0.6),
|
||||
vec3(0.125, 0.44444444, 0.8),
|
||||
vec3(0.625, 0.77777778, 0.04),
|
||||
vec3(0.375, 0.22222222, 0.24),
|
||||
vec3(0.875, 0.55555556, 0.44),
|
||||
vec3(0.0625, 0.88888889, 0.64),
|
||||
vec3(0.5625, 0.03703704, 0.84),
|
||||
vec3(0.3125, 0.37037037, 0.08),
|
||||
vec3(0.8125, 0.7037037, 0.28),
|
||||
vec3(0.1875, 0.14814815, 0.48),
|
||||
vec3(0.6875, 0.48148148, 0.68),
|
||||
vec3(0.4375, 0.81481481, 0.88),
|
||||
vec3(0.9375, 0.25925926, 0.12),
|
||||
vec3(0.03125, 0.59259259, 0.32));
|
||||
|
||||
void main() {
|
||||
vec3 fog_cell_size = 1.0 / vec3(scene_params.fog_volume_size);
|
||||
|
||||
ivec3 pos = ivec3(gl_GlobalInvocationID.xyz) + params.corner;
|
||||
if (any(greaterThanEqual(pos, scene_params.fog_volume_size))) {
|
||||
return; //do not compute
|
||||
}
|
||||
#ifdef MOLTENVK_USED
|
||||
uint lpos = pos.z * scene_params.fog_volume_size.x * scene_params.fog_volume_size.y + pos.y * scene_params.fog_volume_size.x + pos.x;
|
||||
#endif
|
||||
|
||||
vec3 posf = vec3(pos);
|
||||
|
||||
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, scene_params.detail_spread);
|
||||
|
||||
vec3 view_pos;
|
||||
view_pos.xy = (fog_unit_pos.xy * 2.0 - 1.0) * mix(scene_params.fog_frustum_size_begin, scene_params.fog_frustum_size_end, vec2(fog_unit_pos.z));
|
||||
view_pos.z = -scene_params.fog_frustum_end * fog_unit_pos.z;
|
||||
view_pos.y = -view_pos.y;
|
||||
|
||||
if (scene_params.use_temporal_reprojection) {
|
||||
vec3 prev_view = (scene_params.to_prev_view * vec4(view_pos, 1.0)).xyz;
|
||||
//undo transform into prev view
|
||||
prev_view.y = -prev_view.y;
|
||||
//z back to unit size
|
||||
prev_view.z /= -scene_params.fog_frustum_end;
|
||||
//xy back to unit size
|
||||
prev_view.xy /= mix(scene_params.fog_frustum_size_begin, scene_params.fog_frustum_size_end, vec2(prev_view.z));
|
||||
prev_view.xy = prev_view.xy * 0.5 + 0.5;
|
||||
//z back to unspread value
|
||||
prev_view.z = pow(prev_view.z, 1.0 / scene_params.detail_spread);
|
||||
|
||||
if (all(greaterThan(prev_view, vec3(0.0))) && all(lessThan(prev_view, vec3(1.0)))) {
|
||||
//reprojectinon fits
|
||||
// Since we can reproject, now we must jitter the current view pos.
|
||||
// This is done here because cells that can't reproject should not jitter.
|
||||
|
||||
fog_unit_pos = posf * fog_cell_size + fog_cell_size * halton_map[scene_params.temporal_frame]; //center of voxels, offset by halton table
|
||||
fog_unit_pos.z = pow(fog_unit_pos.z, scene_params.detail_spread);
|
||||
|
||||
view_pos.xy = (fog_unit_pos.xy * 2.0 - 1.0) * mix(scene_params.fog_frustum_size_begin, scene_params.fog_frustum_size_end, vec2(fog_unit_pos.z));
|
||||
view_pos.z = -scene_params.fog_frustum_end * fog_unit_pos.z;
|
||||
view_pos.y = -view_pos.y;
|
||||
}
|
||||
}
|
||||
|
||||
float density = 0.0;
|
||||
vec3 emission = vec3(0.0);
|
||||
vec3 albedo = vec3(0.0);
|
||||
|
||||
float cell_depth_size = abs(view_pos.z - get_depth_at_pos(fog_cell_size.z, pos.z + 1));
|
||||
|
||||
vec4 world = scene_params.transform * vec4(view_pos, 1.0);
|
||||
world.xyz /= world.w;
|
||||
|
||||
vec3 uvw = fog_unit_pos;
|
||||
|
||||
vec4 local_pos = params.transform * world;
|
||||
local_pos.xyz /= local_pos.w;
|
||||
|
||||
vec3 half_size = params.size / 2.0;
|
||||
float sdf = -1.0;
|
||||
if (params.shape == 0) {
|
||||
// Ellipsoid
|
||||
// https://www.shadertoy.com/view/tdS3DG
|
||||
float k0 = length(local_pos.xyz / half_size);
|
||||
float k1 = length(local_pos.xyz / (half_size * half_size));
|
||||
sdf = k0 * (k0 - 1.0) / k1;
|
||||
} else if (params.shape == 1) {
|
||||
// Cone
|
||||
// https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
|
||||
|
||||
// Compute the cone angle automatically to fit within the volume's size.
|
||||
float inv_height = 1.0 / max(0.001, half_size.y);
|
||||
float radius = 1.0 / max(0.001, (min(half_size.x, half_size.z) * 0.5));
|
||||
float hypotenuse = sqrt(radius * radius + inv_height * inv_height);
|
||||
float rsin = radius / hypotenuse;
|
||||
float rcos = inv_height / hypotenuse;
|
||||
vec2 c = vec2(rsin, rcos);
|
||||
|
||||
float q = length(local_pos.xz);
|
||||
sdf = max(dot(c, vec2(q, local_pos.y - half_size.y)), -half_size.y - local_pos.y);
|
||||
} else if (params.shape == 2) {
|
||||
// Cylinder
|
||||
// https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
|
||||
vec2 d = abs(vec2(length(local_pos.xz), local_pos.y)) - vec2(min(half_size.x, half_size.z), half_size.y);
|
||||
sdf = min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
|
||||
} else if (params.shape == 3) {
|
||||
// Box
|
||||
// https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
|
||||
vec3 q = abs(local_pos.xyz) - half_size;
|
||||
sdf = length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
|
||||
}
|
||||
|
||||
float cull_mask = 1.0; //used to cull cells that do not contribute
|
||||
if (params.shape <= 3) {
|
||||
#ifndef SDF_USED
|
||||
cull_mask = 1.0 - smoothstep(-0.1, 0.0, sdf);
|
||||
#endif
|
||||
uvw = clamp((local_pos.xyz + half_size) / params.size, 0.0, 1.0);
|
||||
}
|
||||
|
||||
if (cull_mask > 0.0) {
|
||||
{
|
||||
#CODE : FOG
|
||||
}
|
||||
|
||||
#ifdef DENSITY_USED
|
||||
density *= cull_mask;
|
||||
if (abs(density) > 0.001) {
|
||||
int final_density = int(density * DENSITY_SCALE);
|
||||
#ifdef MOLTENVK_USED
|
||||
atomicAdd(density_only_map[lpos], uint(final_density));
|
||||
#else
|
||||
imageAtomicAdd(density_only_map, pos, uint(final_density));
|
||||
#endif
|
||||
|
||||
#ifdef EMISSION_USED
|
||||
{
|
||||
emission *= clamp(density, 0.0, 1.0);
|
||||
emission = clamp(emission, vec3(0.0), vec3(4.0));
|
||||
// Scale to fit into R11G11B10 with a range of 0-4
|
||||
uvec3 emission_u = uvec3(emission.r * 511.0, emission.g * 511.0, emission.b * 255.0);
|
||||
// R and G have 11 bits each and B has 10. Then pack them into a 32 bit uint
|
||||
uint final_emission = emission_u.r << 21 | emission_u.g << 10 | emission_u.b;
|
||||
#ifdef MOLTENVK_USED
|
||||
uint prev_emission = atomicAdd(emissive_only_map[lpos], final_emission);
|
||||
#else
|
||||
uint prev_emission = imageAtomicAdd(emissive_only_map, pos, final_emission);
|
||||
#endif
|
||||
|
||||
// Adding can lead to colors overflowing, so validate
|
||||
uvec3 prev_emission_u = uvec3(prev_emission >> 21, (prev_emission << 11) >> 21, prev_emission % 1024);
|
||||
uint add_emission = final_emission + prev_emission;
|
||||
uvec3 add_emission_u = uvec3(add_emission >> 21, (add_emission << 11) >> 21, add_emission % 1024);
|
||||
|
||||
bvec3 overflowing = lessThan(add_emission_u, prev_emission_u + emission_u);
|
||||
|
||||
if (any(overflowing)) {
|
||||
uvec3 overflow_factor = mix(uvec3(0), uvec3(2047 << 21, 2047 << 10, 1023), overflowing);
|
||||
uint force_max = overflow_factor.r | overflow_factor.g | overflow_factor.b;
|
||||
#ifdef MOLTENVK_USED
|
||||
atomicOr(emissive_only_map[lpos], force_max);
|
||||
#else
|
||||
imageAtomicOr(emissive_only_map, pos, force_max);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef ALBEDO_USED
|
||||
{
|
||||
vec3 scattering = albedo * clamp(density, 0.0, 1.0);
|
||||
scattering = clamp(scattering, vec3(0.0), vec3(1.0));
|
||||
uvec3 scattering_u = uvec3(scattering.r * 2047.0, scattering.g * 2047.0, scattering.b * 1023.0);
|
||||
// R and G have 11 bits each and B has 10. Then pack them into a 32 bit uint
|
||||
uint final_scattering = scattering_u.r << 21 | scattering_u.g << 10 | scattering_u.b;
|
||||
#ifdef MOLTENVK_USED
|
||||
uint prev_scattering = atomicAdd(light_only_map[lpos], final_scattering);
|
||||
#else
|
||||
uint prev_scattering = imageAtomicAdd(light_only_map, pos, final_scattering);
|
||||
#endif
|
||||
|
||||
// Adding can lead to colors overflowing, so validate
|
||||
uvec3 prev_scattering_u = uvec3(prev_scattering >> 21, (prev_scattering << 11) >> 21, prev_scattering % 1024);
|
||||
uint add_scattering = final_scattering + prev_scattering;
|
||||
uvec3 add_scattering_u = uvec3(add_scattering >> 21, (add_scattering << 11) >> 21, add_scattering % 1024);
|
||||
|
||||
bvec3 overflowing = lessThan(add_scattering_u, prev_scattering_u + scattering_u);
|
||||
|
||||
if (any(overflowing)) {
|
||||
uvec3 overflow_factor = mix(uvec3(0), uvec3(2047 << 21, 2047 << 10, 1023), overflowing);
|
||||
uint force_max = overflow_factor.r | overflow_factor.g | overflow_factor.b;
|
||||
#ifdef MOLTENVK_USED
|
||||
atomicOr(light_only_map[lpos], force_max);
|
||||
#else
|
||||
imageAtomicOr(light_only_map, pos, force_max);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif // ALBEDO_USED
|
||||
}
|
||||
#endif // DENSITY_USED
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,784 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
/* Do not use subgroups here, seems there is not much advantage and causes glitches
|
||||
#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic)
|
||||
#extension GL_KHR_shader_subgroup_ballot: enable
|
||||
#extension GL_KHR_shader_subgroup_arithmetic: enable
|
||||
|
||||
#define USE_SUBGROUPS
|
||||
#endif
|
||||
*/
|
||||
|
||||
#ifdef MODE_DENSITY
|
||||
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
|
||||
#else
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
#endif
|
||||
|
||||
#include "../cluster_data_inc.glsl"
|
||||
#include "../light_data_inc.glsl"
|
||||
|
||||
#define M_PI 3.14159265359
|
||||
|
||||
#define DENSITY_SCALE 1024.0
|
||||
|
||||
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 OmniLights {
|
||||
LightData data[];
|
||||
}
|
||||
omni_lights;
|
||||
|
||||
layout(set = 0, binding = 4, std430) restrict readonly buffer SpotLights {
|
||||
LightData data[];
|
||||
}
|
||||
spot_lights;
|
||||
|
||||
layout(set = 0, binding = 5, std140) uniform DirectionalLights {
|
||||
DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
|
||||
}
|
||||
directional_lights;
|
||||
|
||||
layout(set = 0, binding = 6, std430) buffer restrict readonly ClusterBuffer {
|
||||
uint data[];
|
||||
}
|
||||
cluster_buffer;
|
||||
|
||||
layout(set = 0, binding = 7) uniform sampler linear_sampler;
|
||||
|
||||
#ifdef MODE_DENSITY
|
||||
layout(rgba16f, set = 0, binding = 8) uniform restrict writeonly image3D density_map;
|
||||
#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_COPY
|
||||
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
|
||||
|
||||
#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_VOXEL_GI_INSTANCES 8
|
||||
|
||||
struct VoxelGIData {
|
||||
mat4 xform; // 64 - 64
|
||||
|
||||
vec3 bounds; // 12 - 76
|
||||
float dynamic_range; // 4 - 80
|
||||
|
||||
float bias; // 4 - 84
|
||||
float normal_bias; // 4 - 88
|
||||
bool blend_ambient; // 4 - 92
|
||||
uint mipmaps; // 4 - 96
|
||||
|
||||
vec3 pad; // 12 - 108
|
||||
float exposure_normalization; // 4 - 112
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 11, std140) uniform VoxelGIs {
|
||||
VoxelGIData data[MAX_VOXEL_GI_INSTANCES];
|
||||
}
|
||||
voxel_gi_instances;
|
||||
|
||||
layout(set = 0, binding = 12) uniform texture3D voxel_gi_textures[MAX_VOXEL_GI_INSTANCES];
|
||||
|
||||
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 SDFVoxelGICascadeData {
|
||||
vec3 position;
|
||||
float to_probe;
|
||||
ivec3 probe_world_offset;
|
||||
float to_cell; // 1/bounds * grid_size
|
||||
vec3 pad;
|
||||
float exposure_normalization;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
SDFVoxelGICascadeData 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(set = 0, binding = 14, std140) uniform Params {
|
||||
vec2 fog_frustum_size_begin;
|
||||
vec2 fog_frustum_size_end;
|
||||
|
||||
float fog_frustum_end;
|
||||
float ambient_inject;
|
||||
float z_far;
|
||||
int filter_axis;
|
||||
|
||||
vec3 ambient_color;
|
||||
float sky_contribution;
|
||||
|
||||
ivec3 fog_volume_size;
|
||||
uint directional_light_count;
|
||||
|
||||
vec3 base_emission;
|
||||
float base_density;
|
||||
|
||||
vec3 base_scattering;
|
||||
float phase_g;
|
||||
|
||||
float detail_spread;
|
||||
float gi_inject;
|
||||
uint max_voxel_gi_instances;
|
||||
uint cluster_type_size;
|
||||
|
||||
vec2 screen_size;
|
||||
uint cluster_shift;
|
||||
uint cluster_width;
|
||||
|
||||
uint max_cluster_element_count_div_32;
|
||||
bool use_temporal_reprojection;
|
||||
uint temporal_frame;
|
||||
float temporal_blend;
|
||||
|
||||
mat3x4 cam_rotation;
|
||||
mat4 to_prev_view;
|
||||
|
||||
mat3 radiance_inverse_xform;
|
||||
}
|
||||
params;
|
||||
#ifndef MODE_COPY
|
||||
layout(set = 0, binding = 15) uniform texture3D prev_density_texture;
|
||||
|
||||
#ifdef MOLTENVK_USED
|
||||
layout(set = 0, binding = 16) buffer density_only_map_buffer {
|
||||
uint density_only_map[];
|
||||
};
|
||||
layout(set = 0, binding = 17) buffer light_only_map_buffer {
|
||||
uint light_only_map[];
|
||||
};
|
||||
layout(set = 0, binding = 18) buffer emissive_only_map_buffer {
|
||||
uint emissive_only_map[];
|
||||
};
|
||||
#else
|
||||
layout(r32ui, set = 0, binding = 16) uniform uimage3D density_only_map;
|
||||
layout(r32ui, set = 0, binding = 17) uniform uimage3D light_only_map;
|
||||
layout(r32ui, set = 0, binding = 18) uniform uimage3D emissive_only_map;
|
||||
#endif
|
||||
|
||||
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
|
||||
layout(set = 0, binding = 19) uniform textureCubeArray sky_texture;
|
||||
#else
|
||||
layout(set = 0, binding = 19) uniform textureCube sky_texture;
|
||||
#endif
|
||||
#endif // MODE_COPY
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
float get_omni_attenuation(float dist, float inv_range, float decay) {
|
||||
float nd = dist * inv_range;
|
||||
nd *= nd;
|
||||
nd *= nd; // nd^4
|
||||
nd = max(1.0 - nd, 0.0);
|
||||
nd *= nd; // nd^2
|
||||
return nd * pow(max(dist, 0.0001), -decay);
|
||||
}
|
||||
|
||||
void cluster_get_item_range(uint p_offset, out uint item_min, out uint item_max, out uint item_from, out uint item_to) {
|
||||
uint item_min_max = cluster_buffer.data[p_offset];
|
||||
item_min = item_min_max & 0xFFFF;
|
||||
item_max = item_min_max >> 16;
|
||||
|
||||
item_from = item_min >> 5;
|
||||
item_to = (item_max == 0) ? 0 : ((item_max - 1) >> 5) + 1; //side effect of how it is stored, as item_max 0 means no elements
|
||||
}
|
||||
|
||||
uint cluster_get_range_clip_mask(uint i, uint z_min, uint z_max) {
|
||||
int local_min = clamp(int(z_min) - int(i) * 32, 0, 31);
|
||||
int mask_width = min(int(z_max) - int(z_min), 32 - local_min);
|
||||
return bitfieldInsert(uint(0), uint(0xFFFFFFFF), local_min, mask_width);
|
||||
}
|
||||
|
||||
float henyey_greenstein(float cos_theta, float g) {
|
||||
const float k = 0.0795774715459; // 1 / (4 * PI)
|
||||
return k * (1.0 - g * g) / (pow(1.0 + g * g - 2.0 * g * cos_theta, 1.5));
|
||||
}
|
||||
|
||||
#define TEMPORAL_FRAMES 16
|
||||
|
||||
const vec3 halton_map[TEMPORAL_FRAMES] = vec3[](
|
||||
vec3(0.5, 0.33333333, 0.2),
|
||||
vec3(0.25, 0.66666667, 0.4),
|
||||
vec3(0.75, 0.11111111, 0.6),
|
||||
vec3(0.125, 0.44444444, 0.8),
|
||||
vec3(0.625, 0.77777778, 0.04),
|
||||
vec3(0.375, 0.22222222, 0.24),
|
||||
vec3(0.875, 0.55555556, 0.44),
|
||||
vec3(0.0625, 0.88888889, 0.64),
|
||||
vec3(0.5625, 0.03703704, 0.84),
|
||||
vec3(0.3125, 0.37037037, 0.08),
|
||||
vec3(0.8125, 0.7037037, 0.28),
|
||||
vec3(0.1875, 0.14814815, 0.48),
|
||||
vec3(0.6875, 0.48148148, 0.68),
|
||||
vec3(0.4375, 0.81481481, 0.88),
|
||||
vec3(0.9375, 0.25925926, 0.12),
|
||||
vec3(0.03125, 0.59259259, 0.32));
|
||||
|
||||
// Higher values will make light in volumetric fog fade out sooner when it's occluded by shadow.
|
||||
const float INV_FOG_FADE = 10.0;
|
||||
|
||||
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
|
||||
}
|
||||
#ifdef MOLTENVK_USED
|
||||
uint lpos = pos.z * params.fog_volume_size.x * params.fog_volume_size.y + pos.y * params.fog_volume_size.x + pos.x;
|
||||
#endif
|
||||
|
||||
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
|
||||
|
||||
uvec2 screen_pos = uvec2(fog_unit_pos.xy * params.screen_size);
|
||||
uvec2 cluster_pos = screen_pos >> params.cluster_shift;
|
||||
uint cluster_offset = (params.cluster_width * cluster_pos.y + cluster_pos.x) * (params.max_cluster_element_count_div_32 + 32);
|
||||
//positions in screen are too spread apart, no hopes for optimizing with subgroups
|
||||
|
||||
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;
|
||||
|
||||
vec4 reprojected_density = vec4(0.0);
|
||||
float reproject_amount = 0.0;
|
||||
|
||||
if (params.use_temporal_reprojection) {
|
||||
vec3 prev_view = (params.to_prev_view * vec4(view_pos, 1.0)).xyz;
|
||||
//undo transform into prev view
|
||||
prev_view.y = -prev_view.y;
|
||||
//z back to unit size
|
||||
prev_view.z /= -params.fog_frustum_end;
|
||||
//xy back to unit size
|
||||
prev_view.xy /= mix(params.fog_frustum_size_begin, params.fog_frustum_size_end, vec2(prev_view.z));
|
||||
prev_view.xy = prev_view.xy * 0.5 + 0.5;
|
||||
//z back to unspread value
|
||||
prev_view.z = pow(prev_view.z, 1.0 / params.detail_spread);
|
||||
|
||||
if (all(greaterThan(prev_view, vec3(0.0))) && all(lessThan(prev_view, vec3(1.0)))) {
|
||||
//reprojectinon fits
|
||||
|
||||
reprojected_density = textureLod(sampler3D(prev_density_texture, linear_sampler), prev_view, 0.0);
|
||||
reproject_amount = params.temporal_blend;
|
||||
|
||||
// Since we can reproject, now we must jitter the current view pos.
|
||||
// This is done here because cells that can't reproject should not jitter.
|
||||
|
||||
fog_unit_pos = posf * fog_cell_size + fog_cell_size * halton_map[params.temporal_frame]; //center of voxels, offset by halton table
|
||||
|
||||
screen_pos = uvec2(fog_unit_pos.xy * params.screen_size);
|
||||
cluster_pos = screen_pos >> params.cluster_shift;
|
||||
cluster_offset = (params.cluster_width * cluster_pos.y + cluster_pos.x) * (params.max_cluster_element_count_div_32 + 32);
|
||||
//positions in screen are too spread apart, no hopes for optimizing with subgroups
|
||||
|
||||
fog_unit_pos.z = pow(fog_unit_pos.z, params.detail_spread);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
uint cluster_z = uint(clamp((abs(view_pos.z) / params.z_far) * 32.0, 0.0, 31.0));
|
||||
|
||||
vec3 total_light = vec3(0.0);
|
||||
|
||||
float total_density = params.base_density;
|
||||
#ifdef MOLTENVK_USED
|
||||
uint local_density = density_only_map[lpos];
|
||||
#else
|
||||
uint local_density = imageLoad(density_only_map, pos).x;
|
||||
#endif
|
||||
|
||||
total_density += float(int(local_density)) / DENSITY_SCALE;
|
||||
total_density = max(0.0, total_density);
|
||||
|
||||
#ifdef MOLTENVK_USED
|
||||
uint scattering_u = light_only_map[lpos];
|
||||
#else
|
||||
uint scattering_u = imageLoad(light_only_map, pos).x;
|
||||
#endif
|
||||
vec3 scattering = vec3(scattering_u >> 21, (scattering_u << 11) >> 21, scattering_u % 1024) / vec3(2047.0, 2047.0, 1023.0);
|
||||
scattering += params.base_scattering * params.base_density;
|
||||
|
||||
#ifdef MOLTENVK_USED
|
||||
uint emission_u = emissive_only_map[lpos];
|
||||
#else
|
||||
uint emission_u = imageLoad(emissive_only_map, pos).x;
|
||||
#endif
|
||||
vec3 emission = vec3(emission_u >> 21, (emission_u << 11) >> 21, emission_u % 1024) / vec3(511.0, 511.0, 255.0);
|
||||
emission += params.base_emission * 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
|
||||
|
||||
if (total_density > 0.00005) {
|
||||
for (uint i = 0; i < params.directional_light_count; i++) {
|
||||
if (directional_lights.data[i].volumetric_fog_energy > 0.001) {
|
||||
vec3 shadow_attenuation = vec3(1.0);
|
||||
|
||||
if (directional_lights.data[i].shadow_opacity > 0.001) {
|
||||
float depth_z = -view_pos.z;
|
||||
|
||||
vec4 pssm_coord;
|
||||
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, (pssm_coord.z - depth)) * z_range * INV_FOG_FADE);
|
||||
|
||||
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(vec3(1.0 - directional_lights.data[i].shadow_opacity), vec3(1.0), shadow);
|
||||
}
|
||||
|
||||
total_light += shadow_attenuation * directional_lights.data[i].color * directional_lights.data[i].energy * henyey_greenstein(dot(normalize(view_pos), normalize(directional_lights.data[i].direction)), params.phase_g) * directional_lights.data[i].volumetric_fog_energy;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute light from sky
|
||||
if (params.ambient_inject > 0.0) {
|
||||
vec3 isotropic = vec3(0.0);
|
||||
vec3 anisotropic = vec3(0.0);
|
||||
if (params.sky_contribution > 0.0) {
|
||||
float mip_bias = 2.0 + total_density * (MAX_SKY_LOD - 2.0); // Not physically based, but looks nice
|
||||
vec3 scatter_direction = (params.radiance_inverse_xform * normalize(view_pos)) * sign(params.phase_g);
|
||||
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
|
||||
isotropic = texture(samplerCubeArray(sky_texture, linear_sampler_with_mipmaps), vec4(0.0, 1.0, 0.0, mip_bias)).rgb;
|
||||
anisotropic = texture(samplerCubeArray(sky_texture, linear_sampler_with_mipmaps), vec4(scatter_direction, mip_bias)).rgb;
|
||||
#else
|
||||
isotropic = textureLod(samplerCube(sky_texture, linear_sampler_with_mipmaps), vec3(0.0, 1.0, 0.0), mip_bias).rgb;
|
||||
anisotropic = textureLod(samplerCube(sky_texture, linear_sampler_with_mipmaps), vec3(scatter_direction), mip_bias).rgb;
|
||||
#endif //USE_RADIANCE_CUBEMAP_ARRAY
|
||||
}
|
||||
|
||||
total_light += mix(params.ambient_color, mix(isotropic, anisotropic, abs(params.phase_g)), params.sky_contribution) * params.ambient_inject;
|
||||
}
|
||||
|
||||
//compute lights from cluster
|
||||
|
||||
{ //omni lights
|
||||
|
||||
uint cluster_omni_offset = cluster_offset;
|
||||
|
||||
uint item_min;
|
||||
uint item_max;
|
||||
uint item_from;
|
||||
uint item_to;
|
||||
|
||||
cluster_get_item_range(cluster_omni_offset + params.max_cluster_element_count_div_32 + cluster_z, item_min, item_max, item_from, item_to);
|
||||
|
||||
#ifdef USE_SUBGROUPS
|
||||
item_from = subgroupBroadcastFirst(subgroupMin(item_from));
|
||||
item_to = subgroupBroadcastFirst(subgroupMax(item_to));
|
||||
#endif
|
||||
|
||||
for (uint i = item_from; i < item_to; i++) {
|
||||
uint mask = cluster_buffer.data[cluster_omni_offset + i];
|
||||
mask &= cluster_get_range_clip_mask(i, item_min, item_max);
|
||||
#ifdef USE_SUBGROUPS
|
||||
uint merged_mask = subgroupBroadcastFirst(subgroupOr(mask));
|
||||
#else
|
||||
uint merged_mask = mask;
|
||||
#endif
|
||||
|
||||
while (merged_mask != 0) {
|
||||
uint bit = findMSB(merged_mask);
|
||||
merged_mask &= ~(1 << bit);
|
||||
#ifdef USE_SUBGROUPS
|
||||
if (((1 << bit) & mask) == 0) { //do not process if not originally here
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
uint light_index = 32 * i + bit;
|
||||
|
||||
//if (!bool(omni_omni_lights.data[light_index].mask & draw_call.layer_mask)) {
|
||||
// continue; //not masked
|
||||
//}
|
||||
|
||||
vec3 light_pos = omni_lights.data[light_index].position;
|
||||
float d = distance(omni_lights.data[light_index].position, view_pos);
|
||||
float shadow_attenuation = 1.0;
|
||||
|
||||
if (omni_lights.data[light_index].volumetric_fog_energy > 0.001 && d * omni_lights.data[light_index].inv_radius < 1.0) {
|
||||
float attenuation = get_omni_attenuation(d, omni_lights.data[light_index].inv_radius, omni_lights.data[light_index].attenuation);
|
||||
|
||||
vec3 light = omni_lights.data[light_index].color;
|
||||
|
||||
if (omni_lights.data[light_index].shadow_opacity > 0.001) {
|
||||
//has shadow
|
||||
vec4 uv_rect = omni_lights.data[light_index].atlas_rect;
|
||||
vec2 flip_offset = omni_lights.data[light_index].direction.xy;
|
||||
|
||||
vec3 local_vert = (omni_lights.data[light_index].shadow_matrix * vec4(view_pos, 1.0)).xyz;
|
||||
|
||||
float shadow_len = length(local_vert); //need to remember shadow len from here
|
||||
vec3 shadow_sample = normalize(local_vert);
|
||||
|
||||
if (shadow_sample.z >= 0.0) {
|
||||
uv_rect.xy += flip_offset;
|
||||
}
|
||||
|
||||
shadow_sample.z = 1.0 + abs(shadow_sample.z);
|
||||
vec3 pos = vec3(shadow_sample.xy / shadow_sample.z, shadow_len - omni_lights.data[light_index].shadow_bias);
|
||||
pos.z *= omni_lights.data[light_index].inv_radius;
|
||||
|
||||
pos.xy = pos.xy * 0.5 + 0.5;
|
||||
pos.xy = uv_rect.xy + pos.xy * uv_rect.zw;
|
||||
|
||||
float depth = texture(sampler2D(shadow_atlas, linear_sampler), pos.xy).r;
|
||||
|
||||
shadow_attenuation = mix(1.0 - omni_lights.data[light_index].shadow_opacity, 1.0, exp(min(0.0, (pos.z - depth)) / omni_lights.data[light_index].inv_radius * INV_FOG_FADE));
|
||||
}
|
||||
total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_pos - view_pos), normalize(view_pos)), params.phase_g) * omni_lights.data[light_index].volumetric_fog_energy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ //spot lights
|
||||
|
||||
uint cluster_spot_offset = cluster_offset + params.cluster_type_size;
|
||||
|
||||
uint item_min;
|
||||
uint item_max;
|
||||
uint item_from;
|
||||
uint item_to;
|
||||
|
||||
cluster_get_item_range(cluster_spot_offset + params.max_cluster_element_count_div_32 + cluster_z, item_min, item_max, item_from, item_to);
|
||||
|
||||
#ifdef USE_SUBGROUPS
|
||||
item_from = subgroupBroadcastFirst(subgroupMin(item_from));
|
||||
item_to = subgroupBroadcastFirst(subgroupMax(item_to));
|
||||
#endif
|
||||
|
||||
for (uint i = item_from; i < item_to; i++) {
|
||||
uint mask = cluster_buffer.data[cluster_spot_offset + i];
|
||||
mask &= cluster_get_range_clip_mask(i, item_min, item_max);
|
||||
#ifdef USE_SUBGROUPS
|
||||
uint merged_mask = subgroupBroadcastFirst(subgroupOr(mask));
|
||||
#else
|
||||
uint merged_mask = mask;
|
||||
#endif
|
||||
|
||||
while (merged_mask != 0) {
|
||||
uint bit = findMSB(merged_mask);
|
||||
merged_mask &= ~(1 << bit);
|
||||
#ifdef USE_SUBGROUPS
|
||||
if (((1 << bit) & mask) == 0) { //do not process if not originally here
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
//if (!bool(omni_lights.data[light_index].mask & draw_call.layer_mask)) {
|
||||
// continue; //not masked
|
||||
//}
|
||||
|
||||
uint light_index = 32 * i + bit;
|
||||
|
||||
vec3 light_pos = spot_lights.data[light_index].position;
|
||||
vec3 light_rel_vec = spot_lights.data[light_index].position - view_pos;
|
||||
float d = length(light_rel_vec);
|
||||
float shadow_attenuation = 1.0;
|
||||
|
||||
if (spot_lights.data[light_index].volumetric_fog_energy > 0.001 && d * spot_lights.data[light_index].inv_radius < 1.0) {
|
||||
float attenuation = get_omni_attenuation(d, spot_lights.data[light_index].inv_radius, spot_lights.data[light_index].attenuation);
|
||||
|
||||
vec3 spot_dir = spot_lights.data[light_index].direction;
|
||||
highp float cone_angle = spot_lights.data[light_index].cone_angle;
|
||||
float scos = max(dot(-normalize(light_rel_vec), spot_dir), cone_angle);
|
||||
float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - cone_angle));
|
||||
attenuation *= 1.0 - pow(spot_rim, spot_lights.data[light_index].cone_attenuation);
|
||||
|
||||
vec3 light = spot_lights.data[light_index].color;
|
||||
|
||||
if (spot_lights.data[light_index].shadow_opacity > 0.001) {
|
||||
//has shadow
|
||||
vec4 uv_rect = spot_lights.data[light_index].atlas_rect;
|
||||
|
||||
vec4 v = vec4(view_pos, 1.0);
|
||||
|
||||
vec4 splane = (spot_lights.data[light_index].shadow_matrix * v);
|
||||
splane.z -= spot_lights.data[light_index].shadow_bias / (d * spot_lights.data[light_index].inv_radius);
|
||||
splane /= splane.w;
|
||||
|
||||
vec3 pos = vec3(splane.xy * spot_lights.data[light_index].atlas_rect.zw + spot_lights.data[light_index].atlas_rect.xy, splane.z);
|
||||
|
||||
float depth = texture(sampler2D(shadow_atlas, linear_sampler), pos.xy).r;
|
||||
|
||||
shadow_attenuation = mix(1.0 - spot_lights.data[light_index].shadow_opacity, 1.0, exp(min(0.0, (pos.z - depth)) / spot_lights.data[light_index].inv_radius * INV_FOG_FADE));
|
||||
}
|
||||
total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_rel_vec), normalize(view_pos)), params.phase_g) * spot_lights.data[light_index].volumetric_fog_energy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec3 world_pos = mat3(params.cam_rotation) * view_pos;
|
||||
|
||||
for (uint i = 0; i < params.max_voxel_gi_instances; i++) {
|
||||
vec3 position = (voxel_gi_instances.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, voxel_gi_instances.data[i].bounds))))) {
|
||||
position /= voxel_gi_instances.data[i].bounds;
|
||||
|
||||
vec4 light = vec4(0.0);
|
||||
for (uint j = 0; j < voxel_gi_instances.data[i].mipmaps; j++) {
|
||||
vec4 slight = textureLod(sampler3D(voxel_gi_textures[i], linear_sampler_with_mipmaps), position, float(j));
|
||||
float a = (1.0 - light.a);
|
||||
light += a * slight;
|
||||
}
|
||||
|
||||
light.rgb *= voxel_gi_instances.data[i].dynamic_range * params.gi_inject * voxel_gi_instances.data[i].exposure_normalization;
|
||||
|
||||
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 * sdfgi.cascades[i].exposure_normalization;
|
||||
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
|
||||
}
|
||||
|
||||
vec4 final_density = vec4(total_light * scattering + emission, total_density);
|
||||
|
||||
final_density = mix(final_density, reprojected_density, reproject_amount);
|
||||
|
||||
imageStore(density_map, pos, final_density);
|
||||
#ifdef MOLTENVK_USED
|
||||
density_only_map[lpos] = 0;
|
||||
light_only_map[lpos] = 0;
|
||||
emissive_only_map[lpos] = 0;
|
||||
#else
|
||||
imageStore(density_only_map, pos, uvec4(0));
|
||||
imageStore(light_only_map, pos, uvec4(0));
|
||||
imageStore(emissive_only_map, pos, uvec4(0));
|
||||
#endif
|
||||
#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, 0.0, 0.0, 1.0);
|
||||
float prev_z = 0.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 transmittance using beer's law
|
||||
float transmittance = exp(-d * fog.a);
|
||||
|
||||
fog_accum.rgb += ((fog.rgb - fog.rgb * transmittance) / max(fog.a, 0.00001)) * fog_accum.a;
|
||||
fog_accum.a *= transmittance;
|
||||
|
||||
prev_z = z;
|
||||
|
||||
imageStore(fog_map, fog_pos, vec4(fog_accum.rgb, 1.0 - fog_accum.a));
|
||||
}
|
||||
|
||||
#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
|
||||
#ifdef MODE_COPY
|
||||
ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);
|
||||
if (any(greaterThanEqual(pos, params.fog_volume_size))) {
|
||||
return; //do not compute
|
||||
}
|
||||
|
||||
imageStore(dest_map, pos, imageLoad(source_map, pos));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,616 @@
|
|||
#[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
|
||||
|
||||
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 cos_spot_angle;
|
||||
|
||||
vec3 position;
|
||||
float inv_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;
|
||||
|
||||
#endif // MODE_SECOND_BOUNCE
|
||||
|
||||
#ifndef MODE_DYNAMIC
|
||||
|
||||
layout(push_constant, 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;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_DYNAMIC
|
||||
|
||||
layout(push_constant, 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;
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
float get_omni_attenuation(float distance, float inv_range, float decay) {
|
||||
float nd = distance * inv_range;
|
||||
nd *= nd;
|
||||
nd *= nd; // nd^4
|
||||
nd = max(1.0 - nd, 0.0);
|
||||
nd *= nd; // nd^2
|
||||
return nd * pow(max(distance, 0.0001), -decay);
|
||||
}
|
||||
|
||||
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 = get_omni_attenuation(distance, 1.0 / lights.data[light].radius, lights.data[light].attenuation);
|
||||
|
||||
if (lights.data[light].type == LIGHT_TYPE_SPOT) {
|
||||
vec3 rel = normalize(pos - light_pos);
|
||||
float cos_spot_angle = lights.data[light].cos_spot_angle;
|
||||
float cos_angle = dot(rel, lights.data[light].direction);
|
||||
if (cos_angle < cos_spot_angle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float scos = max(cos_angle, cos_spot_angle);
|
||||
float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - cos_spot_angle));
|
||||
attenuation *= 1.0 - pow(spot_rim, lights.data[light].inv_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;
|
||||
|
||||
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, pos, normal.xyz, light, light_dir)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
light *= albedo.rgb;
|
||||
|
||||
if (length(normal) > 0.2) {
|
||||
accum += max(0.0, dot(normal, -light_dir)) * light;
|
||||
} else {
|
||||
//all directions
|
||||
accum += light;
|
||||
}
|
||||
}
|
||||
|
||||
outputs.data[cell_index] = vec4(accum + emission, 0.0);
|
||||
|
||||
#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);
|
||||
|
||||
vec3 accum = outputs.data[cell_index].rgb;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
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
|
||||
accum += color.rgb;
|
||||
}
|
||||
}
|
||||
|
||||
outputs.data[cell_index] = vec4(accum, 0.0);
|
||||
|
||||
#endif // MODE_SECOND_BOUNCE
|
||||
|
||||
/////////////////UPDATE MIPMAPS///////////////////////////////
|
||||
|
||||
#ifdef MODE_UPDATE_MIPMAPS
|
||||
|
||||
{
|
||||
vec3 light_accum = vec3(0.0);
|
||||
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;
|
||||
}
|
||||
light_accum += outputs.data[child_index].rgb;
|
||||
|
||||
count += 1.0;
|
||||
}
|
||||
|
||||
float divisor = mix(8.0, count, params.propagation);
|
||||
outputs.data[cell_index] = vec4(light_accum / divisor, 0.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////WRITE TEXTURE/////////////////////////////
|
||||
|
||||
#ifdef MODE_WRITE_TEXTURE
|
||||
{
|
||||
imageStore(color_tex, ivec3(posu), vec4(outputs.data[cell_index].rgb / params.dynamic_range, albedo.a));
|
||||
}
|
||||
#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 = normalize(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));
|
||||
}
|
||||
#endif // MODE_DYNAMIC_SHRINK_PLOT
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MODE DYNAMIC
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
#[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;
|
||||
|
||||
layout(push_constant, 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
|
||||
color_interp = texelFetch(sampler3D(color_tex, tex_sampler), ivec3(posu), int(params.level));
|
||||
color_interp.xyz *params.dynamic_range;
|
||||
#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
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
|
||||
|
||||
#define MAX_DISTANCE 100000.0
|
||||
|
||||
#define NO_CHILDREN 0xFFFFFFFF
|
||||
|
||||
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, std430) uniform Params {
|
||||
uint offset;
|
||||
uint end;
|
||||
uint pad0;
|
||||
uint pad1;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
vec3 pos = vec3(gl_GlobalInvocationID);
|
||||
float closest_dist = MAX_DISTANCE;
|
||||
|
||||
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, 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
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
if "RD_GLSL" in env["BUILDERS"]:
|
||||
# find all include files
|
||||
gl_include_files = [str(f) for f in Glob("*_inc.glsl")] + [str(f) for f in Glob("../*_inc.glsl")]
|
||||
|
||||
# find all shader code(all glsl files excluding our include files)
|
||||
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
|
||||
|
||||
# make sure we recompile shaders if include files change
|
||||
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#glsl_builders.py"])
|
||||
|
||||
# compile shaders
|
||||
for glsl_file in glsl_files:
|
||||
env.RD_GLSL(glsl_file)
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(r8, set = 0, binding = 0) uniform restrict writeonly image2D current_image;
|
||||
|
||||
// This shader is used to generate a "best fit normal texture" as described by:
|
||||
// https://advances.realtimerendering.com/s2010/Kaplanyan-CryEngine3(SIGGRAPH%202010%20Advanced%20RealTime%20Rendering%20Course).pdf
|
||||
// This texture tells you what length of normal can be used to store a unit vector
|
||||
// with the lest amount of error.
|
||||
|
||||
vec3 quantize(vec3 c) {
|
||||
return round(clamp(c * 0.5 + 0.5, 0.0, 1.0) * 255.0) * (1.0 / 255.0) * 2.0 - 1.0;
|
||||
}
|
||||
|
||||
float find_minimum_error(vec3 normal) {
|
||||
float min_error = 100000.0;
|
||||
float t_best = 0.0;
|
||||
for (float nstep = 1.5; nstep < 127.5; ++nstep) {
|
||||
float t = nstep / 127.5;
|
||||
vec3 vp = normal * t;
|
||||
vec3 quantizedp = quantize(vp);
|
||||
vec3 vdiff = (quantizedp - vp) / t;
|
||||
float error = max(abs(vdiff.x), max(abs(vdiff.y), abs(vdiff.z)));
|
||||
if (error < min_error) {
|
||||
min_error = error;
|
||||
t_best = t;
|
||||
}
|
||||
}
|
||||
return t_best;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 uv = vec2(gl_GlobalInvocationID.xy) * vec2(1.0 / 1024.0) + vec2(0.5 / 1024.0);
|
||||
uv.y *= uv.x;
|
||||
|
||||
vec3 dir = vec3(uv.x, uv.y, 1.0);
|
||||
imageStore(current_image, ivec2(gl_GlobalInvocationID.xy), vec4(find_minimum_error(dir), 1.0, 1.0, 1.0));
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,350 @@
|
|||
#define M_PI 3.14159265359
|
||||
#define ROUGHNESS_MAX_LOD 5
|
||||
|
||||
#define MAX_VOXEL_GI_INSTANCES 8
|
||||
#define MAX_VIEWS 2
|
||||
|
||||
#ifndef MOLTENVK_USED
|
||||
#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic)
|
||||
|
||||
#extension GL_KHR_shader_subgroup_ballot : enable
|
||||
#extension GL_KHR_shader_subgroup_arithmetic : enable
|
||||
|
||||
#define USE_SUBGROUPS
|
||||
#endif
|
||||
#endif // MOLTENVK_USED
|
||||
|
||||
#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
|
||||
#extension GL_EXT_multiview : enable
|
||||
#endif
|
||||
|
||||
#include "../cluster_data_inc.glsl"
|
||||
#include "../decal_data_inc.glsl"
|
||||
#include "../scene_data_inc.glsl"
|
||||
|
||||
#if !defined(MODE_RENDER_DEPTH) || defined(MODE_RENDER_MATERIAL) || defined(MODE_RENDER_SDF) || defined(MODE_RENDER_NORMAL_ROUGHNESS) || defined(MODE_RENDER_VOXEL_GI) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
|
||||
#ifndef NORMAL_USED
|
||||
#define NORMAL_USED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(TANGENT_USED) && (defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED))
|
||||
#define TANGENT_USED
|
||||
#endif
|
||||
|
||||
layout(push_constant, std430) uniform DrawCall {
|
||||
uint instance_index;
|
||||
uint uv_offset;
|
||||
uint multimesh_motion_vectors_current_offset;
|
||||
uint multimesh_motion_vectors_previous_offset;
|
||||
}
|
||||
draw_call;
|
||||
|
||||
#define SDFGI_MAX_CASCADES 8
|
||||
|
||||
/* Set 0: Base Pass (never changes) */
|
||||
|
||||
#include "../light_data_inc.glsl"
|
||||
|
||||
layout(set = 0, binding = 2) uniform sampler shadow_sampler;
|
||||
|
||||
#define INSTANCE_FLAGS_DYNAMIC (1 << 3)
|
||||
#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4)
|
||||
#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5)
|
||||
#define INSTANCE_FLAGS_USE_SDFGI (1 << 6)
|
||||
#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 7)
|
||||
#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 8)
|
||||
#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 9)
|
||||
#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 10)
|
||||
#define INSTANCE_FLAGS_PARTICLES (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_PARTICLE_TRAIL_SHIFT 16
|
||||
#define INSTANCE_FLAGS_FADE_SHIFT 24
|
||||
//3 bits of stride
|
||||
#define INSTANCE_FLAGS_PARTICLE_TRAIL_MASK 0xFF
|
||||
|
||||
#define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSAO 1
|
||||
#define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL 2
|
||||
|
||||
layout(set = 0, binding = 3, std430) restrict readonly buffer OmniLights {
|
||||
LightData data[];
|
||||
}
|
||||
omni_lights;
|
||||
|
||||
layout(set = 0, binding = 4, std430) restrict readonly buffer SpotLights {
|
||||
LightData data[];
|
||||
}
|
||||
spot_lights;
|
||||
|
||||
layout(set = 0, binding = 5, std430) restrict readonly buffer ReflectionProbeData {
|
||||
ReflectionData data[];
|
||||
}
|
||||
reflections;
|
||||
|
||||
layout(set = 0, binding = 6, 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;
|
||||
vec3 pad;
|
||||
float exposure_normalization;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps {
|
||||
Lightmap data[];
|
||||
}
|
||||
lightmaps;
|
||||
|
||||
struct LightmapCapture {
|
||||
vec4 sh[9];
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 8, std140) restrict readonly buffer LightmapCaptures {
|
||||
LightmapCapture data[];
|
||||
}
|
||||
lightmap_captures;
|
||||
|
||||
layout(set = 0, binding = 9) uniform texture2D decal_atlas;
|
||||
layout(set = 0, binding = 10) uniform texture2D decal_atlas_srgb;
|
||||
|
||||
layout(set = 0, binding = 11, std430) restrict readonly buffer Decals {
|
||||
DecalData data[];
|
||||
}
|
||||
decals;
|
||||
|
||||
layout(set = 0, binding = 12, std430) restrict readonly buffer GlobalShaderUniformData {
|
||||
vec4 data[];
|
||||
}
|
||||
global_shader_uniforms;
|
||||
|
||||
struct SDFVoxelGICascadeData {
|
||||
vec3 position;
|
||||
float to_probe;
|
||||
ivec3 probe_world_offset;
|
||||
float to_cell; // 1/bounds * grid_size
|
||||
vec3 pad;
|
||||
float exposure_normalization;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 13, 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;
|
||||
|
||||
SDFVoxelGICascadeData cascades[SDFGI_MAX_CASCADES];
|
||||
}
|
||||
sdfgi;
|
||||
|
||||
layout(set = 0, binding = 14) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP;
|
||||
|
||||
layout(set = 0, binding = 15) uniform texture2D best_fit_normal_texture;
|
||||
|
||||
/* Set 1: Render Pass (changes per render pass) */
|
||||
|
||||
layout(set = 1, binding = 0, std140) uniform SceneDataBlock {
|
||||
SceneData data;
|
||||
SceneData prev_data;
|
||||
}
|
||||
scene_data_block;
|
||||
|
||||
struct ImplementationData {
|
||||
uint cluster_shift;
|
||||
uint cluster_width;
|
||||
uint cluster_type_size;
|
||||
uint max_cluster_element_count_div_32;
|
||||
|
||||
uint ss_effects_flags;
|
||||
float ssao_light_affect;
|
||||
float ssao_ao_affect;
|
||||
uint pad1;
|
||||
|
||||
mat4 sdf_to_bounds;
|
||||
|
||||
ivec3 sdf_offset;
|
||||
uint pad2;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
layout(set = 1, binding = 1, std140) uniform ImplementationDataBlock {
|
||||
ImplementationData data;
|
||||
}
|
||||
implementation_data_block;
|
||||
|
||||
#define implementation_data implementation_data_block.data
|
||||
|
||||
struct InstanceData {
|
||||
mat4 transform;
|
||||
mat4 prev_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;
|
||||
vec4 compressed_aabb_position_pad; // Only .xyz is used. .w is padding.
|
||||
vec4 compressed_aabb_size_pad; // Only .xyz is used. .w is padding.
|
||||
vec4 uv_scale;
|
||||
};
|
||||
|
||||
layout(set = 1, binding = 2, std430) buffer restrict readonly InstanceDataBuffer {
|
||||
InstanceData data[];
|
||||
}
|
||||
instances;
|
||||
|
||||
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
|
||||
|
||||
layout(set = 1, binding = 3) uniform textureCubeArray radiance_cubemap;
|
||||
|
||||
#else
|
||||
|
||||
layout(set = 1, binding = 3) uniform textureCube radiance_cubemap;
|
||||
|
||||
#endif
|
||||
|
||||
layout(set = 1, binding = 4) uniform textureCubeArray reflection_atlas;
|
||||
|
||||
layout(set = 1, binding = 5) uniform texture2D shadow_atlas;
|
||||
|
||||
layout(set = 1, binding = 6) uniform texture2D directional_shadow_atlas;
|
||||
|
||||
layout(set = 1, binding = 7) uniform texture2DArray lightmap_textures[MAX_LIGHTMAP_TEXTURES];
|
||||
|
||||
layout(set = 1, binding = 8) uniform texture3D voxel_gi_textures[MAX_VOXEL_GI_INSTANCES];
|
||||
|
||||
layout(set = 1, binding = 9, std430) buffer restrict readonly ClusterBuffer {
|
||||
uint data[];
|
||||
}
|
||||
cluster_buffer;
|
||||
|
||||
layout(set = 1, binding = 10) uniform sampler decal_sampler;
|
||||
|
||||
layout(set = 1, binding = 11) uniform sampler light_projector_sampler;
|
||||
|
||||
layout(set = 1, binding = 12 + 0) uniform sampler SAMPLER_NEAREST_CLAMP;
|
||||
layout(set = 1, binding = 12 + 1) uniform sampler SAMPLER_LINEAR_CLAMP;
|
||||
layout(set = 1, binding = 12 + 2) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP;
|
||||
layout(set = 1, binding = 12 + 3) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP;
|
||||
layout(set = 1, binding = 12 + 4) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP;
|
||||
layout(set = 1, binding = 12 + 5) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP;
|
||||
layout(set = 1, binding = 12 + 6) uniform sampler SAMPLER_NEAREST_REPEAT;
|
||||
layout(set = 1, binding = 12 + 7) uniform sampler SAMPLER_LINEAR_REPEAT;
|
||||
layout(set = 1, binding = 12 + 8) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT;
|
||||
layout(set = 1, binding = 12 + 9) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT;
|
||||
layout(set = 1, binding = 12 + 10) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT;
|
||||
layout(set = 1, binding = 12 + 11) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT;
|
||||
|
||||
#ifdef MODE_RENDER_SDF
|
||||
|
||||
layout(r16ui, set = 1, binding = 24) uniform restrict writeonly uimage3D albedo_volume_grid;
|
||||
layout(r32ui, set = 1, binding = 25) uniform restrict writeonly uimage3D emission_grid;
|
||||
layout(r32ui, set = 1, binding = 26) uniform restrict writeonly uimage3D emission_aniso_grid;
|
||||
layout(r32ui, set = 1, binding = 27) 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
|
||||
|
||||
#define multiviewSampler sampler2D
|
||||
#else
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(set = 1, binding = 24) uniform texture2DArray depth_buffer;
|
||||
layout(set = 1, binding = 25) uniform texture2DArray color_buffer;
|
||||
layout(set = 1, binding = 26) uniform texture2DArray normal_roughness_buffer;
|
||||
layout(set = 1, binding = 27) uniform texture2DArray ao_buffer;
|
||||
layout(set = 1, binding = 28) uniform texture2DArray ambient_buffer;
|
||||
layout(set = 1, binding = 29) uniform texture2DArray reflection_buffer;
|
||||
#define multiviewSampler sampler2DArray
|
||||
#else // USE_MULTIVIEW
|
||||
layout(set = 1, binding = 24) uniform texture2D depth_buffer;
|
||||
layout(set = 1, binding = 25) uniform texture2D color_buffer;
|
||||
layout(set = 1, binding = 26) uniform texture2D normal_roughness_buffer;
|
||||
layout(set = 1, binding = 27) uniform texture2D ao_buffer;
|
||||
layout(set = 1, binding = 28) uniform texture2D ambient_buffer;
|
||||
layout(set = 1, binding = 29) uniform texture2D reflection_buffer;
|
||||
#define multiviewSampler sampler2D
|
||||
#endif
|
||||
layout(set = 1, binding = 30) uniform texture2DArray sdfgi_lightprobe_texture;
|
||||
layout(set = 1, binding = 31) uniform texture3D sdfgi_occlusion_cascades;
|
||||
|
||||
struct VoxelGIData {
|
||||
mat4 xform; // 64 - 64
|
||||
|
||||
vec3 bounds; // 12 - 76
|
||||
float dynamic_range; // 4 - 80
|
||||
|
||||
float bias; // 4 - 84
|
||||
float normal_bias; // 4 - 88
|
||||
bool blend_ambient; // 4 - 92
|
||||
uint mipmaps; // 4 - 96
|
||||
|
||||
vec3 pad; // 12 - 108
|
||||
float exposure_normalization; // 4 - 112
|
||||
};
|
||||
|
||||
layout(set = 1, binding = 32, std140) uniform VoxelGIs {
|
||||
VoxelGIData data[MAX_VOXEL_GI_INSTANCES];
|
||||
}
|
||||
voxel_gi_instances;
|
||||
|
||||
layout(set = 1, binding = 33) uniform texture3D volumetric_fog_texture;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(set = 1, binding = 34) uniform texture2DArray ssil_buffer;
|
||||
#else
|
||||
layout(set = 1, binding = 34) uniform texture2D ssil_buffer;
|
||||
#endif // USE_MULTIVIEW
|
||||
|
||||
#endif
|
||||
|
||||
vec4 normal_roughness_compatibility(vec4 p_normal_roughness) {
|
||||
float roughness = p_normal_roughness.w;
|
||||
if (roughness > 0.5) {
|
||||
roughness = 1.0 - roughness;
|
||||
}
|
||||
roughness /= (127.0 / 255.0);
|
||||
return vec4(normalize(p_normal_roughness.xyz * 2.0 - 1.0) * 0.5 + 0.5, roughness);
|
||||
}
|
||||
|
||||
/* Set 2 Skeleton & Instancing (can change per item) */
|
||||
|
||||
layout(set = 2, binding = 0, std430) restrict readonly buffer Transforms {
|
||||
vec4 data[];
|
||||
}
|
||||
transforms;
|
||||
|
||||
/* Set 3 User Material */
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
if "RD_GLSL" in env["BUILDERS"]:
|
||||
# find all include files
|
||||
gl_include_files = [str(f) for f in Glob("*_inc.glsl")] + [str(f) for f in Glob("../*_inc.glsl")]
|
||||
|
||||
# find all shader code(all glsl files excluding our include files)
|
||||
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
|
||||
|
||||
# make sure we recompile shaders if include files change
|
||||
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#glsl_builders.py"])
|
||||
|
||||
# compile shaders
|
||||
for glsl_file in glsl_files:
|
||||
env.RD_GLSL(glsl_file)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,191 @@
|
|||
#define M_PI 3.14159265359
|
||||
#define MAX_VIEWS 2
|
||||
|
||||
#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
|
||||
#extension GL_EXT_multiview : enable
|
||||
#endif
|
||||
|
||||
#include "../decal_data_inc.glsl"
|
||||
#include "../scene_data_inc.glsl"
|
||||
|
||||
#if !defined(MODE_RENDER_DEPTH) || defined(MODE_RENDER_MATERIAL) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
|
||||
#ifndef NORMAL_USED
|
||||
#define NORMAL_USED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define USING_MOBILE_RENDERER
|
||||
|
||||
layout(push_constant, std430) uniform DrawCall {
|
||||
vec2 uv_offset;
|
||||
uint instance_index;
|
||||
uint pad;
|
||||
}
|
||||
draw_call;
|
||||
|
||||
/* Set 0: Base Pass (never changes) */
|
||||
|
||||
#include "../light_data_inc.glsl"
|
||||
|
||||
layout(set = 0, binding = 2) uniform sampler shadow_sampler;
|
||||
|
||||
#define INSTANCE_FLAGS_DYNAMIC (1 << 3)
|
||||
#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4)
|
||||
#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5)
|
||||
#define INSTANCE_FLAGS_USE_SDFGI (1 << 6)
|
||||
#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 7)
|
||||
#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 8)
|
||||
#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 9)
|
||||
#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 10)
|
||||
#define INSTANCE_FLAGS_PARTICLES (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_PARTICLE_TRAIL_SHIFT 16
|
||||
//3 bits of stride
|
||||
#define INSTANCE_FLAGS_PARTICLE_TRAIL_MASK 0xFF
|
||||
|
||||
layout(set = 0, binding = 3, std430) restrict readonly buffer OmniLights {
|
||||
LightData data[];
|
||||
}
|
||||
omni_lights;
|
||||
|
||||
layout(set = 0, binding = 4, std430) restrict readonly buffer SpotLights {
|
||||
LightData data[];
|
||||
}
|
||||
spot_lights;
|
||||
|
||||
layout(set = 0, binding = 5, std430) restrict readonly buffer ReflectionProbeData {
|
||||
ReflectionData data[];
|
||||
}
|
||||
reflections;
|
||||
|
||||
layout(set = 0, binding = 6, 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 {
|
||||
mediump mat3 normal_xform;
|
||||
vec3 pad;
|
||||
float exposure_normalization;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps {
|
||||
Lightmap data[];
|
||||
}
|
||||
lightmaps;
|
||||
|
||||
struct LightmapCapture {
|
||||
mediump vec4 sh[9];
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 8, std140) restrict readonly buffer LightmapCaptures {
|
||||
LightmapCapture data[];
|
||||
}
|
||||
lightmap_captures;
|
||||
|
||||
layout(set = 0, binding = 9) uniform mediump texture2D decal_atlas;
|
||||
layout(set = 0, binding = 10) uniform mediump texture2D decal_atlas_srgb;
|
||||
|
||||
layout(set = 0, binding = 11, std430) restrict readonly buffer Decals {
|
||||
DecalData data[];
|
||||
}
|
||||
decals;
|
||||
|
||||
layout(set = 0, binding = 12, std430) restrict readonly buffer GlobalShaderUniformData {
|
||||
highp vec4 data[];
|
||||
}
|
||||
global_shader_uniforms;
|
||||
|
||||
layout(set = 0, binding = 13) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP;
|
||||
|
||||
/* Set 1: Render Pass (changes per render pass) */
|
||||
|
||||
layout(set = 1, binding = 0, std140) uniform SceneDataBlock {
|
||||
SceneData data;
|
||||
SceneData prev_data;
|
||||
}
|
||||
scene_data_block;
|
||||
|
||||
struct InstanceData {
|
||||
highp mat4 transform; // 64 - 64
|
||||
uint flags; // 04 - 68
|
||||
uint instance_uniforms_ofs; // Base offset in global buffer for instance variables. // 04 - 72
|
||||
uint gi_offset; // GI information when using lightmapping (VCT or lightmap index). // 04 - 76
|
||||
uint layer_mask; // 04 - 80
|
||||
highp vec4 lightmap_uv_scale; // 16 - 96 Doubles as uv_offset when needed.
|
||||
|
||||
uvec2 reflection_probes; // 08 - 104
|
||||
uvec2 omni_lights; // 08 - 112
|
||||
uvec2 spot_lights; // 08 - 120
|
||||
uvec2 decals; // 08 - 128
|
||||
|
||||
vec4 compressed_aabb_position_pad; // 16 - 144 // Only .xyz is used. .w is padding.
|
||||
vec4 compressed_aabb_size_pad; // 16 - 160 // Only .xyz is used. .w is padding.
|
||||
vec4 uv_scale; // 16 - 176
|
||||
};
|
||||
|
||||
layout(set = 1, binding = 1, std430) buffer restrict readonly InstanceDataBuffer {
|
||||
InstanceData data[];
|
||||
}
|
||||
instances;
|
||||
|
||||
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
|
||||
|
||||
layout(set = 1, binding = 2) uniform mediump textureCubeArray radiance_cubemap;
|
||||
|
||||
#else
|
||||
|
||||
layout(set = 1, binding = 2) uniform mediump textureCube radiance_cubemap;
|
||||
|
||||
#endif
|
||||
|
||||
layout(set = 1, binding = 3) uniform mediump textureCubeArray reflection_atlas;
|
||||
|
||||
layout(set = 1, binding = 4) uniform highp texture2D shadow_atlas;
|
||||
|
||||
layout(set = 1, binding = 5) uniform highp texture2D directional_shadow_atlas;
|
||||
|
||||
// this needs to change to providing just the lightmap we're using..
|
||||
layout(set = 1, binding = 6) uniform texture2DArray lightmap_textures[MAX_LIGHTMAP_TEXTURES];
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(set = 1, binding = 9) uniform highp texture2DArray depth_buffer;
|
||||
layout(set = 1, binding = 10) uniform mediump texture2DArray color_buffer;
|
||||
#define multiviewSampler sampler2DArray
|
||||
#else
|
||||
layout(set = 1, binding = 9) uniform highp texture2D depth_buffer;
|
||||
layout(set = 1, binding = 10) uniform mediump texture2D color_buffer;
|
||||
#define multiviewSampler sampler2D
|
||||
#endif // USE_MULTIVIEW
|
||||
|
||||
layout(set = 1, binding = 11) uniform sampler decal_sampler;
|
||||
|
||||
layout(set = 1, binding = 12) uniform sampler light_projector_sampler;
|
||||
|
||||
layout(set = 1, binding = 13 + 0) uniform sampler SAMPLER_NEAREST_CLAMP;
|
||||
layout(set = 1, binding = 13 + 1) uniform sampler SAMPLER_LINEAR_CLAMP;
|
||||
layout(set = 1, binding = 13 + 2) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP;
|
||||
layout(set = 1, binding = 13 + 3) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP;
|
||||
layout(set = 1, binding = 13 + 4) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP;
|
||||
layout(set = 1, binding = 13 + 5) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP;
|
||||
layout(set = 1, binding = 13 + 6) uniform sampler SAMPLER_NEAREST_REPEAT;
|
||||
layout(set = 1, binding = 13 + 7) uniform sampler SAMPLER_LINEAR_REPEAT;
|
||||
layout(set = 1, binding = 13 + 8) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT;
|
||||
layout(set = 1, binding = 13 + 9) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT;
|
||||
layout(set = 1, binding = 13 + 10) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT;
|
||||
layout(set = 1, binding = 13 + 11) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT;
|
||||
|
||||
/* Set 2 Skeleton & Instancing (can change per item) */
|
||||
|
||||
layout(set = 2, binding = 0, std430) restrict readonly buffer Transforms {
|
||||
highp vec4 data[];
|
||||
}
|
||||
transforms;
|
||||
|
||||
/* Set 3 User Material */
|
||||
275
engine/servers/rendering/renderer_rd/shaders/giprobe_write.glsl
Normal file
275
engine/servers/rendering/renderer_rd/shaders/giprobe_write.glsl
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
#define NO_CHILDREN 0xFFFFFFFF
|
||||
|
||||
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 cos_spot_angle;
|
||||
|
||||
vec3 position;
|
||||
float inv_spot_attenuation;
|
||||
|
||||
vec3 direction;
|
||||
bool has_shadow;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 3, std140) uniform Lights {
|
||||
Light data[MAX_LIGHTS];
|
||||
}
|
||||
lights;
|
||||
|
||||
#endif
|
||||
|
||||
layout(push_constant, 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 cos_spot_angle = lights.data[light].cos_spot_angle;
|
||||
float cos_angle = dot(rel, lights.data[light].direction);
|
||||
if (cos_angle < cos_spot_angle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float scos = max(cos_angle, cos_spot_angle);
|
||||
float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - cos_spot_angle));
|
||||
attenuation *= 1.0 - pow(spot_rim, lights.data[light].inv_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);
|
||||
|
||||
vec3 accum = vec3(0.0);
|
||||
|
||||
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;
|
||||
|
||||
if (length(normal.xyz) > 0.2) {
|
||||
accum += max(0.0, dot(normal.xyz, -light_dir)) * light + emission;
|
||||
} else {
|
||||
//all directions
|
||||
accum += light + emission;
|
||||
}
|
||||
}
|
||||
|
||||
output.data[cell_index] = vec4(accum, 0.0);
|
||||
|
||||
#endif //MODE_COMPUTE_LIGHT
|
||||
|
||||
#ifdef MODE_UPDATE_MIPMAPS
|
||||
|
||||
{
|
||||
vec3 light_accum = vec3(0.0);
|
||||
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;
|
||||
}
|
||||
light_accum += output.data[child_index].rgb;
|
||||
|
||||
count += 1.0;
|
||||
}
|
||||
|
||||
float divisor = mix(8.0, count, params.propagation);
|
||||
output.data[cell_index] = vec4(light_accum / divisor, 0.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MODE_WRITE_TEXTURE
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
#define LIGHT_BAKE_DISABLED 0
|
||||
#define LIGHT_BAKE_STATIC 1
|
||||
#define LIGHT_BAKE_DYNAMIC 2
|
||||
|
||||
struct LightData { //this structure needs to be as packed as possible
|
||||
highp vec3 position;
|
||||
highp float inv_radius;
|
||||
|
||||
mediump vec3 direction;
|
||||
highp float size;
|
||||
|
||||
mediump vec3 color;
|
||||
mediump float attenuation;
|
||||
|
||||
mediump float cone_attenuation;
|
||||
mediump float cone_angle;
|
||||
mediump float specular_amount;
|
||||
mediump float shadow_opacity;
|
||||
|
||||
highp vec4 atlas_rect; // rect in the shadow atlas
|
||||
highp mat4 shadow_matrix;
|
||||
highp float shadow_bias;
|
||||
highp float shadow_normal_bias;
|
||||
highp float transmittance_bias;
|
||||
highp float soft_shadow_size; // for spot, it's the size in uv coordinates of the light, for omni it's the span angle
|
||||
highp float soft_shadow_scale; // scales the shadow kernel for blurrier shadows
|
||||
uint mask;
|
||||
mediump float volumetric_fog_energy;
|
||||
uint bake_mode;
|
||||
highp 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 {
|
||||
highp vec3 box_extents;
|
||||
mediump float index;
|
||||
highp vec3 box_offset;
|
||||
uint mask;
|
||||
mediump vec3 ambient; // ambient color
|
||||
mediump float intensity;
|
||||
bool exterior;
|
||||
bool box_project;
|
||||
uint ambient_mode;
|
||||
float exposure_normalization;
|
||||
//0-8 is intensity,8-9 is ambient, mode
|
||||
highp 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 {
|
||||
mediump vec3 direction;
|
||||
highp float energy; // needs to be highp to avoid NaNs being created with high energy values (i.e. when using physical light units and over-exposing the image)
|
||||
mediump vec3 color;
|
||||
mediump float size;
|
||||
mediump float specular;
|
||||
uint mask;
|
||||
highp float softshadow_angle;
|
||||
highp float soft_shadow_scale;
|
||||
bool blend_splits;
|
||||
mediump float shadow_opacity;
|
||||
highp float fade_from;
|
||||
highp float fade_to;
|
||||
uvec2 pad;
|
||||
uint bake_mode;
|
||||
mediump float volumetric_fog_energy;
|
||||
highp vec4 shadow_bias;
|
||||
highp vec4 shadow_normal_bias;
|
||||
highp vec4 shadow_transmittance_bias;
|
||||
highp vec4 shadow_z_range;
|
||||
highp vec4 shadow_range_begin;
|
||||
highp vec4 shadow_split_offsets;
|
||||
highp mat4 shadow_matrix1;
|
||||
highp mat4 shadow_matrix2;
|
||||
highp mat4 shadow_matrix3;
|
||||
highp mat4 shadow_matrix4;
|
||||
highp vec2 uv_scale1;
|
||||
highp vec2 uv_scale2;
|
||||
highp vec2 uv_scale3;
|
||||
highp vec2 uv_scale4;
|
||||
};
|
||||
678
engine/servers/rendering/renderer_rd/shaders/particles.glsl
Normal file
678
engine/servers/rendering/renderer_rd/shaders/particles.glsl
Normal file
|
|
@ -0,0 +1,678 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
#define SDF_MAX_LENGTH 16384.0
|
||||
|
||||
/* SET 0: GLOBAL DATA */
|
||||
|
||||
#include "samplers_inc.glsl"
|
||||
|
||||
layout(set = 0, binding = 2, std430) restrict readonly buffer GlobalShaderUniformData {
|
||||
vec4 data[];
|
||||
}
|
||||
global_shader_uniforms;
|
||||
|
||||
/* 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
|
||||
#define COLLIDER_TYPE_2D_SDF 4
|
||||
|
||||
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 frame;
|
||||
float amount_ratio;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
|
||||
uint random_seed;
|
||||
uint attractor_count;
|
||||
uint collider_count;
|
||||
float particle_size;
|
||||
|
||||
mat4 emission_transform;
|
||||
vec3 emitter_velocity;
|
||||
float interp_to_end;
|
||||
|
||||
Attractor attractors[MAX_ATTRACTORS];
|
||||
Collider colliders[MAX_COLLIDERS];
|
||||
};
|
||||
|
||||
layout(set = 1, binding = 0, std430) restrict buffer FrameHistory {
|
||||
FrameParams data[];
|
||||
}
|
||||
frame_history;
|
||||
|
||||
#define PARTICLE_FLAG_ACTIVE uint(1)
|
||||
#define PARTICLE_FLAG_STARTED uint(2)
|
||||
#define PARTICLE_FLAG_TRAILED uint(4)
|
||||
#define PARTICLE_FRAME_MASK uint(0xFFFF)
|
||||
#define PARTICLE_FRAME_SHIFT uint(16)
|
||||
|
||||
struct ParticleData {
|
||||
mat4 xform;
|
||||
vec3 velocity;
|
||||
uint flags;
|
||||
vec4 color;
|
||||
vec4 custom;
|
||||
#ifdef USERDATA1_USED
|
||||
vec4 userdata1;
|
||||
#endif
|
||||
#ifdef USERDATA2_USED
|
||||
vec4 userdata2;
|
||||
#endif
|
||||
#ifdef USERDATA3_USED
|
||||
vec4 userdata3;
|
||||
#endif
|
||||
#ifdef USERDATA4_USED
|
||||
vec4 userdata4;
|
||||
#endif
|
||||
#ifdef USERDATA5_USED
|
||||
vec4 userdata5;
|
||||
#endif
|
||||
#ifdef USERDATA6_USED
|
||||
vec4 userdata6;
|
||||
#endif
|
||||
};
|
||||
|
||||
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 MATERIAL_UNIFORMS_USED
|
||||
layout(set = 3, binding = 0, std140) uniform MaterialUniforms{
|
||||
|
||||
#MATERIAL_UNIFORMS
|
||||
|
||||
} material;
|
||||
#endif
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float lifetime;
|
||||
bool clear;
|
||||
uint total_particles;
|
||||
uint trail_size;
|
||||
bool use_fractional_delta;
|
||||
bool sub_emitter_mode;
|
||||
bool can_emit;
|
||||
bool trail_pass;
|
||||
}
|
||||
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_subparticle(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;
|
||||
}
|
||||
|
||||
vec3 safe_normalize(vec3 direction) {
|
||||
const float EPSILON = 0.001;
|
||||
if (length(direction) < EPSILON) {
|
||||
return vec3(0.0);
|
||||
}
|
||||
return normalize(direction);
|
||||
}
|
||||
|
||||
#GLOBALS
|
||||
|
||||
void main() {
|
||||
uint particle = gl_GlobalInvocationID.x;
|
||||
|
||||
if (params.trail_size > 1) {
|
||||
if (params.trail_pass) {
|
||||
if (particle >= params.total_particles * (params.trail_size - 1)) {
|
||||
return;
|
||||
}
|
||||
particle += (particle / (params.trail_size - 1)) + 1;
|
||||
} else {
|
||||
if (particle >= params.total_particles) {
|
||||
return;
|
||||
}
|
||||
particle *= params.trail_size;
|
||||
}
|
||||
}
|
||||
|
||||
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.flags = 0;
|
||||
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));
|
||||
}
|
||||
|
||||
//clear started flag if set
|
||||
|
||||
if (params.trail_pass) {
|
||||
//trail started
|
||||
uint src_idx = index * params.trail_size;
|
||||
if (bool(particles.data[src_idx].flags & PARTICLE_FLAG_STARTED)) {
|
||||
//save start conditions for trails
|
||||
PARTICLE.color = particles.data[src_idx].color;
|
||||
PARTICLE.custom = particles.data[src_idx].custom;
|
||||
PARTICLE.velocity = particles.data[src_idx].velocity;
|
||||
PARTICLE.flags = PARTICLE_FLAG_TRAILED | ((frame_history.data[0].frame & PARTICLE_FRAME_MASK) << PARTICLE_FRAME_SHIFT); //mark it as trailed, save in which frame it will start
|
||||
PARTICLE.xform = particles.data[src_idx].xform;
|
||||
#ifdef USERDATA1_USED
|
||||
PARTICLE.userdata1 = particles.data[src_idx].userdata1;
|
||||
#endif
|
||||
#ifdef USERDATA2_USED
|
||||
PARTICLE.userdata2 = particles.data[src_idx].userdata2;
|
||||
#endif
|
||||
#ifdef USERDATA3_USED
|
||||
PARTICLE.userdata3 = particles.data[src_idx].userdata3;
|
||||
#endif
|
||||
#ifdef USERDATA4_USED
|
||||
PARTICLE.userdata4 = particles.data[src_idx].userdata4;
|
||||
#endif
|
||||
#ifdef USERDATA5_USED
|
||||
PARTICLE.userdata5 = particles.data[src_idx].userdata5;
|
||||
#endif
|
||||
#ifdef USERDATA6_USED
|
||||
PARTICLE.userdata6 = particles.data[src_idx].userdata6;
|
||||
#endif
|
||||
}
|
||||
if (!bool(particles.data[src_idx].flags & PARTICLE_FLAG_ACTIVE)) {
|
||||
// Disable the entire trail if the parent is no longer active.
|
||||
PARTICLE.flags = 0;
|
||||
return;
|
||||
}
|
||||
if (bool(PARTICLE.flags & PARTICLE_FLAG_TRAILED) && ((PARTICLE.flags >> PARTICLE_FRAME_SHIFT) == (FRAME.frame & PARTICLE_FRAME_MASK))) { //check this is trailed and see if it should start now
|
||||
// we just assume that this is the first frame of the particle, the rest is deterministic
|
||||
PARTICLE.flags = PARTICLE_FLAG_ACTIVE | (particles.data[src_idx].flags & (PARTICLE_FRAME_MASK << PARTICLE_FRAME_SHIFT));
|
||||
return; //- this appears like it should be correct, but it seems not to be.. wonder why.
|
||||
}
|
||||
|
||||
} else {
|
||||
PARTICLE.flags &= ~PARTICLE_FLAG_STARTED;
|
||||
}
|
||||
|
||||
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 (bool(PARTICLE.flags & PARTICLE_FLAG_ACTIVE)) {
|
||||
PARTICLE.xform[3].xyz += PARTICLE.velocity * local_delta;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!params.trail_pass && params.sub_emitter_mode) {
|
||||
if (!bool(PARTICLE.flags & PARTICLE_FLAG_ACTIVE)) {
|
||||
int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
|
||||
|
||||
if (src_index >= 0) {
|
||||
PARTICLE.flags = (PARTICLE_FLAG_ACTIVE | PARTICLE_FLAG_STARTED | (FRAME.cycle << PARTICLE_FRAME_SHIFT));
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (params.trail_pass) {
|
||||
restart = false;
|
||||
}
|
||||
|
||||
if (restart) {
|
||||
PARTICLE.flags = FRAME.emitting ? (PARTICLE_FLAG_ACTIVE | PARTICLE_FLAG_STARTED | (FRAME.cycle << PARTICLE_FRAME_SHIFT)) : 0;
|
||||
restart_position = true;
|
||||
restart_rotation_scale = true;
|
||||
restart_velocity = true;
|
||||
restart_color = true;
|
||||
restart_custom = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool particle_active = bool(PARTICLE.flags & PARTICLE_FLAG_ACTIVE);
|
||||
|
||||
uint particle_number = (PARTICLE.flags >> PARTICLE_FRAME_SHIFT) * uint(params.total_particles) + index;
|
||||
|
||||
if (restart && particle_active) {
|
||||
#CODE : START
|
||||
}
|
||||
|
||||
if (particle_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 = safe_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 = safe_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 + 1.0) * 0.5;
|
||||
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], SAMPLER_LINEAR_CLAMP), uvw_pos).xyz * -2.0 + 1.0;
|
||||
dir = mat3(FRAME.attractors[i].transform) * safe_normalize(s); //revert direction
|
||||
amount = length(s);
|
||||
|
||||
} break;
|
||||
}
|
||||
amount = pow(amount, FRAME.attractors[i].attenuation);
|
||||
dir = safe_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_COLLISION_SCALE
|
||||
|
||||
particle_size *= dot(vec3(length(PARTICLE.xform[0].xyz), length(PARTICLE.xform[1].xyz), length(PARTICLE.xform[2].xyz)), vec3(0.33333333333));
|
||||
|
||||
#endif
|
||||
|
||||
if (FRAME.collider_count == 1 && FRAME.colliders[0].type == COLLIDER_TYPE_2D_SDF) {
|
||||
//2D collision
|
||||
|
||||
vec2 pos = PARTICLE.xform[3].xy;
|
||||
vec4 to_sdf_x = FRAME.colliders[0].transform[0];
|
||||
vec4 to_sdf_y = FRAME.colliders[0].transform[1];
|
||||
vec2 sdf_pos = vec2(dot(vec4(pos, 0, 1), to_sdf_x), dot(vec4(pos, 0, 1), to_sdf_y));
|
||||
|
||||
vec4 sdf_to_screen = vec4(FRAME.colliders[0].extents, FRAME.colliders[0].scale);
|
||||
|
||||
vec2 uv_pos = sdf_pos * sdf_to_screen.xy + sdf_to_screen.zw;
|
||||
|
||||
if (all(greaterThan(uv_pos, vec2(0.0))) && all(lessThan(uv_pos, vec2(1.0)))) {
|
||||
vec2 pos2 = pos + vec2(0, particle_size);
|
||||
vec2 sdf_pos2 = vec2(dot(vec4(pos2, 0, 1), to_sdf_x), dot(vec4(pos2, 0, 1), to_sdf_y));
|
||||
float sdf_particle_size = distance(sdf_pos, sdf_pos2);
|
||||
|
||||
float d = texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uv_pos).r * SDF_MAX_LENGTH;
|
||||
|
||||
d -= sdf_particle_size;
|
||||
|
||||
if (d < 0.0) {
|
||||
const float EPSILON = 0.001;
|
||||
vec2 n = normalize(vec2(
|
||||
texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uv_pos + vec2(EPSILON, 0.0)).r - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uv_pos - vec2(EPSILON, 0.0)).r,
|
||||
texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uv_pos + vec2(0.0, EPSILON)).r - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uv_pos - vec2(0.0, EPSILON)).r));
|
||||
|
||||
collided = true;
|
||||
sdf_pos2 = sdf_pos + n * d;
|
||||
pos2 = vec2(dot(vec4(sdf_pos2, 0, 1), FRAME.colliders[0].transform[2]), dot(vec4(sdf_pos2, 0, 1), FRAME.colliders[0].transform[3]));
|
||||
|
||||
n = pos - pos2;
|
||||
|
||||
collision_normal = normalize(vec3(n, 0.0));
|
||||
collision_depth = length(n);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
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);
|
||||
|
||||
// Allowing for a small epsilon to allow particle just touching colliders to count as collided
|
||||
const float EPSILON = 0.001;
|
||||
switch (FRAME.colliders[i].type) {
|
||||
case COLLIDER_TYPE_SPHERE: {
|
||||
float d = length(rel_vec) - (particle_size + FRAME.colliders[i].extents.x);
|
||||
|
||||
if (d <= EPSILON) {
|
||||
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 <= EPSILON) {
|
||||
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], SAMPLER_LINEAR_CLAMP), uvw_pos).r;
|
||||
s *= FRAME.colliders[i].scale;
|
||||
s += extra_dist;
|
||||
if (s <= particle_size + EPSILON) {
|
||||
col = true;
|
||||
depth = particle_size - s;
|
||||
|
||||
normal = mat3(FRAME.colliders[i].transform) *
|
||||
normalize(
|
||||
vec3(
|
||||
texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos - vec3(EPSILON, 0.0, 0.0)).r,
|
||||
texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos - vec3(0.0, EPSILON, 0.0)).r,
|
||||
texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], 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 = texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz).r;
|
||||
|
||||
if (y + EPSILON >= 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, texture(sampler2D(height_field_texture, 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, texture(sampler2D(height_field_texture, 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 (particle_active) {
|
||||
#CODE : PROCESS
|
||||
}
|
||||
|
||||
PARTICLE.flags &= ~PARTICLE_FLAG_ACTIVE;
|
||||
if (particle_active) {
|
||||
PARTICLE.flags |= PARTICLE_FLAG_ACTIVE;
|
||||
}
|
||||
}
|
||||
237
engine/servers/rendering/renderer_rd/shaders/particles_copy.glsl
Normal file
237
engine/servers/rendering/renderer_rd/shaders/particles_copy.glsl
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
#define PARTICLE_FLAG_ACTIVE uint(1)
|
||||
#define PARTICLE_FLAG_STARTED uint(2)
|
||||
#define PARTICLE_FLAG_TRAILED uint(4)
|
||||
|
||||
struct ParticleData {
|
||||
mat4 xform;
|
||||
vec3 velocity;
|
||||
uint flags;
|
||||
vec4 color;
|
||||
vec4 custom;
|
||||
#ifdef USERDATA_COUNT
|
||||
vec4 userdata[USERDATA_COUNT];
|
||||
#endif
|
||||
};
|
||||
|
||||
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(set = 2, binding = 0, std430) restrict readonly buffer TrailBindPoses {
|
||||
mat4 data[];
|
||||
}
|
||||
trail_bind_poses;
|
||||
|
||||
#define PARAMS_FLAG_ORDER_BY_LIFETIME 1
|
||||
#define PARAMS_FLAG_COPY_MODE_2D 2
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec3 sort_direction;
|
||||
uint total_particles;
|
||||
|
||||
uint trail_size;
|
||||
uint trail_total;
|
||||
float frame_delta;
|
||||
float frame_remainder;
|
||||
|
||||
vec3 align_up;
|
||||
uint align_mode;
|
||||
|
||||
uint lifetime_split;
|
||||
bool lifetime_reverse;
|
||||
uint motion_vectors_current_offset;
|
||||
uint flags;
|
||||
|
||||
mat4 inv_emission_transform;
|
||||
}
|
||||
params;
|
||||
|
||||
#define TRANSFORM_ALIGN_DISABLED 0
|
||||
#define TRANSFORM_ALIGN_Z_BILLBOARD 1
|
||||
#define TRANSFORM_ALIGN_Y_TO_VELOCITY 2
|
||||
#define TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY 3
|
||||
|
||||
void main() {
|
||||
#ifdef MODE_FILL_SORT_BUFFER
|
||||
|
||||
uint particle = gl_GlobalInvocationID.x;
|
||||
if (particle >= params.total_particles) {
|
||||
return; //discard
|
||||
}
|
||||
|
||||
uint src_particle = particle;
|
||||
if (params.trail_size > 1) {
|
||||
src_particle = src_particle * params.trail_size + params.trail_size / 2; //use trail center for sorting
|
||||
}
|
||||
sort_buffer.data[particle].x = dot(params.sort_direction, particles.data[src_particle].xform[3].xyz);
|
||||
sort_buffer.data[particle].y = float(particle);
|
||||
#endif
|
||||
|
||||
#ifdef MODE_FILL_INSTANCES
|
||||
|
||||
uint particle = gl_GlobalInvocationID.x;
|
||||
|
||||
if (particle >= params.total_particles) {
|
||||
return; //discard
|
||||
}
|
||||
|
||||
#ifdef USE_SORT_BUFFER
|
||||
|
||||
if (params.trail_size > 1) {
|
||||
particle = uint(sort_buffer.data[particle / params.trail_size].y) + (particle % params.trail_size);
|
||||
} else {
|
||||
particle = uint(sort_buffer.data[particle].y); //use index from sort buffer
|
||||
}
|
||||
#else
|
||||
if (bool(params.flags & PARAMS_FLAG_ORDER_BY_LIFETIME)) {
|
||||
if (params.trail_size > 1) {
|
||||
uint limit = (params.total_particles / params.trail_size) - params.lifetime_split;
|
||||
|
||||
uint base_index = particle / params.trail_size;
|
||||
uint base_offset = particle % params.trail_size;
|
||||
|
||||
if (params.lifetime_reverse) {
|
||||
base_index = (params.total_particles / params.trail_size) - base_index - 1;
|
||||
}
|
||||
|
||||
if (base_index < limit) {
|
||||
base_index = params.lifetime_split + base_index;
|
||||
} else {
|
||||
base_index -= limit;
|
||||
}
|
||||
|
||||
particle = base_index * params.trail_size + base_offset;
|
||||
|
||||
} else {
|
||||
uint limit = params.total_particles - params.lifetime_split;
|
||||
|
||||
if (params.lifetime_reverse) {
|
||||
particle = params.total_particles - particle - 1;
|
||||
}
|
||||
|
||||
if (particle < limit) {
|
||||
particle = params.lifetime_split + particle;
|
||||
} else {
|
||||
particle -= limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // USE_SORT_BUFFER
|
||||
|
||||
mat4 txform;
|
||||
|
||||
if (bool(particles.data[particle].flags & PARTICLE_FLAG_ACTIVE) || bool(particles.data[particle].flags & PARTICLE_FLAG_TRAILED)) {
|
||||
txform = particles.data[particle].xform;
|
||||
if (params.trail_size > 1) {
|
||||
// Since the steps don't fit precisely in the history frames, must do a tiny bit of
|
||||
// interpolation to get them close to their intended location.
|
||||
uint part_ofs = particle % params.trail_size;
|
||||
float natural_ofs = fract((float(part_ofs) / float(params.trail_size)) * float(params.trail_total)) * params.frame_delta;
|
||||
|
||||
txform[3].xyz -= particles.data[particle].velocity * natural_ofs;
|
||||
}
|
||||
|
||||
switch (params.align_mode) {
|
||||
case TRANSFORM_ALIGN_DISABLED: {
|
||||
} break; //nothing
|
||||
case TRANSFORM_ALIGN_Z_BILLBOARD: {
|
||||
mat3 local = mat3(normalize(cross(params.align_up, params.sort_direction)), params.align_up, params.sort_direction);
|
||||
local = local * mat3(txform);
|
||||
txform[0].xyz = local[0];
|
||||
txform[1].xyz = local[1];
|
||||
txform[2].xyz = local[2];
|
||||
|
||||
} break;
|
||||
case TRANSFORM_ALIGN_Y_TO_VELOCITY: {
|
||||
vec3 v = particles.data[particle].velocity;
|
||||
float s = (length(txform[0]) + length(txform[1]) + length(txform[2])) / 3.0;
|
||||
if (length(v) > 0.0) {
|
||||
txform[1].xyz = normalize(v);
|
||||
} else {
|
||||
txform[1].xyz = normalize(txform[1].xyz);
|
||||
}
|
||||
|
||||
txform[0].xyz = normalize(cross(txform[1].xyz, txform[2].xyz));
|
||||
txform[2].xyz = vec3(0.0, 0.0, 1.0) * s;
|
||||
txform[0].xyz *= s;
|
||||
txform[1].xyz *= s;
|
||||
} break;
|
||||
case TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY: {
|
||||
vec3 v = particles.data[particle].velocity;
|
||||
vec3 sv = v - params.sort_direction * dot(params.sort_direction, v); //screen velocity
|
||||
|
||||
if (length(sv) == 0) {
|
||||
sv = params.align_up;
|
||||
}
|
||||
|
||||
sv = normalize(sv);
|
||||
|
||||
txform[0].xyz = normalize(cross(sv, params.sort_direction)) * length(txform[0]);
|
||||
txform[1].xyz = sv * length(txform[1]);
|
||||
txform[2].xyz = params.sort_direction * length(txform[2]);
|
||||
|
||||
} break;
|
||||
}
|
||||
|
||||
txform[3].xyz += particles.data[particle].velocity * params.frame_remainder;
|
||||
|
||||
if (params.trail_size > 1) {
|
||||
uint part_ofs = particle % params.trail_size;
|
||||
txform = txform * trail_bind_poses.data[part_ofs];
|
||||
}
|
||||
|
||||
if (bool(params.flags & PARAMS_FLAG_COPY_MODE_2D)) {
|
||||
// In global mode, bring 2D particles to local coordinates
|
||||
// as they will be drawn with the node position as origin.
|
||||
txform = params.inv_emission_transform * txform;
|
||||
}
|
||||
} else {
|
||||
// Set scale to zero and translate to -INF so particle will be invisible
|
||||
// even for materials that ignore rotation/scale (i.e. billboards).
|
||||
txform = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(-1.0 / 0.0, -1.0 / 0.0, -1.0 / 0.0, 0.0));
|
||||
}
|
||||
txform = transpose(txform);
|
||||
|
||||
uint instance_index = gl_GlobalInvocationID.x + params.motion_vectors_current_offset;
|
||||
if (bool(params.flags & PARAMS_FLAG_COPY_MODE_2D)) {
|
||||
uint write_offset = instance_index * (2 + 1 + 1); //xform + color + custom
|
||||
|
||||
instances.data[write_offset + 0] = txform[0];
|
||||
instances.data[write_offset + 1] = txform[1];
|
||||
instances.data[write_offset + 2] = particles.data[particle].color;
|
||||
instances.data[write_offset + 3] = particles.data[particle].custom;
|
||||
} else {
|
||||
uint write_offset = instance_index * (3 + 1 + 1); //xform + color + custom
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 0) uniform sampler SAMPLER_NEAREST_CLAMP;
|
||||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 1) uniform sampler SAMPLER_LINEAR_CLAMP;
|
||||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 2) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP;
|
||||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 3) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP;
|
||||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 4) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP;
|
||||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 5) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP;
|
||||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 6) uniform sampler SAMPLER_NEAREST_REPEAT;
|
||||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 7) uniform sampler SAMPLER_LINEAR_REPEAT;
|
||||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 8) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT;
|
||||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 9) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT;
|
||||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 10) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT;
|
||||
layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 11) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT;
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// Scene data stores all our 3D rendering globals for a frame such as our matrices
|
||||
// where this information is independent of the different RD implementations.
|
||||
// This enables us to use this UBO in our main scene render shaders but also in
|
||||
// effects that need access to this data.
|
||||
|
||||
struct SceneData {
|
||||
highp mat4 projection_matrix;
|
||||
highp mat4 inv_projection_matrix;
|
||||
highp mat4 inv_view_matrix;
|
||||
highp mat4 view_matrix;
|
||||
|
||||
// only used for multiview
|
||||
highp mat4 projection_matrix_view[MAX_VIEWS];
|
||||
highp mat4 inv_projection_matrix_view[MAX_VIEWS];
|
||||
highp vec4 eye_offset[MAX_VIEWS];
|
||||
|
||||
// Used for billboards to cast correct shadows.
|
||||
highp mat4 main_cam_inv_view_matrix;
|
||||
|
||||
highp vec2 viewport_size;
|
||||
highp vec2 screen_pixel_size;
|
||||
|
||||
// Use vec4s because std140 doesn't play nice with vec2s, z and w are wasted.
|
||||
highp vec4 directional_penumbra_shadow_kernel[32];
|
||||
highp vec4 directional_soft_shadow_kernel[32];
|
||||
highp vec4 penumbra_shadow_kernel[32];
|
||||
highp vec4 soft_shadow_kernel[32];
|
||||
|
||||
mediump mat3 radiance_inverse_xform;
|
||||
|
||||
mediump vec4 ambient_light_color_energy;
|
||||
|
||||
mediump float ambient_color_sky_mix;
|
||||
bool use_ambient_light;
|
||||
bool use_ambient_cubemap;
|
||||
bool use_reflection_cubemap;
|
||||
|
||||
highp vec2 shadow_atlas_pixel_size;
|
||||
highp vec2 directional_shadow_pixel_size;
|
||||
|
||||
uint directional_light_count;
|
||||
mediump float dual_paraboloid_side;
|
||||
highp float z_far;
|
||||
highp float z_near;
|
||||
|
||||
bool roughness_limiter_enabled;
|
||||
mediump float roughness_limiter_amount;
|
||||
mediump float roughness_limiter_limit;
|
||||
mediump float opaque_prepass_threshold;
|
||||
|
||||
bool fog_enabled;
|
||||
uint fog_mode;
|
||||
highp float fog_density;
|
||||
highp float fog_height;
|
||||
highp float fog_height_density;
|
||||
|
||||
highp float fog_depth_curve;
|
||||
highp float pad;
|
||||
highp float fog_depth_begin;
|
||||
|
||||
mediump vec3 fog_light_color;
|
||||
highp float fog_depth_end;
|
||||
|
||||
mediump float fog_sun_scatter;
|
||||
mediump float fog_aerial_perspective;
|
||||
highp float time;
|
||||
mediump float reflection_multiplier; // one normally, zero when rendering reflections
|
||||
|
||||
vec2 taa_jitter;
|
||||
bool material_uv2_mode;
|
||||
float emissive_exposure_normalization;
|
||||
|
||||
float IBL_exposure_normalization;
|
||||
bool pancake_shadows;
|
||||
uint camera_visible_layers;
|
||||
float pass_alpha_multiplier;
|
||||
};
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
#ifdef ALPHA_HASH_USED
|
||||
|
||||
float hash_2d(vec2 p) {
|
||||
return fract(1.0e4 * sin(17.0 * p.x + 0.1 * p.y) *
|
||||
(0.1 + abs(sin(13.0 * p.y + p.x))));
|
||||
}
|
||||
|
||||
float hash_3d(vec3 p) {
|
||||
return hash_2d(vec2(hash_2d(p.xy), p.z));
|
||||
}
|
||||
|
||||
float compute_alpha_hash_threshold(vec3 pos, float hash_scale) {
|
||||
vec3 dx = dFdx(pos);
|
||||
vec3 dy = dFdy(pos);
|
||||
|
||||
float delta_max_sqr = max(length(dx), length(dy));
|
||||
float pix_scale = 1.0 / (hash_scale * delta_max_sqr);
|
||||
|
||||
vec2 pix_scales =
|
||||
vec2(exp2(floor(log2(pix_scale))), exp2(ceil(log2(pix_scale))));
|
||||
|
||||
vec2 a_thresh = vec2(hash_3d(floor(pix_scales.x * pos.xyz)),
|
||||
hash_3d(floor(pix_scales.y * pos.xyz)));
|
||||
|
||||
float lerp_factor = fract(log2(pix_scale));
|
||||
|
||||
float a_interp = (1.0 - lerp_factor) * a_thresh.x + lerp_factor * a_thresh.y;
|
||||
|
||||
float min_lerp = min(lerp_factor, 1.0 - lerp_factor);
|
||||
|
||||
vec3 cases = vec3(a_interp * a_interp / (2.0 * min_lerp * (1.0 - min_lerp)),
|
||||
(a_interp - 0.5 * min_lerp) / (1.0 - min_lerp),
|
||||
1.0 - ((1.0 - a_interp) * (1.0 - a_interp) / (2.0 * min_lerp * (1.0 - min_lerp))));
|
||||
|
||||
float alpha_hash_threshold =
|
||||
(a_interp < (1.0 - min_lerp)) ? ((a_interp < min_lerp) ? cases.x : cases.y) : cases.z;
|
||||
|
||||
return clamp(alpha_hash_threshold, 0.00001, 1.0);
|
||||
}
|
||||
|
||||
#endif // ALPHA_HASH_USED
|
||||
|
||||
#ifdef ALPHA_ANTIALIASING_EDGE_USED
|
||||
|
||||
float calc_mip_level(vec2 texture_coord) {
|
||||
vec2 dx = dFdx(texture_coord);
|
||||
vec2 dy = dFdy(texture_coord);
|
||||
float delta_max_sqr = max(dot(dx, dx), dot(dy, dy));
|
||||
return max(0.0, 0.5 * log2(delta_max_sqr));
|
||||
}
|
||||
|
||||
float compute_alpha_antialiasing_edge(float input_alpha, vec2 texture_coord, float alpha_edge) {
|
||||
input_alpha *= 1.0 + max(0, calc_mip_level(texture_coord)) * 0.25; // 0.25 mip scale, magic number
|
||||
input_alpha = (input_alpha - alpha_edge) / max(fwidth(input_alpha), 0.0001) + 0.5;
|
||||
return clamp(input_alpha, 0.0, 1.0);
|
||||
}
|
||||
|
||||
#endif // ALPHA_ANTIALIASING_USED
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
// Functions related to gi/sdfgi for our forward renderer
|
||||
|
||||
//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, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), 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 tan_half_angle, float max_distance, float p_bias) {
|
||||
float dist = p_bias;
|
||||
vec4 color = vec4(0.0);
|
||||
float radius = max(0.5, tan_half_angle * 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, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, lod_level);
|
||||
lod_level += 1.0;
|
||||
|
||||
float a = (1.0 - color.a);
|
||||
scolor *= a;
|
||||
color += scolor;
|
||||
dist += radius;
|
||||
radius = max(0.5, tan_half_angle * dist);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void voxel_gi_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 normal_xform, float roughness, vec3 ambient, vec3 environment, inout vec4 out_spec, inout vec4 out_diff) {
|
||||
position = (voxel_gi_instances.data[index].xform * vec4(position, 1.0)).xyz;
|
||||
ref_vec = normalize((voxel_gi_instances.data[index].xform * vec4(ref_vec, 0.0)).xyz);
|
||||
normal = normalize((voxel_gi_instances.data[index].xform * vec4(normal, 0.0)).xyz);
|
||||
|
||||
position += normal * voxel_gi_instances.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, voxel_gi_instances.data[index].bounds))))) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 blendv = abs(position / voxel_gi_instances.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(voxel_gi_instances.data[index].bounds);
|
||||
vec3 cell_size = 1.0 / voxel_gi_instances.data[index].bounds;
|
||||
|
||||
//radiance
|
||||
|
||||
#define MAX_CONE_DIRS 4
|
||||
|
||||
vec3 cone_dirs[MAX_CONE_DIRS] = 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[MAX_CONE_DIRS] = float[](0.25, 0.25, 0.25, 0.25);
|
||||
float cone_angle_tan = 0.98269;
|
||||
|
||||
vec3 light = vec3(0.0);
|
||||
|
||||
for (int i = 0; i < MAX_CONE_DIRS; i++) {
|
||||
vec3 dir = normalize((voxel_gi_instances.data[index].xform * vec4(normal_xform * cone_dirs[i], 0.0)).xyz);
|
||||
|
||||
vec4 cone_light = voxel_cone_trace_45_degrees(voxel_gi_textures[index], cell_size, position, dir, cone_angle_tan, max_distance, voxel_gi_instances.data[index].bias);
|
||||
|
||||
if (voxel_gi_instances.data[index].blend_ambient) {
|
||||
cone_light.rgb = mix(ambient, cone_light.rgb, min(1.0, cone_light.a / 0.95));
|
||||
}
|
||||
|
||||
light += cone_weights[i] * cone_light.rgb;
|
||||
}
|
||||
|
||||
light *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization;
|
||||
out_diff += vec4(light * blend, blend);
|
||||
|
||||
//irradiance
|
||||
vec4 irr_light = voxel_cone_trace(voxel_gi_textures[index], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, voxel_gi_instances.data[index].bias);
|
||||
if (voxel_gi_instances.data[index].blend_ambient) {
|
||||
irr_light.rgb = mix(environment, irr_light.rgb, min(1.0, irr_light.a / 0.95));
|
||||
}
|
||||
irr_light.rgb *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization;
|
||||
//irr_light=vec3(0.0);
|
||||
|
||||
out_spec += vec4(irr_light.rgb * blend, blend);
|
||||
}
|
||||
|
||||
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 sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal, vec3 cam_specular_normal, bool use_specular, float roughness, out vec3 diffuse_light, out vec3 specular_light, out float blend) {
|
||||
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;
|
||||
|
||||
if (use_specular) {
|
||||
specular_accum = vec3(0.0);
|
||||
specular_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_specular_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size;
|
||||
}
|
||||
|
||||
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(sdfgi_occlusion_cascades, SAMPLER_LINEAR_CLAMP), 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(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw, 0.0).rgb;
|
||||
|
||||
diffuse_accum += vec4(diffuse * weight * sdfgi.cascades[cascade].exposure_normalization, weight);
|
||||
|
||||
if (use_specular) {
|
||||
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(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw + vec3(0, 0, float(sdfgi.max_cascades)), 0.0).rgb;
|
||||
}
|
||||
if (roughness > 0.5) {
|
||||
specular = mix(specular, textureLod(sampler2DArray(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw, 0.0).rgb, (roughness - 0.5) * 2.0);
|
||||
}
|
||||
|
||||
specular_accum += specular * weight * sdfgi.cascades[cascade].exposure_normalization;
|
||||
}
|
||||
}
|
||||
|
||||
if (diffuse_accum.a > 0.0) {
|
||||
diffuse_accum.rgb /= diffuse_accum.a;
|
||||
}
|
||||
|
||||
diffuse_light = diffuse_accum.rgb;
|
||||
|
||||
if (use_specular) {
|
||||
if (diffuse_accum.a > 0.0) {
|
||||
specular_accum /= diffuse_accum.a;
|
||||
}
|
||||
|
||||
specular_light = specular_accum;
|
||||
}
|
||||
|
||||
{
|
||||
//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;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
306
engine/servers/rendering/renderer_rd/shaders/skeleton.glsl
Normal file
306
engine/servers/rendering/renderer_rd/shaders/skeleton.glsl
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
layout(set = 0, binding = 1, std430) buffer restrict writeonly DstVertexData {
|
||||
uint data[];
|
||||
}
|
||||
dst_vertices;
|
||||
|
||||
layout(set = 0, binding = 2, std430) buffer restrict readonly BlendShapeWeights {
|
||||
float data[];
|
||||
}
|
||||
blend_shape_weights;
|
||||
|
||||
layout(set = 1, binding = 0, std430) buffer restrict readonly SrcVertexData {
|
||||
uint data[];
|
||||
}
|
||||
src_vertices;
|
||||
|
||||
layout(set = 1, binding = 1, std430) buffer restrict readonly BoneWeightData {
|
||||
uint data[];
|
||||
}
|
||||
src_bone_weights;
|
||||
|
||||
layout(set = 1, binding = 2, std430) buffer restrict readonly BlendShapeData {
|
||||
uint data[];
|
||||
}
|
||||
src_blend_shapes;
|
||||
|
||||
layout(set = 2, binding = 0, std430) buffer restrict readonly SkeletonData {
|
||||
vec4 data[];
|
||||
}
|
||||
bone_transforms;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
bool has_normal;
|
||||
bool has_tangent;
|
||||
bool has_skeleton;
|
||||
bool has_blend_shape;
|
||||
|
||||
uint vertex_count;
|
||||
uint vertex_stride;
|
||||
uint skin_stride;
|
||||
uint skin_weight_offset;
|
||||
|
||||
uint blend_shape_count;
|
||||
bool normalized_blend_shapes;
|
||||
uint normal_tangent_stride;
|
||||
uint pad1;
|
||||
|
||||
vec2 skeleton_transform_x;
|
||||
vec2 skeleton_transform_y;
|
||||
|
||||
vec2 skeleton_transform_offset;
|
||||
vec2 inverse_transform_x;
|
||||
|
||||
vec2 inverse_transform_y;
|
||||
vec2 inverse_transform_offset;
|
||||
}
|
||||
params;
|
||||
|
||||
vec2 uint_to_vec2(uint base) {
|
||||
uvec2 decode = (uvec2(base) >> uvec2(0, 16)) & uvec2(0xFFFF, 0xFFFF);
|
||||
return vec2(decode) / vec2(65535.0, 65535.0) * 2.0 - 1.0;
|
||||
}
|
||||
|
||||
vec3 oct_to_vec3(vec2 oct) {
|
||||
vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));
|
||||
float t = max(-v.z, 0.0);
|
||||
v.xy += t * -sign(v.xy);
|
||||
return normalize(v);
|
||||
}
|
||||
|
||||
vec3 decode_uint_oct_to_norm(uint base) {
|
||||
return oct_to_vec3(uint_to_vec2(base));
|
||||
}
|
||||
|
||||
vec4 decode_uint_oct_to_tang(uint base) {
|
||||
vec2 oct_sign_encoded = uint_to_vec2(base);
|
||||
// Binormal sign encoded in y component
|
||||
vec2 oct = vec2(oct_sign_encoded.x, abs(oct_sign_encoded.y) * 2.0 - 1.0);
|
||||
return vec4(oct_to_vec3(oct), sign(oct_sign_encoded.y));
|
||||
}
|
||||
|
||||
vec2 signNotZero(vec2 v) {
|
||||
return mix(vec2(-1.0), vec2(1.0), greaterThanEqual(v.xy, vec2(0.0)));
|
||||
}
|
||||
|
||||
uint vec2_to_uint(vec2 base) {
|
||||
uvec2 enc = uvec2(clamp(ivec2(base * vec2(65535, 65535)), ivec2(0), ivec2(0xFFFF, 0xFFFF))) << uvec2(0, 16);
|
||||
return enc.x | enc.y;
|
||||
}
|
||||
|
||||
vec2 vec3_to_oct(vec3 e) {
|
||||
e /= abs(e.x) + abs(e.y) + abs(e.z);
|
||||
vec2 oct = e.z >= 0.0f ? e.xy : (vec2(1.0f) - abs(e.yx)) * signNotZero(e.xy);
|
||||
return oct * 0.5f + 0.5f;
|
||||
}
|
||||
|
||||
uint encode_norm_to_uint_oct(vec3 base) {
|
||||
return vec2_to_uint(vec3_to_oct(base));
|
||||
}
|
||||
|
||||
uint encode_tang_to_uint_oct(vec4 base) {
|
||||
vec2 oct = vec3_to_oct(base.xyz);
|
||||
// Encode binormal sign in y component
|
||||
oct.y = oct.y * 0.5f + 0.5f;
|
||||
oct.y = base.w >= 0.0f ? oct.y : 1 - oct.y;
|
||||
return vec2_to_uint(oct);
|
||||
}
|
||||
|
||||
void main() {
|
||||
uint index = gl_GlobalInvocationID.x;
|
||||
if (index >= params.vertex_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint src_offset = index * params.vertex_stride;
|
||||
|
||||
#ifdef MODE_2D
|
||||
vec2 vertex = uintBitsToFloat(uvec2(src_vertices.data[src_offset + 0], src_vertices.data[src_offset + 1]));
|
||||
|
||||
if (params.has_blend_shape) {
|
||||
float blend_total = 0.0;
|
||||
vec2 blend_vertex = vec2(0.0);
|
||||
|
||||
for (uint i = 0; i < params.blend_shape_count; i++) {
|
||||
float w = blend_shape_weights.data[i];
|
||||
if (abs(w) > 0.0001) {
|
||||
uint base_offset = (params.vertex_count * i + index) * params.vertex_stride;
|
||||
|
||||
blend_vertex += uintBitsToFloat(uvec2(src_blend_shapes.data[base_offset + 0], src_blend_shapes.data[base_offset + 1])) * w;
|
||||
|
||||
base_offset += 2;
|
||||
|
||||
blend_total += w;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.normalized_blend_shapes) {
|
||||
vertex = (1.0 - blend_total) * vertex;
|
||||
}
|
||||
|
||||
vertex += blend_vertex;
|
||||
}
|
||||
|
||||
if (params.has_skeleton) {
|
||||
uint skin_offset = params.skin_stride * index;
|
||||
|
||||
uvec2 bones = uvec2(src_bone_weights.data[skin_offset + 0], src_bone_weights.data[skin_offset + 1]);
|
||||
uvec2 bones_01 = uvec2(bones.x & 0xFFFF, bones.x >> 16) * 2; //pre-add xform offset
|
||||
uvec2 bones_23 = uvec2(bones.y & 0xFFFF, bones.y >> 16) * 2;
|
||||
|
||||
skin_offset += params.skin_weight_offset;
|
||||
|
||||
uvec2 weights = uvec2(src_bone_weights.data[skin_offset + 0], src_bone_weights.data[skin_offset + 1]);
|
||||
|
||||
vec2 weights_01 = unpackUnorm2x16(weights.x);
|
||||
vec2 weights_23 = unpackUnorm2x16(weights.y);
|
||||
|
||||
mat4 m = mat4(bone_transforms.data[bones_01.x], bone_transforms.data[bones_01.x + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.x;
|
||||
m += mat4(bone_transforms.data[bones_01.y], bone_transforms.data[bones_01.y + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.y;
|
||||
m += mat4(bone_transforms.data[bones_23.x], bone_transforms.data[bones_23.x + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.x;
|
||||
m += mat4(bone_transforms.data[bones_23.y], bone_transforms.data[bones_23.y + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.y;
|
||||
|
||||
mat4 skeleton_matrix = mat4(vec4(params.skeleton_transform_x, 0.0, 0.0), vec4(params.skeleton_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(params.skeleton_transform_offset, 0.0, 1.0));
|
||||
mat4 inverse_matrix = mat4(vec4(params.inverse_transform_x, 0.0, 0.0), vec4(params.inverse_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(params.inverse_transform_offset, 0.0, 1.0));
|
||||
|
||||
m = skeleton_matrix * transpose(m) * inverse_matrix;
|
||||
|
||||
vertex = (m * vec4(vertex, 0.0, 1.0)).xy;
|
||||
}
|
||||
|
||||
uint dst_offset = index * params.vertex_stride;
|
||||
|
||||
uvec2 uvertex = floatBitsToUint(vertex);
|
||||
dst_vertices.data[dst_offset + 0] = uvertex.x;
|
||||
dst_vertices.data[dst_offset + 1] = uvertex.y;
|
||||
|
||||
#else
|
||||
vec3 vertex;
|
||||
vec3 normal;
|
||||
vec4 tangent;
|
||||
|
||||
vertex = uintBitsToFloat(uvec3(src_vertices.data[src_offset + 0], src_vertices.data[src_offset + 1], src_vertices.data[src_offset + 2]));
|
||||
|
||||
uint src_normal = params.vertex_count * params.vertex_stride + index * params.normal_tangent_stride;
|
||||
|
||||
if (params.has_normal) {
|
||||
normal = decode_uint_oct_to_norm(src_vertices.data[src_normal]);
|
||||
src_normal++;
|
||||
}
|
||||
|
||||
if (params.has_tangent) {
|
||||
tangent = decode_uint_oct_to_tang(src_vertices.data[src_normal]);
|
||||
}
|
||||
|
||||
if (params.has_blend_shape) {
|
||||
float blend_total = 0.0;
|
||||
vec3 blend_vertex = vec3(0.0);
|
||||
vec3 blend_normal = vec3(0.0);
|
||||
vec3 blend_tangent = vec3(0.0);
|
||||
|
||||
for (uint i = 0; i < params.blend_shape_count; i++) {
|
||||
float w = blend_shape_weights.data[i];
|
||||
if (abs(w) > 0.0001) {
|
||||
uint base_offset = params.vertex_count * i * (params.vertex_stride + params.normal_tangent_stride) + index * params.vertex_stride;
|
||||
|
||||
blend_vertex += uintBitsToFloat(uvec3(src_blend_shapes.data[base_offset + 0], src_blend_shapes.data[base_offset + 1], src_blend_shapes.data[base_offset + 2])) * w;
|
||||
|
||||
uint base_normal = params.vertex_count * i * (params.vertex_stride + params.normal_tangent_stride) + params.vertex_count * params.vertex_stride + index * params.normal_tangent_stride;
|
||||
|
||||
if (params.has_normal) {
|
||||
blend_normal += decode_uint_oct_to_norm(src_blend_shapes.data[base_normal]) * w;
|
||||
base_normal++;
|
||||
}
|
||||
|
||||
if (params.has_tangent) {
|
||||
blend_tangent += decode_uint_oct_to_tang(src_blend_shapes.data[base_normal]).rgb * w;
|
||||
}
|
||||
|
||||
blend_total += w;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.normalized_blend_shapes) {
|
||||
vertex = (1.0 - blend_total) * vertex;
|
||||
normal = (1.0 - blend_total) * normal;
|
||||
tangent.rgb = (1.0 - blend_total) * tangent.rgb;
|
||||
}
|
||||
|
||||
vertex += blend_vertex;
|
||||
normal = normalize(normal + blend_normal);
|
||||
tangent.rgb = normalize(tangent.rgb + blend_tangent);
|
||||
}
|
||||
|
||||
if (params.has_skeleton) {
|
||||
uint skin_offset = params.skin_stride * index;
|
||||
|
||||
uvec2 bones = uvec2(src_bone_weights.data[skin_offset + 0], src_bone_weights.data[skin_offset + 1]);
|
||||
uvec2 bones_01 = uvec2(bones.x & 0xFFFF, bones.x >> 16) * 3; //pre-add xform offset
|
||||
uvec2 bones_23 = uvec2(bones.y & 0xFFFF, bones.y >> 16) * 3;
|
||||
|
||||
skin_offset += params.skin_weight_offset;
|
||||
|
||||
uvec2 weights = uvec2(src_bone_weights.data[skin_offset + 0], src_bone_weights.data[skin_offset + 1]);
|
||||
|
||||
vec2 weights_01 = unpackUnorm2x16(weights.x);
|
||||
vec2 weights_23 = unpackUnorm2x16(weights.y);
|
||||
|
||||
mat4 m = mat4(bone_transforms.data[bones_01.x], bone_transforms.data[bones_01.x + 1], bone_transforms.data[bones_01.x + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.x;
|
||||
m += mat4(bone_transforms.data[bones_01.y], bone_transforms.data[bones_01.y + 1], bone_transforms.data[bones_01.y + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.y;
|
||||
m += mat4(bone_transforms.data[bones_23.x], bone_transforms.data[bones_23.x + 1], bone_transforms.data[bones_23.x + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.x;
|
||||
m += mat4(bone_transforms.data[bones_23.y], bone_transforms.data[bones_23.y + 1], bone_transforms.data[bones_23.y + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.y;
|
||||
|
||||
if (params.skin_weight_offset == 4) {
|
||||
//using 8 bones/weights
|
||||
skin_offset = params.skin_stride * index + 2;
|
||||
|
||||
bones = uvec2(src_bone_weights.data[skin_offset + 0], src_bone_weights.data[skin_offset + 1]);
|
||||
bones_01 = uvec2(bones.x & 0xFFFF, bones.x >> 16) * 3; //pre-add xform offset
|
||||
bones_23 = uvec2(bones.y & 0xFFFF, bones.y >> 16) * 3;
|
||||
|
||||
skin_offset += params.skin_weight_offset;
|
||||
|
||||
weights = uvec2(src_bone_weights.data[skin_offset + 0], src_bone_weights.data[skin_offset + 1]);
|
||||
|
||||
weights_01 = unpackUnorm2x16(weights.x);
|
||||
weights_23 = unpackUnorm2x16(weights.y);
|
||||
|
||||
m += mat4(bone_transforms.data[bones_01.x], bone_transforms.data[bones_01.x + 1], bone_transforms.data[bones_01.x + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.x;
|
||||
m += mat4(bone_transforms.data[bones_01.y], bone_transforms.data[bones_01.y + 1], bone_transforms.data[bones_01.y + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.y;
|
||||
m += mat4(bone_transforms.data[bones_23.x], bone_transforms.data[bones_23.x + 1], bone_transforms.data[bones_23.x + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.x;
|
||||
m += mat4(bone_transforms.data[bones_23.y], bone_transforms.data[bones_23.y + 1], bone_transforms.data[bones_23.y + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.y;
|
||||
}
|
||||
|
||||
//reverse order because its transposed
|
||||
vertex = (vec4(vertex, 1.0) * m).xyz;
|
||||
normal = normalize((vec4(normal, 0.0) * m).xyz);
|
||||
tangent.xyz = normalize((vec4(tangent.xyz, 0.0) * m).xyz);
|
||||
}
|
||||
|
||||
uint dst_offset = index * params.vertex_stride;
|
||||
|
||||
uvec3 uvertex = floatBitsToUint(vertex);
|
||||
dst_vertices.data[dst_offset + 0] = uvertex.x;
|
||||
dst_vertices.data[dst_offset + 1] = uvertex.y;
|
||||
dst_vertices.data[dst_offset + 2] = uvertex.z;
|
||||
|
||||
uint dst_normal = params.vertex_count * params.vertex_stride + index * params.normal_tangent_stride;
|
||||
|
||||
if (params.has_normal) {
|
||||
dst_vertices.data[dst_normal] = encode_norm_to_uint_oct(normal);
|
||||
dst_normal++;
|
||||
}
|
||||
|
||||
if (params.has_tangent) {
|
||||
dst_vertices.data[dst_normal] = encode_tang_to_uint_oct(tangent);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue