init
This commit is contained in:
208
src/Universe.App/Graphics/Shaders/ocean.frag.glsl
Normal file
208
src/Universe.App/Graphics/Shaders/ocean.frag.glsl
Normal file
@@ -0,0 +1,208 @@
|
||||
#version 330 core
|
||||
#include "noise.glsl"
|
||||
#include "planet-height.glsl"
|
||||
#include "camera-ray.glsl"
|
||||
#include "cloud-common.glsl"
|
||||
in vec2 vUv;
|
||||
uniform sampler2D uScene;
|
||||
uniform sampler2D uDepth;
|
||||
uniform float uFar;
|
||||
uniform float uSeaRadius;
|
||||
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 uWaveSettings; // maximum displacement, choppiness, pixel angular size
|
||||
uniform vec3 uWaterOptics; // roughness, reflection, refraction
|
||||
uniform vec3 uDeepColor;
|
||||
uniform vec3 uShallowColor;
|
||||
uniform vec3 uAbsorption;
|
||||
uniform float uFoamStrength;
|
||||
uniform int uLightCount;
|
||||
uniform vec3 uLightDirections[2];
|
||||
uniform vec3 uLightColors[2];
|
||||
uniform float uLightFlux[2];
|
||||
uniform float uTime;
|
||||
out vec4 fragColor;
|
||||
const float PI=3.14159265359;
|
||||
vec3 toLocal(vec3 v) { return vec3(dot(v,uAxisX),dot(v,uAxisY),dot(v,uAxisZ)); }
|
||||
vec3 toWorld(vec3 v) { return uAxisX*v.x+uAxisY*v.y+uAxisZ*v.z; }
|
||||
float sceneDistance(vec2 uv,vec3 ray) {
|
||||
return (exp2(texture(uDepth,uv).r*log2(uFar+1.0))-1.0)/max(.001,dot(ray,uForward));
|
||||
}
|
||||
// Stable roots avoid subtracting two planet-sized floats when the camera is metres above water.
|
||||
vec2 oceanSphere(vec3 ro,vec3 rd,float expansion) {
|
||||
float b=dot(ro+vec3(0,uSeaRadius,0),rd);
|
||||
float c=dot(ro,ro)+2.0*uSeaRadius*(ro.y-expansion)-expansion*expansion;
|
||||
float h=b*b-c;
|
||||
if(h<0.0)return vec2(1e25,-1e25);
|
||||
float q=-b-(b<0.0?-1.0:1.0)*sqrt(h);
|
||||
float a=q, d=c/(abs(q)<1e-12?1e-12:q);
|
||||
return vec2(min(a,d),max(a,d));
|
||||
}
|
||||
float radialHeight(vec3 p) {
|
||||
return (dot(p,p)+2.0*uSeaRadius*p.y)/(length(p+vec3(0,uSeaRadius,0))+uSeaRadius);
|
||||
}
|
||||
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];
|
||||
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 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;
|
||||
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;
|
||||
}
|
||||
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];
|
||||
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);
|
||||
}
|
||||
return normalize(up-gradient);
|
||||
}
|
||||
vec3 skyReflection(vec3 direction,vec3 up) {
|
||||
float daylight=0.0;
|
||||
for(int l=0;l<2;l++){if(l>=uLightCount)break;daylight+=sqrt(uLightFlux[l])*smoothstep(-.08,.16,dot(up,uLightDirections[l]));}
|
||||
float elevation=max(0.0,dot(direction,up));
|
||||
vec3 sky=mix(vec3(.56,.73,.87),vec3(.12,.32,.64),sqrt(elevation));
|
||||
for(int l=0;l<2;l++) {
|
||||
if(l>=uLightCount)break;
|
||||
sky+=uLightColors[l]*pow(max(0.0,dot(direction,uLightDirections[l])),24.0)*.22*uLightFlux[l];
|
||||
}
|
||||
return sky*(.006+daylight*1.35);
|
||||
}
|
||||
vec3 screenReflection(vec3 position,vec3 direction,vec3 fallback) {
|
||||
vec3 world=toWorld(position-vec3(0,uCameraHeight,0));
|
||||
vec3 reflected=toWorld(direction);
|
||||
float stepSize=3.0;
|
||||
for(int i=0;i<9;i++) {
|
||||
vec3 q=world+reflected*stepSize;
|
||||
float z=dot(q,uForward);
|
||||
if(z>.1) {
|
||||
vec2 uv=.5+vec2(dot(q,uRight)/(uAspect*uTanHalfFov),dot(q,uUp)/uTanHalfFov)/(2.0*z);
|
||||
if(all(greaterThan(uv,vec2(.015))) && all(lessThan(uv,vec2(.985)))) {
|
||||
float depth=exp2(texture(uDepth,uv).r*log2(uFar+1.0))-1.0;
|
||||
if(z>depth && z-depth<max(1.0,stepSize*.09))return texture(uScene,uv).rgb;
|
||||
}
|
||||
}
|
||||
stepSize*=1.9;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
void main() {
|
||||
vec3 scene=texture(uScene,vUv).rgb;
|
||||
float originalDepth=texture(uDepth,vUv).r;
|
||||
gl_FragDepth=originalDepth;
|
||||
vec3 worldRay=cameraRay(vUv), rd=toLocal(worldRay), ro=vec3(0,uCameraHeight,0);
|
||||
float opaque=sceneDistance(vUv,worldRay);
|
||||
float displacement=uWaveSettings.x;
|
||||
bool underwater=uCameraHeight<displacement && uCameraHeight<waveHeight(ro,.02);
|
||||
vec2 envelope=oceanSphere(ro,rd,displacement);
|
||||
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) {
|
||||
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;}
|
||||
float reference=radialHeight(ro+rd*low)-waveHeight(ro+rd*low,max(.03,low*uWaveSettings.z));
|
||||
float endValue=radialHeight(ro+rd*high)-waveHeight(ro+rd*high,max(.03,high*uWaveSettings.z));
|
||||
if(reference*endValue<0.0 && high>low) {
|
||||
for(int i=0;i<18;i++) {
|
||||
float mid=(low+high)*.5;
|
||||
vec3 p=ro+rd*mid;
|
||||
float height=radialHeight(p)-waveHeight(p,max(.03,mid*uWaveSettings.z));
|
||||
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));
|
||||
}
|
||||
}
|
||||
vec3 p=ro+rd*max(0.0,t);
|
||||
vec3 radial=normalize(uAxisY+toWorld(p)/uSeaRadius);
|
||||
float waterMask=uHasOceanMask!=0?oceanMaskValue(bodyDirection(radial)):1.0;
|
||||
hit=hit && waterMask>.01 && t<opaque+.15;
|
||||
if(!hit) {
|
||||
float maskHere=uHasOceanMask!=0?oceanMaskValue(bodyDirection(uAxisY)):1.0;
|
||||
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;
|
||||
}
|
||||
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));
|
||||
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);
|
||||
vec3 reflectionDirection=reflect(rd,n);
|
||||
vec3 reflected=skyReflection(reflectionDirection,up);
|
||||
reflected=cloudReflection(toWorld(p-ro),toWorld(reflectionDirection),reflected);
|
||||
if(t<1500.0 && !underwater)reflected=screenReflection(p,reflectionDirection,reflected);
|
||||
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);
|
||||
for(int l=0;l<2;l++) {
|
||||
if(l>=uLightCount)break;
|
||||
vec3 light=uLightDirections[l];
|
||||
float sunHeight=dot(up,light), visible=smoothstep(-.02,.03,sunHeight);
|
||||
visible*=cloudShadow(toWorld(p-ro),toWorld(light));
|
||||
float ndl=max(.001,dot(n,light));
|
||||
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 distribution=a2/(PI*den*den);
|
||||
float k=(alpha+1.0)*(alpha+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;
|
||||
}
|
||||
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);
|
||||
color=mix(color,vec3(.72,.86,.88)*lighting,foam);
|
||||
if(underwater) {
|
||||
vec3 through=exp(-uAbsorption*min(t,400.0));
|
||||
color=color*through+uShallowColor*(1.0-through)*.5;
|
||||
}
|
||||
fragColor=vec4(max(color,vec3(0)),1);
|
||||
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