engine/bin/data/shaders/display/vxgi.comp.glsl
2021-04-27 00:00:00 -05:00

359 lines
10 KiB
GLSL

#version 450
#extension GL_EXT_samplerless_texture_functions : require
layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
#define MULTISAMPLING 1
#define RAY_MARCH_FOG 1
#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;
struct Matrices {
mat4 view[2];
mat4 projection[2];
mat4 iView[2];
mat4 iProjection[2];
mat4 iProjectionView[2];
mat4 voxel;
};
struct Space {
vec3 eye;
vec3 world;
} position, normal, view;
struct Fog {
vec3 color;
float stepScale;
vec3 offset;
float densityScale;
float densityThreshold;
float densityMultiplier;
float absorbtion;
float padding1;
vec2 range;
float padding2;
float padding3;
};
struct Mode {
uint type;
uint scalar;
vec2 padding;
vec4 parameters;
};
struct Light {
vec3 position;
float radius;
vec3 color;
float power;
int type;
int mapIndex;
float depthBias;
float padding;
mat4 view;
mat4 projection;
};
struct Material {
vec4 colorBase;
vec4 colorEmissive;
float factorMetallic;
float factorRoughness;
float factorOcclusion;
float factorAlphaCutoff;
int indexAlbedo;
int indexNormal;
int indexEmissive;
int indexOcclusion;
int indexMetallicRoughness;
int indexAtlas;
int indexLightmap;
int modeAlpha;
};
struct Texture {
int index;
int samp;
int remap;
float blend;
vec4 lerp;
};
struct DrawCall {
int materialIndex;
uint materials;
int textureIndex;
uint textures;
};
layout (binding = 4) uniform UBO {
Matrices matrices;
Mode mode;
Fog fog;
uint lights;
uint materials;
uint textures;
uint drawCalls;
vec3 ambient;
float kexp;
uint msaa;
uint poissonSamples;
float gamma;
float exposure;
} ubo;
layout (std140, binding = 5) readonly buffer Lights {
Light lights[];
};
layout (std140, binding = 6) readonly buffer Materials {
Material materials[];
};
layout (std140, binding = 7) readonly buffer Textures {
Texture textures[];
};
layout (std140, binding = 8) readonly buffer DrawCalls {
DrawCall drawCalls[];
};
layout (binding = 9, rg16ui) uniform volatile coherent uimage3D voxelID;
layout (binding = 10, rg16f) uniform volatile coherent image3D voxelNormal;
layout (binding = 11, rg16f) uniform volatile coherent image3D voxelUv;
layout (binding = 12, rgba8) uniform volatile coherent image3D voxelAlbedo;
layout (binding = 13) uniform sampler3D samplerNoise;
layout (binding = 14) uniform samplerCube samplerSkybox;
layout (binding = 15) uniform sampler2D samplerTextures[TEXTURES];
// 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);
}
// Single term for separable Schlick-GGX below.
float gaSchlickG1(float cosTheta, float k) {
return cosTheta / (cosTheta * (1.0 - k) + k);
}
// 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);
}
float random(vec3 seed, int i){
return fract(sin(dot(vec4(seed,i), vec4(12.9898,78.233,45.164,94.673))) * 43758.5453);
}
// 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;
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 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 ) );
}
const uvec2 ID = uvec2(imageLoad(voxelID, ivec3(tUvw) ).xy);
if ( ID.x == 0 || ID.y == 0 ) {
imageStore(voxelAlbedo, ivec3(tUvw), vec4(0));
return;
}
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
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];
A = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv );
}
// OPAQUE
if ( material.modeAlpha == 0 ) {
A.a = 1;
// BLEND
} else if ( material.modeAlpha == 1 ) {
// MASK
} 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 litFactor = 1.0;
if ( 0 <= material.indexLightmap ) {
fragColor = A.rgb + ubo.ambient.rgb * (1 - AO);
} else {
fragColor = A.rgb * ubo.ambient.rgb * (1 - AO);
}
{
const float R = material.factorRoughness * 2.0f;
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 ) {
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;
const float cosLi = max(0.0, dot(N, Li));
const vec3 Lh = normalize(Li + Lo);
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;
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;
if ( !(0 <= material.indexLightmap) ) fragColor.rgb += (diffuseBRDF) * Lr * cosLi;
litFactor += light.power * La * Ls;
}
}
imageStore(voxelAlbedo, ivec3(tUvw), vec4(fragColor.rgb, 1));
}