85 lines
6.9 KiB
GLSL
85 lines
6.9 KiB
GLSL
uniform sampler2D uSceneTexture;
|
|
uniform mat4 uContentViewProjection,uContentInverseViewProjection;
|
|
uniform vec3 uContentCameraPosition,uLightPosition,uBackgroundColor;
|
|
uniform float uGlassZ,uGridSize,uStrength,uMosaicStyle,uCurvature,uGlossStrength;
|
|
uniform vec2 uViewOffset,uPetSize,uPaneSize;
|
|
uniform vec4 uPaneRect;
|
|
varying vec2 vUv;
|
|
vec2 projectUv(vec3 p){vec4 c=uContentViewProjection*vec4(p,1.);float w=abs(c.w)<.0001?(c.w<0.?-.0001:.0001):c.w;return c.xy/w*.5+.5;}
|
|
vec4 sampleScene(vec2 uv){return texture2D(uSceneTexture,clamp(uv,0.,1.));}
|
|
vec4 samplePane(vec2 pane){return sampleScene(projectUv(vec3((pane-.5)*uPaneSize,uGlassZ)));}
|
|
float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}
|
|
vec2 hash2(vec2 p){return vec2(hash(p),hash(p+vec2(19.19,73.73)));}
|
|
float safeZ(float z){return abs(z)<.0001?(z<0.?-.0001:.0001):z;}
|
|
vec3 bend(vec3 i,vec3 n){vec3 r=refract(i,n,.75);return length(r)<.0001?i:r;}
|
|
float softLimit(float value,float limit){return value*limit/(limit+abs(value));}
|
|
void evaluateCell(vec2 cell,vec2 local,vec2 grid,vec2 cellSize,vec2 cellDimensions,float cellWorld,float style,out vec2 affineUv,out mat2 transform,out vec2 curvedSlope,out vec2 facetSlope){
|
|
vec2 random=hash2(cell),widths;
|
|
if(style>.5){
|
|
widths=mix(vec2(.82),vec2(1.12),hash2(cell+vec2(11.4,9.8)));
|
|
}else{
|
|
float widthSeed=hash(cell+vec2(53.1,27.4));
|
|
if(widthSeed<.3)widths=mix(vec2(.90),vec2(1.08),hash2(cell+vec2(7.2,3.1)));
|
|
else if(widthSeed<.9)widths=mix(vec2(.46),vec2(.70),hash2(cell+vec2(7.2,3.1)));
|
|
else widths=mix(vec2(1.10),vec2(1.28),hash2(cell+vec2(7.2,3.1)));
|
|
}
|
|
float angle=(random.x-.5)*.08,c=cos(angle),s=sin(angle);
|
|
transform=mat2(widths.x*c,-widths.y*s,widths.x*s,widths.y*c);
|
|
vec2 sourceCenter=(cell+.5)/grid+(random-.5)*.16*cellSize;
|
|
affineUv=sourceCenter+transform*(local*cellSize);
|
|
|
|
// Positive height bulges toward the observer (+Z); negative height is concave.
|
|
float curvature=clamp(uCurvature,-1.,1.),excess=max(curvature-.65,0.),surfaceCurvature=curvature-(.25/.35)*excess*excess;
|
|
float height=.009*cellWorld*surfaceCurvature*mix(.92,1.08,hash(cell+vec2(41.7,17.3)));
|
|
vec2 q=clamp(2.*local,-1.,1.),q2=q*q,q3=q2*q,b=1.-q2*q2;
|
|
vec2 skew=(hash2(cell+vec2(23.8,61.2))-.5)*.28;float asym=1.+dot(skew,q);
|
|
vec2 dhdq=height*vec2(-4.*q3.x*b.y*asym+b.x*b.y*skew.x,-4.*q3.y*b.x*asym+b.x*b.y*skew.y);
|
|
vec2 grad=-2.*dhdq/cellDimensions;
|
|
curvedSlope=grad+(random-.5)*.012;facetSlope=(random-.5)*.012;
|
|
}
|
|
void main(){
|
|
if(uStrength<=.001){gl_FragColor=sampleScene(vUv);
|
|
#include <colorspace_fragment>
|
|
return;}
|
|
vec4 f=uContentInverseViewProjection*vec4(vUv*2.-1.,1.,1.);float fw=abs(f.w)<.0001?(f.w<0.?-.0001:.0001):f.w;vec3 farWorld=f.xyz/fw;vec3 i=normalize(farWorld-uContentCameraPosition);float gt=(uGlassZ-uContentCameraPosition.z)/safeZ(i.z);vec3 gh=uContentCameraPosition+i*gt;
|
|
vec2 paneUv=gh.xy/uPaneSize+.5;vec2 grid=vec2(uGridSize,max(1.,uGridSize*uPaneSize.y/uPaneSize.x));vec2 gridUv=paneUv*grid,cell=floor(gridUv),local=fract(gridUv)-.5,cellSize=1./grid;float style=clamp(uMosaicStyle,0.,1.),cellWorld=min(uPaneSize.x/grid.x,uPaneSize.y/grid.y);
|
|
if(paneUv.x<0.||paneUv.x>1.||paneUv.y<0.||paneUv.y>1.){gl_FragColor=vec4(uBackgroundColor,1.);
|
|
#include <colorspace_fragment>
|
|
return;}
|
|
vec2 cellDimensions=uPaneSize/grid;
|
|
vec2 affineUv,curvedSlope,facetSlope;mat2 transform;
|
|
evaluateCell(cell,local,grid,cellSize,cellDimensions,cellWorld,style,affineUv,transform,curvedSlope,facetSlope);
|
|
vec2 edge=.5-abs(local),neighborSign=mix(vec2(-1.),vec2(1.),step(vec2(0.),local));
|
|
vec2 blendAxis=1.-smoothstep(vec2(-.16),vec2(.16),edge);
|
|
vec2 weights=blendAxis;
|
|
if(weights.x>0.||weights.y>0.){
|
|
vec2 neighborAffine,neighborCurved,neighborFacet;mat2 neighborTransform;
|
|
vec2 xWeight=vec2(weights.x*(1.-weights.y),0.);
|
|
vec2 yWeight=vec2(0.,(1.-weights.x)*weights.y);
|
|
float diagonalWeight=weights.x*weights.y;
|
|
float centerWeight=(1.-weights.x)*(1.-weights.y);
|
|
affineUv*=centerWeight;transform*=centerWeight;curvedSlope*=centerWeight;facetSlope*=centerWeight;
|
|
if(xWeight.x>0.){
|
|
vec2 offset=vec2(neighborSign.x,0.);
|
|
evaluateCell(cell+offset,local-offset,grid,cellSize,cellDimensions,cellWorld,style,neighborAffine,neighborTransform,neighborCurved,neighborFacet);
|
|
affineUv+=neighborAffine*xWeight.x;transform+=neighborTransform*xWeight.x;curvedSlope+=neighborCurved*xWeight.x;facetSlope+=neighborFacet*xWeight.x;
|
|
}
|
|
if(yWeight.y>0.){
|
|
vec2 offset=vec2(0.,neighborSign.y);
|
|
evaluateCell(cell+offset,local-offset,grid,cellSize,cellDimensions,cellWorld,style,neighborAffine,neighborTransform,neighborCurved,neighborFacet);
|
|
affineUv+=neighborAffine*yWeight.y;transform+=neighborTransform*yWeight.y;curvedSlope+=neighborCurved*yWeight.y;facetSlope+=neighborFacet*yWeight.y;
|
|
}
|
|
if(diagonalWeight>0.){
|
|
vec2 offset=neighborSign;
|
|
evaluateCell(cell+offset,local-offset,grid,cellSize,cellDimensions,cellWorld,style,neighborAffine,neighborTransform,neighborCurved,neighborFacet);
|
|
affineUv+=neighborAffine*diagonalWeight;transform+=neighborTransform*diagonalWeight;curvedSlope+=neighborCurved*diagonalWeight;facetSlope+=neighborFacet*diagonalWeight;
|
|
}
|
|
}
|
|
vec3 nCurved=normalize(vec3(curvedSlope,1.)),nFacet=normalize(vec3(facetSlope,1.));
|
|
vec3 straightHit=gh+i*(-gh.z/safeZ(i.z));vec3 facetRay=bend(i,nFacet),curvedRay=bend(i,nCurved);vec3 facetHit=gh+facetRay*(-gh.z/safeZ(facetRay.z)),curvedHit=gh+curvedRay*(-gh.z/safeZ(curvedRay.z));vec2 base=facetHit.xy-straightHit.xy,curve=curvedHit.xy-facetHit.xy;vec2 worldDelta=vec2(softLimit(base.x,cellWorld*.08),softLimit(base.y,cellWorld*.08));vec2 d=curve/cellWorld;worldDelta+=vec2(.3*d.x/(.3+abs(d.x)),.3*d.y/(.3+abs(d.y)))*cellWorld;
|
|
vec2 sampleUv=affineUv+transform*(worldDelta/uPaneSize);
|
|
float edgeDistance=min(min(paneUv.x,paneUv.y),min(1.-paneUv.x,1.-paneUv.y));float edgeWeight=smoothstep(0.,max(cellSize.x,cellSize.y)*.75,edgeDistance);float strength=clamp(uStrength,0.,1.)*edgeWeight;vec2 blurStep=cellSize*mix(.006,.012,hash(cell+vec2(5.)));vec2 centerUv=mix(paneUv,sampleUv,strength);vec4 color=samplePane(centerUv)*.4;color+=samplePane(mix(paneUv,sampleUv+vec2(blurStep.x,0.),strength))*.15;color+=samplePane(mix(paneUv,sampleUv-vec2(blurStep.x,0.),strength))*.15;color+=samplePane(mix(paneUv,sampleUv+vec2(0.,blurStep.y),strength))*.15;color+=samplePane(mix(paneUv,sampleUv-vec2(0.,blurStep.y),strength))*.15;color.a=1.;
|
|
vec3 v=normalize(uContentCameraPosition-gh),reflected=reflect(-v,nCurved); float lightT=(uLightPosition.z-gh.z)/safeZ(reflected.z); vec2 lightHit=gh.xy+reflected.xy*lightT; vec2 halfSize=vec2(.32,.42)*min(uPaneSize.x,uPaneSize.y); vec2 windowCoord=abs(lightHit-uLightPosition.xy)/halfSize; vec2 windowMask=1.-smoothstep(vec2(.45),vec2(1.15),windowCoord); float spec=windowMask.x*windowMask.y*step(.0001,reflected.z)*step(0.,lightT); float gloss=spec*clamp(uGlossStrength,0.,1.)*.12*edgeWeight*clamp(uStrength,0.,1.); color.rgb=mix(color.rgb,vec3(1.),gloss); gl_FragColor=color;
|
|
#include <colorspace_fragment>
|
|
}
|