Commit for 2021.04.20.7z

This commit is contained in:
mrq 2021-04-20 00:00:00 -05:00
parent 7794e730aa
commit 1fbf98155f
21 changed files with 971 additions and 1215 deletions

View File

@ -2,8 +2,32 @@
#extension GL_EXT_samplerless_texture_functions : require
#define MULTISAMPLING 1
#define DEFERRED_SAMPLING 0
#define RAY_MARCH_FOG 1
#define UF_DEFERRED_SAMPLING 0
const float PI = 3.14159265359;
const float EPSILON = 0.00001;
const float LIGHT_POWER_CUTOFF = 0.005;
const vec2 poissonDisk[16] = vec2[](
vec2( -0.94201624, -0.39906216 ),
vec2( 0.94558609, -0.76890725 ),
vec2( -0.094184101, -0.92938870 ),
vec2( 0.34495938, 0.29387760 ),
vec2( -0.91588581, 0.45771432 ),
vec2( -0.81544232, -0.87912464 ),
vec2( -0.38277543, 0.27676845 ),
vec2( 0.97484398, 0.75648379 ),
vec2( 0.44323325, -0.97511554 ),
vec2( 0.53742981, -0.47373420 ),
vec2( -0.26496911, -0.41893023 ),
vec2( 0.79197514, 0.19090188 ),
vec2( -0.24188840, 0.99706507 ),
vec2( -0.81409955, 0.91437590 ),
vec2( 0.19984126, 0.78641367 ),
vec2( 0.14383161, -0.14100790 )
);
layout (constant_id = 0) const uint TEXTURES = 256;
@ -98,7 +122,7 @@ struct DrawCall {
#if !MULTISAMPLING
layout (input_attachment_index = 0, binding = 0) uniform usubpassInput samplerId;
layout (input_attachment_index = 1, binding = 1) uniform subpassInput samplerNormal;
#if UF_DEFERRED_SAMPLING
#if DEFERRED_SAMPLING
layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerUv;
#else
layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerAlbedo;
@ -107,7 +131,7 @@ struct DrawCall {
#else
layout (input_attachment_index = 0, binding = 0) uniform usubpassInputMS samplerId;
layout (input_attachment_index = 1, binding = 1) uniform subpassInputMS samplerNormal;
#if UF_DEFERRED_SAMPLING
#if DEFERRED_SAMPLING
layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerUv;
#else
layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerAlbedo;
@ -131,8 +155,8 @@ layout (binding = 4) uniform UBO {
uint msaa;
uint poissonSamples;
uint padding1;
uint padding2;
float gamma;
float exposure;
} ubo;
layout (std140, binding = 5) readonly buffer Lights {
@ -158,321 +182,40 @@ layout (location = 1) in flat uint inPushConstantPass;
layout (location = 0) out vec4 outFragColor;
vec2 poissonDisk[16] = vec2[](
vec2( -0.94201624, -0.39906216 ),
vec2( 0.94558609, -0.76890725 ),
vec2( -0.094184101, -0.92938870 ),
vec2( 0.34495938, 0.29387760 ),
vec2( -0.91588581, 0.45771432 ),
vec2( -0.81544232, -0.87912464 ),
vec2( -0.38277543, 0.27676845 ),
vec2( 0.97484398, 0.75648379 ),
vec2( 0.44323325, -0.97511554 ),
vec2( 0.53742981, -0.47373420 ),
vec2( -0.26496911, -0.41893023 ),
vec2( 0.79197514, 0.19090188 ),
vec2( -0.24188840, 0.99706507 ),
vec2( -0.81409955, 0.91437590 ),
vec2( 0.19984126, 0.78641367 ),
vec2( 0.14383161, -0.14100790 )
);
void phong( Light light, vec4 albedoSpecular, inout vec3 i ) {
vec3 Ls = vec3(1.0, 1.0, 1.0); // light specular
vec3 Ld = light.color; // light color
vec3 La = vec3(1.0, 1.0, 1.0);
vec3 Ks = vec3(albedoSpecular.a); // material specular
vec3 Kd = albedoSpecular.rgb; // material diffuse
vec3 Ka = vec3(1.0, 1.0, 1.0);
float Kexp = ubo.kexp;
vec3 V = position.eye;
vec3 N = normal.eye;
vec3 L = light.position.xyz - V;
float dist = length(L);
// if ( light.radius > 0.001 && light.radius < dist ) return;
vec3 D = normalize(L);
float d_dot = max(dot( D, N ), 0.0);
vec3 R = reflect( -D, N );
vec3 S = normalize(-V);
float s_factor = pow( max(dot( R, S ), 0.0), Kexp );
if ( Kexp < 0.0001 ) s_factor = 0;
float radiance = light.power / (dist * dist);
vec3 Ia = La * Ka;
vec3 Id = Ld * Kd * d_dot * radiance;
vec3 Is = Ls * Ks * s_factor * radiance;
i += Id + Is;
}
void phongSpecular( Light light, vec4 albedoSpecular, inout vec3 i ) {
vec3 Ls = vec3(1.0, 1.0, 1.0); // light specular
vec3 Ld = light.color; // light color
vec3 La = vec3(1.0, 1.0, 1.0);
vec3 Ks = vec3(albedoSpecular.a); // material specular
vec3 Kd = albedoSpecular.rgb; // material diffuse
vec3 Ka = vec3(1.0, 1.0, 1.0);
float Kexp = ubo.kexp;
vec3 V = position.eye;
vec3 N = normal.eye;
vec3 L = light.position.xyz - V;
float dist = length(L);
vec3 D = normalize(L);
vec3 R = reflect( -D, N );
vec3 S = normalize(-V);
float s_factor = pow( max(dot( R, S ), 0.0), Kexp );
if ( Kexp < 0.0001 ) s_factor = 0;
float radiance = light.power / (dist * dist);
vec3 Ia = La * Ka;
vec3 Is = Ls * Ks * s_factor * radiance;
i += Is;
}
const float PI = 3.14159265359;
float DistributionGGX(vec3 N, vec3 H, float roughness) {
float a = roughness*roughness;
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float num = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return num / denom;
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2.
float ndfGGX(float cosLh, float roughness) {
const float alpha = roughness * roughness;
const float alphaSq = alpha * alpha;
const float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
float GeometrySchlickGGX(float NdotV, float roughness) {
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
// Single term for separable Schlick-GGX below.
float gaSchlickG1(float cosTheta, float k) {
return cosTheta / (cosTheta * (1.0 - k) + k);
}
float num = NdotV;
float denom = NdotV * (1.0 - k) + k;
return num / denom;
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float cosLo, float roughness) {
const float r = roughness + 1.0;
const float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
vec3 fresnelSchlick(vec3 F0, float cosTheta) {
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
float random(vec3 seed, int i){
vec4 seed4 = vec4(seed,i);
float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673));
return fract(sin(dot_product) * 43758.5453);
return fract(sin(dot(vec4(seed,i), vec4(12.9898,78.233,45.164,94.673))) * 43758.5453);
}
float shadowFactor( Light light, uint shadowMap, float def ) {
vec4 positionClip = light.projection * light.view * vec4(position.world, 1.0);
positionClip.xyz /= positionClip.w;
if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; //0.0;
if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; //0.0;
if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; //0.0;
float factor = 1.0;
// spot light
if ( light.type == 1 || light.type == 2 ) {
float dist = length( positionClip.xy );
if ( dist > 0.5 ) return def; //0.0;
// spot light with attenuation
if ( light.type == 2 ) {
factor = 1.0 - (pow(dist * 2,2.0));
}
}
vec2 uv = positionClip.xy * 0.5 + 0.5;
float bias = light.depthBias;
/*
if ( true ) {
float cosTheta = clamp(dot(normal.eye, normalize(light.position.xyz - position.eye)), 0, 1);
bias = clamp(bias * tan(acos(cosTheta)), 0, 0.01);
} else if ( true ) {
bias = max(bias * 10 * (1.0 - dot(normal.eye, normalize(light.position.xyz - position.eye))), bias);
}
*/
float eyeDepth = positionClip.z;
int samples = int(ubo.poissonSamples);
if ( samples <= 1 ) {
return eyeDepth < texture(samplerTextures[shadowMap], uv).r - bias ? 0.0 : factor;
}
for ( int i = 0; i < samples; ++i ) {
// int index = i;
// int index = int( float(samples) * random(gl_FragCoord.xyy, i) ) % samples;
int index = int( float(samples) * random(floor(position.world.xyz * 1000.0), i)) % samples;
float lightDepth = texture(samplerTextures[shadowMap], uv + poissonDisk[index] / 700.0 ).r;
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
}
return factor;
// Returns a vector that is orthogonal to u.
vec3 orthogonal(vec3 u){
u = normalize(u);
const vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector.
return abs(dot(u, v)) > 0.99999f ? cross(u, vec3(0, 1, 0)) : cross(u, v);
}
vec3 hslToRgb(vec3 HSL) {
vec3 RGB; {
float H = HSL.x;
float R = abs(H * 6 - 3) - 1;
float G = 2 - abs(H * 6 - 2);
float B = 2 - abs(H * 6 - 4);
RGB = clamp(vec3(R,G,B), 0, 1);
}
float C = (1 - abs(2 * HSL.z - 1)) * HSL.y;
return (RGB - 0.5) * C + HSL.z;
}
vec3 rgbToHsl(vec3 RGB) {
float Epsilon = 1e-10;
vec3 HCV; {
vec4 P = (RGB.g < RGB.b) ? vec4(RGB.bg, -1.0, 2.0/3.0) : vec4(RGB.gb, 0.0, -1.0/3.0);
vec4 Q = (RGB.r < P.x) ? vec4(P.xyw, RGB.r) : vec4(RGB.r, P.yzx);
float C = Q.x - min(Q.w, Q.y);
float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z);
HCV = vec3(H, C, Q.x);
}
float L = HCV.z - HCV.y * 0.5;
float S = HCV.y / (1 - abs(L * 2 - 1) + Epsilon);
return vec3(HCV.x, S, L);
}
float hueDistance(float h1, float h2) {
float diff = abs((h1 - h2));
return min(abs((1.0 - diff)), diff);
}
const float lightnessSteps = 4.0;
float lightnessStep(float l) {
return floor((0.5 + l * lightnessSteps)) / lightnessSteps;
}
const int indexMatrix16x16[256] = int[](0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255,
128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127,
32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223,
160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95,
8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247,
136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119,
40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215,
168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87,
2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253,
130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125,
34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221,
162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93,
10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245,
138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117,
42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213,
170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85);
float indexValue16x16( float scale ) {
int x = int(mod(gl_FragCoord.x * scale, 16));
int y = int(mod(gl_FragCoord.y * scale, 16));
return indexMatrix16x16[(x + y * 16)] / 256.0;
}
const int indexMatrix8x8[64] = int[](0, 32, 8, 40, 2, 34, 10, 42,
48, 16, 56, 24, 50, 18, 58, 26,
12, 44, 4, 36, 14, 46, 6, 38,
60, 28, 52, 20, 62, 30, 54, 22,
3, 35, 11, 43, 1, 33, 9, 41,
51, 19, 59, 27, 49, 17, 57, 25,
15, 47, 7, 39, 13, 45, 5, 37,
63, 31, 55, 23, 61, 29, 53, 21);
float indexValue8x8( float scale ) {
int x = int(mod(gl_FragCoord.x * scale, 8));
int y = int(mod(gl_FragCoord.y * scale, 8));
return indexMatrix8x8[(x + y * 8)] / 64.0;
}
const int indexMatrix4x4[16] = int[](0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5);
float indexValue4x4( float scale ) {
int x = int(mod(gl_FragCoord.x * scale, 4));
int y = int(mod(gl_FragCoord.y * scale, 4));
return indexMatrix4x4[(x + y * 4)] / 16.0;
}
vec3[2] closestColors(float hue) {
vec3 ret[2];
vec3 closest = vec3(-2, 0, 0);
vec3 secondClosest = vec3(-2, 0, 0);
vec3 temp;
/*
for (int i = 0; i < palette.length(); ++i) {
temp = rgbToHsl(palette[i].rgb);
float tempDistance = hueDistance(temp.x, hue);
if (tempDistance < hueDistance(closest.x, hue)) {
secondClosest = closest;
closest = temp;
} else {
if (tempDistance < hueDistance(secondClosest.x, hue)) {
secondClosest = temp;
}
}
}
*/
ret[0] = closest;
ret[1] = secondClosest;
return ret;
}
float dither(float color) {
float closestColor = (color < 0.5) ? 0 : 1;
float secondClosestColor = 1 - closestColor;
float d = 1; // -0.5 - fract(ubo.mode.parameters.w / 8.0);
if ( ubo.mode.scalar == 16 ) {
d = indexValue16x16(1);
} else if ( ubo.mode.scalar == 8 ) {
d = indexValue8x8(1);
} else if ( ubo.mode.scalar == 4 ) {
d = indexValue4x4(1);
}
float distance = abs(closestColor - color);
return (distance < d) ? closestColor : secondClosestColor;
}
void dither(inout vec3 color) {
vec3 hsl = rgbToHsl(color);
hsl.x = dither(hsl.x);
color = hslToRgb(hsl);
}
void dither1(inout vec3 color) {
vec3 hsl = rgbToHsl(color);
float d = 0;
vec3 cs[2] = { hsl, hsl }; // closestColors(hsl.x);
float scale = 1;
if ( ubo.mode.scalar == 16 ) {
d = indexValue16x16(scale);
} else if ( ubo.mode.scalar == 8 ) {
d = indexValue8x8(scale);
} else if ( ubo.mode.scalar == 4 ) {
d = indexValue4x4(scale);
}
float hueDiff = hueDistance(hsl.x, cs[0].x) / hueDistance(cs[1].x, cs[0].x);
float l1 = lightnessStep(max((hsl.z - 0.125), 0.0));
float l2 = lightnessStep(min((hsl.z + 0.124), 1.0));
float lightnessDiff = (hsl.z - l1) / (l2 - l1);
vec3 resultColor = (hueDiff < d) ? cs[0] : cs[1];
resultColor.z = (lightnessDiff < d) ? l1 : l2;
color = hslToRgb(resultColor);
}
vec3 dither2() {
vec3 vDither = dot( vec2( 171.0, 231.0 ), inUv.xy + ubo.mode.parameters.w ).xxx;
vDither.rgb = fract( vDither.rgb / vec3( 103.0, 71.0, 97.0 ) ) - vec3( 0.5, 0.5, 0.5 );
return ( vDither.rgb / 255.0 ) * 0.375;
}
float rand2(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 143758.5453);
}
@ -480,95 +223,31 @@ float rand3(vec3 co){
return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 37.719))) * 143758.5453);
}
void whitenoise(inout vec3 color) {
float flicker = ubo.mode.parameters.x;
float pieces = ubo.mode.parameters.y;
float blend = ubo.mode.parameters.z;
float time = ubo.mode.parameters.w;
const float flicker = ubo.mode.parameters.x;
const float pieces = ubo.mode.parameters.y;
const float blend = ubo.mode.parameters.z;
const float time = ubo.mode.parameters.w;
if ( blend < 0.0001 ) return;
float freq = sin(pow(mod(time, flicker) + flicker, 1.9));
// float whiteNoise = rand3( floor(position.world / pieces) + floor(time * 2) );
float whiteNoise = rand2( floor(gl_FragCoord.xy / pieces) + mod(time, freq) );
const float freq = sin(pow(mod(time, flicker) + flicker, 1.9));
const float whiteNoise = rand2( floor(gl_FragCoord.xy / pieces) + mod(time, freq) );
color = mix( color, vec3(whiteNoise), blend );
}
void pbr( Light light, vec3 albedo, float metallic, float roughness, vec3 lightPositionWorld, inout vec3 i ) {
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metallic);
vec3 N = normalize(normal.eye);
vec3 L = light.position.xyz - position.eye;
float dist = length(L);
L = normalize(L);
vec3 V = normalize(-position.eye);
vec3 H = normalize(V + L);
float NdotL = max(dot(N, L), 0.0);
float NdotV = max(dot(N, V), 0.0);
float attenuation = light.power / (dist * dist);
vec3 radiance = light.color.rgb * attenuation;
// cook-torrance brdf
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
vec3 kD = vec3(1.0) - F;
kD *= 1.0 - metallic;
vec3 numerator = NDF * G * F;
float denominator = 4.0 * NdotV * NdotL;
vec3 specular = numerator / max(denominator, 0.001);
// add to outgoing radiance Lo
i += (kD * albedo / PI + specular) * radiance * NdotL;
vec3 gamma( vec3 i ) {
return pow(i.rgb, vec3(1.0 / 2.2));
}
void pbrSpecular( Light light, vec3 albedo, float metallic, float roughness, inout vec3 i ) {
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metallic);
vec3 N = normalize(normal.eye);
vec3 L = light.position.xyz - position.eye;
float dist = length(L);
L = normalize(L);
vec3 V = normalize(-position.eye);
vec3 H = normalize(V + L);
float NdotL = max(dot(N, L), 0.0);
float NdotV = max(dot(N, V), 0.0);
float attenuation = light.power / (dist * dist);
vec3 radiance = light.color.rgb * attenuation;
// cook-torrance brdf
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
vec3 kD = vec3(1.0) - F;
kD *= 1.0 - metallic;
vec3 numerator = NDF * G * F;
float denominator = 4.0 * NdotV * NdotL;
vec3 specular = numerator / max(denominator, 0.001);
// add to outgoing radiance Lo
i += specular * radiance * NdotL;
}
vec2 rayBoxDst( vec3 boundsMin, vec3 boundsMax, vec3 rayO, vec3 rayD ) {
vec3 t0 = (boundsMin - rayO) / rayD;
vec3 t1 = (boundsMax - rayO) / rayD;
vec3 tmin = min(t0, t1);
vec3 tmax = max(t0, t1);
float dstA = max( max(tmin.x, tmin.y), tmin.z );
float dstB = min( tmax.x, min(tmax.y, tmax.z) );
float dstToBox = max(0, dstA);
float dstInsideBox = max(0, dstB - dstToBox);
return vec2(dstToBox, dstInsideBox);
const vec3 t0 = (boundsMin - rayO) / rayD;
const vec3 t1 = (boundsMax - rayO) / rayD;
const vec3 tmin = min(t0, t1);
const vec3 tmax = max(t0, t1);
const float tStart = max(0, max( max(tmin.x, tmin.y), tmin.z ));
const float tEnd = max(0, min( tmax.x, min(tmax.y, tmax.z) ) - tStart);
return vec2(tStart, tEnd);
}
float sampleDensity( vec3 position ) {
vec3 uvw = position * ubo.fog.densityScale * 0.001 + ubo.fog.offset * 0.01;
const vec3 uvw = position * ubo.fog.densityScale * 0.001 + ubo.fog.offset * 0.01;
return max(0, texture(samplerNoise, uvw).r - ubo.fog.densityThreshold) * ubo.fog.densityMultiplier;
}
@ -577,15 +256,15 @@ void fog( vec3 rayO, vec3 rayD, inout vec3 i, float scale ) {
if ( ubo.fog.range.x == 0 || ubo.fog.range.y == 0 ) return;
#if RAY_MARCH_FOG
float range = ubo.fog.range.y;
vec3 boundsMin = vec3(-range,-range,-range) + rayO;
vec3 boundsMax = vec3(range,range,range) + rayO;
int numSteps = int(length(boundsMax - boundsMin) * ubo.fog.stepScale );
const float range = ubo.fog.range.y;
const vec3 boundsMin = vec3(-range,-range,-range) + rayO;
const vec3 boundsMax = vec3(range,range,range) + rayO;
const int numSteps = int(length(boundsMax - boundsMin) * ubo.fog.stepScale );
vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, rayO, rayD );
float dstToBox = rayBoxInfo.x;
float dstInsideBox = rayBoxInfo.y;
float depth = position.eye.z;
const vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, rayO, rayD );
const float dstToBox = rayBoxInfo.x;
const float dstInsideBox = rayBoxInfo.y;
const float depth = position.eye.z;
float lightEnergy = 0;
// march
@ -608,22 +287,20 @@ void fog( vec3 rayO, vec3 rayD, inout vec3 i, float scale ) {
}
#endif
vec3 color = ubo.fog.color.rgb;
float inner = ubo.fog.range.x;
float outer = ubo.fog.range.y * scale;
float distance = length(-position.eye);
float factor = (distance - inner) / (outer - inner);
factor = clamp( factor, 0.0, 1.0 );
const vec3 color = ubo.fog.color.rgb;
const float inner = ubo.fog.range.x;
const float outer = ubo.fog.range.y * scale;
const float distance = length(-position.eye);
const float factor = clamp( (distance - inner) / (outer - inner), 0.0, 1.0 );
i.rgb = mix(i.rgb, color, factor);
}
vec3 decodeNormals( vec2 enc ) {
#define kPI 3.1415926536f
vec2 ang = enc*2-1;
vec2 scth = vec2( sin(ang.x * kPI), cos(ang.x * kPI) );
vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y);
return vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y);
const vec2 ang = enc*2-1;
const vec2 scth = vec2( sin(ang.x * PI), cos(ang.x * PI) );
const vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y);
return normalize( vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y) );
/*
vec2 fenc = enc*4-2;
float f = dot(fenc,fenc);
@ -638,15 +315,15 @@ vec2 wrap( vec2 uv ) {
return vec2( wrap( uv.x ), wrap( uv.y ) );
}
float mipLevel( in vec2 uv ) {
vec2 dx_vtc = dFdx(uv);
vec2 dy_vtc = dFdy(uv);
const vec2 dx_vtc = dFdx(uv);
const vec2 dy_vtc = dFdy(uv);
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
}
bool validTextureIndex( int textureIndex ) {
return 0 <= textureIndex; // && textureIndex < ubo.textures;
}
vec4 resolve( subpassInputMS t ) {
int samples = int(ubo.msaa);
const int samples = int(ubo.msaa);
vec4 resolved = vec4(0);
for ( int i = 0; i < samples; ++i ) {
resolved += subpassLoad(t, i);
@ -655,7 +332,7 @@ vec4 resolve( subpassInputMS t ) {
return resolved;
}
uvec4 resolve( usubpassInputMS t ) {
int samples = int(ubo.msaa);
const int samples = int(ubo.msaa);
uvec4 resolved = uvec4(0);
for ( int i = 0; i < samples; ++i ) {
resolved += subpassLoad(t, i);
@ -664,6 +341,42 @@ uvec4 resolve( usubpassInputMS t ) {
return resolved;
}
float shadowFactor( const Light light, float def ) {
if ( !validTextureIndex(light.mapIndex) ) return 1.0;
vec4 positionClip = light.projection * light.view * vec4(position.world, 1.0);
positionClip.xyz /= positionClip.w;
if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; //0.0;
if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; //0.0;
if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; //0.0;
float factor = 1.0;
// spot light
if ( abs(light.type) == 2 || abs(light.type) == 3 ) {
const float dist = length( positionClip.xy );
if ( dist > 0.5 ) return def; //0.0;
// spot light with attenuation
if ( abs(light.type) == 3 ) {
factor = 1.0 - (pow(dist * 2,2.0));
}
}
const vec2 uv = positionClip.xy * 0.5 + 0.5;
const float bias = light.depthBias;
const float eyeDepth = positionClip.z;
const int samples = int(ubo.poissonSamples);
if ( samples <= 1 ) return eyeDepth < texture(samplerTextures[light.mapIndex], uv).r - bias ? 0.0 : factor;
for ( int i = 0; i < samples; ++i ) {
const int index = int( float(samples) * random(floor(position.world.xyz * 1000.0), i)) % samples;
const float lightDepth = texture(samplerTextures[light.mapIndex], uv + poissonDisk[index] / 700.0 ).r;
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
}
return factor;
}
void main() {
vec3 rayO = vec3(0);
vec3 rayD = vec3(0);
@ -671,77 +384,72 @@ void main() {
{
#if !MULTISAMPLING
float depth = subpassLoad(samplerDepth).r;
const float depth = subpassLoad(samplerDepth).r;
#else
float depth = resolve( samplerDepth ).r;
const float depth = resolve( samplerDepth ).r;
#endif
vec4 positionClip = vec4(inUv * 2.0 - 1.0, depth, 1.0);
vec4 positionEye = ubo.matrices.iProjection[inPushConstantPass] * positionClip;
vec4 positionEye = ubo.matrices.iProjection[inPushConstantPass] * vec4(inUv * 2.0 - 1.0, depth, 1.0);
positionEye /= positionEye.w;
position.eye = positionEye.xyz;
vec4 positionWorld = ubo.matrices.iView[inPushConstantPass] * positionEye;
position.world = positionWorld.xyz;
position.world = vec3( ubo.matrices.iView[inPushConstantPass] * positionEye );
}
{
vec4 near4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
vec4 far4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
vec3 near3 = near4.xyz / near4.w;
vec3 far3 = far4.xyz / far4.w;
const vec4 near4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
const vec4 far4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
const vec3 near3 = near4.xyz / near4.w;
const vec3 far3 = far4.xyz / far4.w;
rayO = near3;
rayD = normalize( far3 - near3 );
}
{
mat4 iProjectionView = inverse( ubo.matrices.projection[inPushConstantPass] * mat4(mat3(ubo.matrices.view[inPushConstantPass])) );
vec4 near4 = iProjectionView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
vec4 far4 = iProjectionView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
vec3 near3 = near4.xyz / near4.w;
vec3 far3 = far4.xyz / far4.w;
// separate our ray direction due to floating point precision problems
if ( false ) {
const mat4 iProjectionView = inverse( ubo.matrices.projection[inPushConstantPass] * mat4(mat3(ubo.matrices.view[inPushConstantPass])) );
const vec4 near4 = iProjectionView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
const vec4 far4 = iProjectionView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
const vec3 near3 = near4.xyz / near4.w;
const vec3 far3 = far4.xyz / far4.w;
rayD = normalize( far3 - near3 );
}
#if !MULTISAMPLING
normal.world = decodeNormals( subpassLoad(samplerNormal).xy );
uvec2 ID = subpassLoad(samplerId).xy;
const uvec2 ID = subpassLoad(samplerId).xy;
#else
normal.world = decodeNormals( resolve(samplerNormal).xy );
uvec2 ID = resolve(samplerId).xy;
const uvec2 ID = resolve(samplerId).xy;
#endif
normal.eye = vec3( ubo.matrices.view[inPushConstantPass] * vec4(normal.world, 0.0) );
uint drawId = ID.x;
uint materialId = ID.y;
if ( drawId == 0 || materialId == 0 ) {
if ( ID.x == 0 || ID.y == 0 ) {
fragColor.rgb = texture( samplerSkybox, rayD ).rgb;
fog(rayO, rayD, fragColor, 0.0);
outFragColor = vec4(fragColor,1);
return;
}
--drawId;
--materialId;
DrawCall drawCall = drawCalls[drawId];
materialId += drawCall.materialIndex;
Material material = materials[materialId];
vec4 C = material.colorBase;
#if UF_DEFERRED_SAMPLING
const uint drawId = ID.x - 1;
const DrawCall drawCall = drawCalls[drawId];
const uint materialId = ID.y + drawCall.materialIndex - 1;
const Material material = materials[materialId];
vec4 A = material.colorBase;
#if DEFERRED_SAMPLING
#if !MULTISAMPLING
vec2 uv = subpassLoad(samplerUv).xy;
const vec2 uv = subpassLoad(samplerUv).xy;
#else
vec2 uv = resolve(samplerUv).xy;
const vec2 uv = resolve(samplerUv).xy;
#endif
bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas );
const float mip = mipLevel(inUv.xy);
const bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas );
Texture textureAtlas;
if ( useAtlas ) textureAtlas = textures[drawCall.textureIndex + material.indexAtlas];
if ( validTextureIndex( drawCall.textureIndex + material.indexAtlas ) ) {
Texture t = textures[drawCall.textureIndex + material.indexAlbedo];
C = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
A = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
}
// OPAQUE
if ( material.modeAlpha == 0 ) {
C.a = 1;
A.a = 1;
// BLEND
} else if ( material.modeAlpha == 1 ) {
@ -751,78 +459,65 @@ void main() {
}
#else
#if !MULTISAMPLING
C = subpassLoad(samplerAlbedo);
A = subpassLoad(samplerAlbedo);
#else
C = resolve(samplerAlbedo);
A = resolve(samplerAlbedo);
#endif
#endif
float M = material.factorMetallic;
float R = material.factorRoughness * 4.0;
float AO = material.factorOcclusion;
const float M = material.factorMetallic;
const float R = material.factorRoughness;
const float AO = 1.0f - material.factorOcclusion;
bool usePbr = true;
bool gammaCorrect = false;
float litFactor = 1.0;
bool useLightmap = 0 <= material.indexLightmap;
if ( useLightmap ) {
fragColor = C.rgb + ubo.ambient.rgb;
for ( uint i = 0; i < ubo.lights; ++i ) {
Light light = lights[i];
if ( light.power <= 0.001 ) continue;
light.position.xyz = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position.xyz, 1));
if ( validTextureIndex(light.mapIndex) ) {
float factor = shadowFactor( light, light.mapIndex, 0.0 );
light.power *= factor;
litFactor += light.power;
}
if ( light.power <= 0.0001 ) continue;
if ( usePbr ) pbrSpecular( light, C.rgb, M, R, fragColor ); else phongSpecular( light, C, fragColor );
}
/*
float totalShadows = 0.0;
float totalLights = 0.0;
if ( 0 <= material.indexLightmap ) {
fragColor = A.rgb + ubo.ambient.rgb * (1 - AO);
} else {
fragColor = A.rgb * ubo.ambient.rgb * (1 - AO);
}
{
const vec3 N = normal.eye;
const vec3 F0 = mix(vec3(0.04), A.rgb, M);
const vec3 Lo = normalize( -position.eye );
const float cosLo = max(0.0, dot(N, Lo));
for ( uint i = 0; i < ubo.lights; ++i ) {
Light light = lights[i];
const Light light = lights[i];
if ( light.power <= LIGHT_POWER_CUTOFF ) continue;
const vec3 Lp = light.position;
const vec3 Liu = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position, 1)) - position.eye;
const vec3 Li = normalize(Liu);
const float Ls = shadowFactor( light, 0.0 );
const float La = 1.0 / (PI * pow(length(Liu), 2.0));
if ( light.power * La * Ls <= LIGHT_POWER_CUTOFF ) continue;
const float cosLi = max(0.0, dot(N, Li));
const vec3 Lh = normalize(Li + Lo);
const float cosLh = max(0.0, dot(N, Lh));
if ( light.power <= 0.001 ) continue;
vec3 lightPositionWorld = light.position.xyz;
light.position.xyz = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position.xyz, 1));
++totalLights;
if ( !validTextureIndex(light.mapIndex) ) continue;
totalShadows += shadowFactor( light, light.mapIndex, 1.0 );
}
fragColor = vec3(totalShadows / totalLights);
*/
} else {
fragColor = C.rgb * ubo.ambient.rgb * AO;
for ( uint i = 0; i < ubo.lights; ++i ) {
Light light = lights[i];
if ( light.power <= 0.001 ) continue;
vec3 lightPositionWorld = light.position.xyz;
light.position.xyz = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position.xyz, 1));
if ( validTextureIndex(light.mapIndex) ) {
float factor = shadowFactor( light, light.mapIndex, 0.0 );
light.power *= factor;
litFactor += light.power;
}
if ( light.power <= 0.0001 ) continue;
if ( usePbr ) pbr( light, C.rgb, M, R, lightPositionWorld, fragColor ); else phong( light, C, fragColor );
const vec3 Lr = light.color.rgb * light.power * La * Ls;
const vec3 F = fresnelSchlick( F0, max( 0.0, dot(Lh, Lo) ) );
const float D = ndfGGX( cosLh, R );
const float G = gaSchlickGGX(cosLi, cosLo, R);
const vec3 diffuseBRDF = mix( vec3(1.0) - F, vec3(0.0), M ) * A.rgb;
const vec3 specularBRDF = (F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo);
if ( light.type >= 0 && 0 <= material.indexLightmap ) fragColor.rgb += (specularBRDF) * Lr * cosLi;
// else if ( light.type == 0 ) fragColor.rgb += (diffuseBRDF) * Lr * cosLi;
else fragColor.rgb += (diffuseBRDF + specularBRDF) * Lr * cosLi;
litFactor += light.power * La * Ls;
}
}
if ( gammaCorrect ) {
fragColor = fragColor / (fragColor + vec3(1.0));
fragColor = pow(fragColor, vec3(1.0/2.2));
}
fog(rayO, rayD, fragColor, litFactor);
#if TONE_MAP
// fragColor = fragColor / (fragColor + 1.0f);
fragColor = vec3(1.0) - exp(-fragColor * ubo.exposure);
#endif
#if GAMMA_CORRECT
fragColor = pow(fragColor, vec3(1.0 / ubo.gama));
#endif
/*
if ( (ubo.mode.type & (0x1 << 0)) == (0x1 << 0) ) {
@ -833,5 +528,6 @@ void main() {
if ( (ubo.mode.type & (0x1 << 1)) == (0x1 << 1) ) {
whitenoise(fragColor);
}
outFragColor = vec4(fragColor,1);
}

View File

@ -3,7 +3,36 @@
#define MULTISAMPLING 1
#define RAY_MARCH_FOG 1
#define UF_DEFERRED_SAMPLING 0
#define DEFERRED_SAMPLING 0
#define SHADOW_CONE_TRACED 0
#define VOXEL_TRACE_IN_NDC 0
#define GAMMA_CORRECT 1
#define TONE_MAP 1
const float PI = 3.14159265359;
const float EPSILON = 0.00001;
const float LIGHT_POWER_CUTOFF = 0.005;
const vec2 poissonDisk[16] = vec2[](
vec2( -0.94201624, -0.39906216 ),
vec2( 0.94558609, -0.76890725 ),
vec2( -0.094184101, -0.92938870 ),
vec2( 0.34495938, 0.29387760 ),
vec2( -0.91588581, 0.45771432 ),
vec2( -0.81544232, -0.87912464 ),
vec2( -0.38277543, 0.27676845 ),
vec2( 0.97484398, 0.75648379 ),
vec2( 0.44323325, -0.97511554 ),
vec2( 0.53742981, -0.47373420 ),
vec2( -0.26496911, -0.41893023 ),
vec2( 0.79197514, 0.19090188 ),
vec2( -0.24188840, 0.99706507 ),
vec2( -0.81409955, 0.91437590 ),
vec2( 0.19984126, 0.78641367 ),
vec2( 0.14383161, -0.14100790 )
);
layout (constant_id = 0) const uint TEXTURES = 256;
@ -105,7 +134,7 @@ struct Voxel {
#if !MULTISAMPLING
layout (input_attachment_index = 0, binding = 0) uniform usubpassInput samplerId;
layout (input_attachment_index = 1, binding = 1) uniform subpassInput samplerNormal;
#if UF_DEFERRED_SAMPLING
#if DEFERRED_SAMPLING
layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerUv;
#else
layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerAlbedo;
@ -114,7 +143,7 @@ struct Voxel {
#else
layout (input_attachment_index = 0, binding = 0) uniform usubpassInputMS samplerId;
layout (input_attachment_index = 1, binding = 1) uniform subpassInputMS samplerNormal;
#if UF_DEFERRED_SAMPLING
#if DEFERRED_SAMPLING
layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerUv;
#else
layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerAlbedo;
@ -138,8 +167,8 @@ layout (binding = 4) uniform UBO {
uint msaa;
uint poissonSamples;
uint padding1;
uint padding2;
float gamma;
float exposure;
} ubo;
layout (std140, binding = 5) readonly buffer Lights {
@ -170,35 +199,12 @@ layout (location = 1) in flat uint inPushConstantPass;
layout (location = 0) out vec4 outFragColor;
layout (location = 1) out vec4 outDebugValue;
vec2 poissonDisk[16] = vec2[](
vec2( -0.94201624, -0.39906216 ),
vec2( 0.94558609, -0.76890725 ),
vec2( -0.094184101, -0.92938870 ),
vec2( 0.34495938, 0.29387760 ),
vec2( -0.91588581, 0.45771432 ),
vec2( -0.81544232, -0.87912464 ),
vec2( -0.38277543, 0.27676845 ),
vec2( 0.97484398, 0.75648379 ),
vec2( 0.44323325, -0.97511554 ),
vec2( 0.53742981, -0.47373420 ),
vec2( -0.26496911, -0.41893023 ),
vec2( 0.79197514, 0.19090188 ),
vec2( -0.24188840, 0.99706507 ),
vec2( -0.81409955, 0.91437590 ),
vec2( 0.19984126, 0.78641367 ),
vec2( 0.14383161, -0.14100790 )
);
const float PI = 3.14159265359;
const float EPSILON = 0.00001;
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2.
float ndfGGX(float cosLh, float roughness) {
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
const float alpha = roughness * roughness;
const float alphaSq = alpha * alpha;
const float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
@ -209,8 +215,8 @@ float gaSchlickG1(float cosTheta, float k) {
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float cosLo, float roughness) {
float r = roughness + 1.0;
float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
const float r = roughness + 1.0;
const float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
}
vec3 fresnelSchlick(vec3 F0, float cosTheta) {
@ -218,54 +224,13 @@ vec3 fresnelSchlick(vec3 F0, float cosTheta) {
}
float random(vec3 seed, int i){
vec4 seed4 = vec4(seed,i);
float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673));
return fract(sin(dot_product) * 43758.5453);
return fract(sin(dot(vec4(seed,i), vec4(12.9898,78.233,45.164,94.673))) * 43758.5453);
}
float shadowFactor( Light light, uint shadowMap, float def ) {
vec4 positionClip = light.projection * light.view * vec4(position.world, 1.0);
positionClip.xyz /= positionClip.w;
if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; //0.0;
if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; //0.0;
if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; //0.0;
float factor = 1.0;
// spot light
if ( light.type == 1 || light.type == 2 ) {
float dist = length( positionClip.xy );
if ( dist > 0.5 ) return def; //0.0;
// spot light with attenuation
if ( light.type == 2 ) {
factor = 1.0 - (pow(dist * 2,2.0));
}
}
vec2 uv = positionClip.xy * 0.5 + 0.5;
float bias = light.depthBias;
float eyeDepth = positionClip.z;
int samples = int(ubo.poissonSamples);
if ( samples <= 1 ) {
return eyeDepth < texture(samplerTextures[shadowMap], uv).r - bias ? 0.0 : factor;
}
for ( int i = 0; i < samples; ++i ) {
// int index = i;
// int index = int( float(samples) * random(gl_FragCoord.xyy, i) ) % samples;
int index = int( float(samples) * random(floor(position.world.xyz * 1000.0), i)) % samples;
float lightDepth = texture(samplerTextures[shadowMap], uv + poissonDisk[index] / 700.0 ).r;
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
}
return factor;
}
// Returns a vector that is orthogonal to u.
vec3 orthogonal(vec3 u){
u = normalize(u);
vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector.
const vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector.
return abs(dot(u, v)) > 0.99999f ? cross(u, vec3(0, 1, 0)) : cross(u, v);
}
float rand2(vec2 co){
@ -275,14 +240,13 @@ float rand3(vec3 co){
return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 37.719))) * 143758.5453);
}
void whitenoise(inout vec3 color) {
float flicker = ubo.mode.parameters.x;
float pieces = ubo.mode.parameters.y;
float blend = ubo.mode.parameters.z;
float time = ubo.mode.parameters.w;
const float flicker = ubo.mode.parameters.x;
const float pieces = ubo.mode.parameters.y;
const float blend = ubo.mode.parameters.z;
const float time = ubo.mode.parameters.w;
if ( blend < 0.0001 ) return;
float freq = sin(pow(mod(time, flicker) + flicker, 1.9));
// float whiteNoise = rand3( floor(position.world / pieces) + floor(time * 2) );
float whiteNoise = rand2( floor(gl_FragCoord.xy / pieces) + mod(time, freq) );
const float freq = sin(pow(mod(time, flicker) + flicker, 1.9));
const float whiteNoise = rand2( floor(gl_FragCoord.xy / pieces) + mod(time, freq) );
color = mix( color, vec3(whiteNoise), blend );
}
vec3 gamma( vec3 i ) {
@ -290,19 +254,17 @@ vec3 gamma( vec3 i ) {
}
vec2 rayBoxDst( vec3 boundsMin, vec3 boundsMax, vec3 rayO, vec3 rayD ) {
vec3 t0 = (boundsMin - rayO) / rayD;
vec3 t1 = (boundsMax - rayO) / rayD;
vec3 tmin = min(t0, t1);
vec3 tmax = max(t0, t1);
float dstA = max( max(tmin.x, tmin.y), tmin.z );
float dstB = min( tmax.x, min(tmax.y, tmax.z) );
float tStart = max(0, dstA);
float tEnd = max(0, dstB - tStart);
const vec3 t0 = (boundsMin - rayO) / rayD;
const vec3 t1 = (boundsMax - rayO) / rayD;
const vec3 tmin = min(t0, t1);
const vec3 tmax = max(t0, t1);
const float tStart = max(0, max( max(tmin.x, tmin.y), tmin.z ));
const float tEnd = max(0, min( tmax.x, min(tmax.y, tmax.z) ) - tStart);
return vec2(tStart, tEnd);
}
float sampleDensity( vec3 position ) {
vec3 uvw = position * ubo.fog.densityScale * 0.001 + ubo.fog.offset * 0.01;
const vec3 uvw = position * ubo.fog.densityScale * 0.001 + ubo.fog.offset * 0.01;
return max(0, texture(samplerNoise, uvw).r - ubo.fog.densityThreshold) * ubo.fog.densityMultiplier;
}
@ -311,15 +273,15 @@ void fog( vec3 rayO, vec3 rayD, inout vec3 i, float scale ) {
if ( ubo.fog.range.x == 0 || ubo.fog.range.y == 0 ) return;
#if RAY_MARCH_FOG
float range = ubo.fog.range.y;
vec3 boundsMin = vec3(-range,-range,-range) + rayO;
vec3 boundsMax = vec3(range,range,range) + rayO;
int numSteps = int(length(boundsMax - boundsMin) * ubo.fog.stepScale );
const float range = ubo.fog.range.y;
const vec3 boundsMin = vec3(-range,-range,-range) + rayO;
const vec3 boundsMax = vec3(range,range,range) + rayO;
const int numSteps = int(length(boundsMax - boundsMin) * ubo.fog.stepScale );
vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, rayO, rayD );
float dstToBox = rayBoxInfo.x;
float dstInsideBox = rayBoxInfo.y;
float depth = position.eye.z;
const vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, rayO, rayD );
const float dstToBox = rayBoxInfo.x;
const float dstInsideBox = rayBoxInfo.y;
const float depth = position.eye.z;
float lightEnergy = 0;
// march
@ -342,21 +304,19 @@ void fog( vec3 rayO, vec3 rayD, inout vec3 i, float scale ) {
}
#endif
vec3 color = ubo.fog.color.rgb;
float inner = ubo.fog.range.x;
float outer = ubo.fog.range.y * scale;
float distance = length(-position.eye);
float factor = (distance - inner) / (outer - inner);
factor = clamp( factor, 0.0, 1.0 );
const vec3 color = ubo.fog.color.rgb;
const float inner = ubo.fog.range.x;
const float outer = ubo.fog.range.y * scale;
const float distance = length(-position.eye);
const float factor = clamp( (distance - inner) / (outer - inner), 0.0, 1.0 );
i.rgb = mix(i.rgb, color, factor);
}
vec3 decodeNormals( vec2 enc ) {
#define kPI 3.1415926536f
vec2 ang = enc*2-1;
vec2 scth = vec2( sin(ang.x * kPI), cos(ang.x * kPI) );
vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y);
const vec2 ang = enc*2-1;
const vec2 scth = vec2( sin(ang.x * PI), cos(ang.x * PI) );
const vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y);
return normalize( vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y) );
/*
vec2 fenc = enc*4-2;
@ -372,15 +332,15 @@ vec2 wrap( vec2 uv ) {
return vec2( wrap( uv.x ), wrap( uv.y ) );
}
float mipLevel( in vec2 uv ) {
vec2 dx_vtc = dFdx(uv);
vec2 dy_vtc = dFdy(uv);
const vec2 dx_vtc = dFdx(uv);
const vec2 dy_vtc = dFdy(uv);
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
}
bool validTextureIndex( int textureIndex ) {
return 0 <= textureIndex; // && textureIndex < ubo.textures;
}
vec4 resolve( subpassInputMS t ) {
int samples = int(ubo.msaa);
const int samples = int(ubo.msaa);
vec4 resolved = vec4(0);
for ( int i = 0; i < samples; ++i ) {
resolved += subpassLoad(t, i);
@ -389,7 +349,7 @@ vec4 resolve( subpassInputMS t ) {
return resolved;
}
uvec4 resolve( usubpassInputMS t ) {
int samples = int(ubo.msaa);
const int samples = int(ubo.msaa);
uvec4 resolved = uvec4(0);
for ( int i = 0; i < samples; ++i ) {
resolved += subpassLoad(t, i);
@ -398,8 +358,44 @@ uvec4 resolve( usubpassInputMS t ) {
return resolved;
}
float shadowFactor( const Light light, float def ) {
if ( !validTextureIndex(light.mapIndex) ) return 1.0;
vec4 positionClip = light.projection * light.view * vec4(position.world, 1.0);
positionClip.xyz /= positionClip.w;
if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; //0.0;
if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; //0.0;
if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; //0.0;
float factor = 1.0;
// spot light
if ( abs(light.type) == 2 || abs(light.type) == 3 ) {
const float dist = length( positionClip.xy );
if ( dist > 0.5 ) return def; //0.0;
// spot light with attenuation
if ( abs(light.type) == 3 ) {
factor = 1.0 - (pow(dist * 2,2.0));
}
}
const vec2 uv = positionClip.xy * 0.5 + 0.5;
const float bias = light.depthBias;
const float eyeDepth = positionClip.z;
const int samples = int(ubo.poissonSamples);
if ( samples <= 1 ) return eyeDepth < texture(samplerTextures[light.mapIndex], uv).r - bias ? 0.0 : factor;
for ( int i = 0; i < samples; ++i ) {
const int index = int( float(samples) * random(floor(position.world.xyz * 1000.0), i)) % samples;
const float lightDepth = texture(samplerTextures[light.mapIndex], uv + poissonDisk[index] / 700.0 ).r;
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
}
return factor;
}
Voxel getVoxel( vec3 P ) {
vec3 uvw = vec3( ubo.matrices.voxel * vec4( P, 1.0f ) ) * 0.5f + 0.5f;
const vec3 uvw = vec3( ubo.matrices.voxel * vec4( P, 1.0f ) ) * 0.5f + 0.5f;
Voxel voxel;
voxel.id = uvec2(texture(voxelId, uvw).xy);
@ -411,64 +407,65 @@ Voxel getVoxel( vec3 P ) {
return voxel;
}
#define VOXEL_TRACE_IN_NDC 0
vec4 voxelConeTrace( vec3 rayO, vec3 rayD, float aperture ) {
// bounds
float albedoSize = textureSize( voxelAlbedo, 0 ).x;
float mipmapLevels = textureQueryLod( voxelAlbedo, vec3(0) ).x;
struct VoxelInfo {
vec3 min;
vec3 max;
float mipmapLevels;
float albedoSize;
float voxelSize;
};
VoxelInfo voxelInfo;
vec4 voxelConeTrace( vec3 rayO, vec3 rayD, float aperture, float maxDistance ) {
#if VOXEL_TRACE_IN_NDC
rayO = vec3( ubo.matrices.voxel * vec4( rayO, 1.0 ) );
rayD = vec3( ubo.matrices.voxel * vec4( rayD, 0.0 ) );
vec3 boundsMin = vec3( -1 );
vec3 boundsMax = vec3( 1 );
float voxelSize = 1.0 / albedoSize;
#else
mat4 inverseOrtho = inverse( ubo.matrices.voxel );
vec3 boundsMin = vec3( inverseOrtho * vec4( -1, -1, -1, 1 ) );
vec3 boundsMax = vec3( inverseOrtho * vec4( 1, 1, 1, 1 ) );
float voxelSize = 1;
#endif
float granularity = 1.0 / 6.0; // (2.0 * sqrt(2.0) );
const float granularity = 1.0 / 6.0; // (2.0 * sqrt(2.0) );
// box
vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, rayO, rayD );
float tStart = rayBoxInfo.x;
float tEnd = rayBoxInfo.y;
const vec2 rayBoxInfo = rayBoxDst( voxelInfo.min, voxelInfo.max, rayO, rayD );
const float tStart = rayBoxInfo.x;
const float tEnd = maxDistance > 0 ? min(maxDistance, rayBoxInfo.y) : rayBoxInfo.y;
// steps
float tDelta = voxelSize * granularity;
uint maxSteps = uint(albedoSize / granularity);
const float tDelta = voxelInfo.voxelSize * granularity;
const uint maxSteps = uint(voxelInfo.albedoSize / granularity);
// marcher
float t = tStart + tDelta * 2.0;
vec3 rayPos = vec3(0);
vec4 radiance = vec4(0);
vec3 uvw = vec3(0);
// cone mipmap shit
float coneCoefficient = 2.0 * tan(aperture * 0.5);
const float coneCoefficient = 2.0 * tan(aperture * 0.5);
float coneDiameter = coneCoefficient * t;
float level = aperture > 0 ? log2( coneDiameter / albedoSize ) : 0;
float level = aperture > 0 ? log2( coneDiameter / voxelInfo.albedoSize ) : 0;
// results
vec4 color = vec4(0);
float occlusion = 0.0;
// do
uint stepCounter = 0;
while ( t < tEnd && occlusion < 1.0 && stepCounter++ < maxSteps ) {
t += tDelta;
const float falloff = 256.0f;
const vec3 voxelBoundsRecip = 1.0f / (voxelInfo.max - voxelInfo.min);
while ( t < tEnd && color.a < 1.0 && occlusion < 1.0 && stepCounter++ < maxSteps ) {
t += tDelta * pow(2, coneDiameter / voxelInfo.albedoSize);
rayPos = rayO + rayD * t;
#if VOXEL_TRACE_IN_NDC
uvw = rayPos * 0.5 + 0.5;
#else
uvw = (rayPos - boundsMin) / (boundsMax - boundsMin); // vec3( ubo.matrices.voxel * vec4( rayPos, 1 ) ) * 0.5 + 0.5;
uvw = (rayPos - voxelInfo.min) * voxelBoundsRecip;
#endif
if ( abs(uvw.x) > 1.0 || abs(uvw.y) > 1.0 || abs(uvw.z) > 1.0 ) break;
coneDiameter = coneCoefficient * t;
level = log2( coneDiameter / albedoSize );
level = log2( coneDiameter / voxelInfo.albedoSize );
radiance = texture(voxelAlbedo, uvw, level) * granularity;
occlusion += radiance.a;
color.rgb += (1.0 - occlusion) * radiance.rgb;
color += (1.0 - color.a) * radiance;
occlusion += ((1.0f - occlusion) * radiance.a) / (1.0f + falloff * coneDiameter);
}
return vec4(color.rgb, occlusion);
}
vec4 voxelConeTrace( vec3 rayO, vec3 rayD, float aperture ) {
return voxelConeTrace( rayO, rayD, aperture, 0 );
}
void main() {
vec3 rayO = vec3(0);
@ -477,77 +474,72 @@ void main() {
{
#if !MULTISAMPLING
float depth = subpassLoad(samplerDepth).r;
const float depth = subpassLoad(samplerDepth).r;
#else
float depth = resolve( samplerDepth ).r;
const float depth = resolve( samplerDepth ).r;
#endif
vec4 positionClip = vec4(inUv * 2.0 - 1.0, depth, 1.0);
vec4 positionEye = ubo.matrices.iProjection[inPushConstantPass] * positionClip;
vec4 positionEye = ubo.matrices.iProjection[inPushConstantPass] * vec4(inUv * 2.0 - 1.0, depth, 1.0);
positionEye /= positionEye.w;
position.eye = positionEye.xyz;
vec4 positionWorld = ubo.matrices.iView[inPushConstantPass] * positionEye;
position.world = positionWorld.xyz;
position.world = vec3( ubo.matrices.iView[inPushConstantPass] * positionEye );
}
{
vec4 near4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
vec4 far4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
vec3 near3 = near4.xyz / near4.w;
vec3 far3 = far4.xyz / far4.w;
const vec4 near4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
const vec4 far4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
const vec3 near3 = near4.xyz / near4.w;
const vec3 far3 = far4.xyz / far4.w;
rayO = near3;
rayD = normalize( far3 - near3 );
}
{
mat4 iProjectionView = inverse( ubo.matrices.projection[inPushConstantPass] * mat4(mat3(ubo.matrices.view[inPushConstantPass])) );
vec4 near4 = iProjectionView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
vec4 far4 = iProjectionView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
vec3 near3 = near4.xyz / near4.w;
vec3 far3 = far4.xyz / far4.w;
// separate our ray direction due to floating point precision problems
if ( true ) {
const mat4 iProjectionView = inverse( ubo.matrices.projection[inPushConstantPass] * mat4(mat3(ubo.matrices.view[inPushConstantPass])) );
const vec4 near4 = iProjectionView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
const vec4 far4 = iProjectionView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
const vec3 near3 = near4.xyz / near4.w;
const vec3 far3 = far4.xyz / far4.w;
rayD = normalize( far3 - near3 );
}
#if !MULTISAMPLING
normal.world = decodeNormals( subpassLoad(samplerNormal).xy );
uvec2 ID = subpassLoad(samplerId).xy;
const uvec2 ID = subpassLoad(samplerId).xy;
#else
normal.world = decodeNormals( resolve(samplerNormal).xy );
uvec2 ID = resolve(samplerId).xy;
const uvec2 ID = resolve(samplerId).xy;
#endif
normal.eye = vec3( ubo.matrices.view[inPushConstantPass] * vec4(normal.world, 0.0) );
uint drawId = ID.x;
uint materialId = ID.y;
if ( drawId == 0 || materialId == 0 ) {
if ( ID.x == 0 || ID.y == 0 ) {
fragColor.rgb = texture( samplerSkybox, rayD ).rgb;
fog(rayO, rayD, fragColor, 0.0);
fog(rayO, rayD, fragColor, 0.5);
outFragColor = vec4(fragColor,1);
return;
}
--drawId;
--materialId;
DrawCall drawCall = drawCalls[drawId];
materialId += drawCall.materialIndex;
Material material = materials[materialId];
vec4 C = material.colorBase;
#if UF_DEFERRED_SAMPLING
const uint drawId = ID.x - 1;
const DrawCall drawCall = drawCalls[drawId];
const uint materialId = ID.y + drawCall.materialIndex - 1;
const Material material = materials[materialId];
vec4 A = material.colorBase;
#if DEFERRED_SAMPLING
#if !MULTISAMPLING
vec2 uv = subpassLoad(samplerUv).xy;
const vec2 uv = subpassLoad(samplerUv).xy;
#else
vec2 uv = resolve(samplerUv).xy;
const vec2 uv = resolve(samplerUv).xy;
#endif
bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas );
const float mip = mipLevel(inUv.xy);
const bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas );
Texture textureAtlas;
if ( useAtlas ) textureAtlas = textures[drawCall.textureIndex + material.indexAtlas];
if ( validTextureIndex( drawCall.textureIndex + material.indexAtlas ) ) {
Texture t = textures[drawCall.textureIndex + material.indexAlbedo];
C = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
A = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
}
// OPAQUE
if ( material.modeAlpha == 0 ) {
C.a = 1;
A.a = 1;
// BLEND
} else if ( material.modeAlpha == 1 ) {
@ -557,131 +549,128 @@ void main() {
}
#else
#if !MULTISAMPLING
C = subpassLoad(samplerAlbedo);
A = subpassLoad(samplerAlbedo);
#else
C = resolve(samplerAlbedo);
A = resolve(samplerAlbedo);
#endif
#endif
float M = material.factorMetallic;
float R = material.factorRoughness;
const float M = material.factorMetallic;
const float R = material.factorRoughness;
float AO = material.factorOcclusion;
vec4 indirectLighting = vec4(0);
// GI
{
vec3 P = position.world;
vec3 N = normal.world;
// outFragColor = voxelConeTrace(rayO, rayD, 0 );
// return;
const float ROUGHNESS_SCALE = clamp(sqrt( 0.111111f / ( R * R + 0.111111f ) ), 0.0, 1.0);
const float ANGLE_MIX = 0.5f;
const float DIFFUSE_CONE_APERTURE = 0.325;
const float DIFFUSE_INDIRECT_FACTOR = ROUGHNESS_SCALE; //0.11111f * 0.111111f;
const vec3 ortho = normalize(orthogonal(N));
const vec3 ortho2 = normalize(cross(ortho, N));
const vec3 corner = 0.5f * (ortho + ortho2);
const vec3 corner2 = 0.5f * (ortho - ortho2);
const vec3 CONES[9] = {
N,
mix(N, ortho, ANGLE_MIX),
mix(N, -ortho, ANGLE_MIX),
mix(N, ortho2, ANGLE_MIX),
mix(N, -ortho2, ANGLE_MIX),
mix(N, corner, ANGLE_MIX),
mix(N, -corner, ANGLE_MIX),
mix(N, corner2, ANGLE_MIX),
mix(N, -corner2, ANGLE_MIX),
};
vec4 indirectDiffuse = vec4(0);
vec4 indirectSpecular = vec4(0);
// indirectDiffuse += voxelConeTrace( P, N, 0.325 );
for ( uint i = 0; i < 1; ++i ) {
indirectDiffuse += voxelConeTrace(P, CONES[i], DIFFUSE_CONE_APERTURE );
const vec3 P = position.world;
const vec3 N = normal.world;
const float DIFFUSE_CONE_APERTURE = 0.57735f;
const float DIFFUSE_INDIRECT_FACTOR = 1.0f;
const float SPECULAR_CONE_APERTURE = clamp(tan(PI * 0.5f * R), 0.0174533f, PI);
const float SPECULAR_INDIRECT_FACTOR = 1.0f;
const vec4 CONES[] = {
vec4(0.0f, 1.0f, 0.0f, PI / 4.0f),
vec4(0.0f, 0.5f, 0.866025f, 3.0f * PI / 20.0f),
vec4(0.823639f, 0.5f, 0.267617f, 3.0f * PI / 20.0f),
vec4(0.509037f, 0.5f, -0.7006629f, 3.0f * PI / 20.0f),
vec4(-0.50937f, 0.5f, -0.7006629f, 3.0f * PI / 20.0f),
vec4(-0.823639f, 0.5f, 0.267617f, 3.0f * PI / 20.0f)
};
if ( DIFFUSE_INDIRECT_FACTOR > 0.0f ) {
voxelInfo.albedoSize = textureSize( voxelAlbedo, 0 ).x;
voxelInfo.mipmapLevels = textureQueryLod( voxelAlbedo, vec3(0) ).x;
#if VOXEL_TRACE_IN_NDC
voxelInfo.min = vec3( -1 );
voxelInfo.max = vec3( 1 );
voxelInfo.voxelSize = 1.0 / voxelInfo.albedoSize;
#else
const mat4 inverseOrtho = inverse( ubo.matrices.voxel );
voxelInfo.min = vec3( inverseOrtho * vec4( -1, -1, -1, 1 ) );
voxelInfo.max = vec3( inverseOrtho * vec4( 1, 1, 1, 1 ) );
voxelInfo.voxelSize = 1;
#endif
}
if ( SPECULAR_INDIRECT_FACTOR > 0.0f ) {
vec3 guide = vec3(0.0f, 1.0f, 0.0f);
if (abs(dot(N,guide)) == 1.0f) guide = vec3(0.0f, 0.0f, 1.0f);
const float SPECULAR_CONE_APERTURE = acos( ROUGHNESS_SCALE );
const float SPECULAR_INDIRECT_FACTOR = ROUGHNESS_SCALE; // 0.1;
vec3 R = reflect( normalize(P - rayO), N );
indirectSpecular = voxelConeTrace( P, R, SPECULAR_CONE_APERTURE );
indirectLighting = indirectDiffuse * DIFFUSE_INDIRECT_FACTOR + indirectSpecular * SPECULAR_INDIRECT_FACTOR;
// AO = 1.0 - clamp(indirectDiffuse.a, 0.0, 1.0);
// outFragColor.rgb = vec3(AO);
// return;
// outFragColor.rgb = indirectLighting.rgb;
// outFragColor.rgb = indirectDiffuse.rgb;
// outFragColor.rgb = indirectSpecular.rgb;
// return;
const vec3 right = normalize(guide - dot(N, guide) * N);
const vec3 up = cross(right, N);
for ( uint i = 0; i < 6; ++i ) {
const vec3 coneDirection = normalize(N + CONES[i].x * right + CONES[i].z * up);
indirectDiffuse += voxelConeTrace(P, coneDirection, DIFFUSE_CONE_APERTURE ) * CONES[i].w;
}
indirectDiffuse.rgb *= A.rgb;
AO = indirectDiffuse.a;
// outFragColor.rgb = indirectDiffuse.rgb; return;
}
{
const vec3 R = reflect( normalize(P - rayO), N );
indirectSpecular = voxelConeTrace( P, R, SPECULAR_CONE_APERTURE );
indirectLighting = indirectDiffuse * DIFFUSE_INDIRECT_FACTOR + indirectSpecular * SPECULAR_INDIRECT_FACTOR;
// outFragColor.rgb = indirectSpecular.rgb; return;
}
// outFragColor.rgb = indirectLighting.rgb; return;
}
R *= 2.0f;
bool usePbr = true;
bool gammaCorrect = false;
float litFactor = 1.0;
bool useLightmap = 0 <= material.indexLightmap;
if ( useLightmap ) {
fragColor = C.rgb + ubo.ambient.rgb + indirectLighting.rgb;
if ( 0 <= material.indexLightmap ) {
fragColor = A.rgb + ubo.ambient.rgb * (1 - AO) + indirectLighting.rgb;
} else {
fragColor = C.rgb * ubo.ambient.rgb * (1 - AO) + indirectLighting.rgb;
fragColor = A.rgb * ubo.ambient.rgb * (1 - AO) + indirectLighting.rgb;
}
{
const float LIGHT_POWER_CUTOFF = 0.0001;
vec3 N = normal.eye;
vec3 F0 = mix(vec3(0.04), C.rgb, M);
const vec3 N = normal.eye;
const vec3 F0 = mix(vec3(0.04), A.rgb, M);
const vec3 Lo = normalize( -position.eye );
const float cosLo = max(0.0, dot(N, Lo));
#if 0
for ( uint i = 0; i < ubo.lights; ++i ) {
Light light = lights[i];
if ( light.power <= LIGHT_POWER_CUTOFF ) continue;
light.position = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position.xyz, 1));
if ( validTextureIndex(light.mapIndex) ) {
float factor = shadowFactor( light, light.mapIndex, 0.0 );
light.power *= factor;
litFactor += light.power;
}
vec3 Li = light.position - position.eye;
light.power *= 1.0 / (PI * pow(length(Li), 2.0));
const Light light = lights[i];
if ( light.power <= LIGHT_POWER_CUTOFF ) continue;
const vec3 Lp = light.position;
const vec3 Liu = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position, 1)) - position.eye;
const vec3 Li = normalize(Liu);
const float Ls = shadowFactor( light, 0.0 );
const float La = 1.0 / (PI * pow(length(Liu), 2.0));
if ( light.power * La * Ls <= LIGHT_POWER_CUTOFF ) continue;
Li = normalize(Li);
vec3 Lo = normalize( -position.eye );
vec3 Lh = normalize(Li + Lo);
const float cosLi = max(0.0, dot(N, Li));
const vec3 Lh = normalize(Li + Lo);
const float cosLh = max(0.0, dot(N, Lh));
vec3 Lradiance = light.color.rgb * light.power;
vec3 albedo = C.rgb;
float cosLi = max(0.0, dot(N, Li));
float cosLo = max(0.0, dot(N, Lo));
float cosLh = max(0.0, dot(N, Lh));
vec3 F = fresnelSchlick( F0, max( 0.0, dot(Lh, Lo) ) );
float D = ndfGGX( cosLh, R );
float G = gaSchlickGGX(cosLi, cosLo, R);
vec3 Kd = mix( vec3(1.0) - F, vec3(0.0), M );
vec3 diffuseBRDF = Kd * albedo;
vec3 specularBRDF = (F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo);
if ( useLightmap ) {
fragColor.rgb += (specularBRDF) * Lradiance * cosLi;
} else {
fragColor.rgb += (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
const vec3 Lr = light.color.rgb * light.power * La * Ls;
const vec3 F = fresnelSchlick( F0, max( 0.0, dot(Lh, Lo) ) );
const float D = ndfGGX( cosLh, R );
const float G = gaSchlickGGX(cosLi, cosLo, R);
const vec3 diffuseBRDF = mix( vec3(1.0) - F, vec3(0.0), M ) * A.rgb;
const vec3 specularBRDF = (F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo);
if ( light.type >= 0 && 0 <= material.indexLightmap ) fragColor.rgb += (specularBRDF) * Lr * cosLi;
// else if ( abs(light.type) == 1 ) fragColor.rgb += (diffuseBRDF) * Lr * cosLi;
else fragColor.rgb += (diffuseBRDF + specularBRDF) * Lr * cosLi;
litFactor += light.power * La * Ls;
}
#endif
}
if ( gammaCorrect ) fragColor = gamma( fragColor );
fog(rayO, rayD, fragColor, litFactor);
fog(rayO, rayD, fragColor, 1.0 ); //litFactor);
#if TONE_MAP
fragColor = vec3(1.0) - exp(-fragColor * ubo.exposure);
#endif
#if GAMMA_CORRECT
fragColor = pow(fragColor, vec3(1.0 / ubo.gamma));
#endif
if ( (ubo.mode.type & (0x1 << 1)) == (0x1 << 1) ) {
whitenoise(fragColor);
}
outFragColor = vec4(fragColor,1);
outFragColor = vec4(fragColor,1);
}

View File

@ -5,7 +5,31 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
#define MULTISAMPLING 1
#define RAY_MARCH_FOG 1
#define UF_DEFERRED_SAMPLING 0
#define DEFERRED_SAMPLING 0
const float PI = 3.14159265359;
const float EPSILON = 0.00001;
const float LIGHT_POWER_CUTOFF = 0.005;
const vec2 poissonDisk[16] = vec2[](
vec2( -0.94201624, -0.39906216 ),
vec2( 0.94558609, -0.76890725 ),
vec2( -0.094184101, -0.92938870 ),
vec2( 0.34495938, 0.29387760 ),
vec2( -0.91588581, 0.45771432 ),
vec2( -0.81544232, -0.87912464 ),
vec2( -0.38277543, 0.27676845 ),
vec2( 0.97484398, 0.75648379 ),
vec2( 0.44323325, -0.97511554 ),
vec2( 0.53742981, -0.47373420 ),
vec2( -0.26496911, -0.41893023 ),
vec2( 0.79197514, 0.19090188 ),
vec2( -0.24188840, 0.99706507 ),
vec2( -0.81409955, 0.91437590 ),
vec2( 0.19984126, 0.78641367 ),
vec2( 0.14383161, -0.14100790 )
);
layout (constant_id = 0) const uint TEXTURES = 256;
@ -113,8 +137,8 @@ layout (binding = 4) uniform UBO {
uint msaa;
uint poissonSamples;
uint padding1;
uint padding2;
float gamma;
float exposure;
} ubo;
layout (std140, binding = 5) readonly buffer Lights {
@ -139,16 +163,12 @@ layout (binding = 13) uniform sampler3D samplerNoise;
layout (binding = 14) uniform samplerCube samplerSkybox;
layout (binding = 15) uniform sampler2D samplerTextures[TEXTURES];
const float PI = 3.14159265359;
const float EPSILON = 0.00001;
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2.
float ndfGGX(float cosLh, float roughness) {
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
const float alpha = roughness * roughness;
const float alphaSq = alpha * alpha;
const float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
@ -159,8 +179,8 @@ float gaSchlickG1(float cosTheta, float k) {
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float cosLo, float roughness) {
float r = roughness + 1.0;
float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
const float r = roughness + 1.0;
const float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
}
vec3 fresnelSchlick(vec3 F0, float cosTheta) {
@ -168,31 +188,45 @@ vec3 fresnelSchlick(vec3 F0, float cosTheta) {
}
float random(vec3 seed, int i){
vec4 seed4 = vec4(seed,i);
float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673));
return fract(sin(dot_product) * 43758.5453);
return fract(sin(dot(vec4(seed,i), vec4(12.9898,78.233,45.164,94.673))) * 43758.5453);
}
vec2 poissonDisk[16] = vec2[](
vec2( -0.94201624, -0.39906216 ),
vec2( 0.94558609, -0.76890725 ),
vec2( -0.094184101, -0.92938870 ),
vec2( 0.34495938, 0.29387760 ),
vec2( -0.91588581, 0.45771432 ),
vec2( -0.81544232, -0.87912464 ),
vec2( -0.38277543, 0.27676845 ),
vec2( 0.97484398, 0.75648379 ),
vec2( 0.44323325, -0.97511554 ),
vec2( 0.53742981, -0.47373420 ),
vec2( -0.26496911, -0.41893023 ),
vec2( 0.79197514, 0.19090188 ),
vec2( -0.24188840, 0.99706507 ),
vec2( -0.81409955, 0.91437590 ),
vec2( 0.19984126, 0.78641367 ),
vec2( 0.14383161, -0.14100790 )
);
// Returns a vector that is orthogonal to u.
vec3 orthogonal(vec3 u){
u = normalize(u);
const vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector.
return abs(dot(u, v)) > 0.99999f ? cross(u, vec3(0, 1, 0)) : cross(u, v);
}
float rand2(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 143758.5453);
}
float rand3(vec3 co){
return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 37.719))) * 143758.5453);
}
vec3 gamma( vec3 i ) {
return pow(i.rgb, vec3(1.0 / 2.2));
}
vec3 decodeNormals( vec2 enc ) {
const vec2 ang = enc*2-1;
const vec2 scth = vec2( sin(ang.x * PI), cos(ang.x * PI) );
const vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y);
return normalize( vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y) );
}
float wrap( float i ) {
return fract(i);
}
vec2 wrap( vec2 uv ) {
return vec2( wrap( uv.x ), wrap( uv.y ) );
}
bool validTextureIndex( int textureIndex ) {
return 0 <= textureIndex; // && textureIndex < ubo.textures;
}
float shadowFactor( const Light light, float def ) {
if ( !validTextureIndex(light.mapIndex) ) return 1.0;
float shadowFactor( Light light, uint shadowMap, float def ) {
vec4 positionClip = light.projection * light.view * vec4(position.world, 1.0);
positionClip.xyz /= positionClip.w;
@ -203,107 +237,66 @@ float shadowFactor( Light light, uint shadowMap, float def ) {
float factor = 1.0;
// spot light
if ( light.type == 1 || light.type == 2 ) {
float dist = length( positionClip.xy );
if ( abs(light.type) == 2 || abs(light.type) == 3 ) {
const float dist = length( positionClip.xy );
if ( dist > 0.5 ) return def; //0.0;
// spot light with attenuation
if ( light.type == 2 ) {
if ( abs(light.type) == 3 ) {
factor = 1.0 - (pow(dist * 2,2.0));
}
}
vec2 uv = positionClip.xy * 0.5 + 0.5;
float bias = light.depthBias;
/*
if ( true ) {
float cosTheta = clamp(dot(normal.eye, normalize(light.position.xyz - position.eye)), 0, 1);
bias = clamp(bias * tan(acos(cosTheta)), 0, 0.01);
} else if ( true ) {
bias = max(bias * 10 * (1.0 - dot(normal.eye, normalize(light.position.xyz - position.eye))), bias);
}
*/
float eyeDepth = positionClip.z;
int samples = int(ubo.poissonSamples);
if ( samples <= 1 ) {
return eyeDepth < texture(samplerTextures[shadowMap], uv).r - bias ? 0.0 : factor;
}
const vec2 uv = positionClip.xy * 0.5 + 0.5;
const float bias = light.depthBias;
const float eyeDepth = positionClip.z;
const int samples = int(ubo.poissonSamples);
if ( samples <= 1 ) return eyeDepth < texture(samplerTextures[light.mapIndex], uv).r - bias ? 0.0 : factor;
for ( int i = 0; i < samples; ++i ) {
// int index = i;
// int index = int( float(samples) * random(gl_FragCoord.xyy, i) ) % samples;
int index = int( float(samples) * random(floor(position.world.xyz * 1000.0), i)) % samples;
float lightDepth = texture(samplerTextures[shadowMap], uv + poissonDisk[index] / 700.0 ).r;
const int index = int( float(samples) * random(floor(position.world.xyz * 1000.0), i)) % samples;
const float lightDepth = texture(samplerTextures[light.mapIndex], uv + poissonDisk[index] / 700.0 ).r;
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
}
return factor;
}
vec3 decodeNormals( vec2 enc ) {
#define kPI 3.1415926536f
vec2 ang = enc*2-1;
vec2 scth = vec2( sin(ang.x * kPI), cos(ang.x * kPI) );
vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y);
return normalize( vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y) );
/*
vec2 fenc = enc*4-2;
float f = dot(fenc,fenc);
float g = sqrt(1-f/4);
return normalize( vec3(fenc * g, 1 - f / 2) );
*/
}
float wrap( float i ) {
return fract(i);
}
vec2 wrap( vec2 uv ) {
return vec2( wrap( uv.x ), wrap( uv.y ) );
}
void main() {
vec3 fragColor = vec3(0);
const vec3 tUvw = gl_GlobalInvocationID.xyz;
{
const vec2 N = vec2(imageLoad(voxelNormal, ivec3(tUvw) ).xy);
normal.world = decodeNormals( N );
normal.eye = vec3( ubo.matrices.voxel * vec4( normal.world, 0.0f ) );
position.eye = vec3(gl_GlobalInvocationID.xyz) / vec3(imageSize(voxelAlbedo)) * 2.0f - 1.0f;
position.world = vec3( inverse(ubo.matrices.voxel) * vec4( position.eye, 1.0f ) );
}
bool validTextureIndex( int textureIndex ) {
return 0 <= textureIndex; // && textureIndex < ubo.textures;
}
void main() {
vec3 tUvw = gl_GlobalInvocationID.xyz;
uvec2 ID = uvec2(imageLoad(voxelID, ivec3(tUvw) ).xy);
vec2 N = vec2(imageLoad(voxelNormal, ivec3(tUvw) ).xy);
normal.world = decodeNormals( N );
normal.eye = vec3( ubo.matrices.voxel * vec4( normal.world, 0.0f ) );
position.eye = vec3(gl_GlobalInvocationID.xyz) / vec3(imageSize(voxelAlbedo)) * 2.0f - 1.0f;
position.world = vec3( inverse(ubo.matrices.voxel) * vec4( position.eye, 1.0f ) );
vec2 uv = imageLoad(voxelUv, ivec3(tUvw) ).xy;
uint drawId = ID.x;
uint materialId = ID.y;
if ( drawId == 0 || materialId == 0 ) {
const uvec2 ID = uvec2(imageLoad(voxelID, ivec3(tUvw) ).xy);
if ( ID.x == 0 || ID.y == 0 ) {
imageStore(voxelAlbedo, ivec3(tUvw), vec4(0));
return;
}
--drawId;
--materialId;
const uint drawId = ID.x - 1;
const DrawCall drawCall = drawCalls[drawId];
const uint materialId = ID.y + drawCall.materialIndex - 1;
const Material material = materials[materialId];
vec4 A = material.colorBase;
DrawCall drawCall = drawCalls[drawId];
materialId += drawCall.materialIndex;
Material material = materials[materialId];
vec4 C = material.colorBase;
bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas );
#if DEFERRED_SAMPLING
const vec2 uv = imageLoad(voxelUv, ivec3(tUvw) ).xy;
const bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas );
Texture textureAtlas;
if ( useAtlas ) textureAtlas = textures[drawCall.textureIndex + material.indexAtlas];
if ( validTextureIndex( drawCall.textureIndex + material.indexAtlas ) ) {
Texture t = textures[drawCall.textureIndex + material.indexAlbedo];
C = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv );
A = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv );
}
// OPAQUE
if ( material.modeAlpha == 0 ) {
C.a = 1;
A.a = 1;
// BLEND
} else if ( material.modeAlpha == 1 ) {
@ -311,61 +304,52 @@ void main() {
} else if ( material.modeAlpha == 2 ) {
}
#else
A = imageLoad(voxelAlbedo, ivec3(tUvw) );
#endif
const float M = material.factorMetallic;
const float R = material.factorRoughness;
const float AO = material.factorOcclusion;
float M = material.factorMetallic;
float R = material.factorRoughness * 4.0;
float AO = material.factorOcclusion;
bool usePbr = true;
bool gammaCorrect = false;
float litFactor = 1.0;
bool useLightmap = 0 <= material.indexLightmap;
vec3 fragColor = vec3(0.0);
if ( useLightmap ) {
fragColor = C.rgb + ubo.ambient.rgb;
if ( 0 <= material.indexLightmap ) {
fragColor = A.rgb + ubo.ambient.rgb * (1 - AO);
} else {
fragColor = C.rgb * ubo.ambient.rgb * (1 - AO);
fragColor = A.rgb * ubo.ambient.rgb * (1 - AO);
}
{
const float LIGHT_POWER_CUTOFF = 0.005;
vec3 N = normal.world;
vec3 F0 = mix(vec3(0.04), C.rgb, M);
const vec3 N = normal.world;
const vec3 F0 = mix(vec3(0.04), A.rgb, M);
const vec3 Lo = normalize( -position.world );
const float cosLo = max(0.0, dot(N, Lo));
for ( uint i = 0; i < ubo.lights; ++i ) {
Light light = lights[i];
if ( light.power <= LIGHT_POWER_CUTOFF ) continue;
if ( validTextureIndex(light.mapIndex) ) {
float factor = shadowFactor( light, light.mapIndex, 0.0 );
light.power *= factor;
litFactor += light.power;
}
vec3 Li = light.position - position.world;
light.power *= 1.0 / (PI * pow(length(Li), 2.0));
const Light light = lights[i];
if ( light.power <= LIGHT_POWER_CUTOFF ) continue;
const vec3 Lp = light.position;
const vec3 Liu = light.position - position.world;
const vec3 Li = normalize(Liu);
const float Ls = shadowFactor( light, 0.0 );
const float La = 1.0 / (PI * pow(length(Liu), 2.0));
if ( light.power * La * Ls <= LIGHT_POWER_CUTOFF ) continue;
Li = normalize(Li);
vec3 Lo = normalize( -position.world );
vec3 Lh = normalize(Li + Lo);
const float cosLi = max(0.0, dot(N, Li));
const vec3 Lh = normalize(Li + Lo);
const float cosLh = max(0.0, dot(N, Lh));
vec3 Lradiance = light.color.rgb * light.power;
vec3 albedo = C.rgb;
float cosLi = max(0.0, dot(N, Li));
float cosLo = max(0.0, dot(N, Lo));
float cosLh = max(0.0, dot(N, Lh));
vec3 F = fresnelSchlick( F0, max( 0.0, dot(Lh, Lo) ) );
float D = ndfGGX( cosLh, R );
float G = gaSchlickGGX(cosLi, cosLo, R);
vec3 Kd = mix( vec3(1.0) - F, vec3(0.0), M );
vec3 diffuseBRDF = Kd * albedo;
vec3 specularBRDF = (F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo);
if ( useLightmap ) {
fragColor.rgb += (specularBRDF) * Lradiance * cosLi;
} else {
fragColor.rgb += (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
const vec3 Lr = light.color.rgb * light.power * La * Ls;
const vec3 F = fresnelSchlick( F0, max( 0.0, dot(Lh, Lo) ) );
const float D = ndfGGX( cosLh, R );
const float G = gaSchlickGGX(cosLi, cosLo, R);
const vec3 diffuseBRDF = mix( vec3(1.0) - F, vec3(0.0), M ) * A.rgb;
const vec3 specularBRDF = (F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo);
if ( light.type >= 0 && 0 <= material.indexLightmap ) fragColor.rgb += (specularBRDF) * Lr * cosLi;
// else if ( light.type == 0 ) fragColor.rgb += (diffuseBRDF) * Lr * cosLi;
else fragColor.rgb += (diffuseBRDF + specularBRDF) * Lr * cosLi;
litFactor += light.power * La * Ls;
}
}
imageStore(voxelAlbedo, ivec3(tUvw), vec4(fragColor.rgb, 1));
}

View File

@ -3,6 +3,8 @@
#define UF_DEFERRED_SAMPLING 0
#define UF_CAN_DISCARD 1
#define PI 3.1415926536f
layout (constant_id = 0) const uint TEXTURES = 1;
layout (binding = 0) uniform sampler2D samplerTextures[TEXTURES];
@ -58,8 +60,7 @@ layout (location = 1) out vec2 outNormals;
vec2 encodeNormals( vec3 n ) {
// return n.xy / sqrt(n.z*8+8) + 0.5;
#define kPI 3.1415926536f
return (vec2(atan(n.y,n.x)/kPI, n.z)+1.0)*0.5;
return (vec2(atan(n.y,n.x)/PI, n.z)+1.0)*0.5;
}
float wrap( float i ) {
@ -69,25 +70,25 @@ vec2 wrap( vec2 uv ) {
return vec2( wrap( uv.x ), wrap( uv.y ) );
}
float mipLevel( in vec2 uv ) {
vec2 dx_vtc = dFdx(uv);
vec2 dy_vtc = dFdy(uv);
const vec2 dx_vtc = dFdx(uv);
const vec2 dy_vtc = dFdy(uv);
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
}
bool validTextureIndex( int textureIndex ) {
return 0 <= textureIndex && textureIndex < TEXTURES;
}
void main() {
float mip = mipLevel(inUv.xy);
vec2 uv = wrap(inUv.xy);
vec4 C = vec4(0, 0, 0, 0);
vec3 P = inPosition;
vec3 N = inNormal;
const float mip = mipLevel(inUv.xy);
const vec2 uv = wrap(inUv.xy);
vec4 A = vec4(0, 0, 0, 0);
const vec3 P = inPosition;
const vec3 N = inNormal;
#if UF_DEFERRED_SAMPLING
outUvs = wrap(inUv.xy);
vec4 outAlbedo = vec4(0,0,0,0);
const vec4 outAlbedo = vec4(0,0,0,0);
#endif
#if !UF_DEFERRED_SAMPLING
C = textureLod( samplerTextures[0], inSt, mip );
A = textureLod( samplerTextures[0], inSt, mip );
#endif
outNormals = encodeNormals( N );
outId = ivec2(inId.w+1, inId.y+1);

View File

@ -1,5 +1,29 @@
#version 450
const float PI = 3.14159265359;
const float EPSILON = 0.00001;
const float LIGHT_POWER_CUTOFF = 0.005;
const vec2 poissonDisk[16] = vec2[](
vec2( -0.94201624, -0.39906216 ),
vec2( 0.94558609, -0.76890725 ),
vec2( -0.094184101, -0.92938870 ),
vec2( 0.34495938, 0.29387760 ),
vec2( -0.91588581, 0.45771432 ),
vec2( -0.81544232, -0.87912464 ),
vec2( -0.38277543, 0.27676845 ),
vec2( 0.97484398, 0.75648379 ),
vec2( 0.44323325, -0.97511554 ),
vec2( 0.53742981, -0.47373420 ),
vec2( -0.26496911, -0.41893023 ),
vec2( 0.79197514, 0.19090188 ),
vec2( -0.24188840, 0.99706507 ),
vec2( -0.81409955, 0.91437590 ),
vec2( 0.19984126, 0.78641367 ),
vec2( 0.14383161, -0.14100790 )
);
layout (constant_id = 0) const uint TEXTURES = 1;
layout (binding = 0) uniform sampler2D samplerTextures[TEXTURES];
@ -64,65 +88,58 @@ layout (location = 7) flat in ivec4 inId;
layout (location = 0) out vec4 outAlbedo;
vec2 poissonDisk[16] = vec2[](
vec2( -0.94201624, -0.39906216 ),
vec2( 0.94558609, -0.76890725 ),
vec2( -0.094184101, -0.92938870 ),
vec2( 0.34495938, 0.29387760 ),
vec2( -0.91588581, 0.45771432 ),
vec2( -0.81544232, -0.87912464 ),
vec2( -0.38277543, 0.27676845 ),
vec2( 0.97484398, 0.75648379 ),
vec2( 0.44323325, -0.97511554 ),
vec2( 0.53742981, -0.47373420 ),
vec2( -0.26496911, -0.41893023 ),
vec2( 0.79197514, 0.19090188 ),
vec2( -0.24188840, 0.99706507 ),
vec2( -0.81409955, 0.91437590 ),
vec2( 0.19984126, 0.78641367 ),
vec2( 0.14383161, -0.14100790 )
);
float random(vec3 seed, int i){
vec4 seed4 = vec4(seed,i);
float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673));
return fract(sin(dot_product) * 43758.5453);
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2.
float ndfGGX(float cosLh, float roughness) {
const float alpha = roughness * roughness;
const float alphaSq = alpha * alpha;
const float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
float shadowFactor( vec3 position, Light light, uint shadowMap, float def ) {
vec4 positionClip = light.projection * light.view * vec4(position, 1.0);
positionClip.xyz /= positionClip.w;
if ( positionClip.x < -1 || positionClip.x >= 1 ) return def;
if ( positionClip.y < -1 || positionClip.y >= 1 ) return def;
if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def;
// Single term for separable Schlick-GGX below.
float gaSchlickG1(float cosTheta, float k) {
return cosTheta / (cosTheta * (1.0 - k) + k);
}
float factor = 1.0;
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float cosLo, float roughness) {
const float r = roughness + 1.0;
const float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
}
vec3 fresnelSchlick(vec3 F0, float cosTheta) {
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
// spot light
if ( light.type == 2 || light.type == 3 ) {
float dist = length( positionClip.xy );
if ( dist > 0.5 ) return def;
// spot light with attenuation
if ( light.type == 3 ) {
factor = 1.0 - (pow(dist * 2,2.0));
}
}
vec2 uv = positionClip.xy * 0.5 + 0.5;
float bias = light.depthBias;
float random(vec3 seed, int i){
return fract(sin(dot(vec4(seed,i), vec4(12.9898,78.233,45.164,94.673))) * 43758.5453);
}
float eyeDepth = positionClip.z;
// Returns a vector that is orthogonal to u.
vec3 orthogonal(vec3 u){
u = normalize(u);
const vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector.
return abs(dot(u, v)) > 0.99999f ? cross(u, vec3(0, 1, 0)) : cross(u, v);
}
float rand2(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 143758.5453);
}
float rand3(vec3 co){
return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 37.719))) * 143758.5453);
}
int samples = 16;
if ( samples <= 1 ) {
return eyeDepth < texture(samplerTextures[shadowMap], uv).r - bias ? 0.0 : factor;
}
for ( int i = 0; i < samples; ++i ) {
int index = int( float(samples) * random(floor(position * 1000.0), i)) % samples;
float lightDepth = texture(samplerTextures[shadowMap], uv + poissonDisk[index] / 700.0 ).r;
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
}
return factor;
vec3 decodeNormals( vec2 enc ) {
const vec2 ang = enc*2-1;
const vec2 scth = vec2( sin(ang.x * PI), cos(ang.x * PI) );
const vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y);
return normalize( vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y) );
/*
vec2 fenc = enc*4-2;
float f = dot(fenc,fenc);
float g = sqrt(1-f/4);
return normalize( vec3(fenc * g, 1 - f / 2) );
*/
}
float wrap( float i ) {
return fract(i);
@ -131,116 +148,83 @@ vec2 wrap( vec2 uv ) {
return vec2( wrap( uv.x ), wrap( uv.y ) );
}
float mipLevel( in vec2 uv ) {
vec2 dx_vtc = dFdx(uv);
vec2 dy_vtc = dFdy(uv);
const vec2 dx_vtc = dFdx(uv);
const vec2 dy_vtc = dFdy(uv);
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
}
bool validTextureIndex( int textureIndex ) {
return 0 <= textureIndex && textureIndex < textures.length();
return 0 <= textureIndex; // && textureIndex < ubo.textures;
}
const float PI = 3.14159265359;
float DistributionGGX(vec3 N, vec3 H, float roughness) {
float a = roughness*roughness;
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float shadowFactor( const Light light, const vec3 P, float def ) {
if ( !validTextureIndex(light.mapIndex) ) return 1.0;
vec4 positionClip = light.projection * light.view * vec4(P, 1.0);
positionClip.xyz /= positionClip.w;
if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; //0.0;
if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; //0.0;
if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; //0.0;
float factor = 1.0;
// spot light
if ( abs(light.type) == 2 || abs(light.type) == 3 ) {
const float dist = length( positionClip.xy );
if ( dist > 0.5 ) return def; //0.0;
// spot light with attenuation
if ( abs(light.type) == 3 ) {
factor = 1.0 - (pow(dist * 2,2.0));
}
}
float num = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return num / denom;
}
float GeometrySchlickGGX(float NdotV, float roughness) {
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float num = NdotV;
float denom = NdotV * (1.0 - k) + k;
return num / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
void pbr( Light light, vec3 albedo, float metallic, float roughness, vec3 normal, vec3 position, inout vec3 i ) {
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metallic);
vec3 N = normalize(normal);
vec3 L = light.position.xyz - position;
float dist = length(L);
L = normalize(L);
vec3 V = normalize(-position);
vec3 H = normalize(V + L);
float NdotL = max(dot(N, L), 0.0);
float NdotV = max(dot(N, V), 0.0);
float attenuation = light.power / (dist * dist);
vec3 radiance = light.color.rgb * attenuation;
// cook-torrance brdf
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
vec3 kD = vec3(1.0) - F;
kD *= 1.0 - metallic;
vec3 numerator = NDF * G * F;
float denominator = 4.0 * NdotV * NdotL;
vec3 specular = numerator / max(denominator, 0.001);
// add to outgoing radiance Lo
// ignore specular
i += (kD * albedo / PI ) * radiance * NdotL;
const vec2 uv = positionClip.xy * 0.5 + 0.5;
const float bias = light.depthBias;
const float eyeDepth = positionClip.z;
const int samples = 16; //int(ubo.poissonSamples);
if ( samples <= 1 ) return eyeDepth < texture(samplerTextures[light.mapIndex], uv).r - bias ? 0.0 : factor;
for ( int i = 0; i < samples; ++i ) {
const int index = int( float(samples) * random(floor(P * 1000.0), i)) % samples;
const float lightDepth = texture(samplerTextures[light.mapIndex], uv + poissonDisk[index] / 700.0 ).r;
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
}
return factor;
}
void main() {
float mip = mipLevel(inUv.xy);
vec2 uv = wrap(inUv.xy);
vec4 C = vec4(1, 1, 1, 1);
vec3 P = inPosition;
vec4 A = vec4(1, 1, 1, 1);
vec3 N = normalize( inNormal );
int materialId = int(inId.y);
Material material = materials[materialId];
const float mip = mipLevel(inUv.xy);
const vec2 uv = wrap(inUv.xy);
const vec3 P = inPosition;
const int materialId = int(inId.y);
const Material material = materials[materialId];
float M = material.factorMetallic;
float R = material.factorRoughness;
float AO = material.factorOcclusion;
const float M = material.factorMetallic;
const float R = material.factorRoughness;
const float AO = 1.0f - material.factorOcclusion;
#if 0
// sample albedo
bool useAtlas = validTextureIndex( material.indexAtlas );
const bool useAtlas = validTextureIndex( material.indexAtlas );
Texture textureAtlas;
if ( useAtlas ) textureAtlas = textures[material.indexAtlas];
if ( !validTextureIndex( material.indexAlbedo ) ) discard;
{
Texture t = textures[material.indexAlbedo];
C = textureLod( samplerTextures[(useAtlas) ? textureAtlas.index : t.index], (useAtlas) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
A = textureLod( samplerTextures[(useAtlas) ? textureAtlas.index : t.index], (useAtlas) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
// alpha mode OPAQUE
if ( material.modeAlpha == 0 ) {
C.a = 1;
A.a = 1;
// alpha mode BLEND
} else if ( material.modeAlpha == 1 ) {
// alpha mode MASK
} else if ( material.modeAlpha == 2 ) {
if ( C.a < abs(material.factorAlphaCutoff) ) discard;
C.a = 1;
if ( A.a < abs(material.factorAlphaCutoff) ) discard;
A.a = 1;
}
if ( C.a == 0 ) discard;
if ( A.a == 0 ) discard;
}
// sample normal
@ -248,20 +232,40 @@ void main() {
Texture t = textures[material.indexNormal];
N = inTBN * normalize( textureLod( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip ).xyz * 2.0 - vec3(1.0));
}
#else
A = vec4(1);
#endif
C = vec4(1);
bool lit = false;
float litFactor = 1.0;
vec3 fragColor = vec3(0);
for ( uint i = 0; i < lights.length(); ++i ) {
Light light = lights[i];
if ( light.power <= 0.001 ) continue;
if ( 0 <= light.mapIndex ) {
float factor = shadowFactor( P, light, light.mapIndex, 0.0 );
light.power *= factor;
{
const vec3 F0 = mix(vec3(0.04), A.rgb, M);
const vec3 Lo = normalize( -P );
const float cosLo = max(0.0, dot(N, Lo));
for ( uint i = 0; i < lights.length(); ++i ) {
const Light light = lights[i];
if ( light.power <= LIGHT_POWER_CUTOFF ) continue;
const vec3 Lp = light.position;
const vec3 Liu = light.position - P;
const float La = 1.0 / (PI * pow(length(Liu), 2.0));
const float Ls = shadowFactor( light, P, 0.0 );
if ( light.power * La * Ls <= LIGHT_POWER_CUTOFF ) continue;
const vec3 Li = normalize(Liu);
const vec3 Lh = normalize(Li + Lo);
const float cosLi = max(0.0, dot(N, Li));
const float cosLh = max(0.0, dot(N, Lh));
const vec3 Lr = light.color.rgb * light.power * La * Ls;
const vec3 F = fresnelSchlick( F0, max( 0.0, dot(Lh, Lo) ) );
const float D = ndfGGX( cosLh, R );
const float G = gaSchlickGGX(cosLi, cosLo, R);
const vec3 diffuseBRDF = mix( vec3(1.0) - F, vec3(0.0), M ) * A.rgb;
fragColor.rgb += (diffuseBRDF) * Lr * cosLi;
litFactor += light.power * La * Ls;
}
if ( light.power <= 0.0001 ) continue;
pbr( light, C.rgb, M, R, N, P, fragColor );
}
outAlbedo = vec4(fragColor, 1);
}

View File

@ -1,7 +1,8 @@
#version 450
#define UF_DEFERRED_SAMPLING 0
#define UF_CAN_DISCARD 1
#define DEFERRED_SAMPLING 0
#define CAN_DISCARD 1
#define USE_LIGHTMAP 1
layout (constant_id = 0) const uint TEXTURES = 1;
layout (binding = 0) uniform sampler2D samplerTextures[TEXTURES];
@ -50,16 +51,17 @@ layout (location = 8) flat in ivec4 inId;
layout (location = 0) out uvec2 outId;
layout (location = 1) out vec2 outNormals;
#if UF_DEFERRED_SAMPLING
#if DEFERRED_SAMPLING
layout (location = 2) out vec2 outUvs;
#else
layout (location = 2) out vec4 outAlbedo;
#endif
#define PI 3.1415926536f
vec2 encodeNormals( vec3 n ) {
// return n.xy / sqrt(n.z*8+8) + 0.5;
#define kPI 3.1415926536f
return (vec2(atan(n.y,n.x)/kPI, n.z)+1.0)*0.5;
return (vec2(atan(n.y,n.x)/PI, n.z)+1.0)*0.5;
}
float wrap( float i ) {
return fract(i);
@ -68,24 +70,24 @@ vec2 wrap( vec2 uv ) {
return vec2( wrap( uv.x ), wrap( uv.y ) );
}
float mipLevel( in vec2 uv ) {
vec2 dx_vtc = dFdx(uv);
vec2 dy_vtc = dFdy(uv);
const vec2 dx_vtc = dFdx(uv);
const vec2 dy_vtc = dFdy(uv);
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
}
bool validTextureIndex( int textureIndex ) {
return 0 <= textureIndex && textureIndex < textures.length();
}
void main() {
float mip = mipLevel(inUv.xy);
vec2 uv = wrap(inUv.xy);
vec4 C = vec4(0, 0, 0, 0);
vec3 P = inPosition;
const float mip = mipLevel(inUv.xy);
const vec2 uv = wrap(inUv.xy);
const vec3 P = inPosition;
vec3 N = inNormal;
#if UF_DEFERRED_SAMPLING
vec4 A = vec4(0, 0, 0, 0);
#if DEFERRED_SAMPLING
vec4 outAlbedo = vec4(0,0,0,0);
#endif
#if !UF_DEFERRED_SAMPLING || UF_CAN_DISCARD
int materialId = int(inId.y);
#if !DEFERRED_SAMPLING || CAN_DISCARD
const int materialId = int(inId.y);
Material material = materials[materialId];
float M = material.factorMetallic;
@ -93,37 +95,39 @@ void main() {
float AO = material.factorOcclusion;
// sample albedo
bool useAtlas = validTextureIndex( material.indexAtlas );
const bool useAtlas = validTextureIndex( material.indexAtlas );
Texture textureAtlas;
if ( useAtlas ) textureAtlas = textures[material.indexAtlas];
if ( !validTextureIndex( material.indexAlbedo ) ) discard; {
Texture t = textures[material.indexAlbedo];
C = textureLod( samplerTextures[(useAtlas) ? textureAtlas.index : t.index], (useAtlas) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
A = textureLod( samplerTextures[(useAtlas) ? textureAtlas.index : t.index], (useAtlas) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
// alpha mode OPAQUE
if ( material.modeAlpha == 0 ) {
C.a = 1;
A.a = 1;
// alpha mode BLEND
} else if ( material.modeAlpha == 1 ) {
// alpha mode MASK
} else if ( material.modeAlpha == 2 ) {
if ( C.a < abs(material.factorAlphaCutoff) ) discard;
C.a = 1;
if ( A.a < abs(material.factorAlphaCutoff) ) discard;
A.a = 1;
}
if ( C.a == 0 ) discard;
if ( A.a == 0 ) discard;
}
#if 1
#if USE_LIGHTMAP
if ( validTextureIndex( material.indexLightmap ) ) {
#if UF_DEFERRED_SAMPLING
#if DEFERRED_SAMPLING
outUvs = inSt;
#else
Texture t = textures[material.indexLightmap];
C *= textureLod( samplerTextures[t.index], inSt, mip );
const float gamma = 1.6;
const vec4 L = pow(textureLod( samplerTextures[t.index], inSt, mip ), vec4(1.0 / gamma));
A *= L;
#endif
}
#endif
#endif
#if !UF_DEFERRED_SAMPLING
#if !DEFERRED_SAMPLING
// sample normal
if ( validTextureIndex( material.indexNormal ) ) {
Texture t = textures[material.indexNormal];
@ -131,9 +135,9 @@ void main() {
}
#if 0
// sample metallic/roughness
if ( validTextureIndex( material.indexNormal ) ) {
Texture t = textures[material.indexNormal];
vec4 sampled = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
if ( validTextureIndex( material.indexMetallicRoughness ) ) {
Texture t = textures[material.indexMetallicRoughness];
const vec4 sampled = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
M = sampled.b;
R = sampled.g;
}
@ -144,7 +148,7 @@ void main() {
AO = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv ).r;
}
#endif
outAlbedo = C * inColor;
outAlbedo = A * inColor;
#else
outUvs = wrap(inUv.xy);
#endif

View File

@ -1,7 +1,9 @@
#version 450
#define UF_DEFERRED_SAMPLING 0
#define UF_CAN_DISCARD 1
#define DEFERRED_SAMPLING 0
#define USE_LIGHTMAP 1
#define PI 3.1415926536f
layout (constant_id = 0) const uint TEXTURES = 1;
@ -56,7 +58,7 @@ layout (binding = 11, rgba8) uniform volatile coherent image3D voxelAlbedo;
layout (location = 0) out uvec2 outId;
layout (location = 1) out vec2 outNormals;
#if UF_DEFERRED_SAMPLING
#if DEFERRED_SAMPLING
layout (location = 2) out vec2 outUvs;
#else
layout (location = 2) out vec4 outAlbedo;
@ -64,8 +66,7 @@ layout (location = 1) out vec2 outNormals;
vec2 encodeNormals( vec3 n ) {
// return n.xy / sqrt(n.z*8+8) + 0.5;
#define kPI 3.1415926536f
return (vec2(atan(n.y,n.x)/kPI, n.z)+1.0)*0.5;
return (vec2(atan(n.y,n.x)/PI, n.z)+1.0)*0.5;
}
float wrap( float i ) {
return fract(i);
@ -74,8 +75,8 @@ vec2 wrap( vec2 uv ) {
return vec2( wrap( uv.x ), wrap( uv.y ) );
}
float mipLevel( in vec2 uv ) {
vec2 dx_vtc = dFdx(uv);
vec2 dy_vtc = dFdy(uv);
const vec2 dx_vtc = dFdx(uv);
const vec2 dy_vtc = dFdy(uv);
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
}
bool validTextureIndex( int textureIndex ) {
@ -83,51 +84,57 @@ bool validTextureIndex( int textureIndex ) {
}
void main() {
vec3 P = inPosition;
const vec3 P = inPosition;
if ( !(abs(P.x) < 1.0 && abs(P.y) < 1 && abs(P.z) < 1) ) discard;
vec4 C = vec4(0, 0, 0, 0);
vec3 N = inNormal;
vec2 uv = wrap(inUv.xy);
float mip = mipLevel(inUv.xy);
vec4 A = vec4(0, 0, 0, 0);
const vec3 N = inNormal;
const vec2 uv = wrap(inUv.xy);
const float mip = mipLevel(inUv.xy);
const int materialId = int(inId.y);
const Material material = materials[materialId];
int materialId = int(inId.y);
Material material = materials[materialId];
float M = material.factorMetallic;
float R = material.factorRoughness;
float AO = material.factorOcclusion;
const float M = material.factorMetallic;
const float R = material.factorRoughness;
const float AO = material.factorOcclusion;
// sample albedo
bool useAtlas = validTextureIndex( material.indexAtlas );
const bool useAtlas = validTextureIndex( material.indexAtlas );
Texture textureAtlas;
if ( useAtlas ) textureAtlas = textures[material.indexAtlas];
if ( !validTextureIndex( material.indexAlbedo ) ) discard; {
Texture t = textures[material.indexAlbedo];
C = textureLod( samplerTextures[(useAtlas) ? textureAtlas.index : t.index], (useAtlas) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
A = textureLod( samplerTextures[(useAtlas) ? textureAtlas.index : t.index], (useAtlas) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip );
// alpha mode OPAQUE
if ( material.modeAlpha == 0 ) {
C.a = 1;
A.a = 1;
// alpha mode BLEND
} else if ( material.modeAlpha == 1 ) {
// alpha mode MASK
} else if ( material.modeAlpha == 2 ) {
if ( C.a < abs(material.factorAlphaCutoff) ) discard;
C.a = 1;
if ( A.a < abs(material.factorAlphaCutoff) ) discard;
A.a = 1;
}
if ( C.a == 0 ) discard;
if ( A.a == 0 ) discard;
}
#if USE_LIGHTMAP
if ( validTextureIndex( material.indexLightmap ) ) {
#if DEFERRED_SAMPLING
outUvs = inSt;
#else
Texture t = textures[material.indexLightmap];
const float gamma = 1.6;
const vec4 L = pow(textureLod( samplerTextures[t.index], inSt, mip ), vec4(1.0 / gamma));
A *= L;
#endif
}
#if UF_DEFERRED_SAMPLING
vec4 outAlbedo;
#else
vec2 outUvs;
#endif
/*vec4*/ outAlbedo = C * inColor;
/*uvec2*/ outId = uvec2(inId.w+1, inId.y+1);
/*vec2*/ outNormals = encodeNormals( normalize( N ) );
/*vec2*/ outUvs = wrap(inUv.xy);
const vec4 outAlbedo = A * inColor;
const uvec2 outId = uvec2(inId.w+1, inId.y+1);
const vec2 outNormals = encodeNormals( normalize( N ) );
const vec2 outUvs = wrap(inUv.xy);
imageStore(voxelID, ivec3(P * imageSize(voxelID)), uvec4(outId, 0, 0));
imageStore(voxelNormal, ivec3(P * imageSize(voxelNormal)), vec4(outNormals, 0, 0));

View File

@ -22,7 +22,11 @@ namespace uf {
ext::json::Value payload;
double timeout;
};
struct {
size_t mtime = 0;
bool enabled = false;
std::string source = "";
} hotReload;
struct {
std::unordered_map<std::string, std::vector<size_t>> bound;
std::vector<Queued> queue;

View File

@ -175,20 +175,19 @@ void uf::ObjectBehavior::destroy( uf::Object& self ) {
void uf::ObjectBehavior::tick( uf::Object& self ) {
// listen for metadata file changes
#if UF_ENTITY_METADATA_USE_JSON
auto& metadata = this->getComponent<uf::Serializer>();
auto& metadataJson = this->getComponent<uf::Serializer>();
#if !UF_ENV_DREAMCAST
if ( metadata["system"]["hot reload"]["enabled"].as<bool>() ) {
size_t mtime = uf::io::mtime( metadata["system"]["source"].as<std::string>() );
if ( metadata["system"]["hot reload"]["mtime"].as<size_t>() < mtime ) {
std::cout << "File reload detected: " << ": " << metadata["system"]["source"].as<std::string>() << ", " << metadata["system"]["hot reload"]["mtime"] << " -> " << mtime << std::endl;
metadata["system"]["hot reload"]["mtime"] = mtime;
if ( metadataJson["system"]["hot reload"]["enabled"].as<bool>() ) {
size_t mtime = uf::io::mtime( metadataJson["system"]["source"].as<std::string>() );
if ( metadataJson["system"]["hot reload"]["mtime"].as<size_t>() < mtime ) {
metadataJson["system"]["hot reload"]["mtime"] = mtime;
this->reload();
}
}
#endif
// Call queued hooks
{
auto& queue = metadata["system"]["hooks"]["queue"];
auto& queue = metadataJson["system"]["hooks"]["queue"];
if ( !uf::Object::timer.running() ) uf::Object::timer.start();
float curTime = uf::Object::timer.elapsed().asDouble();
uf::Serializer newQueue = ext::json::array();
@ -202,11 +201,18 @@ void uf::ObjectBehavior::tick( uf::Object& self ) {
else newQueue.emplace_back(member);
});
}
if ( ext::json::isObject( metadata ) ) queue = newQueue;
if ( ext::json::isObject( metadataJson ) ) queue = newQueue;
}
#else
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
if ( metadata.hotReload.enabled ) {
size_t mtime = uf::io::mtime( metadata.hotReload.source );
if ( metadata.hotReload.mtime < mtime ) {
metadata.hotReload.mtime = mtime;
this->reload();
}
}
auto& queue = metadata.hooks.queue;
if ( !uf::Object::timer.running() ) uf::Object::timer.start();
float curTime = uf::Object::timer.elapsed().asDouble();

View File

@ -129,8 +129,8 @@ void uf::GltfBehavior::tick( uf::Object& self ) {
auto& storageBuffer = *graphic.getStorageBuffer("Models");
graphic.updateBuffer( (void*) instances.data(), instances.size() * sizeof(pod::Matrix4f), graph.instanceBufferIndex /*storageBuffer*/ );
}
if ( graphic.material.hasShader("geometry", "svogi") ) {
auto& shader = graphic.material.getShader("geometry", "svogi");
if ( graphic.material.hasShader("geometry", "vxgi") ) {
auto& shader = graphic.material.getShader("geometry", "vxgi");
pod::Vector3f min = uf::vector::decode( graph.metadata["extents"]["min"], pod::Vector3f{} );
pod::Vector3f max = uf::vector::decode( graph.metadata["extents"]["max"], pod::Vector3f{} );

View File

@ -93,7 +93,13 @@ bool uf::Object::load( const std::string& f, bool inheritRoot ) {
json["root"] = uf::io::directory(filename);
json["source"] = filename;
#if UF_ENTITY_METADATA_USE_JSON
json["hot reload"]["mtime"] = uf::io::mtime(filename) + 10;
#else
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
metadata.hotReload.source = filename;
metadata.hotReload.mtime = uf::io::mtime(filename) + 10;
#endif
return this->load(json);
}
@ -118,7 +124,7 @@ bool uf::Object::reload( bool hard ) {
transform.reference = reference;
}
payload["new"] = metadata;
UF_DEBUG_MSG("Updated metadata for " << uf::string::toString( this ));
// UF_DEBUG_MSG("Updated metadata for " << uf::string::toString( this ));
this->queueHook("object:Reload.%UID%", payload);
return true;
}
@ -160,7 +166,14 @@ bool uf::Object::load( const uf::Serializer& _json ) {
if ( ext::json::isNull( json[key] ) )
json[key] = value;
});
#if UF_ENTITY_METADATA_USE_JSON
json["hot reload"]["enabled"] = json["system"]["hot reload"]["enabled"];
#else
{
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
metadata.hotReload.enabled = json["system"]["hot reload"]["enabled"].as<bool>();
}
#endif
// Basic entity information
// Set name
this->m_name = json["name"].is<std::string>() ? json["name"].as<std::string>() : json["type"].as<std::string>();
@ -325,7 +338,14 @@ bool uf::Object::load( const uf::Serializer& _json ) {
json["root"] = uf::io::directory(filename);
json["source"] = filename;
json["hot reload"]["mtime"] = uf::io::mtime( filename ) + 10;
#if UF_ENTITY_METADATA_USE_JSON
json["hot reload"]["mtime"] = uf::io::mtime(filename) + 10;
#else
{
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
metadata.hotReload.mtime = uf::io::mtime(filename) + 10;
}
#endif
if ( this->loadChildUid(json) == -1 ) continue;
}
@ -363,7 +383,15 @@ uf::Object& uf::Object::loadChild( const std::string& f, bool initialize ) {
json["root"] = uf::io::directory(filename);
json["source"] = filename;
json["hot reload"]["mtime"] = uf::io::mtime( filename ) + 10;
#if UF_ENTITY_METADATA_USE_JSON
json["hot reload"]["mtime"] = uf::io::mtime(filename) + 10;
#else
{
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
metadata.hotReload.mtime = uf::io::mtime(filename) + 10;
}
#endif
return this->loadChild(json, initialize);
}
uf::Object& uf::Object::loadChild( const uf::Serializer& _json, bool initialize ) {

View File

@ -54,7 +54,7 @@ uf::Scene& uf::scene::loadScene( const std::string& name, const std::string& fil
*/
target = uf::string::lowercase( target );
scene->load(filename != "" ? filename : "./scenes/" + target + "/scene.json");
if ( uf::renderer::settings::experimental::deferredMode == "svogi" ) {
if ( uf::renderer::settings::experimental::deferredMode == "vxgi" ) {
uf::instantiator::bind( "VoxelizerBehavior", *scene );
}
scene->initialize();
@ -64,7 +64,7 @@ uf::Scene& uf::scene::loadScene( const std::string& name, const uf::Serializer&
uf::Scene* scene = uf::instantiator::objects->has( name ) ? (uf::Scene*) &uf::instantiator::instantiate( name ) : new uf::Scene;
uf::scene::scenes.emplace_back( scene );
if ( data != "" ) scene->load(data);
if ( uf::renderer::settings::experimental::deferredMode == "svogi" ) {
if ( uf::renderer::settings::experimental::deferredMode == "vxgi" ) {
uf::instantiator::bind( "VoxelizerBehavior", *scene );
}
scene->initialize();

View File

@ -68,8 +68,8 @@ namespace {
}
#endif
}
// svogi pipeline
if ( uf::renderer::settings::experimental::deferredMode == "svogi" ) {
// vxgi pipeline
if ( uf::renderer::settings::experimental::deferredMode == "vxgi" ) {
std::string vertexShaderFilename = graph.metadata["shaders"]["vertex"].as<std::string>("/gltf/base.vert.spv");
std::string geometryShaderFilename = graph.metadata["shaders"]["geometry"].as<std::string>("/gltf/voxelize.geom.spv");
std::string fragmentShaderFilename = graph.metadata["shaders"]["fragment"].as<std::string>("/gltf/voxelize.frag.spv");
@ -79,21 +79,21 @@ namespace {
vertexShaderFilename = graph.metadata["flags"]["SKINNED"].as<bool>() ? "/gltf/skinned.instanced.vert.spv" : "/gltf/instanced.vert.spv";
} else if ( graph.metadata["flags"]["SKINNED"].as<bool>() ) vertexShaderFilename = "/gltf/skinned.vert.spv";
vertexShaderFilename = entity.grabURI( vertexShaderFilename, root );
graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX, "svogi");
graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX, "vxgi");
}
*/
if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) {
geometryShaderFilename = entity.grabURI( geometryShaderFilename, root );
graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY, "svogi");
graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY, "vxgi");
}
{
fragmentShaderFilename = entity.grabURI( fragmentShaderFilename, root );
graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, "svogi");
graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, "vxgi");
}
#if UF_USE_VULKAN
/*
{
auto& shader = graphic.material.getShader("vertex", "svogi");
auto& shader = graphic.material.getShader("vertex", "vxgi");
struct SpecializationConstant {
uint32_t passes = 6;
};
@ -106,7 +106,7 @@ namespace {
*/
/*
if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) {
auto& shader = graphic.material.getShader("geometry", "svogi");
auto& shader = graphic.material.getShader("geometry", "vxgi");
pod::Vector3f min = uf::vector::decode( graph.metadata["extents"]["min"], pod::Vector3f{} );
pod::Vector3f max = uf::vector::decode( graph.metadata["extents"]["max"], pod::Vector3f{} );
@ -121,7 +121,7 @@ namespace {
}
*/
{
auto& shader = graphic.material.getShader("fragment", "svogi");
auto& shader = graphic.material.getShader("fragment", "vxgi");
struct SpecializationConstant {
uint32_t textures = 1;
};
@ -164,7 +164,7 @@ namespace {
graphic.material.samplers.emplace_back( sampler );
}
// bind scene's voxel texture
if ( uf::renderer::settings::experimental::deferredMode == "svogi" ) {
if ( uf::renderer::settings::experimental::deferredMode == "vxgi" ) {
auto& scene = uf::scene::getCurrentScene();
auto& sceneTextures = scene.getComponent<pod::SceneTextures>();
graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.id);

View File

@ -198,10 +198,10 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
blitter.initialize( this->getName() );
blitter.initializeMesh( mesh );
if ( ext::vulkan::settings::experimental::deferredMode == "svogi" ) {
if ( ext::vulkan::settings::experimental::deferredMode == "vxgi" ) {
blitter.material.initializeShaders({
{uf::io::root+"/shaders/display/subpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT},
{uf::io::root+"/shaders/display/subpass.svogi.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT}
{uf::io::root+"/shaders/display/subpass.vxgi.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT}
});
} else {
blitter.material.initializeShaders({

View File

@ -37,10 +37,10 @@ ext::vulkan::GraphicDescriptor ext::vulkan::RenderTargetRenderMode::bindGraphicD
descriptor.parse(metadata["descriptor"]);
std::string type = metadata["type"].as<std::string>();
std::string target = metadata["target"].as<std::string>();
if ( pass == 0 && type == "svogi" ) {
if ( pass == 0 && type == "vxgi" ) {
descriptor.cullMode = VK_CULL_MODE_NONE;
descriptor.depth.test = false;
descriptor.pipeline = "svogi";
descriptor.pipeline = "vxgi";
} else if ( type == "depth" ) {
descriptor.cullMode = VK_CULL_MODE_NONE;
}
@ -63,7 +63,7 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) {
if ( subpasses == 0 ) subpasses = 1;
renderTarget.device = &device;
for ( size_t currentPass = 0; currentPass < subpasses; ++currentPass ) {
if ( type == "depth" /*|| type == "svogi"*/ ) {
if ( type == "depth" || type == "vxgi" ) {
struct {
size_t depth;
} attachments;
@ -341,7 +341,7 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) {
{uf::io::root+"/shaders/display/renderTarget.frag.spv", ext::vulkan::enums::Shader::FRAGMENT}
});
}
if ( metadata["type"].as<std::string>() == "svogi" ) {
if ( metadata["type"].as<std::string>() == "vxgi" ) {
auto& scene = uf::scene::getCurrentScene();
auto& shader = blitter.material.getShader("compute");
@ -409,7 +409,7 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) {
void ext::vulkan::RenderTargetRenderMode::tick() {
ext::vulkan::RenderMode::tick();
if ( metadata["type"].as<std::string>() == "svogi" ) {
if ( metadata["type"].as<std::string>() == "vxgi" ) {
if ( ext::vulkan::states::resized ) {
renderTarget.initialize( *renderTarget.device );
if ( blitter.process ) blitter.getPipeline().update( blitter );

View File

@ -154,9 +154,10 @@ void ext::LightBehavior::tick( uf::Object& self ) {
metadata.type = metadataJson["light"]["type"].as<size_t>();
} else if ( metadataJson["light"]["type"].is<std::string>() ) {
std::string lightType = metadataJson["light"]["type"].as<std::string>();
if ( lightType == "point" ) metadata.type = 0;
else if ( lightType == "spot" ) metadata.type = 1;
if ( lightType == "point" ) metadata.type = 1;
else if ( lightType == "spot" ) metadata.type = 2;
}
if ( metadataJson["light"]["dynamic"].as<bool>() ) metadata.type = -metadata.type;
}
#endif
#if 0
@ -223,7 +224,7 @@ void ext::LightBehavior::tick( uf::Object& self ) {
if ( !metadata.renderer.external ) {
auto& camera = this->getComponent<uf::Camera>();
// omni light
if ( metadata.shadows && metadata.type == 0 ) {
if ( metadata.shadows && std::abs(metadata.type) == 1 ) {
auto transform = camera.getTransform();
std::vector<pod::Quaternion<>> rotations = {
uf::quaternion::axisAngle( { 0, 1, 0 }, 0 * 1.57079633 ),

View File

@ -19,7 +19,7 @@ namespace ext {
float power = 0.0f;
float bias = 0.0f;
bool shadows = false;
size_t type = 0;
int32_t type = 1;
struct {
std::string mode = "in-range";
float limiter = 0.0f;

View File

@ -210,6 +210,8 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) {
metadataJson["light"]["should"] = metadata.light.enabled;
metadataJson["light"]["ambient"] = uf::vector::encode( metadata.light.ambient );
metadataJson["light"]["specular"] = uf::vector::encode( metadata.light.specular );
metadataJson["light"]["exposure"] = metadata.light.exposure;
metadataJson["light"]["gamma"] = metadata.light.gamma;
metadataJson["light"]["fog"]["color"] = uf::vector::encode( metadata.fog.color );
metadataJson["light"]["fog"]["step scale"] = metadata.fog.stepScale;
metadataJson["light"]["fog"]["absorbtion"] = metadata.fog.absorbtion;
@ -229,6 +231,8 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) {
metadata.light.updateThreshold = metadataJson["system"]["config"]["engine"]["scenes"]["lights"]["update threshold"].as<size_t>();
metadata.light.ambient = uf::vector::decode( metadataJson["light"]["ambient"], pod::Vector4f{ 1, 1, 1, 1 } );
metadata.light.specular = uf::vector::decode( metadataJson["light"]["specular"], pod::Vector4f{ 1, 1, 1, 1 } );
metadata.light.exposure = metadataJson["light"]["exposure"].as<float>(1.0f);
metadata.light.gamma = metadataJson["light"]["gamma"].as<float>(2.2f);
metadata.fog.color = uf::vector::decode( metadataJson["light"]["fog"]["color"], pod::Vector3f{ 1, 1, 1 } );
metadata.fog.stepScale = metadataJson["light"]["fog"]["step scale"].as<float>();
metadata.fog.absorbtion = metadataJson["light"]["fog"]["absorbtion"].as<float>();
@ -238,11 +242,14 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) {
metadata.fog.density.threshold = metadataJson["light"]["fog"]["density"]["threshold"].as<float>();
metadata.fog.density.multiplier = metadataJson["light"]["fog"]["density"]["multiplier"].as<float>();
metadata.fog.density.scale = metadataJson["light"]["fog"]["density"]["scale"].as<float>();
UF_DEBUG_MSG( metadata.light.exposure << " | " << metadata.light.gamma );
#if UF_USE_OPENGL_FIXED_FUNCTION
uf::renderer::states::rebuild = true;
#endif
};
this->addHook( "object:UpdateMetadata.%UID%", metadata.deserialize);
this->addHook( "object:Reload.%UID%", metadata.deserialize);
metadata.deserialize();
}
void ext::ExtSceneBehavior::tick( uf::Object& self ) {
@ -361,6 +368,8 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) {
if ( !metadata.light.shadowSamples ) metadata.light.shadowSamples = metadataJson["system"]["config"]["engine"]["scenes"]["lights"]["shadow samples"].as<size_t>();
if ( !metadata.light.shadowThreshold ) metadata.light.shadowThreshold = metadataJson["system"]["config"]["engine"]["scenes"]["lights"]["shadow threshold"].as<size_t>();
if ( !metadata.light.updateThreshold ) metadata.light.updateThreshold = metadataJson["system"]["config"]["engine"]["scenes"]["lights"]["update threshold"].as<size_t>();
if ( !metadata.light.exposure ) metadata.light.exposure = metadataJson["light"]["exposure"].as<float>(1.0f);
if ( !metadata.light.gamma ) metadata.light.gamma = metadataJson["light"]["gamma"].as<float>(2.2f);
#endif
/* Update lights */ if ( metadata.light.enabled ) {
ext::ExtSceneBehavior::bindBuffers( *this );
@ -477,8 +486,8 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const std::string& re
alignas(4) uint32_t msaa;
alignas(4) uint32_t poissonSamples;
alignas(4) uint32_t padding1;
alignas(4) uint32_t padding2;
alignas(4) float gamma;
alignas(4) float exposure;
};
struct SpecializationConstant {
uint32_t maxTextures = 512;
@ -493,7 +502,7 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const std::string& re
float distance = 0;
float bias = 0;
bool shadows = false;
size_t type = 0;
int32_t type = 0;
};
std::vector<LightInfo> entities;
std::vector<pod::Graph*> graphs;
@ -561,6 +570,8 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const std::string& re
uniforms->ambient = metadata.light.ambient;
uniforms->msaa = ext::vulkan::settings::msaa;
uniforms->poissonSamples = shadowSamples;
uniforms->exposure = metadata.light.exposure;
uniforms->gamma = metadata.light.gamma;
uniforms->fog.color = metadata.fog.color;
uniforms->fog.color.w = metadata.fog.stepScale;
@ -586,7 +597,7 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const std::string& re
for ( auto& texture : graphic.material.textures ) previousTextures.emplace_back(texture.image);
graphic.material.textures.clear();
if ( uf::renderer::settings::experimental::deferredMode == "svogi" ) {
if ( uf::renderer::settings::experimental::deferredMode == "vxgi" ) {
graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.id); //this->getComponent<uf::renderer::Texture3D>());
graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.normal); //this->getComponent<uf::renderer::Texture3D>());
graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.uv); //this->getComponent<uf::renderer::Texture3D>());
@ -647,13 +658,13 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const std::string& re
max.z = std::max( max.z, graphMax.z );
}
min.x += floor(controllerTransform.position.x);
min.y -= floor(controllerTransform.position.y);
min.z -= floor(controllerTransform.position.z);
min.x += floor(controllerTransform.position.x );
min.y -= floor(controllerTransform.position.y );
min.z -= floor(controllerTransform.position.z );
max.x += floor(controllerTransform.position.x);
max.y -= floor(controllerTransform.position.y);
max.z -= floor(controllerTransform.position.z);
max.x += floor(controllerTransform.position.x );
max.y -= floor(controllerTransform.position.y );
max.z -= floor(controllerTransform.position.z );
uniforms->matrices.ortho = /*uf::matrix::translate( uf::matrix::identity(), controllerTransform.position ) **/ uf::matrix::ortho( min.x, max.x, min.y, max.y, min.z, max.z );

View File

@ -27,6 +27,8 @@ namespace ext {
size_t updateThreshold = 4;
pod::Vector4f ambient = {0,0,0,1};
pod::Vector4f specular = {1,1,1,1};
float exposure = 1.0f;
float gamma = 1.0f;
} light;
struct {
pod::Vector3f color = {1,1,1};

View File

@ -20,12 +20,18 @@ UF_BEHAVIOR_REGISTER_CPP(ext::VoxelizerBehavior)
void ext::VoxelizerBehavior::initialize( uf::Object& self ) {
#if UF_USE_VULKAN
auto& metadata = this->getComponent<ext::VoxelizerBehavior::Metadata>();
auto& scene = uf::scene::getCurrentScene();
auto& sceneMetadataJson = scene.getComponent<uf::Serializer>();
auto& sceneTextures = this->getComponent<pod::SceneTextures>();
// initialize voxel map
{
if ( metadata.voxelSize.x == 0 ) metadata.voxelSize.x = 256;
if ( metadata.voxelSize.y == 0 ) metadata.voxelSize.y = 256;
if ( metadata.voxelSize.z == 0 ) metadata.voxelSize.z = 256;
const uint32_t DEFAULT_VOXEL_SIZE = sceneMetadataJson["system"]["config"]["engine"]["scenes"]["vxgi"]["size"].as<uint32_t>();
const float DEFAULT_VOXELIZE_LIMITER = sceneMetadataJson["system"]["config"]["engine"]["scenes"]["vxgi"]["limiter"].as<float>();
if ( metadata.voxelSize.x == 0 ) metadata.voxelSize.x = DEFAULT_VOXEL_SIZE;
if ( metadata.voxelSize.y == 0 ) metadata.voxelSize.y = DEFAULT_VOXEL_SIZE;
if ( metadata.voxelSize.z == 0 ) metadata.voxelSize.z = DEFAULT_VOXEL_SIZE;
if ( metadata.renderer.limiter == 0 ) metadata.renderer.limiter = DEFAULT_VOXELIZE_LIMITER;
std::vector<uint8_t> empty(metadata.voxelSize.x * metadata.voxelSize.y * metadata.voxelSize.z * sizeof(uint8_t) * 4);
@ -43,16 +49,16 @@ void ext::VoxelizerBehavior::initialize( uf::Object& self ) {
// if ( metadata.fragmentSize.y == 0 ) metadata.fragmentSize.y = metadata.voxelSize.y * 2;
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
metadata.renderModeName = "SVOGI:" + std::to_string((int) this->getUid());
metadata.renderModeName = "VXGI:" + std::to_string((int) this->getUid());
uf::renderer::addRenderMode( &renderMode, metadata.renderModeName );
renderMode.metadata["type"] = "svogi";
renderMode.metadata["type"] = "vxgi";
renderMode.metadata["samples"] = 1;
renderMode.blitter.device = &ext::vulkan::device;
renderMode.width = metadata.fragmentSize.x;
renderMode.height = metadata.fragmentSize.y;
#if COMP_SHADER_USED
renderMode.metadata["shaders"]["compute"] = "/shaders/display/svogi.comp.spv";
renderMode.metadata["shaders"]["compute"] = "/shaders/display/vxgi.comp.spv";
renderMode.blitter.descriptor.renderMode = metadata.renderModeName;
renderMode.blitter.descriptor.subpass = -1;
renderMode.blitter.process = true;
@ -126,9 +132,18 @@ void ext::VoxelizerBehavior::tick( uf::Object& self ) {
auto& metadata = this->getComponent<ext::VoxelizerBehavior::Metadata>();
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
renderMode.setTarget("");
if ( renderMode.executed && !metadata.initialized ) {
// renderMode.execute = false;
metadata.initialized = true;
if ( renderMode.executed ) {
if ( !metadata.initialized ) metadata.initialized = true;
if ( metadata.renderer.limiter > 0 ) {
if ( metadata.renderer.timer > metadata.renderer.limiter ) {
metadata.renderer.timer = 0;
renderMode.execute = true;
} else {
metadata.renderer.timer = metadata.renderer.timer + uf::physics::time::delta;
renderMode.execute = false;
}
}
}
#if COMP_SHADER_USED
auto& scene = uf::scene::getCurrentScene();

View File

@ -19,12 +19,16 @@ namespace ext {
pod::Vector3ui fragmentSize = { 0, 0 };
pod::Vector3ui voxelSize = { 256, 256, 256 };
pod::Vector3ui dispatchSize = { 8, 8, 8 };
std::string renderModeName = "SVOGI";
std::string renderModeName = "VXGI";
struct {
pod::Vector3f min = {};
pod::Vector3f max = {};
pod::Matrix4f matrix = uf::matrix::identity();
} extents;
struct {
float limiter = 0.0f;
float timer = 0.0f;
} renderer;
bool initialized = false;
};
}