/*
====================================================================================================

    Copyright (C) 2025 Pyvtron VX Shaders - Pyvton

    All Rights Reserved unless otherwise explicitly stated.

    Before You Do Something With Pyvtron VX Shaders, You MUST Read This:

    You Must Read The License: https://pyvton.pages.dev/minecraft-shaders/pyvtron-shaders-license
    And Read The Agreement: https://pyvton.pages.dev/minecraft-shaders/pyvtron-shaders-agreement

====================================================================================================
*/

#define RAYTRACE_QUALITY 128 // [8 16 32 64 128 256 512]
#define RAYTRACE_REFINEMENT 
#define RAYTRACE_REFINEMENT_STEPS 6 // [2 3 4 5 6 7 8 9 10 12 14 16 18 20 22 24 26 28 30 32]
#define SPECULAR_TAIL_CLAMP 0.3

const float zThicknessThreshold=.2;const float EPS=1e-6;uint triple32Hash(uint x){x ^=x>>17;x*=0xed5ad4bbu;x ^=x>>11;x*=0xac4c1b51u;x ^=x>>15;x*=0x31848babu;x ^=x>>14;return x;}uint randStateVar=triple32Hash(uint(gl_FragCoord.x+viewDimensions.x*gl_FragCoord.y)+uint(viewDimensions.x*viewDimensions.y)*frameCounter);uint RandNextFunc(){return randStateVar=triple32Hash(randStateVar);}
#define RandNext2() uvec2(RandNextFunc(), RandNextFunc())
#define RandNext3() uvec3(RandNext2(), RandNextFunc())
#define RandNext4() uvec4(RandNext3(), RandNextFunc())
#define RandNextF() (float(RandNextFunc()) / float(0xffffffffu))
#define RandNext2F() (vec2(RandNext2()) / float(0xffffffffu))
#define RandNext3F() (vec3(RandNext3()) / float(0xffffffffu))
#define RandNext4F() (vec4(RandNext4()) / float(0xffffffffu))
bool rayTrace(vec3 rayOrigin,vec3 rayDir,float NoV,float jitter,bool isHand,inout vec3 rayPosition){const int steps=RAYTRACE_QUALITY+3;const float maxLength=1./float(RAYTRACE_QUALITY);const float minLength=maxLength*.01;float maxDist=far*sqrt(3.);float denom=rayDir.z;float tHit=maxDist;if(abs(denom)>EPS){float t=(-near-rayOrigin.z)/denom;if(t>.0){tHit=clamp(t,.0,maxDist);}else{tHit=maxDist;}}else{tHit=maxDist;}vec3 ssTarget=ViewSpaceToScreenSpace(rayDir*tHit+rayOrigin,gbufferProjection);vec3 direction=ssTarget-rayPosition;float dirZabs=max(abs(direction.z),EPS);direction=normalize(direction);float stepWeight=1./dirZabs;float stepLength=mix(minLength,maxLength,NoV);vec3 increment=direction*vec3(max(pixelSize.x,stepLength),max(pixelSize.y,stepLength),stepLength);rayPosition=rayPosition+increment*(jitter*.5+.5);float depth=isHand?texture(depthtex2,rayPosition.xy).x:texture(depthtex1,rayPosition.xy).x;bool intersect=false;bool isRayExit=false;bool needRefinement=true;for(int i=0;i<=steps;++i){if(rayPosition.x<-.05 || rayPosition.x>1.05 || rayPosition.y<-.05 || rayPosition.y>1.05){return false;}if(depth<rayPosition.z){
#ifdef RAYTRACE_REFINEMENT
if(needRefinement){if(rayPosition.z>=1.){isRayExit=true;break;}vec3 newDir=direction*stepLength;for(int j=0;j<RAYTRACE_REFINEMENT_STEPS;++j){newDir*=.5;if(rayPosition.z>depth){rayPosition-=newDir;}else{rayPosition+=newDir;}depth=isHand?texture(depthtex2,rayPosition.xy).x:texture(depthtex1,rayPosition.xy).x;}needRefinement=false;if(rayPosition.z<depth){continue;}}
#endif
float linearZ=ScreenToViewSpaceDepth(rayPosition.z);float linearD=ScreenToViewSpaceDepth(depth);float dist=abs(linearD-linearZ)/max(linearZ,EPS);if(dist<zThicknessThreshold && linearZ>.0 && linearZ<far){intersect=true;break;}}else{needRefinement=true;}stepLength=clamp(abs(depth-rayPosition.z)*stepWeight,minLength,maxLength);rayPosition+=direction*stepLength;depth=isHand?texture(depthtex2,rayPosition.xy).x:texture(depthtex1,rayPosition.xy).x;}return(depth>=1. && isRayExit)|| intersect;}float SignExtractFunc(float x){return uintBitsToFloat((floatBitsToUint(x)& 0x80000000u)| floatBitsToUint(1.));}mat3 GetRotationMatrix(vec3 from,vec3 to){float cosine=dot(from,to);float tmp=SignExtractFunc(cosine);tmp=1./(tmp+cosine);vec3 axis=cross(to,from);vec3 tmpv=axis*tmp;return mat3(axis.x*tmpv.x+cosine,axis.x*tmpv.y-axis.z,axis.x*tmpv.z+axis.y,axis.y*tmpv.x+axis.z,axis.y*tmpv.y+cosine,axis.y*tmpv.z-axis.x,axis.z*tmpv.x-axis.y,axis.z*tmpv.y+axis.x,axis.z*tmpv.z+cosine);}vec2 ProjectSky(vec3 dir,float lod){float tileSize=min(floor(viewDimensions.x*.5)/1.5,floor(viewDimensions.y*.5))*exp2(-lod);float tileSizeDivide=(.5*tileSize)-1.5;vec2 coord;if(abs(dir.x)>abs(dir.y)&& abs(dir.x)>abs(dir.z)){dir/=abs(dir.x);coord.x=dir.y*tileSizeDivide+tileSize*.5;coord.y=dir.z*tileSizeDivide+tileSize*(dir.x<.0?.5:1.5);}else if(abs(dir.y)>abs(dir.x)&& abs(dir.y)>abs(dir.z)){dir/=abs(dir.y);coord.x=dir.x*tileSizeDivide+tileSize*1.5;coord.y=dir.z*tileSizeDivide+tileSize*(dir.y<.0?.5:1.5);}else{dir/=abs(dir.z);coord.x=dir.x*tileSizeDivide+tileSize*2.5;coord.y=dir.y*tileSizeDivide+tileSize*(dir.z<.0?.5:1.5);}return coord/viewDimensions;}vec3 sampleGGXVNDF(vec3 Ve,float alpha,vec2 Xi){Xi.y=mix(Xi.y,.0,SPECULAR_TAIL_CLAMP);vec3 Vh=normalize(vec3(alpha*Ve.x,alpha*Ve.y,Ve.z));float lensq=Vh.x*Vh.x+Vh.y*Vh.y;vec3 T1=lensq>.0?vec3(-Vh.y,Vh.x,.0)*inversesqrt(lensq):vec3(1.,.0,.0);vec3 T2=cross(Vh,T1);float r=sqrt(Xi.y);float phi=radians(360.)*Xi.x;float s=.5*(1.+Vh.z);float t1=r*cos(phi);float t2=r*sin(phi);t2=(1.-s)*sqrt(max(1.-t1*t1,.0))+s*t2;vec3 Nh=t1*T1+t2*T2+sqrt(max(1.-t1*t1-t2*t2,.0))*Vh;vec3 Ne=normalize(vec3(alpha*Nh.x,alpha*Nh.y,max(Nh.z,.0)));return Ne;}vec4 ReflectionFilter(sampler2D v,GbufferData gbuffer,float size,bool applyNoise){vec4 reflectionData=texture(v,texcoord.st);if(!gbuffer.material.doCSR || reflectionData.w<.0001)return reflectionData;vec3 viewPos=GetViewPosition(texcoord.st,gbuffer.depthW);vec3 viewDir=normalize(viewPos);float linearDepth=ScreenToViewSpaceDepth(texture(gdepthtex,texcoord.st).x);float NdotV=saturate(dot(-viewDir,gbuffer.normalW));float roughness2=gbuffer.material.roughness;float T=size*.9;T*=min(roughness2*20.,1.1);T*=mix(reflectionData.w,1.,.2);vec2 noise=vec2(.0);if(applyNoise)noise=RandNext2F()*.99-.495;vec4 accum=vec4(.0);float weights=.0;float J=reflectionData.w*.475+.025;const float cosHalfPi=cos(1.5708);const float sinHalfPi=sin(1.5708);vec2 D=normalize(cross(gbuffer.normalW,viewDir).xy);vec2 L=D*mat2(cosHalfPi,-sinHalfPi,sinHalfPi,cosHalfPi);D*=mix(.1075,.5,NdotV);L*=mix(.7,.5,NdotV);vec3 nVrN=reflect(-viewDir,gbuffer.normalW);vec2 ScreenTexel4=4.*pixelSize;vec2 ScreenTexel4Inverse=1.-ScreenTexel4;vec2 Temp=T*1.5*pixelSize;L*=Temp;D*=Temp;roughness2=100./roughness2;for(int i=-1;i<=1;++i){vec2 Temp2=D*(float(i)+noise.x)+texcoord.st;for(int j=-1;j<=1;++j){vec2 sampleCoord=Temp2+L*(float(j)+noise.y);sampleCoord=clamp(sampleCoord,ScreenTexel4,ScreenTexel4Inverse);vec4 sampleData=texture(v,sampleCoord);if(sampleData.w<.0001)continue;float sampleLinerDepth=ScreenToViewSpaceDepth(texture(gdepthtex,sampleCoord).x);vec3 nVrSN=reflect(-viewDir,DecodeNormal(texture(colortex4,sampleCoord).xy));float normalWeight=pow(saturate(dot(nVrN,nVrSN)),roughness2);float depthWeight=exp(-(abs(sampleLinerDepth-linearDepth)*1.1));float sampleWeight=normalWeight*depthWeight;vec3 sampleDir=normalize(sampleData.xyz+1e-10);float sampleLenPow=pow(length(sampleData.xyz),J);accum+=vec4(sampleDir*sampleLenPow,sampleData.w)*sampleWeight;weights+=sampleWeight;}}if(weights<.0001)return reflectionData;accum/=weights+.0001;accum.xyz=pow(length(accum.xyz),1./J)*normalize(accum.xyz+1e-06);return accum;}