You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-07 12:30:27 +00:00
Addition of SDFGI for open world global illumination
Move GI to a deferred pass
This commit is contained in:
@@ -28,3 +28,10 @@ if "RD_GLSL" in env["BUILDERS"]:
|
||||
env.RD_GLSL("screen_space_reflection_scale.glsl")
|
||||
env.RD_GLSL("subsurface_scattering.glsl")
|
||||
env.RD_GLSL("specular_merge.glsl")
|
||||
env.RD_GLSL("gi.glsl")
|
||||
env.RD_GLSL("resolve.glsl")
|
||||
env.RD_GLSL("sdfgi_preprocess.glsl")
|
||||
env.RD_GLSL("sdfgi_integrate.glsl")
|
||||
env.RD_GLSL("sdfgi_direct_light.glsl")
|
||||
env.RD_GLSL("sdfgi_debug.glsl")
|
||||
env.RD_GLSL("sdfgi_debug_probes.glsl")
|
||||
|
||||
@@ -47,16 +47,26 @@ layout(push_constant, binding = 1, std430) uniform Params {
|
||||
|
||||
bool force_luminance;
|
||||
bool alpha_to_zero;
|
||||
uint pad[2];
|
||||
bool srgb;
|
||||
uint pad;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
|
||||
#ifdef MODE_TWO_SOURCES
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_color2;
|
||||
#endif
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
vec3 linear_to_srgb(vec3 color) {
|
||||
//if going to srgb, clamp from 0 to 1.
|
||||
color = clamp(color, vec3(0.0), vec3(1.0));
|
||||
const vec3 a = vec3(0.055f);
|
||||
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 uv = uv_interp;
|
||||
|
||||
@@ -89,11 +99,17 @@ void main() {
|
||||
}
|
||||
#endif
|
||||
vec4 color = textureLod(source_color, uv, 0.0);
|
||||
#ifdef MODE_TWO_SOURCES
|
||||
color += textureLod(source_color2, uv, 0.0);
|
||||
#endif
|
||||
if (params.force_luminance) {
|
||||
color.rgb = vec3(max(max(color.r, color.g), color.b));
|
||||
}
|
||||
if (params.alpha_to_zero) {
|
||||
color.rgb *= color.a;
|
||||
}
|
||||
if (params.srgb) {
|
||||
color.rgb = linear_to_srgb(color.rgb);
|
||||
}
|
||||
frag_color = color;
|
||||
}
|
||||
|
||||
663
servers/rendering/rasterizer_rd/shaders/gi.glsl
Normal file
663
servers/rendering/rasterizer_rd/shaders/gi.glsl
Normal file
@@ -0,0 +1,663 @@
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
#define M_PI 3.141592
|
||||
|
||||
#define SDFGI_MAX_CASCADES 8
|
||||
|
||||
//set 0 for SDFGI and render buffers
|
||||
|
||||
layout(set = 0, binding = 1) uniform texture3D sdf_cascades[SDFGI_MAX_CASCADES];
|
||||
layout(set = 0, binding = 2) uniform texture3D light_cascades[SDFGI_MAX_CASCADES];
|
||||
layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[SDFGI_MAX_CASCADES];
|
||||
layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[SDFGI_MAX_CASCADES];
|
||||
layout(set = 0, binding = 5) uniform texture3D occlusion_texture;
|
||||
|
||||
layout(set = 0, binding = 6) uniform sampler linear_sampler;
|
||||
layout(set = 0, binding = 7) uniform sampler linear_sampler_with_mipmaps;
|
||||
|
||||
struct ProbeCascadeData {
|
||||
vec3 position;
|
||||
float to_probe;
|
||||
ivec3 probe_world_offset;
|
||||
float to_cell; // 1/bounds * grid_size
|
||||
};
|
||||
|
||||
layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image2D ambient_buffer;
|
||||
layout(rgba16f, set = 0, binding = 10) uniform restrict writeonly image2D reflection_buffer;
|
||||
|
||||
layout(set = 0, binding = 11) uniform texture2DArray lightprobe_texture;
|
||||
|
||||
layout(set = 0, binding = 12) uniform texture2D depth_buffer;
|
||||
layout(set = 0, binding = 13) uniform texture2D normal_roughness_buffer;
|
||||
layout(set = 0, binding = 14) uniform utexture2D giprobe_buffer;
|
||||
|
||||
layout(set = 0, binding = 15, std140) uniform SDFGI {
|
||||
vec3 grid_size;
|
||||
uint max_cascades;
|
||||
|
||||
bool use_occlusion;
|
||||
int probe_axis_size;
|
||||
float probe_to_uvw;
|
||||
float normal_bias;
|
||||
|
||||
vec3 lightprobe_tex_pixel_size;
|
||||
float energy;
|
||||
|
||||
vec3 lightprobe_uv_offset;
|
||||
float y_mult;
|
||||
|
||||
vec3 occlusion_clamp;
|
||||
uint pad3;
|
||||
|
||||
vec3 occlusion_renormalize;
|
||||
uint pad4;
|
||||
|
||||
vec3 cascade_probe_size;
|
||||
uint pad5;
|
||||
|
||||
ProbeCascadeData cascades[SDFGI_MAX_CASCADES];
|
||||
}
|
||||
sdfgi;
|
||||
|
||||
#define MAX_GI_PROBES 8
|
||||
|
||||
struct GIProbeData {
|
||||
mat4 xform;
|
||||
vec3 bounds;
|
||||
float dynamic_range;
|
||||
|
||||
float bias;
|
||||
float normal_bias;
|
||||
bool blend_ambient;
|
||||
uint texture_slot;
|
||||
|
||||
float anisotropy_strength;
|
||||
float ambient_occlusion;
|
||||
float ambient_occlusion_size;
|
||||
uint pad2;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 16, std140) uniform GIProbes {
|
||||
GIProbeData data[MAX_GI_PROBES];
|
||||
}
|
||||
gi_probes;
|
||||
|
||||
layout(set = 0, binding = 17) uniform texture3D gi_probe_textures[MAX_GI_PROBES];
|
||||
|
||||
layout(push_constant, binding = 0, std430) uniform Params {
|
||||
ivec2 screen_size;
|
||||
float z_near;
|
||||
float z_far;
|
||||
|
||||
vec4 proj_info;
|
||||
|
||||
uint max_giprobes;
|
||||
bool high_quality_vct;
|
||||
bool use_sdfgi;
|
||||
bool orthogonal;
|
||||
|
||||
vec3 ao_color;
|
||||
uint pad;
|
||||
|
||||
mat3x4 cam_rotation;
|
||||
}
|
||||
params;
|
||||
|
||||
vec2 octahedron_wrap(vec2 v) {
|
||||
vec2 signVal;
|
||||
signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
|
||||
signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
|
||||
return (1.0 - abs(v.yx)) * signVal;
|
||||
}
|
||||
|
||||
vec2 octahedron_encode(vec3 n) {
|
||||
// https://twitter.com/Stubbesaurus/status/937994790553227264
|
||||
n /= (abs(n.x) + abs(n.y) + abs(n.z));
|
||||
n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
|
||||
n.xy = n.xy * 0.5 + 0.5;
|
||||
return n.xy;
|
||||
}
|
||||
|
||||
vec4 blend_color(vec4 src, vec4 dst) {
|
||||
vec4 res;
|
||||
float sa = 1.0 - src.a;
|
||||
res.a = dst.a * sa + src.a;
|
||||
if (res.a == 0.0) {
|
||||
res.rgb = vec3(0);
|
||||
} else {
|
||||
res.rgb = (dst.rgb * dst.a * sa + src.rgb * src.a) / res.a;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
vec3 reconstruct_position(ivec2 screen_pos) {
|
||||
vec3 pos;
|
||||
pos.z = texelFetch(sampler2D(depth_buffer, linear_sampler), screen_pos, 0).r;
|
||||
|
||||
pos.z = pos.z * 2.0 - 1.0;
|
||||
if (params.orthogonal) {
|
||||
pos.z = ((pos.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
|
||||
} else {
|
||||
pos.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - pos.z * (params.z_far - params.z_near));
|
||||
}
|
||||
pos.z = -pos.z;
|
||||
|
||||
pos.xy = vec2(screen_pos) * params.proj_info.xy + params.proj_info.zw;
|
||||
if (!params.orthogonal) {
|
||||
pos.xy *= pos.z;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
void sdfgi_probe_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal, vec3 cam_specular_normal, float roughness, out vec3 diffuse_light, out vec3 specular_light) {
|
||||
cascade_pos += cam_normal * sdfgi.normal_bias;
|
||||
|
||||
vec3 base_pos = floor(cascade_pos);
|
||||
//cascade_pos += mix(vec3(0.0),vec3(0.01),lessThan(abs(cascade_pos-base_pos),vec3(0.01))) * cam_normal;
|
||||
ivec3 probe_base_pos = ivec3(base_pos);
|
||||
|
||||
vec4 diffuse_accum = vec4(0.0);
|
||||
vec3 specular_accum;
|
||||
|
||||
ivec3 tex_pos = ivec3(probe_base_pos.xy, int(cascade));
|
||||
tex_pos.x += probe_base_pos.z * sdfgi.probe_axis_size;
|
||||
tex_pos.xy = tex_pos.xy * (SDFGI_OCT_SIZE + 2) + ivec2(1);
|
||||
|
||||
vec3 diffuse_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size;
|
||||
|
||||
vec3 specular_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_specular_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size;
|
||||
|
||||
specular_accum = vec3(0.0);
|
||||
|
||||
vec4 light_accum = vec4(0.0);
|
||||
float weight_accum = 0.0;
|
||||
|
||||
for (uint j = 0; j < 8; j++) {
|
||||
ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1);
|
||||
ivec3 probe_posi = probe_base_pos;
|
||||
probe_posi += offset;
|
||||
|
||||
// Compute weight
|
||||
|
||||
vec3 probe_pos = vec3(probe_posi);
|
||||
vec3 probe_to_pos = cascade_pos - probe_pos;
|
||||
vec3 probe_dir = normalize(-probe_to_pos);
|
||||
|
||||
vec3 trilinear = vec3(1.0) - abs(probe_to_pos);
|
||||
float weight = trilinear.x * trilinear.y * trilinear.z * max(0.005, dot(cam_normal, probe_dir));
|
||||
|
||||
// Compute lightprobe occlusion
|
||||
|
||||
if (sdfgi.use_occlusion) {
|
||||
ivec3 occ_indexv = abs((sdfgi.cascades[cascade].probe_world_offset + probe_posi) & ivec3(1, 1, 1)) * ivec3(1, 2, 4);
|
||||
vec4 occ_mask = mix(vec4(0.0), vec4(1.0), equal(ivec4(occ_indexv.x | occ_indexv.y), ivec4(0, 1, 2, 3)));
|
||||
|
||||
vec3 occ_pos = clamp(cascade_pos, probe_pos - sdfgi.occlusion_clamp, probe_pos + sdfgi.occlusion_clamp) * sdfgi.probe_to_uvw;
|
||||
occ_pos.z += float(cascade);
|
||||
if (occ_indexv.z != 0) { //z bit is on, means index is >=4, so make it switch to the other half of textures
|
||||
occ_pos.x += 1.0;
|
||||
}
|
||||
|
||||
occ_pos *= sdfgi.occlusion_renormalize;
|
||||
float occlusion = dot(textureLod(sampler3D(occlusion_texture, linear_sampler), occ_pos, 0.0), occ_mask);
|
||||
|
||||
weight *= max(occlusion, 0.01);
|
||||
}
|
||||
|
||||
// Compute lightprobe texture position
|
||||
|
||||
vec3 diffuse;
|
||||
vec3 pos_uvw = diffuse_posf;
|
||||
pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy;
|
||||
pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z;
|
||||
diffuse = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb;
|
||||
|
||||
diffuse_accum += vec4(diffuse * weight, weight);
|
||||
|
||||
{
|
||||
vec3 specular = vec3(0.0);
|
||||
vec3 pos_uvw = specular_posf;
|
||||
pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy;
|
||||
pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z;
|
||||
if (roughness < 0.99) {
|
||||
specular = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw + vec3(0, 0, float(sdfgi.max_cascades)), 0.0).rgb;
|
||||
}
|
||||
if (roughness > 0.2) {
|
||||
specular = mix(specular, textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb, (roughness - 0.2) * 1.25);
|
||||
}
|
||||
|
||||
specular_accum += specular * weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (diffuse_accum.a > 0.0) {
|
||||
diffuse_accum.rgb /= diffuse_accum.a;
|
||||
}
|
||||
|
||||
diffuse_light = diffuse_accum.rgb;
|
||||
|
||||
if (diffuse_accum.a > 0.0) {
|
||||
specular_accum /= diffuse_accum.a;
|
||||
}
|
||||
|
||||
specular_light = specular_accum;
|
||||
}
|
||||
|
||||
void sdfgi_process(vec3 vertex, vec3 normal, vec3 reflection, float roughness, out vec4 ambient_light, out vec4 reflection_light) {
|
||||
//make vertex orientation the world one, but still align to camera
|
||||
vertex.y *= sdfgi.y_mult;
|
||||
normal.y *= sdfgi.y_mult;
|
||||
reflection.y *= sdfgi.y_mult;
|
||||
|
||||
//renormalize
|
||||
normal = normalize(normal);
|
||||
reflection = normalize(reflection);
|
||||
|
||||
vec3 cam_pos = vertex;
|
||||
vec3 cam_normal = normal;
|
||||
|
||||
vec4 light_accum = vec4(0.0);
|
||||
float weight_accum = 0.0;
|
||||
|
||||
vec4 light_blend_accum = vec4(0.0);
|
||||
float weight_blend_accum = 0.0;
|
||||
|
||||
float blend = -1.0;
|
||||
|
||||
// helper constants, compute once
|
||||
|
||||
uint cascade = 0xFFFFFFFF;
|
||||
vec3 cascade_pos;
|
||||
vec3 cascade_normal;
|
||||
|
||||
for (uint i = 0; i < sdfgi.max_cascades; i++) {
|
||||
cascade_pos = (cam_pos - sdfgi.cascades[i].position) * sdfgi.cascades[i].to_probe;
|
||||
|
||||
if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, sdfgi.cascade_probe_size))) {
|
||||
continue; //skip cascade
|
||||
}
|
||||
|
||||
cascade = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cascade < SDFGI_MAX_CASCADES) {
|
||||
ambient_light = vec4(0, 0, 0, 1);
|
||||
reflection_light = vec4(0, 0, 0, 1);
|
||||
|
||||
float blend;
|
||||
vec3 diffuse, specular;
|
||||
sdfgi_probe_process(cascade, cascade_pos, cam_pos, cam_normal, reflection, roughness, diffuse, specular);
|
||||
|
||||
{
|
||||
//process blend
|
||||
float blend_from = (float(sdfgi.probe_axis_size - 1) / 2.0) - 2.5;
|
||||
float blend_to = blend_from + 2.0;
|
||||
|
||||
vec3 inner_pos = cam_pos * sdfgi.cascades[cascade].to_probe;
|
||||
|
||||
float len = length(inner_pos);
|
||||
|
||||
inner_pos = abs(normalize(inner_pos));
|
||||
len *= max(inner_pos.x, max(inner_pos.y, inner_pos.z));
|
||||
|
||||
if (len >= blend_from) {
|
||||
blend = smoothstep(blend_from, blend_to, len);
|
||||
} else {
|
||||
blend = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (blend > 0.0) {
|
||||
//blend
|
||||
if (cascade == sdfgi.max_cascades - 1) {
|
||||
ambient_light.a = 1.0 - blend;
|
||||
reflection_light.a = 1.0 - blend;
|
||||
|
||||
} else {
|
||||
vec3 diffuse2, specular2;
|
||||
cascade_pos = (cam_pos - sdfgi.cascades[cascade + 1].position) * sdfgi.cascades[cascade + 1].to_probe;
|
||||
sdfgi_probe_process(cascade + 1, cascade_pos, cam_pos, cam_normal, reflection, roughness, diffuse2, specular2);
|
||||
diffuse = mix(diffuse, diffuse2, blend);
|
||||
specular = mix(specular, specular2, blend);
|
||||
}
|
||||
}
|
||||
|
||||
ambient_light.rgb = diffuse;
|
||||
#if 1
|
||||
if (roughness < 0.2) {
|
||||
vec3 pos_to_uvw = 1.0 / sdfgi.grid_size;
|
||||
vec4 light_accum = vec4(0.0);
|
||||
|
||||
float blend_size = (sdfgi.grid_size.x / float(sdfgi.probe_axis_size - 1)) * 0.5;
|
||||
|
||||
float radius_sizes[SDFGI_MAX_CASCADES];
|
||||
cascade = 0xFFFF;
|
||||
|
||||
float base_distance = length(cam_pos);
|
||||
for (uint i = 0; i < sdfgi.max_cascades; i++) {
|
||||
radius_sizes[i] = (1.0 / sdfgi.cascades[i].to_cell) * (sdfgi.grid_size.x * 0.5 - blend_size);
|
||||
if (cascade == 0xFFFF && base_distance < radius_sizes[i]) {
|
||||
cascade = i;
|
||||
}
|
||||
}
|
||||
|
||||
cascade = min(cascade, sdfgi.max_cascades - 1);
|
||||
|
||||
float max_distance = radius_sizes[sdfgi.max_cascades - 1];
|
||||
vec3 ray_pos = cam_pos;
|
||||
vec3 ray_dir = reflection;
|
||||
|
||||
{
|
||||
float prev_radius = cascade > 0 ? radius_sizes[cascade - 1] : 0.0;
|
||||
float base_blend = (base_distance - prev_radius) / (radius_sizes[cascade] - prev_radius);
|
||||
float bias = (1.0 + base_blend) * 1.1;
|
||||
vec3 abs_ray_dir = abs(ray_dir);
|
||||
//ray_pos += ray_dir * (bias / sdfgi.cascades[cascade].to_cell); //bias to avoid self occlusion
|
||||
ray_pos += (ray_dir * 1.0 / max(abs_ray_dir.x, max(abs_ray_dir.y, abs_ray_dir.z)) + cam_normal * 1.4) * bias / sdfgi.cascades[cascade].to_cell;
|
||||
}
|
||||
|
||||
float softness = 0.2 + min(1.0, roughness * 5.0) * 4.0; //approximation to roughness so it does not seem like a hard fade
|
||||
while (length(ray_pos) < max_distance) {
|
||||
for (uint i = 0; i < sdfgi.max_cascades; i++) {
|
||||
if (i >= cascade && length(ray_pos) < radius_sizes[i]) {
|
||||
cascade = max(i, cascade); //never go down
|
||||
|
||||
vec3 pos = ray_pos - sdfgi.cascades[i].position;
|
||||
pos *= sdfgi.cascades[i].to_cell * pos_to_uvw;
|
||||
|
||||
float distance = texture(sampler3D(sdf_cascades[i], linear_sampler), pos).r * 255.0 - 1.1;
|
||||
|
||||
vec4 hit_light = vec4(0.0);
|
||||
if (distance < softness) {
|
||||
hit_light.rgb = texture(sampler3D(light_cascades[i], linear_sampler), pos).rgb;
|
||||
hit_light.rgb *= 0.5; //approximation given value read is actually meant for anisotropy
|
||||
hit_light.a = clamp(1.0 - (distance / softness), 0.0, 1.0);
|
||||
hit_light.rgb *= hit_light.a;
|
||||
}
|
||||
|
||||
distance /= sdfgi.cascades[i].to_cell;
|
||||
|
||||
if (i < (sdfgi.max_cascades - 1)) {
|
||||
pos = ray_pos - sdfgi.cascades[i + 1].position;
|
||||
pos *= sdfgi.cascades[i + 1].to_cell * pos_to_uvw;
|
||||
|
||||
float distance2 = texture(sampler3D(sdf_cascades[i + 1], linear_sampler), pos).r * 255.0 - 1.1;
|
||||
|
||||
vec4 hit_light2 = vec4(0.0);
|
||||
if (distance2 < softness) {
|
||||
hit_light2.rgb = texture(sampler3D(light_cascades[i + 1], linear_sampler), pos).rgb;
|
||||
hit_light2.rgb *= 0.5; //approximation given value read is actually meant for anisotropy
|
||||
hit_light2.a = clamp(1.0 - (distance2 / softness), 0.0, 1.0);
|
||||
hit_light2.rgb *= hit_light2.a;
|
||||
}
|
||||
|
||||
float prev_radius = i == 0 ? 0.0 : radius_sizes[i - 1];
|
||||
float blend = clamp((length(ray_pos) - prev_radius) / (radius_sizes[i] - prev_radius), 0.0, 1.0);
|
||||
|
||||
distance2 /= sdfgi.cascades[i + 1].to_cell;
|
||||
|
||||
hit_light = mix(hit_light, hit_light2, blend);
|
||||
distance = mix(distance, distance2, blend);
|
||||
}
|
||||
|
||||
light_accum += hit_light;
|
||||
ray_pos += ray_dir * distance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (light_accum.a > 0.99) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 light = light_accum.rgb / max(light_accum.a, 0.00001);
|
||||
float alpha = min(1.0, light_accum.a);
|
||||
|
||||
float b = min(1.0, roughness * 5.0);
|
||||
|
||||
float sa = 1.0 - b;
|
||||
|
||||
reflection_light.a = alpha * sa + b;
|
||||
if (reflection_light.a == 0) {
|
||||
specular = vec3(0.0);
|
||||
} else {
|
||||
specular = (light * alpha * sa + specular * b) / reflection_light.a;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
reflection_light.rgb = specular;
|
||||
|
||||
ambient_light.rgb *= sdfgi.energy;
|
||||
reflection_light.rgb *= sdfgi.energy;
|
||||
} else {
|
||||
ambient_light = vec4(0);
|
||||
reflection_light = vec4(0);
|
||||
}
|
||||
}
|
||||
|
||||
//standard voxel cone trace
|
||||
vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) {
|
||||
float dist = p_bias;
|
||||
vec4 color = vec4(0.0);
|
||||
|
||||
while (dist < max_distance && color.a < 0.95) {
|
||||
float diameter = max(1.0, 2.0 * tan_half_angle * dist);
|
||||
vec3 uvw_pos = (pos + dist * direction) * cell_size;
|
||||
float half_diameter = diameter * 0.5;
|
||||
//check if outside, then break
|
||||
if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + half_diameter * cell_size)))) {
|
||||
break;
|
||||
}
|
||||
vec4 scolor = textureLod(sampler3D(probe, linear_sampler_with_mipmaps), uvw_pos, log2(diameter));
|
||||
float a = (1.0 - color.a);
|
||||
color += a * scolor;
|
||||
dist += half_diameter;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
vec4 voxel_cone_trace_45_degrees(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, float max_distance, float p_bias) {
|
||||
float dist = p_bias;
|
||||
vec4 color = vec4(0.0);
|
||||
float radius = max(0.5, dist);
|
||||
float lod_level = log2(radius * 2.0);
|
||||
|
||||
while (dist < max_distance && color.a < 0.95) {
|
||||
vec3 uvw_pos = (pos + dist * direction) * cell_size;
|
||||
|
||||
//check if outside, then break
|
||||
if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + radius * cell_size)))) {
|
||||
break;
|
||||
}
|
||||
vec4 scolor = textureLod(sampler3D(probe, linear_sampler_with_mipmaps), uvw_pos, lod_level);
|
||||
lod_level += 1.0;
|
||||
|
||||
float a = (1.0 - color.a);
|
||||
scolor *= a;
|
||||
color += scolor;
|
||||
dist += radius;
|
||||
radius = max(0.5, dist);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 normal_xform, float roughness, inout vec4 out_spec, inout vec4 out_diff, inout float out_blend) {
|
||||
position = (gi_probes.data[index].xform * vec4(position, 1.0)).xyz;
|
||||
ref_vec = normalize((gi_probes.data[index].xform * vec4(ref_vec, 0.0)).xyz);
|
||||
normal = normalize((gi_probes.data[index].xform * vec4(normal, 0.0)).xyz);
|
||||
|
||||
position += normal * gi_probes.data[index].normal_bias;
|
||||
|
||||
//this causes corrupted pixels, i have no idea why..
|
||||
if (any(bvec2(any(lessThan(position, vec3(0.0))), any(greaterThan(position, gi_probes.data[index].bounds))))) {
|
||||
return;
|
||||
}
|
||||
|
||||
mat3 dir_xform = mat3(gi_probes.data[index].xform) * normal_xform;
|
||||
|
||||
vec3 blendv = abs(position / gi_probes.data[index].bounds * 2.0 - 1.0);
|
||||
float blend = clamp(1.0 - max(blendv.x, max(blendv.y, blendv.z)), 0.0, 1.0);
|
||||
//float blend=1.0;
|
||||
|
||||
float max_distance = length(gi_probes.data[index].bounds);
|
||||
vec3 cell_size = 1.0 / gi_probes.data[index].bounds;
|
||||
|
||||
//irradiance
|
||||
|
||||
vec4 light = vec4(0.0);
|
||||
|
||||
if (params.high_quality_vct) {
|
||||
const uint cone_dir_count = 6;
|
||||
vec3 cone_dirs[cone_dir_count] = vec3[](
|
||||
vec3(0.0, 0.0, 1.0),
|
||||
vec3(0.866025, 0.0, 0.5),
|
||||
vec3(0.267617, 0.823639, 0.5),
|
||||
vec3(-0.700629, 0.509037, 0.5),
|
||||
vec3(-0.700629, -0.509037, 0.5),
|
||||
vec3(0.267617, -0.823639, 0.5));
|
||||
|
||||
float cone_weights[cone_dir_count] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15);
|
||||
float cone_angle_tan = 0.577;
|
||||
|
||||
for (uint i = 0; i < cone_dir_count; i++) {
|
||||
vec3 dir = normalize(dir_xform * cone_dirs[i]);
|
||||
light += cone_weights[i] * voxel_cone_trace(gi_probe_textures[index], cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
|
||||
}
|
||||
} else {
|
||||
const uint cone_dir_count = 4;
|
||||
vec3 cone_dirs[cone_dir_count] = vec3[](
|
||||
vec3(0.707107, 0.0, 0.707107),
|
||||
vec3(0.0, 0.707107, 0.707107),
|
||||
vec3(-0.707107, 0.0, 0.707107),
|
||||
vec3(0.0, -0.707107, 0.707107));
|
||||
|
||||
float cone_weights[cone_dir_count] = float[](0.25, 0.25, 0.25, 0.25);
|
||||
for (int i = 0; i < cone_dir_count; i++) {
|
||||
vec3 dir = normalize(dir_xform * cone_dirs[i]);
|
||||
light += cone_weights[i] * voxel_cone_trace_45_degrees(gi_probe_textures[index], cell_size, position, dir, max_distance, gi_probes.data[index].bias);
|
||||
}
|
||||
}
|
||||
|
||||
if (gi_probes.data[index].ambient_occlusion > 0.001) {
|
||||
float size = 1.0 + gi_probes.data[index].ambient_occlusion_size * 7.0;
|
||||
|
||||
float taps, blend;
|
||||
blend = modf(size, taps);
|
||||
float ao = 0.0;
|
||||
for (float i = 1.0; i <= taps; i++) {
|
||||
vec3 ofs = (position + normal * (i * 0.5 + 1.0)) * cell_size;
|
||||
ao += textureLod(sampler3D(gi_probe_textures[index], linear_sampler_with_mipmaps), ofs, i - 1.0).a * i;
|
||||
}
|
||||
|
||||
if (blend > 0.001) {
|
||||
vec3 ofs = (position + normal * ((taps + 1.0) * 0.5 + 1.0)) * cell_size;
|
||||
ao += textureLod(sampler3D(gi_probe_textures[index], linear_sampler_with_mipmaps), ofs, taps).a * (taps + 1.0) * blend;
|
||||
}
|
||||
|
||||
ao = 1.0 - min(1.0, ao);
|
||||
|
||||
light.rgb = mix(params.ao_color, light.rgb, mix(1.0, ao, gi_probes.data[index].ambient_occlusion));
|
||||
}
|
||||
|
||||
light.rgb *= gi_probes.data[index].dynamic_range;
|
||||
if (!gi_probes.data[index].blend_ambient) {
|
||||
light.a = 1.0;
|
||||
}
|
||||
|
||||
out_diff += light * blend;
|
||||
|
||||
//radiance
|
||||
vec4 irr_light = voxel_cone_trace(gi_probe_textures[index], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, gi_probes.data[index].bias);
|
||||
irr_light.rgb *= gi_probes.data[index].dynamic_range;
|
||||
if (!gi_probes.data[index].blend_ambient) {
|
||||
irr_light.a = 1.0;
|
||||
}
|
||||
|
||||
out_spec += irr_light * blend;
|
||||
|
||||
out_blend += blend;
|
||||
}
|
||||
|
||||
vec4 fetch_normal_and_roughness(ivec2 pos) {
|
||||
vec4 normal_roughness = texelFetch(sampler2D(normal_roughness_buffer, linear_sampler), pos, 0);
|
||||
|
||||
normal_roughness.xyz = normalize(normal_roughness.xyz * 2.0 - 1.0);
|
||||
return normal_roughness;
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Pixel being shaded
|
||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(pos, params.screen_size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 vertex = reconstruct_position(pos);
|
||||
vertex.y = -vertex.y;
|
||||
|
||||
vec4 normal_roughness = fetch_normal_and_roughness(pos);
|
||||
vec3 normal = normal_roughness.xyz;
|
||||
|
||||
vec4 ambient_light = vec4(0.0), reflection_light = vec4(0.0);
|
||||
|
||||
if (normal.length() > 0.5) {
|
||||
//valid normal, can do GI
|
||||
float roughness = normal_roughness.w;
|
||||
|
||||
vertex = mat3(params.cam_rotation) * vertex;
|
||||
normal = normalize(mat3(params.cam_rotation) * normal);
|
||||
|
||||
vec3 reflection = normalize(reflect(normalize(vertex), normal));
|
||||
|
||||
if (params.use_sdfgi) {
|
||||
sdfgi_process(vertex, normal, reflection, roughness, ambient_light, reflection_light);
|
||||
}
|
||||
|
||||
if (params.max_giprobes > 0) {
|
||||
uvec2 giprobe_tex = texelFetch(usampler2D(giprobe_buffer, linear_sampler), pos, 0).rg;
|
||||
roughness *= roughness;
|
||||
//find arbitrary tangent and bitangent, then build a matrix
|
||||
vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
|
||||
vec3 tangent = normalize(cross(v0, normal));
|
||||
vec3 bitangent = normalize(cross(tangent, normal));
|
||||
mat3 normal_mat = mat3(tangent, bitangent, normal);
|
||||
|
||||
vec4 amb_accum = vec4(0.0);
|
||||
vec4 spec_accum = vec4(0.0);
|
||||
float blend_accum = 0.0;
|
||||
|
||||
for (uint i = 0; i < params.max_giprobes; i++) {
|
||||
if (any(equal(uvec2(i), giprobe_tex))) {
|
||||
gi_probe_compute(i, vertex, normal, reflection, normal_mat, roughness, spec_accum, amb_accum, blend_accum);
|
||||
}
|
||||
}
|
||||
if (blend_accum > 0.0) {
|
||||
amb_accum /= blend_accum;
|
||||
spec_accum /= blend_accum;
|
||||
}
|
||||
|
||||
if (params.use_sdfgi) {
|
||||
reflection_light = blend_color(spec_accum, reflection_light);
|
||||
ambient_light = blend_color(amb_accum, ambient_light);
|
||||
} else {
|
||||
reflection_light = spec_accum;
|
||||
ambient_light = amb_accum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imageStore(ambient_buffer, pos, ambient_light);
|
||||
imageStore(reflection_buffer, pos, reflection_light);
|
||||
}
|
||||
110
servers/rendering/rasterizer_rd/shaders/resolve.glsl
Normal file
110
servers/rendering/rasterizer_rd/shaders/resolve.glsl
Normal file
@@ -0,0 +1,110 @@
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
#ifdef MODE_RESOLVE_GI
|
||||
layout(set = 0, binding = 0) uniform sampler2DMS source_depth;
|
||||
layout(set = 0, binding = 1) uniform sampler2DMS source_normal_roughness;
|
||||
|
||||
layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_depth;
|
||||
layout(rgba8, set = 1, binding = 1) uniform restrict writeonly image2D dest_normal_roughness;
|
||||
|
||||
#ifdef GIPROBE_RESOLVE
|
||||
layout(set = 2, binding = 0) uniform usampler2DMS source_giprobe;
|
||||
layout(rg8ui, set = 3, binding = 0) uniform restrict writeonly uimage2D dest_giprobe;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
layout(push_constant, binding = 16, std430) uniform Params {
|
||||
ivec2 screen_size;
|
||||
int sample_count;
|
||||
uint pad;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
// Pixel being shaded
|
||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(pos, params.screen_size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MODE_RESOLVE_GI
|
||||
|
||||
float best_depth = 1e20;
|
||||
vec4 best_normal_roughness = vec4(0.0);
|
||||
#ifdef GIPROBE_RESOLVE
|
||||
uvec2 best_giprobe;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
|
||||
for(int i=0;i<params.sample_count;i++) {
|
||||
float depth = texelFetch(source_depth,pos,i).r;
|
||||
if (depth < best_depth) { //use the depth closest to camera
|
||||
best_depth = depth;
|
||||
best_normal_roughness = texelFetch(source_normal_roughness,pos,i);
|
||||
|
||||
#ifdef GIPROBE_RESOLVE
|
||||
best_giprobe = texelFetch(source_giprobe,pos,i).rg;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
float depths[16];
|
||||
int depth_indices[16];
|
||||
int depth_amount[16];
|
||||
int depth_count = 0;
|
||||
|
||||
for (int i = 0; i < params.sample_count; i++) {
|
||||
float depth = texelFetch(source_depth, pos, i).r;
|
||||
int depth_index = -1;
|
||||
for (int j = 0; j < depth_count; j++) {
|
||||
if (abs(depths[j] - depth) < 0.000001) {
|
||||
depth_index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (depth_index == -1) {
|
||||
depths[depth_count] = depth;
|
||||
depth_indices[depth_count] = i;
|
||||
depth_amount[depth_count] = 1;
|
||||
depth_count += 1;
|
||||
} else {
|
||||
depth_amount[depth_index] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
int depth_least = 0xFFFF;
|
||||
int best_index = 0;
|
||||
for (int j = 0; j < depth_count; j++) {
|
||||
if (depth_amount[j] < depth_least) {
|
||||
best_index = depth_indices[j];
|
||||
depth_least = depth_amount[j];
|
||||
}
|
||||
}
|
||||
|
||||
best_depth = texelFetch(source_depth, pos, best_index).r;
|
||||
best_normal_roughness = texelFetch(source_normal_roughness, pos, best_index);
|
||||
#ifdef GIPROBE_RESOLVE
|
||||
best_giprobe = texelFetch(source_giprobe, pos, best_index).rg;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
imageStore(dest_depth, pos, vec4(best_depth));
|
||||
imageStore(dest_normal_roughness, pos, vec4(best_normal_roughness));
|
||||
#ifdef GIPROBE_RESOLVE
|
||||
imageStore(dest_giprobe, pos, uvec4(best_giprobe, 0, 0));
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -258,7 +258,6 @@ VERTEX_SHADER_CODE
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MODE_RENDER_MATERIAL
|
||||
if (scene_data.material_uv2_mode) {
|
||||
gl_Position.xy = (uv2_attrib.xy + draw_call.bake_uv2_offset) * 2.0 - 1.0;
|
||||
@@ -341,11 +340,13 @@ layout(location = 4) out float depth_output_buffer;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_RENDER_NORMAL
|
||||
layout(location = 0) out vec4 normal_output_buffer;
|
||||
#ifdef MODE_RENDER_ROUGHNESS
|
||||
layout(location = 1) out float roughness_output_buffer;
|
||||
#endif //MODE_RENDER_ROUGHNESS
|
||||
#ifdef MODE_RENDER_NORMAL_ROUGHNESS
|
||||
layout(location = 0) out vec4 normal_roughness_output_buffer;
|
||||
|
||||
#ifdef MODE_RENDER_GIPROBE
|
||||
layout(location = 1) out uvec2 giprobe_buffer;
|
||||
#endif
|
||||
|
||||
#endif //MODE_RENDER_NORMAL
|
||||
#else // RENDER DEPTH
|
||||
|
||||
@@ -1321,37 +1322,39 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 normal, float roughnes
|
||||
reflection_accum += reflection;
|
||||
}
|
||||
|
||||
#if !defined(USE_LIGHTMAP) && !defined(USE_VOXEL_CONE_TRACING)
|
||||
if (reflections.data[ref_index].ambient.a > 0.0) { //compute ambient using skybox
|
||||
switch (reflections.data[ref_index].ambient_mode) {
|
||||
case REFLECTION_AMBIENT_DISABLED: {
|
||||
//do nothing
|
||||
} break;
|
||||
case REFLECTION_AMBIENT_ENVIRONMENT: {
|
||||
//do nothing
|
||||
vec3 local_amb_vec = (reflections.data[ref_index].local_matrix * vec4(normal, 0.0)).xyz;
|
||||
|
||||
vec3 local_amb_vec = (reflections.data[ref_index].local_matrix * vec4(normal, 0.0)).xyz;
|
||||
vec4 ambient_out;
|
||||
|
||||
vec4 ambient_out;
|
||||
ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb;
|
||||
ambient_out.a = blend;
|
||||
if (reflections.data[ref_index].params.z < 0.5) { //interior
|
||||
ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend);
|
||||
}
|
||||
|
||||
ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb;
|
||||
|
||||
ambient_out.a = blend;
|
||||
ambient_out.rgb = mix(reflections.data[ref_index].ambient.rgb, ambient_out.rgb, reflections.data[ref_index].ambient.a);
|
||||
if (reflections.data[ref_index].params.z < 0.5) {
|
||||
ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend);
|
||||
}
|
||||
|
||||
ambient_out.rgb *= ambient_out.a;
|
||||
ambient_accum += ambient_out;
|
||||
} else {
|
||||
vec4 ambient_out;
|
||||
ambient_out.a = blend;
|
||||
ambient_out.rgb = reflections.data[ref_index].ambient.rgb;
|
||||
if (reflections.data[ref_index].params.z < 0.5) {
|
||||
ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend);
|
||||
}
|
||||
ambient_out.rgb *= ambient_out.a;
|
||||
ambient_accum += ambient_out;
|
||||
ambient_out.rgb *= ambient_out.a;
|
||||
ambient_accum += ambient_out;
|
||||
} break;
|
||||
case REFLECTION_AMBIENT_COLOR: {
|
||||
vec4 ambient_out;
|
||||
ambient_out.a = blend;
|
||||
ambient_out.rgb = reflections.data[ref_index].ambient;
|
||||
if (reflections.data[ref_index].params.z < 0.5) {
|
||||
ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend);
|
||||
}
|
||||
ambient_out.rgb *= ambient_out.a;
|
||||
ambient_accum += ambient_out;
|
||||
} break;
|
||||
}
|
||||
#endif //USE_LIGHTMAP or VCT
|
||||
}
|
||||
|
||||
#ifdef USE_VOXEL_CONE_TRACING
|
||||
#ifdef USE_FORWARD_GI
|
||||
|
||||
//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) {
|
||||
@@ -1375,42 +1378,6 @@ vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction,
|
||||
return color;
|
||||
}
|
||||
|
||||
#ifndef GI_PROBE_HIGH_QUALITY
|
||||
//faster version for 45 degrees
|
||||
|
||||
#ifdef GI_PROBE_USE_ANISOTROPY
|
||||
|
||||
vec4 voxel_cone_trace_anisotropic_45_degrees(texture3D probe, texture3D aniso_pos, texture3D aniso_neg, vec3 normal, 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, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, lod_level);
|
||||
vec3 aniso_neg = textureLod(sampler3D(aniso_neg, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, lod_level).rgb;
|
||||
vec3 aniso_pos = textureLod(sampler3D(aniso_pos, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, lod_level).rgb;
|
||||
|
||||
scolor.rgb *= dot(max(vec3(0.0), (normal * aniso_pos)), vec3(1.0)) + dot(max(vec3(0.0), (-normal * aniso_neg)), vec3(1.0));
|
||||
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;
|
||||
}
|
||||
#else
|
||||
|
||||
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);
|
||||
@@ -1437,41 +1404,6 @@ vec4 voxel_cone_trace_45_degrees(texture3D probe, vec3 cell_size, vec3 pos, vec3
|
||||
return color;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#elif defined(GI_PROBE_USE_ANISOTROPY)
|
||||
|
||||
//standard voxel cone trace
|
||||
vec4 voxel_cone_trace_anisotropic(texture3D probe, texture3D aniso_pos, texture3D aniso_neg, vec3 normal, 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;
|
||||
}
|
||||
float log2_diameter = log2(diameter);
|
||||
vec4 scolor = textureLod(sampler3D(probe, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, log2_diameter);
|
||||
vec3 aniso_neg = textureLod(sampler3D(aniso_neg, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, log2_diameter).rgb;
|
||||
vec3 aniso_pos = textureLod(sampler3D(aniso_pos, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, log2_diameter).rgb;
|
||||
|
||||
scolor.rgb *= dot(max(vec3(0.0), (normal * aniso_pos)), vec3(1.0)) + dot(max(vec3(0.0), (-normal * aniso_neg)), vec3(1.0));
|
||||
|
||||
float a = (1.0 - color.a);
|
||||
scolor *= a;
|
||||
color += scolor;
|
||||
dist += half_diameter;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void gi_probe_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 = (gi_probes.data[index].xform * vec4(position, 1.0)).xyz;
|
||||
ref_vec = normalize((gi_probes.data[index].xform * vec4(ref_vec, 0.0)).xyz);
|
||||
@@ -1493,31 +1425,6 @@ void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3
|
||||
|
||||
//radiance
|
||||
|
||||
#ifdef GI_PROBE_HIGH_QUALITY
|
||||
|
||||
#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 cone_angle_tan = 0.577;
|
||||
|
||||
#elif defined(GI_PROBE_LOW_QUALITY)
|
||||
|
||||
#define MAX_CONE_DIRS 1
|
||||
|
||||
vec3 cone_dirs[MAX_CONE_DIRS] = vec3[](
|
||||
vec3(0.0, 0.0, 1.0));
|
||||
|
||||
float cone_weights[MAX_CONE_DIRS] = float[](1.0);
|
||||
float cone_angle_tan = 4; //~76 degrees
|
||||
#else // MEDIUM QUALITY
|
||||
|
||||
#define MAX_CONE_DIRS 4
|
||||
|
||||
vec3 cone_dirs[MAX_CONE_DIRS] = vec3[](
|
||||
@@ -1529,31 +1436,13 @@ void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3
|
||||
float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.25, 0.25, 0.25);
|
||||
float cone_angle_tan = 0.98269;
|
||||
|
||||
#endif
|
||||
vec3 light = vec3(0.0);
|
||||
|
||||
for (int i = 0; i < MAX_CONE_DIRS; i++) {
|
||||
vec3 dir = normalize((gi_probes.data[index].xform * vec4(normal_xform * cone_dirs[i], 0.0)).xyz);
|
||||
|
||||
#if defined(GI_PROBE_HIGH_QUALITY) || defined(GI_PROBE_LOW_QUALITY)
|
||||
vec4 cone_light = voxel_cone_trace_45_degrees(gi_probe_textures[index], cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
|
||||
|
||||
#ifdef GI_PROBE_USE_ANISOTROPY
|
||||
vec4 cone_light = voxel_cone_trace_anisotropic(gi_probe_textures[gi_probes.data[index].texture_slot], gi_probe_textures[gi_probes.data[index].texture_slot + 1], gi_probe_textures[gi_probes.data[index].texture_slot + 2], normalize(mix(dir, normal, gi_probes.data[index].anisotropy_strength)), cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
|
||||
#else
|
||||
|
||||
vec4 cone_light = voxel_cone_trace(gi_probe_textures[gi_probes.data[index].texture_slot], cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
|
||||
|
||||
#endif // GI_PROBE_USE_ANISOTROPY
|
||||
|
||||
#else
|
||||
|
||||
#ifdef GI_PROBE_USE_ANISOTROPY
|
||||
vec4 cone_light = voxel_cone_trace_anisotropic_45_degrees(gi_probe_textures[gi_probes.data[index].texture_slot], gi_probe_textures[gi_probes.data[index].texture_slot + 1], gi_probe_textures[gi_probes.data[index].texture_slot + 2], normalize(mix(dir, normal, gi_probes.data[index].anisotropy_strength)), cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
|
||||
#else
|
||||
vec4 cone_light = voxel_cone_trace_45_degrees(gi_probe_textures[gi_probes.data[index].texture_slot], cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
|
||||
#endif // GI_PROBE_USE_ANISOTROPY
|
||||
|
||||
#endif
|
||||
if (gi_probes.data[index].blend_ambient) {
|
||||
cone_light.rgb = mix(ambient, cone_light.rgb, min(1.0, cone_light.a / 0.95));
|
||||
}
|
||||
@@ -1562,33 +1451,10 @@ void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3
|
||||
}
|
||||
|
||||
light *= gi_probes.data[index].dynamic_range;
|
||||
|
||||
if (gi_probes.data[index].ambient_occlusion > 0.001) {
|
||||
float size = 1.0 + gi_probes.data[index].ambient_occlusion_size * 7.0;
|
||||
|
||||
float taps, blend;
|
||||
blend = modf(size, taps);
|
||||
float ao = 0.0;
|
||||
for (float i = 1.0; i <= taps; i++) {
|
||||
vec3 ofs = (position + normal * (i * 0.5 + 1.0)) * cell_size;
|
||||
ao += textureLod(sampler3D(gi_probe_textures[gi_probes.data[index].texture_slot], material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ofs, i - 1.0).a * i;
|
||||
}
|
||||
|
||||
if (blend > 0.001) {
|
||||
vec3 ofs = (position + normal * ((taps + 1.0) * 0.5 + 1.0)) * cell_size;
|
||||
ao += textureLod(sampler3D(gi_probe_textures[gi_probes.data[index].texture_slot], material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ofs, taps).a * (taps + 1.0) * blend;
|
||||
}
|
||||
|
||||
ao = 1.0 - min(1.0, ao);
|
||||
|
||||
light = mix(scene_data.ao_color.rgb, light, mix(1.0, ao, gi_probes.data[index].ambient_occlusion));
|
||||
}
|
||||
|
||||
out_diff += vec4(light * blend, blend);
|
||||
|
||||
//irradiance
|
||||
#ifndef GI_PROBE_LOW_QUALITY
|
||||
vec4 irr_light = voxel_cone_trace(gi_probe_textures[gi_probes.data[index].texture_slot], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, gi_probes.data[index].bias);
|
||||
vec4 irr_light = voxel_cone_trace(gi_probe_textures[index], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, gi_probes.data[index].bias);
|
||||
if (gi_probes.data[index].blend_ambient) {
|
||||
irr_light.rgb = mix(environment, irr_light.rgb, min(1.0, irr_light.a / 0.95));
|
||||
}
|
||||
@@ -1596,10 +1462,142 @@ void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3
|
||||
//irr_light=vec3(0.0);
|
||||
|
||||
out_spec += vec4(irr_light.rgb * blend, blend);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif //USE_VOXEL_CONE_TRACING
|
||||
#endif //USE_FORWARD_GI
|
||||
|
||||
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, material_samplers[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, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw, 0.0).rgb;
|
||||
|
||||
diffuse_accum += vec4(diffuse * weight, 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, material_samplers[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, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw, 0.0).rgb, (roughness - 0.5) * 2.0);
|
||||
}
|
||||
|
||||
specular_accum += specular * weight;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
|
||||
|
||||
@@ -1812,6 +1810,15 @@ FRAGMENT_SHADER_CODE
|
||||
#endif //not render depth
|
||||
/////////////////////// LIGHTING //////////////////////////////
|
||||
|
||||
if (scene_data.roughness_limiter_enabled) {
|
||||
//http://www.jp.square-enix.com/tech/library/pdf/ImprovedGeometricSpecularAA.pdf
|
||||
float roughness2 = roughness * roughness;
|
||||
vec3 dndu = dFdx(normal), dndv = dFdx(normal);
|
||||
float variance = scene_data.roughness_limiter_amount * (dot(dndu, dndu) + dot(dndv, dndv));
|
||||
float kernelRoughness2 = min(2.0 * variance, scene_data.roughness_limiter_limit); //limit effect
|
||||
float filteredRoughness2 = min(1.0, roughness2 + kernelRoughness2);
|
||||
roughness = sqrt(filteredRoughness2);
|
||||
}
|
||||
//apply energy conservation
|
||||
|
||||
vec3 specular_light = vec3(0.0, 0.0, 0.0);
|
||||
@@ -1820,11 +1827,6 @@ FRAGMENT_SHADER_CODE
|
||||
|
||||
#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
|
||||
|
||||
if (scene_data.roughness_limiter_enabled) {
|
||||
float limit = texelFetch(sampler2D(roughness_buffer, material_samplers[SAMPLER_NEAREST_CLAMP]), ivec2(gl_FragCoord.xy), 0).r;
|
||||
roughness = max(roughness, limit);
|
||||
}
|
||||
|
||||
if (scene_data.use_reflection_cubemap) {
|
||||
vec3 ref_vec = reflect(-view, normal);
|
||||
ref_vec = scene_data.radiance_inverse_xform * ref_vec;
|
||||
@@ -1871,7 +1873,6 @@ FRAGMENT_SHADER_CODE
|
||||
#endif
|
||||
|
||||
#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
|
||||
//gi probes
|
||||
|
||||
#ifdef USE_LIGHTMAP
|
||||
|
||||
@@ -1928,10 +1929,80 @@ FRAGMENT_SHADER_CODE
|
||||
ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//lightmap capture
|
||||
#elif defined(USE_FORWARD_GI)
|
||||
|
||||
if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_SDFGI)) { //has lightmap capture
|
||||
|
||||
//make vertex orientation the world one, but still align to camera
|
||||
vec3 cam_pos = mat3(scene_data.camera_matrix) * vertex;
|
||||
vec3 cam_normal = mat3(scene_data.camera_matrix) * normal;
|
||||
vec3 cam_reflection = mat3(scene_data.camera_matrix) * reflect(-view, normal);
|
||||
|
||||
//apply y-mult
|
||||
cam_pos.y *= sdfgi.y_mult;
|
||||
cam_normal.y *= sdfgi.y_mult;
|
||||
cam_normal = normalize(cam_normal);
|
||||
cam_reflection.y *= sdfgi.y_mult;
|
||||
cam_normal = normalize(cam_normal);
|
||||
cam_reflection = normalize(cam_reflection);
|
||||
|
||||
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) {
|
||||
bool use_specular = true;
|
||||
float blend;
|
||||
vec3 diffuse, specular;
|
||||
sdfgi_process(cascade, cascade_pos, cam_pos, cam_normal, cam_reflection, use_specular, roughness, diffuse, specular, blend);
|
||||
|
||||
if (blend > 0.0) {
|
||||
//blend
|
||||
if (cascade == sdfgi.max_cascades - 1) {
|
||||
diffuse = mix(diffuse, ambient_light, blend);
|
||||
if (use_specular) {
|
||||
specular = mix(specular, specular_light, blend);
|
||||
}
|
||||
} else {
|
||||
vec3 diffuse2, specular2;
|
||||
float blend2;
|
||||
cascade_pos = (cam_pos - sdfgi.cascades[cascade + 1].position) * sdfgi.cascades[cascade + 1].to_probe;
|
||||
sdfgi_process(cascade + 1, cascade_pos, cam_pos, cam_normal, cam_reflection, use_specular, roughness, diffuse2, specular2, blend2);
|
||||
diffuse = mix(diffuse, diffuse2, blend);
|
||||
if (use_specular) {
|
||||
specular = mix(specular, specular2, blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ambient_light = diffuse;
|
||||
if (use_specular) {
|
||||
specular_light = specular;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_VOXEL_CONE_TRACING
|
||||
if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_GIPROBE)) { // process giprobes
|
||||
|
||||
uint index1 = instances.data[instance_index].gi_offset & 0xFFFF;
|
||||
@@ -1963,6 +2034,56 @@ FRAGMENT_SHADER_CODE
|
||||
specular_light = spec_accum.rgb;
|
||||
ambient_light = amb_accum.rgb;
|
||||
}
|
||||
#else
|
||||
if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_GI_BUFFERS)) { //use GI buffers
|
||||
|
||||
ivec2 coord;
|
||||
|
||||
if (scene_data.gi_upscale_for_msaa) {
|
||||
/*
|
||||
//find the closest depth to upscale from, based on neighbours
|
||||
ivec2 base_coord = ivec2(gl_FragCoord.xy);
|
||||
float z_dist = gl_FragCoord.z;
|
||||
ivec2 closest_coord = base_coord;
|
||||
float closest_z_dist = abs(texelFetch(sampler2D(depth_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), base_coord,0).r-z_dist);
|
||||
|
||||
for(int i=0;i<4;i++) {
|
||||
const ivec2 neighbours[4]=ivec2[](ivec2(-1,0),ivec2(1,0),ivec2(0,-1),ivec2(0,1));
|
||||
ivec2 neighbour_coord = base_coord + neighbours[i];
|
||||
float neighbour_z_dist = abs(texelFetch(sampler2D(depth_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), neighbour_coord,0).r-z_dist);
|
||||
if (neighbour_z_dist < closest_z_dist) {
|
||||
closest_z_dist = neighbour_z_dist;
|
||||
closest_coord = neighbour_coord;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
ivec2 base_coord = ivec2(gl_FragCoord.xy);
|
||||
ivec2 closest_coord = base_coord;
|
||||
float closest_ang = dot(normal, texelFetch(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), base_coord, 0).xyz * 2.0 - 1.0);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
const ivec2 neighbours[4] = ivec2[](ivec2(-1, 0), ivec2(1, 0), ivec2(0, -1), ivec2(0, 1));
|
||||
ivec2 neighbour_coord = base_coord + neighbours[i];
|
||||
float neighbour_ang = dot(normal, texelFetch(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), neighbour_coord, 0).xyz * 2.0 - 1.0);
|
||||
if (neighbour_ang > closest_ang) {
|
||||
closest_ang = neighbour_ang;
|
||||
closest_coord = neighbour_coord;
|
||||
}
|
||||
}
|
||||
|
||||
coord = closest_coord;
|
||||
|
||||
} else {
|
||||
coord = ivec2(gl_FragCoord.xy);
|
||||
}
|
||||
|
||||
vec4 buffer_ambient = texelFetch(sampler2D(ambient_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0);
|
||||
vec4 buffer_reflection = texelFetch(sampler2D(reflection_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0);
|
||||
|
||||
ambient_light = mix(ambient_light, buffer_ambient.rgb, buffer_ambient.a);
|
||||
specular_light = mix(specular_light, buffer_reflection.rgb, buffer_reflection.a);
|
||||
}
|
||||
#endif
|
||||
|
||||
{ // process reflections
|
||||
@@ -2376,6 +2497,93 @@ FRAGMENT_SHADER_CODE
|
||||
|
||||
#ifdef MODE_RENDER_DEPTH
|
||||
|
||||
#ifdef MODE_RENDER_SDF
|
||||
|
||||
{
|
||||
vec3 local_pos = (scene_data.sdf_to_bounds * vec4(vertex, 1.0)).xyz;
|
||||
ivec3 grid_pos = scene_data.sdf_offset + ivec3(local_pos * vec3(scene_data.sdf_size));
|
||||
|
||||
uint albedo16 = 0x1; //solid flag
|
||||
albedo16 |= clamp(uint(albedo.r * 31.0), 0, 31) << 11;
|
||||
albedo16 |= clamp(uint(albedo.g * 31.0), 0, 31) << 6;
|
||||
albedo16 |= clamp(uint(albedo.b * 31.0), 0, 31) << 1;
|
||||
|
||||
imageStore(albedo_volume_grid, grid_pos, uvec4(albedo16));
|
||||
|
||||
uint facing_bits = 0;
|
||||
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));
|
||||
|
||||
vec3 cam_normal = mat3(scene_data.camera_matrix) * normal;
|
||||
|
||||
for (uint i = 0; i < 6; i++) {
|
||||
if (dot(cam_normal, aniso_dir[i]) > 0.001) {
|
||||
facing_bits |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
imageAtomicOr(geom_facing_grid, grid_pos, facing_bits); //store facing bits
|
||||
|
||||
if (length(emission) > 0.001) {
|
||||
float lumas[6];
|
||||
vec3 light_total = vec3(0);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
float strength = max(0.0, dot(cam_normal, aniso_dir[i]));
|
||||
vec3 light = emission * strength;
|
||||
light_total += light;
|
||||
lumas[i] = max(light.r, max(light.g, light.b));
|
||||
}
|
||||
|
||||
float luma_total = max(light_total.r, max(light_total.g, light_total.b));
|
||||
|
||||
uint light_aniso = 0;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
light_aniso |= min(31, uint((lumas[i] / luma_total) * 31.0)) << (i * 5);
|
||||
}
|
||||
|
||||
//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);
|
||||
//store as 8985 to have 2 extra neighbour bits
|
||||
uint light_rgbe = ((uint(sRed) & 0x1FF) >> 1) | ((uint(sGreen) & 0x1FF) << 8) | (((uint(sBlue) & 0x1FF) >> 1) << 17) | ((uint(exps) & 0x1F) << 25);
|
||||
|
||||
imageStore(emission_grid, grid_pos, uvec4(light_rgbe));
|
||||
imageStore(emission_aniso_grid, grid_pos, uvec4(light_aniso));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_RENDER_MATERIAL
|
||||
|
||||
albedo_output_buffer.rgb = albedo;
|
||||
@@ -2398,11 +2606,21 @@ FRAGMENT_SHADER_CODE
|
||||
emission_output_buffer.a = 0.0;
|
||||
#endif
|
||||
|
||||
#ifdef MODE_RENDER_NORMAL
|
||||
normal_output_buffer = vec4(normal * 0.5 + 0.5, 0.0);
|
||||
#ifdef MODE_RENDER_ROUGHNESS
|
||||
roughness_output_buffer = roughness;
|
||||
#endif //MODE_RENDER_ROUGHNESS
|
||||
#ifdef MODE_RENDER_NORMAL_ROUGHNESS
|
||||
normal_roughness_output_buffer = vec4(normal * 0.5 + 0.5, roughness);
|
||||
|
||||
#ifdef MODE_RENDER_GIPROBE
|
||||
if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_GIPROBE)) { // process giprobes
|
||||
uint index1 = instances.data[instance_index].gi_offset & 0xFFFF;
|
||||
uint index2 = instances.data[instance_index].gi_offset >> 16;
|
||||
giprobe_buffer.x = index1 & 0xFF;
|
||||
giprobe_buffer.y = index2 & 0xFF;
|
||||
} else {
|
||||
giprobe_buffer.x = 0xFF;
|
||||
giprobe_buffer.y = 0xFF;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //MODE_RENDER_NORMAL
|
||||
|
||||
//nothing happens, so a tree-ssa optimizer will result in no fragment shader :)
|
||||
@@ -2455,7 +2673,6 @@ FRAGMENT_SHADER_CODE
|
||||
#endif
|
||||
diffuse_buffer = vec4(emission + diffuse_light + ambient_light, sss_strength);
|
||||
specular_buffer = vec4(specular_light, metallic);
|
||||
|
||||
#endif
|
||||
|
||||
#else //MODE_MULTIPLE_RENDER_TARGETS
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#define M_PI 3.14159265359
|
||||
#define ROUGHNESS_MAX_LOD 5
|
||||
|
||||
#define MAX_GI_PROBES 8
|
||||
|
||||
layout(push_constant, binding = 0, std430) uniform DrawCall {
|
||||
uint instance_index;
|
||||
uint pad; //16 bits minimum size
|
||||
@@ -27,6 +29,8 @@ layout(set = 0, binding = 1) uniform sampler material_samplers[12];
|
||||
|
||||
layout(set = 0, binding = 2) uniform sampler shadow_sampler;
|
||||
|
||||
#define SDFGI_MAX_CASCADES 8
|
||||
|
||||
layout(set = 0, binding = 3, std140) uniform SceneData {
|
||||
mat4 projection_matrix;
|
||||
mat4 inv_projection_matrix;
|
||||
@@ -76,11 +80,19 @@ layout(set = 0, binding = 3, std140) uniform SceneData {
|
||||
float ssao_ao_affect;
|
||||
bool roughness_limiter_enabled;
|
||||
|
||||
float roughness_limiter_amount;
|
||||
float roughness_limiter_limit;
|
||||
uvec2 roughness_limiter_pad;
|
||||
|
||||
vec4 ao_color;
|
||||
|
||||
mat4 sdf_to_bounds;
|
||||
|
||||
ivec3 sdf_offset;
|
||||
bool material_uv2_mode;
|
||||
uint pad_material0;
|
||||
uint pad_material1;
|
||||
uint pad_material2;
|
||||
|
||||
ivec3 sdf_size;
|
||||
bool gi_upscale_for_msaa;
|
||||
|
||||
#if 0
|
||||
vec4 ambient_light_color;
|
||||
@@ -120,6 +132,8 @@ layout(set = 0, binding = 3, std140) uniform SceneData {
|
||||
|
||||
scene_data;
|
||||
|
||||
#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 6)
|
||||
#define INSTANCE_FLAGS_USE_SDFGI (1 << 7)
|
||||
#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 8)
|
||||
#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 9)
|
||||
#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 10)
|
||||
@@ -175,13 +189,18 @@ layout(set = 0, binding = 5, std430) restrict readonly buffer Lights {
|
||||
}
|
||||
lights;
|
||||
|
||||
#define REFLECTION_AMBIENT_DISABLED 0
|
||||
#define REFLECTION_AMBIENT_ENVIRONMENT 1
|
||||
#define REFLECTION_AMBIENT_COLOR 2
|
||||
|
||||
struct ReflectionData {
|
||||
vec3 box_extents;
|
||||
float index;
|
||||
vec3 box_offset;
|
||||
uint mask;
|
||||
vec4 params; // intensity, 0, interior , boxproject
|
||||
vec4 ambient; // ambient color, energy
|
||||
vec3 ambient; // ambient color
|
||||
uint ambient_mode;
|
||||
mat4 local_matrix; // up to here for spot and omni, rest is for directional
|
||||
// notes: for ambientblend, use distance to edge to blend between already existing global environment
|
||||
};
|
||||
@@ -229,29 +248,6 @@ layout(set = 0, binding = 7, std140) uniform DirectionalLights {
|
||||
}
|
||||
directional_lights;
|
||||
|
||||
struct GIProbeData {
|
||||
mat4 xform;
|
||||
vec3 bounds;
|
||||
float dynamic_range;
|
||||
|
||||
float bias;
|
||||
float normal_bias;
|
||||
bool blend_ambient;
|
||||
uint texture_slot;
|
||||
|
||||
float anisotropy_strength;
|
||||
float ambient_occlusion;
|
||||
float ambient_occlusion_size;
|
||||
uint pad2;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 8, std140) uniform GIProbes {
|
||||
GIProbeData data[MAX_GI_PROBES];
|
||||
}
|
||||
gi_probes;
|
||||
|
||||
layout(set = 0, binding = 9) uniform texture3D gi_probe_textures[MAX_GI_PROBE_TEXTURES];
|
||||
|
||||
#define LIGHTMAP_FLAG_USE_DIRECTION 1
|
||||
#define LIGHTMAP_FLAG_USE_SPECULAR_DIRECTION 2
|
||||
|
||||
@@ -319,6 +315,41 @@ layout(set = 0, binding = 19, std430) restrict readonly buffer GlobalVariableDat
|
||||
}
|
||||
global_variables;
|
||||
|
||||
struct SDFGIProbeCascadeData {
|
||||
vec3 position;
|
||||
float to_probe;
|
||||
ivec3 probe_world_offset;
|
||||
float to_cell; // 1/bounds * grid_size
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 20, std140) uniform SDFGI {
|
||||
vec3 grid_size;
|
||||
uint max_cascades;
|
||||
|
||||
bool use_occlusion;
|
||||
int probe_axis_size;
|
||||
float probe_to_uvw;
|
||||
float normal_bias;
|
||||
|
||||
vec3 lightprobe_tex_pixel_size;
|
||||
float energy;
|
||||
|
||||
vec3 lightprobe_uv_offset;
|
||||
float y_mult;
|
||||
|
||||
vec3 occlusion_clamp;
|
||||
uint pad3;
|
||||
|
||||
vec3 occlusion_renormalize;
|
||||
uint pad4;
|
||||
|
||||
vec3 cascade_probe_size;
|
||||
uint pad5;
|
||||
|
||||
SDFGIProbeCascadeData cascades[SDFGI_MAX_CASCADES];
|
||||
}
|
||||
sdfgi;
|
||||
|
||||
// decal atlas
|
||||
|
||||
/* Set 1, Radiance */
|
||||
@@ -339,13 +370,57 @@ layout(set = 2, binding = 0) uniform textureCubeArray reflection_atlas;
|
||||
|
||||
layout(set = 2, binding = 1) uniform texture2D shadow_atlas;
|
||||
|
||||
layout(set = 2, binding = 2) uniform texture3D gi_probe_textures[MAX_GI_PROBES];
|
||||
|
||||
/* Set 3, Render Buffers */
|
||||
|
||||
#ifdef MODE_RENDER_SDF
|
||||
|
||||
layout(r16ui, set = 3, binding = 0) uniform restrict writeonly uimage3D albedo_volume_grid;
|
||||
layout(r32ui, set = 3, binding = 1) uniform restrict writeonly uimage3D emission_grid;
|
||||
layout(r32ui, set = 3, binding = 2) uniform restrict writeonly uimage3D emission_aniso_grid;
|
||||
layout(r32ui, set = 3, binding = 3) uniform restrict uimage3D geom_facing_grid;
|
||||
|
||||
//still need to be present for shaders that use it, so remap them to something
|
||||
#define depth_buffer shadow_atlas
|
||||
#define color_buffer shadow_atlas
|
||||
#define normal_roughness_buffer shadow_atlas
|
||||
|
||||
#else
|
||||
|
||||
layout(set = 3, binding = 0) uniform texture2D depth_buffer;
|
||||
layout(set = 3, binding = 1) uniform texture2D color_buffer;
|
||||
layout(set = 3, binding = 2) uniform texture2D normal_buffer;
|
||||
layout(set = 3, binding = 3) uniform texture2D roughness_buffer;
|
||||
layout(set = 3, binding = 2) uniform texture2D normal_roughness_buffer;
|
||||
layout(set = 3, binding = 4) uniform texture2D ao_buffer;
|
||||
layout(set = 3, binding = 5) uniform texture2D ambient_buffer;
|
||||
layout(set = 3, binding = 6) uniform texture2D reflection_buffer;
|
||||
|
||||
layout(set = 3, binding = 7) uniform texture2DArray sdfgi_lightprobe_texture;
|
||||
|
||||
layout(set = 3, binding = 8) uniform texture3D sdfgi_occlusion_cascades;
|
||||
|
||||
struct GIProbeData {
|
||||
mat4 xform;
|
||||
vec3 bounds;
|
||||
float dynamic_range;
|
||||
|
||||
float bias;
|
||||
float normal_bias;
|
||||
bool blend_ambient;
|
||||
uint texture_slot;
|
||||
|
||||
float anisotropy_strength;
|
||||
float ambient_occlusion;
|
||||
float ambient_occlusion_size;
|
||||
uint pad2;
|
||||
};
|
||||
|
||||
layout(set = 3, binding = 9, std140) uniform GIProbes {
|
||||
GIProbeData data[MAX_GI_PROBES];
|
||||
}
|
||||
gi_probes;
|
||||
|
||||
#endif
|
||||
|
||||
/* Set 4 Skeleton & Instancing (Multimesh) */
|
||||
|
||||
|
||||
@@ -12,11 +12,8 @@ layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D ssr_ima
|
||||
#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;
|
||||
layout(rgba8, set = 2, binding = 0) uniform restrict readonly image2D source_normal_roughness;
|
||||
layout(set = 3, binding = 0) uniform sampler2D source_metallic;
|
||||
#ifdef MODE_ROUGH
|
||||
layout(set = 3, binding = 1) uniform sampler2D source_roughness;
|
||||
#endif
|
||||
|
||||
layout(push_constant, binding = 2, std430) uniform Params {
|
||||
vec4 proj_info;
|
||||
@@ -75,7 +72,8 @@ void main() {
|
||||
// World space point being shaded
|
||||
vec3 vertex = reconstructCSPosition(uv * vec2(params.screen_size), base_depth);
|
||||
|
||||
vec3 normal = imageLoad(source_normal, ssC).xyz * 2.0 - 1.0;
|
||||
vec4 normal_roughness = imageLoad(source_normal_roughness, ssC);
|
||||
vec3 normal = normal_roughness.xyz * 2.0 - 1.0;
|
||||
normal = normalize(normal);
|
||||
normal.y = -normal.y; //because this code reads flipped
|
||||
|
||||
@@ -208,7 +206,7 @@ void main() {
|
||||
|
||||
// if roughness is enabled, do screen space cone tracing
|
||||
float blur_radius = 0.0;
|
||||
float roughness = texelFetch(source_roughness, ssC << 1, 0).r;
|
||||
float roughness = normal_roughness.w;
|
||||
|
||||
if (roughness > 0.001) {
|
||||
float cone_angle = min(roughness, 0.999) * M_PI * 0.5;
|
||||
|
||||
275
servers/rendering/rasterizer_rd/shaders/sdfgi_debug.glsl
Normal file
275
servers/rendering/rasterizer_rd/shaders/sdfgi_debug.glsl
Normal file
@@ -0,0 +1,275 @@
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
#define MAX_CASCADES 8
|
||||
|
||||
layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES];
|
||||
layout(set = 0, binding = 2) uniform texture3D light_cascades[MAX_CASCADES];
|
||||
layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[MAX_CASCADES];
|
||||
layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[MAX_CASCADES];
|
||||
layout(set = 0, binding = 5) uniform texture3D occlusion_texture;
|
||||
|
||||
layout(set = 0, binding = 8) uniform sampler linear_sampler;
|
||||
|
||||
struct CascadeData {
|
||||
vec3 offset; //offset of (0,0,0) in world coordinates
|
||||
float to_cell; // 1/bounds * grid_size
|
||||
ivec3 probe_world_offset;
|
||||
uint pad;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 9, std140) uniform Cascades {
|
||||
CascadeData data[MAX_CASCADES];
|
||||
}
|
||||
cascades;
|
||||
|
||||
layout(rgba16f, set = 0, binding = 10) uniform restrict writeonly image2D screen_buffer;
|
||||
|
||||
layout(set = 0, binding = 11) uniform texture2DArray lightprobe_texture;
|
||||
|
||||
layout(push_constant, binding = 0, std430) uniform Params {
|
||||
vec3 grid_size;
|
||||
uint max_cascades;
|
||||
|
||||
ivec2 screen_size;
|
||||
bool use_occlusion;
|
||||
float y_mult;
|
||||
|
||||
vec3 cam_extent;
|
||||
int probe_axis_size;
|
||||
|
||||
mat4 cam_transform;
|
||||
}
|
||||
params;
|
||||
|
||||
vec3 linear_to_srgb(vec3 color) {
|
||||
//if going to srgb, clamp from 0 to 1.
|
||||
color = clamp(color, vec3(0.0), vec3(1.0));
|
||||
const vec3 a = vec3(0.055f);
|
||||
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
|
||||
}
|
||||
|
||||
vec2 octahedron_wrap(vec2 v) {
|
||||
vec2 signVal;
|
||||
signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
|
||||
signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
|
||||
return (1.0 - abs(v.yx)) * signVal;
|
||||
}
|
||||
|
||||
vec2 octahedron_encode(vec3 n) {
|
||||
// https://twitter.com/Stubbesaurus/status/937994790553227264
|
||||
n /= (abs(n.x) + abs(n.y) + abs(n.z));
|
||||
n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
|
||||
n.xy = n.xy * 0.5 + 0.5;
|
||||
return n.xy;
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Pixel being shaded
|
||||
ivec2 screen_pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(screen_pos, params.screen_size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 ray_pos;
|
||||
vec3 ray_dir;
|
||||
{
|
||||
ray_pos = params.cam_transform[3].xyz;
|
||||
|
||||
ray_dir.xy = params.cam_extent.xy * ((vec2(screen_pos) / vec2(params.screen_size)) * 2.0 - 1.0);
|
||||
ray_dir.z = params.cam_extent.z;
|
||||
|
||||
ray_dir = normalize(mat3(params.cam_transform) * ray_dir);
|
||||
}
|
||||
|
||||
ray_pos.y *= params.y_mult;
|
||||
ray_dir.y *= params.y_mult;
|
||||
ray_dir = normalize(ray_dir);
|
||||
|
||||
vec3 pos_to_uvw = 1.0 / params.grid_size;
|
||||
|
||||
vec3 light = vec3(0.0);
|
||||
float blend = 0.0;
|
||||
|
||||
#if 1
|
||||
vec3 inv_dir = 1.0 / ray_dir;
|
||||
|
||||
float rough = 0.5;
|
||||
bool hit = false;
|
||||
|
||||
for (uint i = 0; i < params.max_cascades; i++) {
|
||||
//convert to local bounds
|
||||
vec3 pos = ray_pos - cascades.data[i].offset;
|
||||
pos *= cascades.data[i].to_cell;
|
||||
|
||||
// Should never happen for debug, since we start mostly at the bounds center,
|
||||
// but add anyway.
|
||||
//if (any(lessThan(pos,vec3(0.0))) || any(greaterThanEqual(pos,params.grid_size))) {
|
||||
// continue; //already past bounds for this cascade, goto next
|
||||
//}
|
||||
|
||||
//find maximum advance distance (until reaching bounds)
|
||||
vec3 t0 = -pos * inv_dir;
|
||||
vec3 t1 = (params.grid_size - pos) * inv_dir;
|
||||
vec3 tmax = max(t0, t1);
|
||||
float max_advance = min(tmax.x, min(tmax.y, tmax.z));
|
||||
|
||||
float advance = 0.0;
|
||||
vec3 uvw;
|
||||
hit = false;
|
||||
|
||||
while (advance < max_advance) {
|
||||
//read how much to advance from SDF
|
||||
uvw = (pos + ray_dir * advance) * pos_to_uvw;
|
||||
|
||||
float distance = texture(sampler3D(sdf_cascades[i], linear_sampler), uvw).r * 255.0 - 1.7;
|
||||
|
||||
if (distance < 0.001) {
|
||||
//consider hit
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
advance += distance;
|
||||
}
|
||||
|
||||
if (!hit) {
|
||||
pos += ray_dir * min(advance, max_advance);
|
||||
pos /= cascades.data[i].to_cell;
|
||||
pos += cascades.data[i].offset;
|
||||
ray_pos = pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
//compute albedo, emission and normal at hit point
|
||||
|
||||
const float EPSILON = 0.001;
|
||||
vec3 hit_normal = normalize(vec3(
|
||||
texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(EPSILON, 0.0, 0.0)).r,
|
||||
texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(0.0, EPSILON, 0.0)).r,
|
||||
texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(0.0, 0.0, EPSILON)).r));
|
||||
|
||||
vec3 hit_light = texture(sampler3D(light_cascades[i], linear_sampler), uvw).rgb;
|
||||
vec4 aniso0 = texture(sampler3D(aniso0_cascades[i], linear_sampler), uvw);
|
||||
vec3 hit_aniso0 = aniso0.rgb;
|
||||
vec3 hit_aniso1 = vec3(aniso0.a, texture(sampler3D(aniso1_cascades[i], linear_sampler), uvw).rg);
|
||||
|
||||
hit_light *= (dot(max(vec3(0.0), (hit_normal * hit_aniso0)), vec3(1.0)) + dot(max(vec3(0.0), (-hit_normal * hit_aniso1)), vec3(1.0)));
|
||||
|
||||
if (blend > 0.0) {
|
||||
light = mix(light, hit_light, blend);
|
||||
blend = 0.0;
|
||||
} else {
|
||||
light = hit_light;
|
||||
|
||||
//process blend
|
||||
float blend_from = (float(params.probe_axis_size - 1) / 2.0) - 2.5;
|
||||
float blend_to = blend_from + 2.0;
|
||||
|
||||
vec3 cam_pos = params.cam_transform[3].xyz - cascades.data[i].offset;
|
||||
cam_pos *= cascades.data[i].to_cell;
|
||||
|
||||
pos += ray_dir * min(advance, max_advance);
|
||||
vec3 inner_pos = pos - cam_pos;
|
||||
|
||||
inner_pos = inner_pos * float(params.probe_axis_size - 1) / params.grid_size.x;
|
||||
|
||||
float len = length(inner_pos);
|
||||
|
||||
inner_pos = abs(normalize(inner_pos));
|
||||
len *= max(inner_pos.x, max(inner_pos.y, inner_pos.z));
|
||||
|
||||
if (len >= blend_from) {
|
||||
blend = smoothstep(blend_from, blend_to, len);
|
||||
|
||||
pos /= cascades.data[i].to_cell;
|
||||
pos += cascades.data[i].offset;
|
||||
ray_pos = pos;
|
||||
hit = false; //continue trace for blend
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
light = mix(light, vec3(0.0), blend);
|
||||
|
||||
#else
|
||||
|
||||
vec3 inv_dir = 1.0 / ray_dir;
|
||||
|
||||
bool hit = false;
|
||||
vec4 light_accum = vec4(0.0);
|
||||
|
||||
float blend_size = (params.grid_size.x / float(params.probe_axis_size - 1)) * 0.5;
|
||||
|
||||
float radius_sizes[MAX_CASCADES];
|
||||
for (uint i = 0; i < params.max_cascades; i++) {
|
||||
radius_sizes[i] = (1.0 / cascades.data[i].to_cell) * (params.grid_size.x * 0.5 - blend_size);
|
||||
}
|
||||
|
||||
float max_distance = radius_sizes[params.max_cascades - 1];
|
||||
float advance = 0;
|
||||
while (advance < max_distance) {
|
||||
for (uint i = 0; i < params.max_cascades; i++) {
|
||||
if (advance < radius_sizes[i]) {
|
||||
vec3 pos = (ray_pos + ray_dir * advance) - cascades.data[i].offset;
|
||||
pos *= cascades.data[i].to_cell * pos_to_uvw;
|
||||
|
||||
float distance = texture(sampler3D(sdf_cascades[i], linear_sampler), pos).r * 255.0 - 1.0;
|
||||
|
||||
vec4 hit_light = vec4(0.0);
|
||||
if (distance < 1.0) {
|
||||
hit_light.a = max(0.0, 1.0 - distance);
|
||||
hit_light.rgb = texture(sampler3D(light_cascades[i], linear_sampler), pos).rgb;
|
||||
hit_light.rgb *= hit_light.a;
|
||||
}
|
||||
|
||||
distance /= cascades.data[i].to_cell;
|
||||
|
||||
if (i < (params.max_cascades - 1)) {
|
||||
pos = (ray_pos + ray_dir * advance) - cascades.data[i + 1].offset;
|
||||
pos *= cascades.data[i + 1].to_cell * pos_to_uvw;
|
||||
|
||||
float distance2 = texture(sampler3D(sdf_cascades[i + 1], linear_sampler), pos).r * 255.0 - 1.0;
|
||||
|
||||
vec4 hit_light2 = vec4(0.0);
|
||||
if (distance2 < 1.0) {
|
||||
hit_light2.a = max(0.0, 1.0 - distance2);
|
||||
hit_light2.rgb = texture(sampler3D(light_cascades[i + 1], linear_sampler), pos).rgb;
|
||||
hit_light2.rgb *= hit_light2.a;
|
||||
}
|
||||
|
||||
float prev_radius = i == 0 ? 0.0 : radius_sizes[i - 1];
|
||||
float blend = (advance - prev_radius) / (radius_sizes[i] - prev_radius);
|
||||
|
||||
distance2 /= cascades.data[i + 1].to_cell;
|
||||
|
||||
hit_light = mix(hit_light, hit_light2, blend);
|
||||
distance = mix(distance, distance2, blend);
|
||||
}
|
||||
|
||||
light_accum += hit_light;
|
||||
advance += distance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (light_accum.a > 0.98) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
light = light_accum.rgb / light_accum.a;
|
||||
|
||||
#endif
|
||||
|
||||
imageStore(screen_buffer, screen_pos, vec4(linear_to_srgb(light), 1.0));
|
||||
}
|
||||
231
servers/rendering/rasterizer_rd/shaders/sdfgi_debug_probes.glsl
Normal file
231
servers/rendering/rasterizer_rd/shaders/sdfgi_debug_probes.glsl
Normal file
@@ -0,0 +1,231 @@
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
VERSION_DEFINES
|
||||
|
||||
#define MAX_CASCADES 8
|
||||
|
||||
layout(push_constant, binding = 0, std430) uniform Params {
|
||||
mat4 projection;
|
||||
|
||||
uint band_power;
|
||||
uint sections_in_band;
|
||||
uint band_mask;
|
||||
float section_arc;
|
||||
|
||||
vec3 grid_size;
|
||||
uint cascade;
|
||||
|
||||
uint pad;
|
||||
float y_mult;
|
||||
uint probe_debug_index;
|
||||
int probe_axis_size;
|
||||
}
|
||||
params;
|
||||
|
||||
// http://in4k.untergrund.net/html_articles/hugi_27_-_coding_corner_polaris_sphere_tessellation_101.htm
|
||||
|
||||
vec3 get_sphere_vertex(uint p_vertex_id) {
|
||||
float x_angle = float(p_vertex_id & 1u) + (p_vertex_id >> params.band_power);
|
||||
|
||||
float y_angle =
|
||||
float((p_vertex_id & params.band_mask) >> 1) + ((p_vertex_id >> params.band_power) * params.sections_in_band);
|
||||
|
||||
x_angle *= params.section_arc * 0.5f; // remember - 180AA x rot not 360
|
||||
y_angle *= -params.section_arc;
|
||||
|
||||
vec3 point = vec3(sin(x_angle) * sin(y_angle), cos(x_angle), sin(x_angle) * cos(y_angle));
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
#ifdef MODE_PROBES
|
||||
|
||||
layout(location = 0) out vec3 normal_interp;
|
||||
layout(location = 1) out flat uint probe_index;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_VISIBILITY
|
||||
|
||||
layout(location = 0) out float visibility;
|
||||
|
||||
#endif
|
||||
|
||||
struct CascadeData {
|
||||
vec3 offset; //offset of (0,0,0) in world coordinates
|
||||
float to_cell; // 1/bounds * grid_size
|
||||
ivec3 probe_world_offset;
|
||||
uint pad;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 1, std140) uniform Cascades {
|
||||
CascadeData data[MAX_CASCADES];
|
||||
}
|
||||
cascades;
|
||||
|
||||
layout(set = 0, binding = 4) uniform texture3D occlusion_texture;
|
||||
layout(set = 0, binding = 3) uniform sampler linear_sampler;
|
||||
|
||||
void main() {
|
||||
#ifdef MODE_PROBES
|
||||
probe_index = gl_InstanceIndex;
|
||||
|
||||
normal_interp = get_sphere_vertex(gl_VertexIndex);
|
||||
|
||||
vec3 vertex = normal_interp * 0.2;
|
||||
|
||||
float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
|
||||
|
||||
ivec3 probe_cell;
|
||||
probe_cell.x = int(probe_index % params.probe_axis_size);
|
||||
probe_cell.y = int(probe_index / (params.probe_axis_size * params.probe_axis_size));
|
||||
probe_cell.z = int((probe_index / params.probe_axis_size) % params.probe_axis_size);
|
||||
|
||||
vertex += (cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size) / vec3(1.0, params.y_mult, 1.0);
|
||||
|
||||
gl_Position = params.projection * vec4(vertex, 1.0);
|
||||
#endif
|
||||
|
||||
#ifdef MODE_VISIBILITY
|
||||
|
||||
int probe_index = int(params.probe_debug_index);
|
||||
|
||||
vec3 vertex = get_sphere_vertex(gl_VertexIndex) * 0.01;
|
||||
|
||||
float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
|
||||
|
||||
ivec3 probe_cell;
|
||||
probe_cell.x = int(probe_index % params.probe_axis_size);
|
||||
probe_cell.y = int((probe_index % (params.probe_axis_size * params.probe_axis_size)) / params.probe_axis_size);
|
||||
probe_cell.z = int(probe_index / (params.probe_axis_size * params.probe_axis_size));
|
||||
|
||||
vertex += (cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size) / vec3(1.0, params.y_mult, 1.0);
|
||||
|
||||
int probe_voxels = int(params.grid_size.x) / int(params.probe_axis_size - 1);
|
||||
int occluder_index = int(gl_InstanceIndex);
|
||||
|
||||
int diameter = probe_voxels * 2;
|
||||
ivec3 occluder_pos;
|
||||
occluder_pos.x = int(occluder_index % diameter);
|
||||
occluder_pos.y = int(occluder_index / (diameter * diameter));
|
||||
occluder_pos.z = int((occluder_index / diameter) % diameter);
|
||||
|
||||
float cell_size = 1.0 / cascades.data[params.cascade].to_cell;
|
||||
|
||||
ivec3 occluder_offset = occluder_pos - ivec3(diameter / 2);
|
||||
vertex += ((vec3(occluder_offset) + vec3(0.5)) * cell_size) / vec3(1.0, params.y_mult, 1.0);
|
||||
|
||||
ivec3 global_cell = probe_cell + cascades.data[params.cascade].probe_world_offset;
|
||||
uint occlusion_layer = 0;
|
||||
if ((global_cell.x & 1) != 0) {
|
||||
occlusion_layer |= 1;
|
||||
}
|
||||
if ((global_cell.y & 1) != 0) {
|
||||
occlusion_layer |= 2;
|
||||
}
|
||||
if ((global_cell.z & 1) != 0) {
|
||||
occlusion_layer |= 4;
|
||||
}
|
||||
ivec3 tex_pos = probe_cell * probe_voxels + occluder_offset;
|
||||
|
||||
const vec4 layer_axis[4] = vec4[](
|
||||
vec4(1, 0, 0, 0),
|
||||
vec4(0, 1, 0, 0),
|
||||
vec4(0, 0, 1, 0),
|
||||
vec4(0, 0, 0, 1));
|
||||
|
||||
tex_pos.z += int(params.cascade) * int(params.grid_size);
|
||||
if (occlusion_layer >= 4) {
|
||||
tex_pos.x += int(params.grid_size.x);
|
||||
occlusion_layer &= 3;
|
||||
}
|
||||
|
||||
visibility = dot(texelFetch(sampler3D(occlusion_texture, linear_sampler), tex_pos, 0), layer_axis[occlusion_layer]);
|
||||
|
||||
gl_Position = params.projection * vec4(vertex, 1.0);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
VERSION_DEFINES
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 2) uniform texture2DArray lightprobe_texture;
|
||||
layout(set = 0, binding = 3) uniform sampler linear_sampler;
|
||||
|
||||
layout(push_constant, binding = 0, std430) uniform Params {
|
||||
mat4 projection;
|
||||
|
||||
uint band_power;
|
||||
uint sections_in_band;
|
||||
uint band_mask;
|
||||
float section_arc;
|
||||
|
||||
vec3 grid_size;
|
||||
uint cascade;
|
||||
|
||||
uint pad;
|
||||
float y_mult;
|
||||
uint probe_debug_index;
|
||||
int probe_axis_size;
|
||||
}
|
||||
params;
|
||||
|
||||
#ifdef MODE_PROBES
|
||||
|
||||
layout(location = 0) in vec3 normal_interp;
|
||||
layout(location = 1) in flat uint probe_index;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_VISIBILITY
|
||||
layout(location = 0) in float visibility;
|
||||
#endif
|
||||
|
||||
vec2 octahedron_wrap(vec2 v) {
|
||||
vec2 signVal;
|
||||
signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
|
||||
signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
|
||||
return (1.0 - abs(v.yx)) * signVal;
|
||||
}
|
||||
|
||||
vec2 octahedron_encode(vec3 n) {
|
||||
// https://twitter.com/Stubbesaurus/status/937994790553227264
|
||||
n /= (abs(n.x) + abs(n.y) + abs(n.z));
|
||||
n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
|
||||
n.xy = n.xy * 0.5 + 0.5;
|
||||
return n.xy;
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef MODE_PROBES
|
||||
|
||||
ivec3 tex_pos;
|
||||
tex_pos.x = int(probe_index) % params.probe_axis_size; //x
|
||||
tex_pos.y = int(probe_index) / (params.probe_axis_size * params.probe_axis_size);
|
||||
tex_pos.x += params.probe_axis_size * ((int(probe_index) / params.probe_axis_size) % params.probe_axis_size); //z
|
||||
tex_pos.z = int(params.cascade);
|
||||
|
||||
vec3 tex_pos_ofs = vec3(octahedron_encode(normal_interp) * float(OCT_SIZE), 0.0);
|
||||
vec3 tex_posf = vec3(vec2(tex_pos.xy * (OCT_SIZE + 2) + ivec2(1)), float(tex_pos.z)) + tex_pos_ofs;
|
||||
|
||||
tex_posf.xy /= vec2(ivec2(params.probe_axis_size * params.probe_axis_size * (OCT_SIZE + 2), params.probe_axis_size * (OCT_SIZE + 2)));
|
||||
|
||||
vec4 indirect_light = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), tex_posf, 0.0);
|
||||
|
||||
frag_color = indirect_light;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_VISIBILITY
|
||||
|
||||
frag_color = vec4(vec3(1, visibility, visibility), 1.0);
|
||||
#endif
|
||||
}
|
||||
472
servers/rendering/rasterizer_rd/shaders/sdfgi_direct_light.glsl
Normal file
472
servers/rendering/rasterizer_rd/shaders/sdfgi_direct_light.glsl
Normal file
@@ -0,0 +1,472 @@
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
#define MAX_CASCADES 8
|
||||
|
||||
layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES];
|
||||
layout(set = 0, binding = 2) uniform sampler linear_sampler;
|
||||
|
||||
layout(set = 0, binding = 3, std430) restrict readonly buffer DispatchData {
|
||||
uint x;
|
||||
uint y;
|
||||
uint z;
|
||||
uint total_count;
|
||||
}
|
||||
dispatch_data;
|
||||
|
||||
struct ProcessVoxel {
|
||||
uint position; //xyz 7 bit packed, extra 11 bits for neigbours
|
||||
uint albedo; //rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neibhbours
|
||||
uint light; //rgbe8985 encoded total saved light, extra 2 bits for neighbous
|
||||
uint light_aniso; //55555 light anisotropy, extra 2 bits for neighbours
|
||||
//total neighbours: 26
|
||||
};
|
||||
|
||||
#ifdef MODE_PROCESS_STATIC
|
||||
layout(set = 0, binding = 4, std430) restrict buffer ProcessVoxels {
|
||||
#else
|
||||
layout(set = 0, binding = 4, std430) restrict buffer readonly ProcessVoxels {
|
||||
#endif
|
||||
ProcessVoxel data[];
|
||||
}
|
||||
process_voxels;
|
||||
|
||||
layout(r32ui, set = 0, binding = 5) uniform restrict uimage3D dst_light;
|
||||
layout(rgba8, set = 0, binding = 6) uniform restrict image3D dst_aniso0;
|
||||
layout(rg8, set = 0, binding = 7) uniform restrict image3D dst_aniso1;
|
||||
|
||||
struct CascadeData {
|
||||
vec3 offset; //offset of (0,0,0) in world coordinates
|
||||
float to_cell; // 1/bounds * grid_size
|
||||
ivec3 probe_world_offset;
|
||||
uint pad;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 8, std140) uniform Cascades {
|
||||
CascadeData data[MAX_CASCADES];
|
||||
}
|
||||
cascades;
|
||||
|
||||
#define LIGHT_TYPE_DIRECTIONAL 0
|
||||
#define LIGHT_TYPE_OMNI 1
|
||||
#define LIGHT_TYPE_SPOT 2
|
||||
|
||||
struct Light {
|
||||
vec3 color;
|
||||
float energy;
|
||||
|
||||
vec3 direction;
|
||||
bool has_shadow;
|
||||
|
||||
vec3 position;
|
||||
float attenuation;
|
||||
|
||||
uint type;
|
||||
float spot_angle;
|
||||
float spot_attenuation;
|
||||
float radius;
|
||||
|
||||
vec4 shadow_color;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 9, std140) buffer restrict readonly Lights {
|
||||
Light data[];
|
||||
}
|
||||
lights;
|
||||
|
||||
layout(set = 0, binding = 10) uniform texture2DArray lightprobe_texture;
|
||||
|
||||
layout(push_constant, binding = 0, std430) uniform Params {
|
||||
vec3 grid_size;
|
||||
uint max_cascades;
|
||||
|
||||
uint cascade;
|
||||
uint light_count;
|
||||
uint process_offset;
|
||||
uint process_increment;
|
||||
|
||||
int probe_axis_size;
|
||||
bool multibounce;
|
||||
float y_mult;
|
||||
uint pad;
|
||||
}
|
||||
params;
|
||||
|
||||
vec2 octahedron_wrap(vec2 v) {
|
||||
vec2 signVal;
|
||||
signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
|
||||
signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
|
||||
return (1.0 - abs(v.yx)) * signVal;
|
||||
}
|
||||
|
||||
vec2 octahedron_encode(vec3 n) {
|
||||
// https://twitter.com/Stubbesaurus/status/937994790553227264
|
||||
n /= (abs(n.x) + abs(n.y) + abs(n.z));
|
||||
n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
|
||||
n.xy = n.xy * 0.5 + 0.5;
|
||||
return n.xy;
|
||||
}
|
||||
|
||||
void main() {
|
||||
uint voxel_index = uint(gl_GlobalInvocationID.x);
|
||||
|
||||
//used for skipping voxels every N frames
|
||||
voxel_index = params.process_offset + voxel_index * params.process_increment;
|
||||
|
||||
if (voxel_index >= dispatch_data.total_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint voxel_position = process_voxels.data[voxel_index].position;
|
||||
|
||||
//keep for storing to texture
|
||||
ivec3 positioni = ivec3((uvec3(voxel_position, voxel_position, voxel_position) >> uvec3(0, 7, 14)) & uvec3(0x7F));
|
||||
|
||||
vec3 position = vec3(positioni) + vec3(0.5);
|
||||
position /= cascades.data[params.cascade].to_cell;
|
||||
position += cascades.data[params.cascade].offset;
|
||||
|
||||
uint voxel_albedo = process_voxels.data[voxel_index].albedo;
|
||||
|
||||
vec3 albedo = vec3(uvec3(voxel_albedo >> 10, voxel_albedo >> 5, voxel_albedo) & uvec3(0x1F)) / float(0x1F);
|
||||
vec3 light_accum[6];
|
||||
|
||||
uint valid_aniso = (voxel_albedo >> 15) & 0x3F;
|
||||
|
||||
{
|
||||
uint rgbe = process_voxels.data[voxel_index].light;
|
||||
|
||||
//read rgbe8985
|
||||
float r = float((rgbe & 0xff) << 1);
|
||||
float g = float((rgbe >> 8) & 0x1ff);
|
||||
float b = float(((rgbe >> 17) & 0xff) << 1);
|
||||
float e = float((rgbe >> 25) & 0x1F);
|
||||
float m = pow(2.0, e - 15.0 - 9.0);
|
||||
|
||||
vec3 l = vec3(r, g, b) * m;
|
||||
|
||||
uint aniso = process_voxels.data[voxel_index].light_aniso;
|
||||
for (uint i = 0; i < 6; i++) {
|
||||
float strength = ((aniso >> (i * 5)) & 0x1F) / float(0x1F);
|
||||
light_accum[i] = l * strength;
|
||||
}
|
||||
}
|
||||
|
||||
const vec3 aniso_dir[6] = vec3[](
|
||||
vec3(1, 0, 0),
|
||||
vec3(0, 1, 0),
|
||||
vec3(0, 0, 1),
|
||||
vec3(-1, 0, 0),
|
||||
vec3(0, -1, 0),
|
||||
vec3(0, 0, -1));
|
||||
|
||||
// Raytrace light
|
||||
|
||||
vec3 pos_to_uvw = 1.0 / params.grid_size;
|
||||
vec3 uvw_ofs = pos_to_uvw * 0.5;
|
||||
|
||||
for (uint i = 0; i < params.light_count; i++) {
|
||||
float attenuation = 1.0;
|
||||
vec3 direction;
|
||||
float light_distance = 1e20;
|
||||
|
||||
switch (lights.data[i].type) {
|
||||
case LIGHT_TYPE_DIRECTIONAL: {
|
||||
direction = -lights.data[i].direction;
|
||||
} break;
|
||||
case LIGHT_TYPE_OMNI: {
|
||||
vec3 rel_vec = lights.data[i].position - position;
|
||||
direction = normalize(rel_vec);
|
||||
light_distance = length(rel_vec);
|
||||
rel_vec.y /= params.y_mult;
|
||||
attenuation = pow(clamp(1.0 - length(rel_vec) / lights.data[i].radius, 0.0, 1.0), lights.data[i].attenuation);
|
||||
} break;
|
||||
case LIGHT_TYPE_SPOT: {
|
||||
vec3 rel_vec = lights.data[i].position - position;
|
||||
direction = normalize(rel_vec);
|
||||
light_distance = length(rel_vec);
|
||||
rel_vec.y /= params.y_mult;
|
||||
attenuation = pow(clamp(1.0 - length(rel_vec) / lights.data[i].radius, 0.0, 1.0), lights.data[i].attenuation);
|
||||
|
||||
float angle = acos(dot(normalize(rel_vec), -lights.data[i].direction));
|
||||
if (angle > lights.data[i].spot_angle) {
|
||||
attenuation = 0.0;
|
||||
} else {
|
||||
float d = clamp(angle / lights.data[i].spot_angle, 0, 1);
|
||||
attenuation *= pow(1.0 - d, lights.data[i].spot_attenuation);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
if (attenuation < 0.001) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool hit = false;
|
||||
|
||||
vec3 ray_pos = position;
|
||||
vec3 ray_dir = direction;
|
||||
vec3 inv_dir = 1.0 / ray_dir;
|
||||
|
||||
//this is how to properly bias outgoing rays
|
||||
float cell_size = 1.0 / cascades.data[params.cascade].to_cell;
|
||||
ray_pos += sign(direction) * cell_size * 0.48; // go almost to the box edge but remain inside
|
||||
ray_pos += ray_dir * 0.4 * cell_size; //apply a small bias from there
|
||||
|
||||
for (uint j = params.cascade; j < params.max_cascades; j++) {
|
||||
//convert to local bounds
|
||||
vec3 pos = ray_pos - cascades.data[j].offset;
|
||||
pos *= cascades.data[j].to_cell;
|
||||
float local_distance = light_distance * cascades.data[j].to_cell;
|
||||
|
||||
if (any(lessThan(pos, vec3(0.0))) || any(greaterThanEqual(pos, params.grid_size))) {
|
||||
continue; //already past bounds for this cascade, goto next
|
||||
}
|
||||
|
||||
//find maximum advance distance (until reaching bounds)
|
||||
vec3 t0 = -pos * inv_dir;
|
||||
vec3 t1 = (params.grid_size - pos) * inv_dir;
|
||||
vec3 tmax = max(t0, t1);
|
||||
float max_advance = min(tmax.x, min(tmax.y, tmax.z));
|
||||
|
||||
max_advance = min(local_distance, max_advance);
|
||||
|
||||
float advance = 0.0;
|
||||
float occlusion = 1.0;
|
||||
|
||||
while (advance < max_advance) {
|
||||
//read how much to advance from SDF
|
||||
vec3 uvw = (pos + ray_dir * advance) * pos_to_uvw;
|
||||
|
||||
float distance = texture(sampler3D(sdf_cascades[j], linear_sampler), uvw).r * 255.0 - 1.0;
|
||||
if (distance < 0.001) {
|
||||
//consider hit
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
occlusion = min(occlusion, distance);
|
||||
|
||||
advance += distance;
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
attenuation *= occlusion;
|
||||
break;
|
||||
}
|
||||
|
||||
if (advance >= local_distance) {
|
||||
break; //past light distance, abandon search
|
||||
}
|
||||
//change ray origin to collision with bounds
|
||||
pos += ray_dir * max_advance;
|
||||
pos /= cascades.data[j].to_cell;
|
||||
pos += cascades.data[j].offset;
|
||||
light_distance -= max_advance / cascades.data[j].to_cell;
|
||||
ray_pos = pos;
|
||||
}
|
||||
|
||||
if (!hit) {
|
||||
vec3 light = albedo * lights.data[i].color.rgb * lights.data[i].energy * attenuation;
|
||||
|
||||
for (int j = 0; j < 6; j++) {
|
||||
if (bool(valid_aniso & (1 << j))) {
|
||||
light_accum[j] += max(0.0, dot(aniso_dir[j], direction)) * light;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add indirect light
|
||||
|
||||
if (params.multibounce) {
|
||||
vec3 pos = (vec3(positioni) + vec3(0.5)) * float(params.probe_axis_size - 1) / params.grid_size;
|
||||
ivec3 probe_base_pos = ivec3(pos);
|
||||
|
||||
vec4 probe_accum[6] = vec4[](vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0));
|
||||
float weight_accum[6] = float[](0, 0, 0, 0, 0, 0);
|
||||
|
||||
ivec3 tex_pos = ivec3(probe_base_pos.xy, int(params.cascade));
|
||||
tex_pos.x += probe_base_pos.z * int(params.probe_axis_size);
|
||||
|
||||
tex_pos.xy = tex_pos.xy * (OCT_SIZE + 2) + ivec2(1);
|
||||
|
||||
vec3 base_tex_posf = vec3(tex_pos);
|
||||
vec2 tex_pixel_size = 1.0 / vec2(ivec2((OCT_SIZE + 2) * params.probe_axis_size * params.probe_axis_size, (OCT_SIZE + 2) * params.probe_axis_size));
|
||||
vec3 probe_uv_offset = (ivec3(OCT_SIZE + 2, OCT_SIZE + 2, (OCT_SIZE + 2) * params.probe_axis_size)) * tex_pixel_size.xyx;
|
||||
|
||||
for (uint j = 0; j < 8; j++) {
|
||||
ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1);
|
||||
ivec3 probe_posi = probe_base_pos;
|
||||
probe_posi += offset;
|
||||
|
||||
// Compute weight
|
||||
|
||||
vec3 probe_pos = vec3(probe_posi);
|
||||
vec3 probe_to_pos = pos - probe_pos;
|
||||
vec3 probe_dir = normalize(-probe_to_pos);
|
||||
|
||||
// Compute lightprobe texture position
|
||||
|
||||
vec3 trilinear = vec3(1.0) - abs(probe_to_pos);
|
||||
|
||||
for (uint k = 0; k < 6; k++) {
|
||||
if (bool(valid_aniso & (1 << k))) {
|
||||
vec3 n = aniso_dir[k];
|
||||
float weight = trilinear.x * trilinear.y * trilinear.z * max(0.005, dot(n, probe_dir));
|
||||
|
||||
vec3 tex_posf = base_tex_posf + vec3(octahedron_encode(n) * float(OCT_SIZE), 0.0);
|
||||
tex_posf.xy *= tex_pixel_size;
|
||||
|
||||
vec3 pos_uvw = tex_posf;
|
||||
pos_uvw.xy += vec2(offset.xy) * probe_uv_offset.xy;
|
||||
pos_uvw.x += float(offset.z) * probe_uv_offset.z;
|
||||
vec4 indirect_light = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0);
|
||||
|
||||
probe_accum[k] += indirect_light * weight;
|
||||
weight_accum[k] += weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint k = 0; k < 6; k++) {
|
||||
if (weight_accum[k] > 0.0) {
|
||||
light_accum[k] += probe_accum[k].rgb * albedo / weight_accum[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store the light in the light texture
|
||||
|
||||
float lumas[6];
|
||||
vec3 light_total = vec3(0);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
light_total += light_accum[i];
|
||||
lumas[i] = max(light_accum[i].r, max(light_accum[i].g, light_accum[i].b));
|
||||
}
|
||||
|
||||
float luma_total = max(light_total.r, max(light_total.g, light_total.b));
|
||||
|
||||
uint light_total_rgbe;
|
||||
|
||||
{
|
||||
//compress to RGBE9995 to save space
|
||||
|
||||
const float pow2to9 = 512.0f;
|
||||
const float B = 15.0f;
|
||||
const float N = 9.0f;
|
||||
const float LN2 = 0.6931471805599453094172321215;
|
||||
|
||||
float cRed = clamp(light_total.r, 0.0, 65408.0);
|
||||
float cGreen = clamp(light_total.g, 0.0, 65408.0);
|
||||
float cBlue = clamp(light_total.b, 0.0, 65408.0);
|
||||
|
||||
float cMax = max(cRed, max(cGreen, cBlue));
|
||||
|
||||
float expp = max(-B - 1.0f, floor(log(cMax) / LN2)) + 1.0f + B;
|
||||
|
||||
float sMax = floor((cMax / pow(2.0f, expp - B - N)) + 0.5f);
|
||||
|
||||
float exps = expp + 1.0f;
|
||||
|
||||
if (0.0 <= sMax && sMax < pow2to9) {
|
||||
exps = expp;
|
||||
}
|
||||
|
||||
float sRed = floor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
|
||||
float sGreen = floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
|
||||
float sBlue = floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
|
||||
#ifdef MODE_PROCESS_STATIC
|
||||
//since its self-save, use RGBE8985
|
||||
light_total_rgbe = ((uint(sRed) & 0x1FF) >> 1) | ((uint(sGreen) & 0x1FF) << 8) | (((uint(sBlue) & 0x1FF) >> 1) << 17) | ((uint(exps) & 0x1F) << 25);
|
||||
|
||||
#else
|
||||
light_total_rgbe = (uint(sRed) & 0x1FF) | ((uint(sGreen) & 0x1FF) << 9) | ((uint(sBlue) & 0x1FF) << 18) | ((uint(exps) & 0x1F) << 27);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MODE_PROCESS_DYNAMIC
|
||||
|
||||
vec4 aniso0;
|
||||
aniso0.r = lumas[0] / luma_total;
|
||||
aniso0.g = lumas[1] / luma_total;
|
||||
aniso0.b = lumas[2] / luma_total;
|
||||
aniso0.a = lumas[3] / luma_total;
|
||||
|
||||
vec2 aniso1;
|
||||
aniso1.r = lumas[4] / luma_total;
|
||||
aniso1.g = lumas[5] / luma_total;
|
||||
|
||||
//save to 3D textures
|
||||
imageStore(dst_aniso0, positioni, aniso0);
|
||||
imageStore(dst_aniso1, positioni, vec4(aniso1, 0.0, 0.0));
|
||||
imageStore(dst_light, positioni, uvec4(light_total_rgbe));
|
||||
|
||||
//also fill neighbours, so light interpolation during the indirect pass works
|
||||
|
||||
//recover the neighbour list from the leftover bits
|
||||
uint neighbours = (voxel_albedo >> 21) | ((voxel_position >> 21) << 11) | ((process_voxels.data[voxel_index].light >> 30) << 22) | ((process_voxels.data[voxel_index].light_aniso >> 30) << 24);
|
||||
|
||||
const uint max_neighbours = 26;
|
||||
const ivec3 neighbour_positions[max_neighbours] = ivec3[](
|
||||
ivec3(-1, -1, -1),
|
||||
ivec3(-1, -1, 0),
|
||||
ivec3(-1, -1, 1),
|
||||
ivec3(-1, 0, -1),
|
||||
ivec3(-1, 0, 0),
|
||||
ivec3(-1, 0, 1),
|
||||
ivec3(-1, 1, -1),
|
||||
ivec3(-1, 1, 0),
|
||||
ivec3(-1, 1, 1),
|
||||
ivec3(0, -1, -1),
|
||||
ivec3(0, -1, 0),
|
||||
ivec3(0, -1, 1),
|
||||
ivec3(0, 0, -1),
|
||||
ivec3(0, 0, 1),
|
||||
ivec3(0, 1, -1),
|
||||
ivec3(0, 1, 0),
|
||||
ivec3(0, 1, 1),
|
||||
ivec3(1, -1, -1),
|
||||
ivec3(1, -1, 0),
|
||||
ivec3(1, -1, 1),
|
||||
ivec3(1, 0, -1),
|
||||
ivec3(1, 0, 0),
|
||||
ivec3(1, 0, 1),
|
||||
ivec3(1, 1, -1),
|
||||
ivec3(1, 1, 0),
|
||||
ivec3(1, 1, 1));
|
||||
|
||||
for (uint i = 0; i < max_neighbours; i++) {
|
||||
if (bool(neighbours & (1 << i))) {
|
||||
ivec3 neighbour_pos = positioni + neighbour_positions[i];
|
||||
imageStore(dst_light, neighbour_pos, uvec4(light_total_rgbe));
|
||||
imageStore(dst_aniso0, neighbour_pos, aniso0);
|
||||
imageStore(dst_aniso1, neighbour_pos, vec4(aniso1, 0.0, 0.0));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_PROCESS_STATIC
|
||||
|
||||
//save back the anisotropic
|
||||
|
||||
uint light = process_voxels.data[voxel_index].light & (3 << 30);
|
||||
light |= light_total_rgbe;
|
||||
process_voxels.data[voxel_index].light = light; //replace
|
||||
|
||||
uint light_aniso = process_voxels.data[voxel_index].light_aniso & (3 << 30);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
light_aniso |= min(31, uint((lumas[i] / luma_total) * 31.0)) << (i * 5);
|
||||
}
|
||||
|
||||
process_voxels.data[voxel_index].light_aniso = light_aniso;
|
||||
|
||||
#endif
|
||||
}
|
||||
182
servers/rendering/rasterizer_rd/shaders/sdfgi_fields.glsl
Normal file
182
servers/rendering/rasterizer_rd/shaders/sdfgi_fields.glsl
Normal file
@@ -0,0 +1,182 @@
|
||||
/* clang-format off */
|
||||
[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = OCT_RES, local_size_y = OCT_RES, local_size_z = 1) in;
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#define MAX_CASCADES 8
|
||||
|
||||
layout(rgba16f, set = 0, binding = 1) uniform restrict image2DArray irradiance_texture;
|
||||
layout(rg16f, set = 0, binding = 2) uniform restrict image2DArray depth_texture;
|
||||
|
||||
ayout(rgba32ui, set = 0, binding = 3) uniform restrict uimage2DArray irradiance_history_texture;
|
||||
layout(rg32ui, set = 0, binding = 4) uniform restrict uimage2DArray depth_history_texture;
|
||||
|
||||
struct CascadeData {
|
||||
vec3 offset; //offset of (0,0,0) in world coordinates
|
||||
float to_cell; // 1/bounds * grid_size
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 5, std140) uniform Cascades {
|
||||
CascadeData data[MAX_CASCADES];
|
||||
}
|
||||
cascades;
|
||||
|
||||
#define DEPTH_HISTORY_BITS 24
|
||||
#define IRRADIANCE_HISTORY_BITS 16
|
||||
|
||||
layout(push_constant, binding = 0, std430) uniform Params {
|
||||
vec3 grid_size;
|
||||
uint max_cascades;
|
||||
|
||||
uint probe_axis_size;
|
||||
uint cascade;
|
||||
uint history_size;
|
||||
uint pad0;
|
||||
|
||||
ivec3 scroll; //scroll in probes
|
||||
uint pad1;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
ivec2 local = ivec2(gl_LocalInvocationID.xy);
|
||||
ivec2 probe = ivec2(gl_WorkGroupID.xy);
|
||||
|
||||
ivec3 probe_cell;
|
||||
probe_cell.x = probe.x % int(params.probe_axis_size);
|
||||
probe_cell.y = probe.y;
|
||||
probe_cell.z = probe.x / int(params.probe_axis_size);
|
||||
|
||||
#ifdef MODE_SCROLL_BEGIN
|
||||
|
||||
ivec3 read_cell = probe_cell - params.scroll;
|
||||
|
||||
uint src_layer = (params.history_size + 1) * params.cascade;
|
||||
uint dst_layer = (params.history_size + 1) * params.max_cascades;
|
||||
|
||||
for (uint i = 0; i <= params.history_size; i++) {
|
||||
ivec3 write_pos = ivec3(probe * OCT_RES + local, int(i));
|
||||
|
||||
if (any(lessThan(read_pos, ivec3(0))) || any(greaterThanEqual(read_pos, ivec3(params.probe_axis_size)))) {
|
||||
// nowhere to read from for scrolling, try finding the value from upper probes
|
||||
|
||||
#ifdef MODE_IRRADIANCE
|
||||
imageStore(irradiance_history_texture, write_pos, uvec4(0));
|
||||
#endif
|
||||
#ifdef MODE_DEPTH
|
||||
imageStore(depth_history_texture, write_pos, uvec4(0));
|
||||
#endif
|
||||
} else {
|
||||
ivec3 read_pos;
|
||||
read_pos.xy = read_cell.xy;
|
||||
read_pos.x += read_cell.z * params.probe_axis_size;
|
||||
read_pos.xy = read_pos.xy * OCT_RES + local;
|
||||
read_pos.z = int(i);
|
||||
|
||||
#ifdef MODE_IRRADIANCE
|
||||
uvec4 value = imageLoad(irradiance_history_texture, read_pos);
|
||||
imageStore(irradiance_history_texture, write_pos, value);
|
||||
#endif
|
||||
#ifdef MODE_DEPTH
|
||||
uvec2 value = imageLoad(depth_history_texture, read_pos);
|
||||
imageStore(depth_history_texture, write_pos, value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MODE_SCROLL_BEGIN
|
||||
|
||||
#ifdef MODE_SCROLL_END
|
||||
|
||||
uint src_layer = (params.history_size + 1) * params.max_cascades;
|
||||
uint dst_layer = (params.history_size + 1) * params.cascade;
|
||||
|
||||
for (uint i = 0; i <= params.history_size; i++) {
|
||||
ivec3 pos = ivec3(probe * OCT_RES + local, int(i));
|
||||
|
||||
#ifdef MODE_IRRADIANCE
|
||||
uvec4 value = imageLoad(irradiance_history_texture, read_pos);
|
||||
imageStore(irradiance_history_texture, write_pos, value);
|
||||
#endif
|
||||
#ifdef MODE_DEPTH
|
||||
uvec2 value = imageLoad(depth_history_texture, read_pos);
|
||||
imageStore(depth_history_texture, write_pos, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif //MODE_SCROLL_END
|
||||
|
||||
#ifdef MODE_STORE
|
||||
|
||||
uint src_layer = (params.history_size + 1) * params.cascade + params.history_size;
|
||||
ivec3 read_pos = ivec3(probe * OCT_RES + local, int(src_layer));
|
||||
|
||||
ivec3 write_pos = ivec3(probe * (OCT_RES + 2) + ivec2(1), int(params.cascade));
|
||||
|
||||
ivec3 copy_to[4] = ivec3[](write_pos, ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2));
|
||||
|
||||
#ifdef MODE_IRRADIANCE
|
||||
uvec4 average = imageLoad(irradiance_history_texture, read_pos);
|
||||
vec4 light_accum = vec4(average / params.history_size) / float(1 << IRRADIANCE_HISTORY_BITS);
|
||||
|
||||
#endif
|
||||
#ifdef MODE_DEPTH
|
||||
uvec2 value = imageLoad(depth_history_texture, read_pos);
|
||||
vec2 depth_accum = vec4(average / params.history_size) / float(1 << IRRADIANCE_HISTORY_BITS);
|
||||
|
||||
float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
|
||||
float max_depth = length(params.grid_size / cascades.data[params.max_cascades - 1].to_cell);
|
||||
max_depth /= probe_cell_size;
|
||||
|
||||
depth_value = (vec2(average / params.history_size) / float(1 << DEPTH_HISTORY_BITS)) * vec2(max_depth, max_depth * max_depth);
|
||||
|
||||
#endif
|
||||
|
||||
/* Fill the border if required */
|
||||
|
||||
if (local == ivec2(0, 0)) {
|
||||
copy_to[1] = texture_pos + ivec3(OCT_RES - 1, -1, 0);
|
||||
copy_to[2] = texture_pos + ivec3(-1, OCT_RES - 1, 0);
|
||||
copy_to[3] = texture_pos + ivec3(OCT_RES, OCT_RES, 0);
|
||||
} else if (local == ivec2(OCT_RES - 1, 0)) {
|
||||
copy_to[1] = texture_pos + ivec3(0, -1, 0);
|
||||
copy_to[2] = texture_pos + ivec3(OCT_RES, OCT_RES - 1, 0);
|
||||
copy_to[3] = texture_pos + ivec3(-1, OCT_RES, 0);
|
||||
} else if (local == ivec2(0, OCT_RES - 1)) {
|
||||
copy_to[1] = texture_pos + ivec3(-1, 0, 0);
|
||||
copy_to[2] = texture_pos + ivec3(OCT_RES - 1, OCT_RES, 0);
|
||||
copy_to[3] = texture_pos + ivec3(OCT_RES, -1, 0);
|
||||
} else if (local == ivec2(OCT_RES - 1, OCT_RES - 1)) {
|
||||
copy_to[1] = texture_pos + ivec3(0, OCT_RES, 0);
|
||||
copy_to[2] = texture_pos + ivec3(OCT_RES, 0, 0);
|
||||
copy_to[3] = texture_pos + ivec3(-1, -1, 0);
|
||||
} else if (local.y == 0) {
|
||||
copy_to[1] = texture_pos + ivec3(OCT_RES - local.x - 1, local.y - 1, 0);
|
||||
} else if (local.x == 0) {
|
||||
copy_to[1] = texture_pos + ivec3(local.x - 1, OCT_RES - local.y - 1, 0);
|
||||
} else if (local.y == OCT_RES - 1) {
|
||||
copy_to[1] = texture_pos + ivec3(OCT_RES - local.x - 1, local.y + 1, 0);
|
||||
} else if (local.x == OCT_RES - 1) {
|
||||
copy_to[1] = texture_pos + ivec3(local.x + 1, OCT_RES - local.y - 1, 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (copy_to[i] == ivec3(-2, -2, -2)) {
|
||||
continue;
|
||||
}
|
||||
#ifdef MODE_IRRADIANCE
|
||||
imageStore(irradiance_texture, copy_to[i], light_accum);
|
||||
#endif
|
||||
#ifdef MODE_DEPTH
|
||||
imageStore(depth_texture, copy_to[i], vec4(depth_value, 0.0, 0.0));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // MODE_STORE
|
||||
}
|
||||
605
servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl
Normal file
605
servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl
Normal file
@@ -0,0 +1,605 @@
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
#define MAX_CASCADES 8
|
||||
|
||||
layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES];
|
||||
layout(set = 0, binding = 2) uniform texture3D light_cascades[MAX_CASCADES];
|
||||
layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[MAX_CASCADES];
|
||||
layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[MAX_CASCADES];
|
||||
|
||||
layout(set = 0, binding = 6) uniform sampler linear_sampler;
|
||||
|
||||
struct CascadeData {
|
||||
vec3 offset; //offset of (0,0,0) in world coordinates
|
||||
float to_cell; // 1/bounds * grid_size
|
||||
ivec3 probe_world_offset;
|
||||
uint pad;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 7, std140) uniform Cascades {
|
||||
CascadeData data[MAX_CASCADES];
|
||||
}
|
||||
cascades;
|
||||
|
||||
layout(r32ui, set = 0, binding = 8) uniform restrict uimage2DArray lightprobe_texture_data;
|
||||
layout(rgba16i, set = 0, binding = 9) uniform restrict iimage2DArray lightprobe_history_texture;
|
||||
layout(rgba32i, set = 0, binding = 10) uniform restrict iimage2D lightprobe_average_texture;
|
||||
|
||||
//used for scrolling
|
||||
layout(rgba16i, set = 0, binding = 11) uniform restrict iimage2DArray lightprobe_history_scroll_texture;
|
||||
layout(rgba32i, set = 0, binding = 12) uniform restrict iimage2D lightprobe_average_scroll_texture;
|
||||
|
||||
layout(rgba32i, set = 0, binding = 13) uniform restrict iimage2D lightprobe_average_parent_texture;
|
||||
|
||||
layout(set = 1, binding = 0) uniform textureCube sky_irradiance;
|
||||
|
||||
layout(set = 1, binding = 1) uniform sampler linear_sampler_mipmaps;
|
||||
|
||||
#define HISTORY_BITS 10
|
||||
|
||||
#define SKY_MODE_DISABLED 0
|
||||
#define SKY_MODE_COLOR 1
|
||||
#define SKY_MODE_SKY 2
|
||||
|
||||
layout(push_constant, binding = 0, std430) uniform Params {
|
||||
vec3 grid_size;
|
||||
uint max_cascades;
|
||||
|
||||
uint probe_axis_size;
|
||||
uint cascade;
|
||||
uint history_index;
|
||||
uint history_size;
|
||||
|
||||
uint ray_count;
|
||||
float ray_bias;
|
||||
ivec2 image_size;
|
||||
|
||||
ivec3 world_offset;
|
||||
uint sky_mode;
|
||||
|
||||
ivec3 scroll;
|
||||
float sky_energy;
|
||||
|
||||
vec3 sky_color;
|
||||
float y_mult;
|
||||
}
|
||||
params;
|
||||
|
||||
const float PI = 3.14159265f;
|
||||
const float GOLDEN_ANGLE = PI * (3.0 - sqrt(5.0));
|
||||
|
||||
vec3 vogel_hemisphere(uint p_index, uint p_count, float p_offset) {
|
||||
float r = sqrt(float(p_index) + 0.5f) / sqrt(float(p_count));
|
||||
float theta = float(p_index) * GOLDEN_ANGLE + p_offset;
|
||||
float y = cos(r * PI * 0.5);
|
||||
float l = sin(r * PI * 0.5);
|
||||
return vec3(l * cos(theta), l * sin(theta), y * (float(p_index & 1) * 2.0 - 1.0));
|
||||
}
|
||||
|
||||
uvec3 hash3(uvec3 x) {
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
float hashf3(vec3 co) {
|
||||
return fract(sin(dot(co, vec3(12.9898, 78.233, 137.13451))) * 43758.5453);
|
||||
}
|
||||
|
||||
vec3 octahedron_encode(vec2 f) {
|
||||
// https://twitter.com/Stubbesaurus/status/937994790553227264
|
||||
f = f * 2.0 - 1.0;
|
||||
vec3 n = vec3(f.x, f.y, 1.0f - abs(f.x) - abs(f.y));
|
||||
float t = clamp(-n.z, 0.0, 1.0);
|
||||
n.x += n.x >= 0 ? -t : t;
|
||||
n.y += n.y >= 0 ? -t : t;
|
||||
return normalize(n);
|
||||
}
|
||||
|
||||
uint rgbe_encode(vec3 color) {
|
||||
const float pow2to9 = 512.0f;
|
||||
const float B = 15.0f;
|
||||
const float N = 9.0f;
|
||||
const float LN2 = 0.6931471805599453094172321215;
|
||||
|
||||
float cRed = clamp(color.r, 0.0, 65408.0);
|
||||
float cGreen = clamp(color.g, 0.0, 65408.0);
|
||||
float cBlue = clamp(color.b, 0.0, 65408.0);
|
||||
|
||||
float cMax = max(cRed, max(cGreen, cBlue));
|
||||
|
||||
float expp = max(-B - 1.0f, floor(log(cMax) / LN2)) + 1.0f + B;
|
||||
|
||||
float sMax = floor((cMax / pow(2.0f, expp - B - N)) + 0.5f);
|
||||
|
||||
float exps = expp + 1.0f;
|
||||
|
||||
if (0.0 <= sMax && sMax < pow2to9) {
|
||||
exps = expp;
|
||||
}
|
||||
|
||||
float sRed = floor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
|
||||
float sGreen = floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
|
||||
float sBlue = floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
|
||||
return (uint(sRed) & 0x1FF) | ((uint(sGreen) & 0x1FF) << 9) | ((uint(sBlue) & 0x1FF) << 18) | ((uint(exps) & 0x1F) << 27);
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(pos, params.image_size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MODE_PROCESS
|
||||
|
||||
float probe_cell_size = float(params.grid_size.x / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
|
||||
|
||||
ivec3 probe_cell;
|
||||
probe_cell.x = pos.x % int(params.probe_axis_size);
|
||||
probe_cell.y = pos.y;
|
||||
probe_cell.z = pos.x / int(params.probe_axis_size);
|
||||
|
||||
vec3 probe_pos = cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size;
|
||||
vec3 pos_to_uvw = 1.0 / params.grid_size;
|
||||
|
||||
vec4 probe_sh_accum[SH_SIZE] = vec4[](
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0)
|
||||
#if (SH_SIZE == 16)
|
||||
,
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0),
|
||||
vec4(0.0)
|
||||
#endif
|
||||
);
|
||||
|
||||
// quickly ensure each probe has a different "offset" for the vogel function, based on integer world position
|
||||
uvec3 h3 = hash3(uvec3(params.world_offset + probe_cell));
|
||||
float offset = hashf3(vec3(h3 & uvec3(0xFFFFF)));
|
||||
|
||||
//for a more homogeneous hemisphere, alternate based on history frames
|
||||
uint ray_offset = params.history_index;
|
||||
uint ray_mult = params.history_size;
|
||||
uint ray_total = ray_mult * params.ray_count;
|
||||
|
||||
for (uint i = 0; i < params.ray_count; i++) {
|
||||
vec3 ray_dir = vogel_hemisphere(ray_offset + i * ray_mult, ray_total, offset);
|
||||
ray_dir.y *= params.y_mult;
|
||||
ray_dir = normalize(ray_dir);
|
||||
|
||||
//needs to be visible
|
||||
vec3 ray_pos = probe_pos;
|
||||
vec3 inv_dir = 1.0 / ray_dir;
|
||||
|
||||
bool hit = false;
|
||||
vec3 hit_normal;
|
||||
vec3 hit_light;
|
||||
vec3 hit_aniso0;
|
||||
vec3 hit_aniso1;
|
||||
|
||||
float bias = params.ray_bias;
|
||||
vec3 abs_ray_dir = abs(ray_dir);
|
||||
ray_pos += ray_dir * 1.0 / max(abs_ray_dir.x, max(abs_ray_dir.y, abs_ray_dir.z)) * bias / cascades.data[params.cascade].to_cell;
|
||||
|
||||
for (uint j = params.cascade; j < params.max_cascades; j++) {
|
||||
//convert to local bounds
|
||||
vec3 pos = ray_pos - cascades.data[j].offset;
|
||||
pos *= cascades.data[j].to_cell;
|
||||
|
||||
if (any(lessThan(pos, vec3(0.0))) || any(greaterThanEqual(pos, params.grid_size))) {
|
||||
continue; //already past bounds for this cascade, goto next
|
||||
}
|
||||
|
||||
//find maximum advance distance (until reaching bounds)
|
||||
vec3 t0 = -pos * inv_dir;
|
||||
vec3 t1 = (params.grid_size - pos) * inv_dir;
|
||||
vec3 tmax = max(t0, t1);
|
||||
float max_advance = min(tmax.x, min(tmax.y, tmax.z));
|
||||
|
||||
float advance = 0.0;
|
||||
|
||||
vec3 uvw;
|
||||
|
||||
while (advance < max_advance) {
|
||||
//read how much to advance from SDF
|
||||
uvw = (pos + ray_dir * advance) * pos_to_uvw;
|
||||
|
||||
float distance = texture(sampler3D(sdf_cascades[j], linear_sampler), uvw).r * 255.0 - 1.0;
|
||||
if (distance < 0.001) {
|
||||
//consider hit
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
advance += distance;
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
const float EPSILON = 0.001;
|
||||
hit_normal = normalize(vec3(
|
||||
texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(EPSILON, 0.0, 0.0)).r,
|
||||
texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(0.0, EPSILON, 0.0)).r,
|
||||
texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(0.0, 0.0, EPSILON)).r));
|
||||
|
||||
hit_light = texture(sampler3D(light_cascades[j], linear_sampler), uvw).rgb;
|
||||
vec4 aniso0 = texture(sampler3D(aniso0_cascades[j], linear_sampler), uvw);
|
||||
hit_aniso0 = aniso0.rgb;
|
||||
hit_aniso1 = vec3(aniso0.a, texture(sampler3D(aniso1_cascades[j], linear_sampler), uvw).rg);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//change ray origin to collision with bounds
|
||||
pos += ray_dir * max_advance;
|
||||
pos /= cascades.data[j].to_cell;
|
||||
pos += cascades.data[j].offset;
|
||||
ray_pos = pos;
|
||||
}
|
||||
|
||||
vec4 light;
|
||||
if (hit) {
|
||||
//one liner magic
|
||||
light.rgb = hit_light * (dot(max(vec3(0.0), (hit_normal * hit_aniso0)), vec3(1.0)) + dot(max(vec3(0.0), (-hit_normal * hit_aniso1)), vec3(1.0)));
|
||||
light.a = 1.0;
|
||||
} else if (params.sky_mode == SKY_MODE_SKY) {
|
||||
light.rgb = textureLod(samplerCube(sky_irradiance, linear_sampler_mipmaps), ray_dir, 2.0).rgb; //use second mipmap because we dont usually throw a lot of rays, so this compensates
|
||||
light.rgb *= params.sky_energy;
|
||||
light.a = 0.0;
|
||||
|
||||
} else if (params.sky_mode == SKY_MODE_COLOR) {
|
||||
light.rgb = params.sky_color;
|
||||
light.rgb *= params.sky_energy;
|
||||
light.a = 0.0;
|
||||
} else {
|
||||
light = vec4(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
vec3 ray_dir2 = ray_dir * ray_dir;
|
||||
float c[SH_SIZE] = float[](
|
||||
|
||||
0.282095, //l0
|
||||
0.488603 * ray_dir.y, //l1n1
|
||||
0.488603 * ray_dir.z, //l1n0
|
||||
0.488603 * ray_dir.x, //l1p1
|
||||
1.092548 * ray_dir.x * ray_dir.y, //l2n2
|
||||
1.092548 * ray_dir.y * ray_dir.z, //l2n1
|
||||
0.315392 * (3.0 * ray_dir2.z - 1.0), //l20
|
||||
1.092548 * ray_dir.x * ray_dir.z, //l2p1
|
||||
0.546274 * (ray_dir2.x - ray_dir2.y) //l2p2
|
||||
#if (SH_SIZE == 16)
|
||||
,
|
||||
0.590043 * ray_dir.y * (3.0f * ray_dir2.x - ray_dir2.y),
|
||||
2.890611 * ray_dir.y * ray_dir.x * ray_dir.z,
|
||||
0.646360 * ray_dir.y * (-1.0f + 5.0f * ray_dir2.z),
|
||||
0.373176 * (5.0f * ray_dir2.z * ray_dir.z - 3.0f * ray_dir.z),
|
||||
0.457045 * ray_dir.x * (-1.0f + 5.0f * ray_dir2.z),
|
||||
1.445305 * (ray_dir2.x - ray_dir2.y) * ray_dir.z,
|
||||
0.590043 * ray_dir.x * (ray_dir2.x - 3.0f * ray_dir2.y)
|
||||
|
||||
#endif
|
||||
);
|
||||
|
||||
for (uint j = 0; j < SH_SIZE; j++) {
|
||||
probe_sh_accum[j] += light * c[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i = 0; i < SH_SIZE; i++) {
|
||||
// store in history texture
|
||||
ivec3 prev_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(params.history_index));
|
||||
ivec2 average_pos = prev_pos.xy;
|
||||
|
||||
vec4 value = probe_sh_accum[i] * 4.0 / float(params.ray_count);
|
||||
|
||||
ivec4 ivalue = clamp(ivec4(value * float(1 << HISTORY_BITS)), -32768, 32767); //clamp to 16 bits, so higher values don't break average
|
||||
|
||||
ivec4 prev_value = imageLoad(lightprobe_history_texture, prev_pos);
|
||||
ivec4 average = imageLoad(lightprobe_average_texture, average_pos);
|
||||
|
||||
average -= prev_value;
|
||||
average += ivalue;
|
||||
|
||||
imageStore(lightprobe_history_texture, prev_pos, ivalue);
|
||||
imageStore(lightprobe_average_texture, average_pos, average);
|
||||
}
|
||||
#endif // MODE PROCESS
|
||||
|
||||
#ifdef MODE_STORE
|
||||
|
||||
// converting to octahedral in this step is requiered because
|
||||
// octahedral is much faster to read from the screen than spherical harmonics,
|
||||
// despite the very slight quality loss
|
||||
|
||||
ivec2 sh_pos = (pos / OCT_SIZE) * ivec2(1, SH_SIZE);
|
||||
ivec2 oct_pos = (pos / OCT_SIZE) * (OCT_SIZE + 2) + ivec2(1);
|
||||
ivec2 local_pos = pos % OCT_SIZE;
|
||||
|
||||
//fill the spherical harmonic
|
||||
vec4 sh[SH_SIZE];
|
||||
|
||||
for (uint i = 0; i < SH_SIZE; i++) {
|
||||
// store in history texture
|
||||
ivec2 average_pos = sh_pos + ivec2(0, i);
|
||||
ivec4 average = imageLoad(lightprobe_average_texture, average_pos);
|
||||
|
||||
sh[i] = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS);
|
||||
}
|
||||
|
||||
//compute the octahedral normal for this texel
|
||||
vec3 normal = octahedron_encode(vec2(local_pos) / float(OCT_SIZE));
|
||||
/*
|
||||
// read the spherical harmonic
|
||||
const float c1 = 0.429043;
|
||||
const float c2 = 0.511664;
|
||||
const float c3 = 0.743125;
|
||||
const float c4 = 0.886227;
|
||||
const float c5 = 0.247708;
|
||||
vec4 light = (c1 * sh[8] * (normal.x * normal.x - normal.y * normal.y) +
|
||||
c3 * sh[6] * normal.z * normal.z +
|
||||
c4 * sh[0] -
|
||||
c5 * sh[6] +
|
||||
2.0 * c1 * sh[4] * normal.x * normal.y +
|
||||
2.0 * c1 * sh[7] * normal.x * normal.z +
|
||||
2.0 * c1 * sh[5] * normal.y * normal.z +
|
||||
2.0 * c2 * sh[3] * normal.x +
|
||||
2.0 * c2 * sh[1] * normal.y +
|
||||
2.0 * c2 * sh[2] * normal.z);
|
||||
*/
|
||||
vec3 normal2 = normal * normal;
|
||||
float c[SH_SIZE] = float[](
|
||||
|
||||
0.282095, //l0
|
||||
0.488603 * normal.y, //l1n1
|
||||
0.488603 * normal.z, //l1n0
|
||||
0.488603 * normal.x, //l1p1
|
||||
1.092548 * normal.x * normal.y, //l2n2
|
||||
1.092548 * normal.y * normal.z, //l2n1
|
||||
0.315392 * (3.0 * normal2.z - 1.0), //l20
|
||||
1.092548 * normal.x * normal.z, //l2p1
|
||||
0.546274 * (normal2.x - normal2.y) //l2p2
|
||||
#if (SH_SIZE == 16)
|
||||
,
|
||||
0.590043 * normal.y * (3.0f * normal2.x - normal2.y),
|
||||
2.890611 * normal.y * normal.x * normal.z,
|
||||
0.646360 * normal.y * (-1.0f + 5.0f * normal2.z),
|
||||
0.373176 * (5.0f * normal2.z * normal.z - 3.0f * normal.z),
|
||||
0.457045 * normal.x * (-1.0f + 5.0f * normal2.z),
|
||||
1.445305 * (normal2.x - normal2.y) * normal.z,
|
||||
0.590043 * normal.x * (normal2.x - 3.0f * normal2.y)
|
||||
|
||||
#endif
|
||||
);
|
||||
|
||||
const float l_mult[SH_SIZE] = float[](
|
||||
1.0,
|
||||
2.0 / 3.0,
|
||||
2.0 / 3.0,
|
||||
2.0 / 3.0,
|
||||
1.0 / 4.0,
|
||||
1.0 / 4.0,
|
||||
1.0 / 4.0,
|
||||
1.0 / 4.0,
|
||||
1.0 / 4.0
|
||||
#if (SH_SIZE == 16)
|
||||
, // l4 does not contribute to irradiance
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
#endif
|
||||
);
|
||||
|
||||
vec3 irradiance = vec3(0.0);
|
||||
vec3 radiance = vec3(0.0);
|
||||
|
||||
for (uint i = 0; i < SH_SIZE; i++) {
|
||||
vec3 m = sh[i].rgb * c[i] * 4.0;
|
||||
irradiance += m * l_mult[i];
|
||||
radiance += m;
|
||||
}
|
||||
|
||||
//encode RGBE9995 for the final texture
|
||||
|
||||
uint irradiance_rgbe = rgbe_encode(irradiance);
|
||||
uint radiance_rgbe = rgbe_encode(radiance);
|
||||
|
||||
//store in octahedral map
|
||||
|
||||
ivec3 texture_pos = ivec3(oct_pos, int(params.cascade));
|
||||
ivec3 copy_to[4] = ivec3[](ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2));
|
||||
copy_to[0] = texture_pos + ivec3(local_pos, 0);
|
||||
|
||||
if (local_pos == ivec2(0, 0)) {
|
||||
copy_to[1] = texture_pos + ivec3(OCT_SIZE - 1, -1, 0);
|
||||
copy_to[2] = texture_pos + ivec3(-1, OCT_SIZE - 1, 0);
|
||||
copy_to[3] = texture_pos + ivec3(OCT_SIZE, OCT_SIZE, 0);
|
||||
} else if (local_pos == ivec2(OCT_SIZE - 1, 0)) {
|
||||
copy_to[1] = texture_pos + ivec3(0, -1, 0);
|
||||
copy_to[2] = texture_pos + ivec3(OCT_SIZE, OCT_SIZE - 1, 0);
|
||||
copy_to[3] = texture_pos + ivec3(-1, OCT_SIZE, 0);
|
||||
} else if (local_pos == ivec2(0, OCT_SIZE - 1)) {
|
||||
copy_to[1] = texture_pos + ivec3(-1, 0, 0);
|
||||
copy_to[2] = texture_pos + ivec3(OCT_SIZE - 1, OCT_SIZE, 0);
|
||||
copy_to[3] = texture_pos + ivec3(OCT_SIZE, -1, 0);
|
||||
} else if (local_pos == ivec2(OCT_SIZE - 1, OCT_SIZE - 1)) {
|
||||
copy_to[1] = texture_pos + ivec3(0, OCT_SIZE, 0);
|
||||
copy_to[2] = texture_pos + ivec3(OCT_SIZE, 0, 0);
|
||||
copy_to[3] = texture_pos + ivec3(-1, -1, 0);
|
||||
} else if (local_pos.y == 0) {
|
||||
copy_to[1] = texture_pos + ivec3(OCT_SIZE - local_pos.x - 1, local_pos.y - 1, 0);
|
||||
} else if (local_pos.x == 0) {
|
||||
copy_to[1] = texture_pos + ivec3(local_pos.x - 1, OCT_SIZE - local_pos.y - 1, 0);
|
||||
} else if (local_pos.y == OCT_SIZE - 1) {
|
||||
copy_to[1] = texture_pos + ivec3(OCT_SIZE - local_pos.x - 1, local_pos.y + 1, 0);
|
||||
} else if (local_pos.x == OCT_SIZE - 1) {
|
||||
copy_to[1] = texture_pos + ivec3(local_pos.x + 1, OCT_SIZE - local_pos.y - 1, 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (copy_to[i] == ivec3(-2, -2, -2)) {
|
||||
continue;
|
||||
}
|
||||
imageStore(lightprobe_texture_data, copy_to[i], uvec4(irradiance_rgbe));
|
||||
imageStore(lightprobe_texture_data, copy_to[i] + ivec3(0, 0, int(params.max_cascades)), uvec4(radiance_rgbe));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_SCROLL
|
||||
|
||||
ivec3 probe_cell;
|
||||
probe_cell.x = pos.x % int(params.probe_axis_size);
|
||||
probe_cell.y = pos.y;
|
||||
probe_cell.z = pos.x / int(params.probe_axis_size);
|
||||
|
||||
ivec3 read_probe = probe_cell - params.scroll;
|
||||
|
||||
if (all(greaterThanEqual(read_probe, ivec3(0))) && all(lessThan(read_probe, ivec3(params.probe_axis_size)))) {
|
||||
// can scroll
|
||||
ivec2 tex_pos;
|
||||
tex_pos = read_probe.xy;
|
||||
tex_pos.x += read_probe.z * int(params.probe_axis_size);
|
||||
|
||||
//scroll
|
||||
for (uint j = 0; j < params.history_size; j++) {
|
||||
for (int i = 0; i < SH_SIZE; i++) {
|
||||
// copy from history texture
|
||||
ivec3 src_pos = ivec3(tex_pos.x, tex_pos.y * SH_SIZE + i, int(j));
|
||||
ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j));
|
||||
ivec4 value = imageLoad(lightprobe_history_texture, src_pos);
|
||||
imageStore(lightprobe_history_scroll_texture, dst_pos, value);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < SH_SIZE; i++) {
|
||||
// copy from average texture
|
||||
ivec2 src_pos = ivec2(tex_pos.x, tex_pos.y * SH_SIZE + i);
|
||||
ivec2 dst_pos = ivec2(pos.x, pos.y * SH_SIZE + i);
|
||||
ivec4 value = imageLoad(lightprobe_average_texture, src_pos);
|
||||
imageStore(lightprobe_average_scroll_texture, dst_pos, value);
|
||||
}
|
||||
} else if (params.cascade < params.max_cascades - 1) {
|
||||
//cant scroll, must look for position in parent cascade
|
||||
|
||||
//to global coords
|
||||
float probe_cell_size = float(params.grid_size.x / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
|
||||
vec3 probe_pos = cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size;
|
||||
|
||||
//to parent local coords
|
||||
probe_pos -= cascades.data[params.cascade + 1].offset;
|
||||
probe_pos *= cascades.data[params.cascade + 1].to_cell;
|
||||
probe_pos = probe_pos * float(params.probe_axis_size - 1) / float(params.grid_size.x);
|
||||
|
||||
ivec3 probe_posi = ivec3(probe_pos);
|
||||
//add up all light, no need to use occlusion here, since occlusion will do its work afterwards
|
||||
|
||||
vec4 average_light[SH_SIZE] = vec4[](vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0)
|
||||
#if (SH_SIZE == 16)
|
||||
,
|
||||
vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0)
|
||||
#endif
|
||||
);
|
||||
float total_weight = 0.0;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ivec3 offset = probe_posi + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1));
|
||||
|
||||
vec3 trilinear = vec3(1.0) - abs(probe_pos - vec3(offset));
|
||||
float weight = trilinear.x * trilinear.y * trilinear.z;
|
||||
|
||||
ivec2 tex_pos;
|
||||
tex_pos = offset.xy;
|
||||
tex_pos.x += offset.z * int(params.probe_axis_size);
|
||||
|
||||
for (int j = 0; j < SH_SIZE; j++) {
|
||||
// copy from history texture
|
||||
ivec2 src_pos = ivec2(tex_pos.x, tex_pos.y * SH_SIZE + j);
|
||||
ivec4 average = imageLoad(lightprobe_average_parent_texture, src_pos);
|
||||
vec4 value = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS);
|
||||
average_light[j] += value * weight;
|
||||
}
|
||||
|
||||
total_weight += weight;
|
||||
}
|
||||
|
||||
if (total_weight > 0.0) {
|
||||
total_weight = 1.0 / total_weight;
|
||||
}
|
||||
//store the averaged values everywhere
|
||||
|
||||
for (int i = 0; i < SH_SIZE; i++) {
|
||||
ivec4 ivalue = clamp(ivec4(average_light[i] * total_weight * float(1 << HISTORY_BITS)), ivec4(-32768), ivec4(32767)); //clamp to 16 bits, so higher values don't break average
|
||||
// copy from history texture
|
||||
ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, 0);
|
||||
for (uint j = 0; j < params.history_size; j++) {
|
||||
dst_pos.z = int(j);
|
||||
imageStore(lightprobe_history_scroll_texture, dst_pos, ivalue);
|
||||
}
|
||||
|
||||
ivalue *= int(params.history_size); //average needs to have all history added up
|
||||
imageStore(lightprobe_average_scroll_texture, dst_pos.xy, ivalue);
|
||||
}
|
||||
|
||||
} else {
|
||||
// clear and let it re-raytrace, only for the last cascade, which happens very un-often
|
||||
//scroll
|
||||
for (uint j = 0; j < params.history_size; j++) {
|
||||
for (int i = 0; i < SH_SIZE; i++) {
|
||||
// copy from history texture
|
||||
ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j));
|
||||
imageStore(lightprobe_history_scroll_texture, dst_pos, ivec4(0));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < SH_SIZE; i++) {
|
||||
// copy from average texture
|
||||
ivec2 dst_pos = ivec2(pos.x, pos.y * SH_SIZE + i);
|
||||
imageStore(lightprobe_average_scroll_texture, dst_pos, ivec4(0));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_SCROLL_STORE
|
||||
|
||||
//do not update probe texture, as these will be updated later
|
||||
|
||||
for (uint j = 0; j < params.history_size; j++) {
|
||||
for (int i = 0; i < SH_SIZE; i++) {
|
||||
// copy from history texture
|
||||
ivec3 spos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j));
|
||||
ivec4 value = imageLoad(lightprobe_history_scroll_texture, spos);
|
||||
imageStore(lightprobe_history_texture, spos, value);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < SH_SIZE; i++) {
|
||||
// copy from average texture
|
||||
ivec2 spos = ivec2(pos.x, pos.y * SH_SIZE + i);
|
||||
ivec4 average = imageLoad(lightprobe_average_scroll_texture, spos);
|
||||
imageStore(lightprobe_average_texture, spos, average);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
1155
servers/rendering/rasterizer_rd/shaders/sdfgi_preprocess.glsl
Normal file
1155
servers/rendering/rasterizer_rd/shaders/sdfgi_preprocess.glsl
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user