skog, mjuka skuggor, jämnare övergång vatten på av avstånd
This commit is contained in:
@@ -12,11 +12,12 @@ uniform float uCameraHeight;
|
||||
uniform vec3 uAxisX;
|
||||
uniform vec3 uAxisY;
|
||||
uniform vec3 uAxisZ;
|
||||
uniform vec3 uWaveVectors[12];
|
||||
uniform float uWavePhases[12];
|
||||
uniform float uWaveAmplitudes[12];
|
||||
uniform vec3 uDetailOrigins[2];
|
||||
uniform vec3 uWaveVectors[32];
|
||||
uniform float uWavePhases[32];
|
||||
uniform float uWaveAmplitudes[32];
|
||||
uniform vec3 uDetailOrigins[3];
|
||||
uniform vec3 uWaveSettings; // maximum displacement, choppiness, pixel angular size
|
||||
uniform vec3 uSurfaceDetail; // ripple strength, geometry range (metres), whitecap threshold
|
||||
uniform vec3 uWaterOptics; // roughness, reflection, refraction
|
||||
uniform vec3 uDeepColor;
|
||||
uniform vec3 uShallowColor;
|
||||
@@ -49,43 +50,64 @@ vec2 oceanSphere(vec3 ro,vec3 rd,float expansion) {
|
||||
float radialHeight(vec3 p) {
|
||||
return (dot(p,p)+2.0*uSeaRadius*p.y)/(length(p+vec3(0,uSeaRadius,0))+uSeaRadius);
|
||||
}
|
||||
float wavePhase(int i,vec3 p,out vec3 phaseGradient) {
|
||||
int partner=(i/4)*4+(i+1)%4;
|
||||
float crossing=dot(p,uWaveVectors[partner])+uWavePhases[partner];
|
||||
// Crossing waves bend and break up the crests without a tiled height texture.
|
||||
phaseGradient=uWaveVectors[i]+uWaveVectors[partner]*(.55*cos(crossing));
|
||||
return dot(p,uWaveVectors[i])+uWavePhases[i]+.55*sin(crossing);
|
||||
}
|
||||
float waveHeight(vec3 p,float footprint) {
|
||||
float height=0.0;
|
||||
for(int i=0;i<8;i++) {
|
||||
float weight=1.0-smoothstep(.35,1.0,length(uWaveVectors[i])*footprint);
|
||||
float base=dot(p,uWaveVectors[i])+uWavePhases[i];
|
||||
for(int i=0;i<16;i++) {
|
||||
float width=length(uWaveVectors[i])*footprint;
|
||||
float weight=exp(-.5*width*width);
|
||||
vec3 phaseGradient;
|
||||
float base=wavePhase(i,p,phaseGradient);
|
||||
float phase=base;
|
||||
for(int j=0;j<2;j++)phase=base-uWaveSettings.y*.48*cos(phase);
|
||||
height+=sin(phase)*uWaveAmplitudes[i]*weight;
|
||||
}
|
||||
return height;
|
||||
}
|
||||
vec3 waveNormal(vec3 p,float footprint,out float compression) {
|
||||
vec3 waveNormal(vec3 p,vec3 dx,vec3 dy,float footprint,out float compression,out float variance) {
|
||||
vec3 gradient=vec3(0); compression=0.0;
|
||||
float total=0.0;
|
||||
for(int i=0;i<12;i++) {
|
||||
float weight=1.0-smoothstep(.35,1.0,length(uWaveVectors[i])*footprint);
|
||||
float base=dot(p,uWaveVectors[i])+uWavePhases[i], phase=base;
|
||||
// Waves below the smallest normal band still contribute microscopic surface roughness.
|
||||
variance=.004*uSurfaceDetail.x*uSurfaceDetail.x;
|
||||
vec3 up=normalize(p+vec3(0,uSeaRadius,0));
|
||||
for(int i=0;i<32;i++) {
|
||||
// Integrate each direction over the actual pixel footprint, including grazing views.
|
||||
vec3 phaseGradient;
|
||||
float base=wavePhase(i,p,phaseGradient);
|
||||
float width=length(vec2(dot(phaseGradient,dx),dot(phaseGradient,dy)));
|
||||
float weight=exp(-.5*width*width);
|
||||
float phase=base;
|
||||
float q=uWaveSettings.y*.48;
|
||||
for(int j=0;j<3;j++)phase=base-q*cos(phase);
|
||||
float a=uWaveAmplitudes[i]*weight;
|
||||
gradient+=uWaveVectors[i]*(a*cos(phase)/max(.15,1.0-q*sin(phase)));
|
||||
compression+=max(0.0,sin(phase))*a;
|
||||
total+=a;
|
||||
gradient+=phaseGradient*(a*cos(phase)/max(.15,1.0-q*sin(phase)));
|
||||
vec3 tangent=uWaveVectors[i]-up*dot(up,uWaveVectors[i]);
|
||||
float slope=uWaveAmplitudes[i]*length(tangent);
|
||||
// Unresolved waves become BRDF roughness instead of disappearing into a flat mirror.
|
||||
variance+=.5*slope*slope*(1.0-weight*weight);
|
||||
compression+=sin(phase)*slope*weight*uWaveSettings.y;
|
||||
}
|
||||
compression/=max(.001,total);
|
||||
vec3 up=normalize(p+vec3(0,uSeaRadius,0));
|
||||
gradient-=up*dot(gradient,up);
|
||||
// Capillary ripples use a planet-anchored noise field and disappear below the pixel footprint.
|
||||
vec3 bodyPoint=mat3(uWorldToBody)*toWorld(p);
|
||||
vec3 bodyX=mat3(uWorldToBody)*uAxisX,bodyZ=mat3(uWorldToBody)*uAxisZ;
|
||||
for(int detail=0;detail<2;detail++) {
|
||||
float frequency=detail==0?.35:1.4;
|
||||
float weight=1.0-smoothstep(.25,.8,footprint*frequency);
|
||||
vec3 q=bodyPoint*frequency+uDetailOrigins[detail];
|
||||
for(int detail=0;detail<3;detail++) {
|
||||
float frequency=detail==0?.045:(detail==1?.35:1.4);
|
||||
float width=footprint*frequency*2.0;
|
||||
float weight=exp(-.5*width*width);
|
||||
// Modulo origins on the CPU preserve centimetre detail without planet-sized floats.
|
||||
vec3 drift=vec3(uTime*.018,0,uTime*.011)*sqrt(frequency);
|
||||
vec3 q=bodyPoint*frequency+uDetailOrigins[detail]+drift;
|
||||
float gx=(noise3(q+bodyX*.04)-noise3(q-bodyX*.04))/.08;
|
||||
float gz=(noise3(q+bodyZ*.04)-noise3(q-bodyZ*.04))/.08;
|
||||
gradient+=vec3(gx,0,gz)*weight*(detail==0?.13:.075);
|
||||
float strength=(detail==0?.055:(detail==1?.14:.08))*uSurfaceDetail.x;
|
||||
gradient+=vec3(gx,0,gz)*weight*strength;
|
||||
variance+=strength*strength*.35*(1.0-weight*weight);
|
||||
}
|
||||
return normalize(up-gradient);
|
||||
}
|
||||
@@ -101,7 +123,7 @@ vec3 skyReflection(vec3 direction,vec3 up) {
|
||||
return sky*(.006+daylight*1.35);
|
||||
}
|
||||
void main() {
|
||||
// Premultiplied-alpha MRT blending preserves earlier ocean planets outside this one's silhouette.
|
||||
// Misses discard, preserving earlier oceans and their reflection metadata.
|
||||
reflectionNormal=vec4(0); reflectionFallback=vec4(0);
|
||||
vec3 scene=texture(uScene,vUv).rgb;
|
||||
float originalDepth=texture(uDepth,vUv).r;
|
||||
@@ -114,7 +136,8 @@ void main() {
|
||||
vec2 base=oceanSphere(ro,rd,0.0);
|
||||
float t=base.x>0.0?base.x:base.y;
|
||||
bool hit=envelope.y>0.0 && t>0.0;
|
||||
if(hit && abs(uCameraHeight)<2500.0 && t<2000.0) {
|
||||
float geometryRange=uSurfaceDetail.y;
|
||||
if(hit && abs(uCameraHeight)<geometryRange && t<geometryRange) {
|
||||
vec2 inner=oceanSphere(ro,rd,-displacement);
|
||||
float low=max(.015,envelope.x),high=inner.x>0.0?inner.x:envelope.y;
|
||||
if(underwater) {low=max(.015,inner.y);high=envelope.y;}
|
||||
@@ -128,10 +151,14 @@ void main() {
|
||||
if(height*reference>0.0)low=mid;else high=mid;
|
||||
}
|
||||
float solved=(low+high)*.5;
|
||||
t=mix(solved,t,smoothstep(800.0,2000.0,t));
|
||||
float geometry=1.0-smoothstep(geometryRange*.35,geometryRange,t);
|
||||
geometry*=1.0-smoothstep(geometryRange*.5,geometryRange,abs(uCameraHeight));
|
||||
t=mix(t,solved,geometry);
|
||||
}
|
||||
}
|
||||
vec3 p=ro+rd*max(0.0,t);
|
||||
// Evaluate derivatives before any divergent discard at the shoreline/horizon.
|
||||
vec3 dx=dFdx(p),dy=dFdy(p);
|
||||
vec3 radial=normalize(uAxisY+toWorld(p)/uSeaRadius);
|
||||
float waterMask=uHasOceanMask!=0?oceanMaskValue(bodyDirection(radial)):1.0;
|
||||
hit=hit && waterMask>.01 && t<opaque+.15;
|
||||
@@ -140,25 +167,33 @@ void main() {
|
||||
if(underwater && maskHere>.5) {
|
||||
vec3 transmission=exp(-uAbsorption*min(opaque,400.0));
|
||||
scene=scene*transmission+uShallowColor*(1.0-transmission)*.5;
|
||||
fragColor=vec4(scene,1);return;
|
||||
}
|
||||
fragColor=vec4(scene,1);return;
|
||||
discard;
|
||||
}
|
||||
float footprint=max(.02,t*uWaveSettings.z/max(.12,abs(dot(rd,normalize(p+vec3(0,uSeaRadius,0))))));
|
||||
float compression;
|
||||
vec3 n=waveNormal(p,footprint,compression), up=normalize(p+vec3(0,uSeaRadius,0));
|
||||
float footprint=max(.02,max(length(dx),length(dy)));
|
||||
float compression,variance;
|
||||
vec3 n=waveNormal(p,dx,dy,footprint,compression,variance), up=normalize(p+vec3(0,uSeaRadius,0));
|
||||
float alpha=clamp(sqrt(pow(uWaterOptics.x,4.0)+variance*.7),.001,1.0);
|
||||
float roughness=clamp(sqrt(alpha),.04,1.0);
|
||||
vec3 view=-rd;
|
||||
if(dot(n,view)<0.0)n=-n;
|
||||
float ndv=max(.02,dot(n,view));
|
||||
float fresnel=.0204+.9796*pow(1.0-ndv,5.0);
|
||||
// Average Fresnel over unresolved slopes, rather than applying the flat-surface grazing spike.
|
||||
float environmentNdv=min(1.0,sqrt(ndv*ndv+2.0*variance));
|
||||
float fresnel=.0204+(max(.0204,1.0-roughness*.65)-.0204)*pow(1.0-environmentNdv,5.0);
|
||||
vec3 reflectionDirection=reflect(rd,n);
|
||||
vec3 reflected=skyReflection(reflectionDirection,up);
|
||||
reflected=cloudReflection(toWorld(p-ro),toWorld(reflectionDirection),reflected);
|
||||
vec3 reflected=skyReflection(normalize(mix(reflectionDirection,up,roughness*.3)),up);
|
||||
reflected=cloudReflection(toWorld(p-ro),toWorld(reflectionDirection),reflected,roughness*roughness);
|
||||
vec2 refractUv=clamp(vUv+vec2(dot(toWorld(n),uRight),dot(toWorld(n),uUp))*uWaterOptics.z/(1.0+t*.012),vec2(.001),vec2(.999));
|
||||
float floorDistance=sceneDistance(refractUv,cameraRay(refractUv));
|
||||
float thickness=max(.0,min(400.0,floorDistance-t));
|
||||
vec3 transmission=exp(-uAbsorption*thickness);
|
||||
vec3 scatter=mix(uShallowColor,uDeepColor,1.0-exp(-thickness*.075));
|
||||
vec3 lighting=vec3(.04), highlights=vec3(0);
|
||||
float skyDaylight=0.0;
|
||||
for(int l=0;l<2;l++){if(l>=uLightCount)break;skyDaylight+=sqrt(uLightFlux[l])*smoothstep(-.08,.16,dot(up,uLightDirections[l]));}
|
||||
// Diffuse skylight keeps deep water blue even when a cloud blocks the direct sun.
|
||||
vec3 lighting=vec3(.004)+vec3(.12,.2,.3)*skyDaylight, highlights=vec3(0);
|
||||
for(int l=0;l<2;l++) {
|
||||
if(l>=uLightCount)break;
|
||||
vec3 light=uLightDirections[l];
|
||||
@@ -168,21 +203,22 @@ void main() {
|
||||
lighting+=uLightColors[l]*uLightFlux[l]*visible*(.25+.75*max(0.0,sunHeight));
|
||||
vec3 halfVector=normalize(view+light);
|
||||
float ndh=max(0.0,dot(n,halfVector));
|
||||
float alpha=max(.045,uWaterOptics.x*uWaterOptics.x+min(.14,footprint*.001));
|
||||
float a2=alpha*alpha, den=ndh*ndh*(a2-1.0)+1.0;
|
||||
float brdfAlpha=max(.025,alpha);
|
||||
float a2=brdfAlpha*brdfAlpha, den=ndh*ndh*(a2-1.0)+1.0;
|
||||
float distribution=a2/(PI*den*den);
|
||||
float k=(alpha+1.0)*(alpha+1.0)/8.0;
|
||||
float k=(brdfAlpha+1.0)*(brdfAlpha+1.0)/8.0;
|
||||
float geometry=ndv/(ndv*(1.0-k)+k)*ndl/(ndl*(1.0-k)+k);
|
||||
float f=.0204+.9796*pow(1.0-max(0.0,dot(view,halfVector)),5.0);
|
||||
highlights+=uLightColors[l]*uLightFlux[l]*visible*min(25.0,distribution*geometry*f/(4.0*ndv))*1.8;
|
||||
scatter+=uShallowColor*pow(max(0.0,dot(view,-light)),4.0)*compression*.28*visible;
|
||||
highlights+=uLightColors[l]*uLightFlux[l]*visible*min(12.0,distribution*geometry*f/(4.0*ndv));
|
||||
scatter+=uShallowColor*pow(max(0.0,dot(view,-light)),4.0)*max(0.0,compression)*.28*visible;
|
||||
}
|
||||
vec3 refracted=texture(uScene,refractUv).rgb*transmission+scatter*(1.0-transmission)*lighting;
|
||||
vec3 color=mix(refracted,reflected*uWaterOptics.y,fresnel)+highlights;
|
||||
float shore=(1.0-smoothstep(.2,3.5,thickness))*(.5+.5*sin(uTime*1.3+t*.03));
|
||||
vec3 bodyDetail=mat3(uWorldToBody)*toWorld(p)*.35+uDetailOrigins[0];
|
||||
float breakup=noise3(bodyDetail+vec3(uTime*.09,0,uTime*.06))*.5+.5;
|
||||
float foam=clamp((smoothstep(.54,.78,compression)+shore*.7)*uFoamStrength*smoothstep(.22,.72,breakup),0.0,.9);
|
||||
vec3 bodyDetail=mat3(uWorldToBody)*toWorld(p)*.045+uDetailOrigins[0];
|
||||
float breakup=noise3(bodyDetail+vec3(uTime*.025,0,uTime*.016))*.5+.5;
|
||||
float breaking=smoothstep(uSurfaceDetail.z,uSurfaceDetail.z+.12,compression);
|
||||
float foam=clamp((breaking*smoothstep(.25,.75,breakup)+shore*.7)*uFoamStrength,0.0,.9);
|
||||
color=mix(color,vec3(.72,.86,.88)*lighting,foam);
|
||||
if(underwater) {
|
||||
vec3 through=exp(-uAbsorption*min(t,400.0));
|
||||
@@ -191,6 +227,6 @@ void main() {
|
||||
fragColor=vec4(max(color,vec3(0)),1);
|
||||
float reflectance=underwater?0.0:fresnel*uWaterOptics.y*(1.0-foam);
|
||||
reflectionNormal=vec4(toWorld(n)*reflectance,1);
|
||||
reflectionFallback=vec4(reflected*reflectance,1);
|
||||
reflectionFallback=vec4(reflected*reflectance,roughness);
|
||||
gl_FragDepth=clamp(log2(1.0+t*max(.001,dot(worldRay,uForward)))/log2(uFar+1.0),0.0,1.0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user