2023-01-12 15:00:14 -05:00
uniform int framemod8;
const vec2[8] offsets = vec2[8](vec2(1./8.,-3./8.),
vec2(-1.,3.)/8.,
vec2(5.0,1.)/8.,
vec2(-3,-5.)/8.,
vec2(-5.,5.)/8.,
vec2(-7.,-1.)/8.,
vec2(3,7.)/8.,
vec2(7.,-7.)/8.);
2023-04-16 16:18:26 -04:00
vec3 mix_vec3(vec3 X, vec3 Y, float A){
return X * (1.0 - A) + Y * A;
2023-01-12 15:00:14 -05:00
}
2023-07-25 22:48:08 -04:00
2023-04-16 16:18:26 -04:00
float mix_float(float X, float Y, float A){
return X * (1.0 - A) + Y * A;
2023-01-12 15:00:14 -05:00
}
2023-07-25 22:48:08 -04:00
2023-04-16 16:18:26 -04:00
float square(float x){
return x*x;
2023-01-12 15:00:14 -05:00
}
float invLinZ (float lindepth){
return -((2.0*near/lindepth)-far-near)/(far-near);
}
2023-07-25 22:48:08 -04:00
2023-01-12 15:00:14 -05:00
vec3 toClipSpace3(vec3 viewSpacePosition) {
return projMAD(gbufferProjection, viewSpacePosition) / -viewSpacePosition.z * 0.5 + 0.5;
}
2023-07-25 22:48:08 -04:00
2023-01-12 15:00:14 -05:00
float linZ(float depth) {
return (2.0 * near) / (far + near - depth * (far - near));
// l = (2*n)/(f+n-d(f-n))
// f+n-d(f-n) = 2n/l
// -d(f-n) = ((2n/l)-f-n)
// d = -((2n/l)-f-n)/(f-n)
}
2023-04-16 16:18:26 -04:00
void frisvad(in vec3 n, out vec3 f, out vec3 r){
if(n.z < -0.9) {
f = vec3(0.,-1,0);
r = vec3(-1, 0, 0);
} else {
float a = 1./(1.+n.z);
float b = -n.x*n.y*a;
f = vec3(1. - n.x*n.x*a, b, -n.x) ;
r = vec3(b, 1. - n.y*n.y*a , -n.y);
}
}
2023-07-25 22:48:08 -04:00
2023-04-16 16:18:26 -04:00
mat3 CoordBase(vec3 n){
vec3 x,y;
frisvad(n,x,y);
return mat3(x,y,n);
}
2023-07-25 22:48:08 -04:00
2023-04-16 16:18:26 -04:00
float unpackRoughness(float x){
float r = 1.0 - x;
return clamp(r*r,0,1);
}
2023-07-25 22:48:08 -04:00
vec2 R2_Sample(int n){
2023-04-16 16:18:26 -04:00
vec2 alpha = vec2(0.75487765, 0.56984026);
return fract(alpha * n);
}
2023-07-25 22:48:08 -04:00
vec3 rayTraceSpeculars(vec3 dir, vec3 position, float dither, float quality, bool hand, inout float reflectLength){
2023-01-12 15:00:14 -05:00
vec3 clipPosition = toClipSpace3(position);
float rayLength = ((position.z + dir.z * far*sqrt(3.)) > -near) ?
(-near -position.z) / dir.z : far*sqrt(3.);
vec3 direction = normalize(toClipSpace3(position+dir*rayLength)-clipPosition); //convert to clip space
direction.xy = normalize(direction.xy);
//get at which length the ray intersects with the edge of the screen
2023-07-26 22:03:04 -04:00
vec3 maxLengths = (step(0.0,direction)-clipPosition) / direction;
2023-01-12 15:00:14 -05:00
float mult = min(min(maxLengths.x,maxLengths.y),maxLengths.z);
vec3 stepv = direction * mult / quality*vec3(RENDER_SCALE,1.0);
vec3 spos = clipPosition*vec3(RENDER_SCALE,1.0) + stepv*dither;
2023-01-12 15:28:19 -05:00
float minZ = spos.z;
float maxZ = spos.z;
2023-01-12 15:00:14 -05:00
spos.xy += TAA_Offset*texelSize*0.5/RENDER_SCALE;
2023-07-26 22:03:04 -04:00
float depthcancleoffset = pow(1.0-(quality/reflection_quality),1.0);
2023-01-12 15:00:14 -05:00
2023-07-26 22:03:04 -04:00
float dist = 1.0 + clamp(position.z*position.z/50.0,0.0,2.0); // shrink sample size as distance increases
2023-01-12 15:00:14 -05:00
for (int i = 0; i <= int(quality); i++) {
vec2 testthing = hand ? spos.xy*texelSize : spos.xy/texelSize/4.0; // fix for ssr on hand
2023-04-16 16:18:26 -04:00
float sp = sqrt((texelFetch2D(colortex4,ivec2(testthing),0).a+0.1)/65000.0);
2023-01-12 15:00:14 -05:00
sp = invLinZ(sp);
2023-04-16 16:18:26 -04:00
2023-01-12 15:00:14 -05:00
if(sp <= max(maxZ,minZ) && sp >= min(maxZ,minZ) ) return vec3(spos.xy/RENDER_SCALE,sp);
spos += stepv;
//small bias
2023-06-24 18:30:46 -04:00
float biasamount = (0.0002 + 0.0015*pow(depthcancleoffset,5) ) / dist;
2023-06-22 14:58:16 -04:00
// float biasamount = 0.0002 / dist;
2023-01-12 15:00:14 -05:00
if(hand) biasamount = 0.01;
minZ = maxZ-biasamount / ld(spos.z);
maxZ += stepv.z;
2023-04-16 16:18:26 -04:00
reflectLength += 1.0 / quality; // for shit
2023-07-06 17:05:01 -04:00
2023-01-12 15:00:14 -05:00
}
2023-04-16 16:18:26 -04:00
return vec3(1.1);
2023-01-12 15:00:14 -05:00
}
2023-04-16 16:18:26 -04:00
2023-06-22 13:29:02 -04:00
float xonk_fma(float a,float b,float c){
return a * b + c;
}
2023-07-25 22:48:08 -04:00
2023-06-22 13:29:02 -04:00
//// thank you Zombye | the paper: https://ggx-research.github.io/publication/2023/06/09/publication-ggx.html
vec3 SampleVNDFGGX(
vec3 viewerDirection, // Direction pointing towards the viewer, oriented such that +Z corresponds to the surface normal
vec2 alpha, // Roughness parameter along X and Y of the distribution
vec2 xy // Pair of uniformly distributed numbers in [0, 1)
) {
2023-06-22 14:58:16 -04:00
// alpha *= alpha;
2023-06-22 13:29:02 -04:00
// Transform viewer direction to the hemisphere configuration
viewerDirection = normalize(vec3(alpha * viewerDirection.xy, viewerDirection.z));
// Sample a reflection direction off the hemisphere
const float tau = 6.2831853; // 2 * pi
float phi = tau * xy.x;
2023-07-12 19:29:20 -04:00
2023-06-22 14:58:16 -04:00
float cosTheta = xonk_fma(1.0 - xy.y, 1.0 + viewerDirection.z, -viewerDirection.z) ;
float sinTheta = sqrt(clamp(1.0 - cosTheta * cosTheta, 0.0, 1.0));
2023-07-12 19:29:20 -04:00
// xonk note, i dont know what im doing but this kinda does what i want so whatever
float attemptTailClamp = clamp(sinTheta,max(cosTheta-0.25,0), cosTheta);
float attemptTailClamp2 = clamp(cosTheta,max(sinTheta-0.25,0), sinTheta);
vec3 reflected = vec3(vec2(cos(phi), sin(phi)) * attemptTailClamp2, attemptTailClamp);
// vec3 reflected = vec3(vec2(cos(phi), sin(phi)) * sinTheta, cosTheta);
2023-06-22 13:29:02 -04:00
// Evaluate halfway direction
// This gives the normal on the hemisphere
2023-07-12 19:29:20 -04:00
vec3 halfway = reflected + viewerDirection;
2023-06-22 13:29:02 -04:00
// Transform the halfway direction back to hemiellispoid configuation
// This gives the final sampled normal
return normalize(vec3(alpha * halfway.xy, halfway.z));
}
2023-07-25 22:48:08 -04:00
float GGX(vec3 n, vec3 v, vec3 l, float r, float f0) {
2023-06-24 18:30:46 -04:00
r = max(pow(r,2.5), 0.0001);
2023-04-16 16:18:26 -04:00
vec3 h = l + v;
float hn = inversesqrt(dot(h, h));
float dotLH = clamp(dot(h,l)*hn,0.,1.);
float dotNH = clamp(dot(h,n)*hn,0.,1.) ;
float dotNL = clamp(dot(n,l),0.,1.);
float dotNHsq = dotNH*dotNH;
float denom = dotNHsq * r - dotNHsq + 1.;
float D = r / (3.141592653589793 * denom * denom);
2023-07-25 22:48:08 -04:00
float F = f0 + (1. - f0) * exp2((-5.55473*dotLH-6.98316)*dotLH);
2023-04-16 16:18:26 -04:00
float k2 = .25 * r;
return dotNL * D * F / (dotLH*dotLH*(1.0-k2)+k2);
}
2023-01-12 15:00:14 -05:00
2023-07-25 22:48:08 -04:00
void DoSpecularReflections(
2023-01-12 15:00:14 -05:00
inout vec3 Output,
2023-07-25 22:48:08 -04:00
vec3 FragPos, // toScreenspace(vec3(screenUV, depth)
vec3 WorldPos,
vec3 LightPos, // should be in world space
vec3 Noise, // xy = noise texure. z = simple blue noise
2023-04-16 16:18:26 -04:00
2023-07-25 22:48:08 -04:00
vec3 Normal, // normals in world space
float Roughness, // red channel of specular texture _S
float F0, // green channel of specular texture _S
2023-01-12 15:00:14 -05:00
2023-07-25 22:48:08 -04:00
vec3 Albedo,
vec3 Diffuse, // should contain the light color and NdotL. and maybe shadows.
2023-01-12 15:28:19 -05:00
2023-07-25 22:48:08 -04:00
float Lightmap, // in anything other than world0, this should be 1.0;
bool Hand // mask for the hand
){
vec3 Final_Reflection = Output;
vec3 Background_Reflection = Output;
vec3 Lightsource_Reflection = vec3(0.0);
vec4 SS_Reflections = vec4(0.0);
2023-06-22 16:47:25 -04:00
2023-07-25 22:48:08 -04:00
Lightmap = clamp((Lightmap-0.6)*5.0, 0.0,1.0);
2023-06-27 00:18:41 -04:00
2023-07-25 22:48:08 -04:00
Roughness = unpackRoughness(Roughness);
F0 = F0 == 0.0 ? 0.02 : F0;
2023-06-24 18:30:46 -04:00
2023-07-25 22:48:08 -04:00
// Roughness = 0.0;
// F0 = 0.9;
2023-04-16 16:18:26 -04:00
2023-07-25 22:48:08 -04:00
mat3 Basis = CoordBase(Normal);
vec3 ViewDir = -WorldPos*Basis;
2023-01-12 15:28:19 -05:00
2023-04-16 16:18:26 -04:00
#ifdef Rough_reflections
2023-07-25 22:48:08 -04:00
vec3 SamplePoints = SampleVNDFGGX(ViewDir, vec2(Roughness), fract(R2_Sample(frameCounter%40000) + Noise.xy));
if(Hand) SamplePoints = normalize(vec3(0.0,0.0,1.0));
2023-04-16 16:18:26 -04:00
#else
2023-07-25 22:48:08 -04:00
vec3 SamplePoints = normalize(vec3(0.0,0.0,1.0));
2023-01-12 15:00:14 -05:00
#endif
2023-01-12 15:28:19 -05:00
2023-07-25 22:48:08 -04:00
vec3 Ln = reflect(-ViewDir, SamplePoints);
vec3 L = Basis * Ln;
2023-01-12 15:28:19 -05:00
2023-07-25 22:48:08 -04:00
float Fresnel = pow(clamp(1.0 + dot(-Ln, SamplePoints),0.0,1.0), 5.0); // Schlick's approximation
float RayContribution = mix_float(F0, 1.0, Fresnel); // ensure that when the angle is 0 that the correct F0 is used.
2023-04-16 16:18:26 -04:00
#ifdef Rough_reflections
2023-07-25 22:48:08 -04:00
if(Hand) RayContribution = RayContribution * pow(1.0-Roughness,3.0);
2023-04-16 16:18:26 -04:00
#else
2023-07-25 22:48:08 -04:00
RayContribution = RayContribution * pow(1.0-Roughness,3.0);
2023-04-16 16:18:26 -04:00
#endif
2023-07-25 22:48:08 -04:00
bool hasReflections = Roughness_Threshold == 1.0 ? true : F0 * (1.0 - Roughness * Roughness_Threshold) > 0.01;
2023-01-12 15:28:19 -05:00
2023-07-25 22:48:08 -04:00
// mulitply all reflections by the albedo if it is a metal.
vec3 Metals = F0 > 229.5/255.0 ? mix_vec3(Albedo, vec3(1.0), Fresnel) : vec3(1.0);
2023-01-12 15:28:19 -05:00
2023-07-25 22:48:08 -04:00
// --------------- BACKGROUND REFLECTIONS
// apply background reflections to the final color. make sure it does not exist based on the lightmap
2023-01-12 15:00:14 -05:00
#ifdef Sky_reflection
2023-07-25 22:48:08 -04:00
if(hasReflections) Background_Reflection = (skyCloudsFromTexLOD(L, colortex4, sqrt(Roughness) * 9.0).rgb / 30.0) * Metals;
// take fresnel and lightmap levels into account and write to the final color
Final_Reflection = mix_vec3(Output, Background_Reflection, Lightmap * RayContribution);
2023-04-16 16:18:26 -04:00
#endif
2023-07-25 22:48:08 -04:00
// --------------- SCREENSPACE REFLECTIONS
// apply screenspace reflections to the final color and mask out background reflections.
2023-04-16 16:18:26 -04:00
#ifdef Screen_Space_Reflections
2023-07-25 22:48:08 -04:00
if(hasReflections){
#ifdef Dynamic_SSR_quality
float SSR_Quality = mix_float(reflection_quality, 6.0, RayContribution); // Scale quality with ray contribution
#else
float SSR_Quality = reflection_quality;
#endif
2023-04-16 16:18:26 -04:00
2023-07-25 22:48:08 -04:00
float reflectLength = 0.0;
vec3 RaytracePos = rayTraceSpeculars(mat3(gbufferModelView) * L, FragPos, Noise.z, SSR_Quality, Hand, reflectLength);
float LOD = clamp(pow(reflectLength, pow(1.0-sqrt(Roughness),5.0) * 3.0) * 6.0, 0.0, 6.0); // use higher LOD as the reflection goes on, to blur it. this helps denoise a little.
if (RaytracePos.z < 1.0){
vec3 previousPosition = mat3(gbufferModelViewInverse) * toScreenSpace(RaytracePos) + gbufferModelViewInverse[3].xyz + cameraPosition-previousCameraPosition;
2023-04-16 16:18:26 -04:00
previousPosition = mat3(gbufferPreviousModelView) * previousPosition + gbufferPreviousModelView[3].xyz;
previousPosition.xy = projMAD(gbufferPreviousProjection, previousPosition).xy / -previousPosition.z * 0.5 + 0.5;
2023-07-25 22:48:08 -04:00
2023-04-16 16:18:26 -04:00
if (previousPosition.x > 0.0 && previousPosition.y > 0.0 && previousPosition.x < 1.0 && previousPosition.x < 1.0) {
2023-07-25 22:48:08 -04:00
SS_Reflections.a = 1.0;
SS_Reflections.rgb = texture2DLod(colortex5, previousPosition.xy, LOD).rgb * Metals;
2023-04-16 16:18:26 -04:00
}
}
2023-07-25 22:48:08 -04:00
// make sure it takes the fresnel into account for SSR.
SS_Reflections.rgb = mix_vec3(Output, SS_Reflections.rgb, RayContribution);
// occlude the background with the SSR and write to the final color.
Final_Reflection = mix_vec3(Final_Reflection, SS_Reflections.rgb, SS_Reflections.a);
2023-04-16 16:18:26 -04:00
}
#endif
2023-07-25 22:48:08 -04:00
// --------------- LIGHTSOURCE REFLECTIONS
// slap the main lightsource reflections to the final color.
#ifdef LIGHTSOURCE_REFLECTION
Lightsource_Reflection = Diffuse * GGX(Normal, -WorldPos, LightPos, Roughness, F0) * Metals;
Final_Reflection += Lightsource_Reflection;
2023-01-12 15:00:14 -05:00
#endif
2023-07-25 22:48:08 -04:00
Output = Final_Reflection;
2023-01-12 15:00:14 -05:00
}