mirror of
https://github.com/X0nk/Bliss-Shader.git
synced 2026-10-10 05:03:04 +08:00
735 lines
27 KiB
GLSL
735 lines
27 KiB
GLSL
#define ALTOSTRATUS_LAYER 2
|
|
#define LARGECUMULUS_LAYER 1
|
|
#define SMALLCUMULUS_LAYER 0
|
|
float curvatureoffset = 0.04;
|
|
uniform vec2 windDirection;
|
|
// uniform float animation;
|
|
#if CLOUD_ANIMATION_MODE == 0
|
|
uniform int worldDay;
|
|
uniform int worldTime;
|
|
// uniform float worldTimeAnimation;
|
|
float cloud_movement = (worldTime + mod(worldDay,100)*24000.0) / 24.0 * Cloud_Speed;
|
|
#elif CLOUD_ANIMATION_MODE == 1
|
|
// uniform float frameTimeCounter;
|
|
float cloud_movement = frameTimeCounter * Cloud_Speed;
|
|
#endif
|
|
|
|
float densityAtPos(in vec3 pos){
|
|
pos /= 18.;
|
|
pos.xz *= 0.5;
|
|
vec3 p = floor(pos);
|
|
vec3 f = fract(pos);
|
|
vec2 uv = p.xz + f.xz + p.y * vec2(0.0,193.0);
|
|
vec2 coord = uv / 512.0;
|
|
|
|
//The y channel has an offset to avoid using two textures fetches
|
|
vec2 xy = texture(noisetex, coord).yx;
|
|
|
|
return mix(xy.r,xy.g, f.y);
|
|
}
|
|
|
|
vec3 getPlanetAbsorb(in vec3 worldPos, in vec3 sunVector, sampler2D colortex){
|
|
|
|
float position = clamp((worldPos.y - (FAKE_PLANET_START_HEIGHT + 1.0/abs(sunVector.y*0.1)))*256.0 / FAKE_PLANET_GRADIENT_LENGTH,0.0,257.0);
|
|
// float position = clamp((worldPos.y + 60.0)*256.0 / 1500.0,0.0,257.0);
|
|
|
|
vec3 skyAbsorb = texture(colortex, vec2(16.5, position)*texelSize).rgb / 2400.0;
|
|
|
|
#ifdef ReflectedFog
|
|
return skyAbsorb*2400.0/150.0 * 2.5;
|
|
#else
|
|
return skyAbsorb;
|
|
#endif
|
|
}
|
|
|
|
float getCloudShape(int LayerIndex, int LOD, in vec3 position, float minHeight, float maxHeight){
|
|
|
|
vec3 samplePos = position*vec3(0.25, 0.005, 0.25);
|
|
|
|
float coverage = 0.0;
|
|
float shape = 0.0;
|
|
float largeCloud = 0.0;
|
|
float smallCloud = 0.0;
|
|
|
|
switch (LayerIndex){
|
|
default : { break; }
|
|
|
|
case SMALLCUMULUS_LAYER: {
|
|
coverage = parameters.smallCumulus.x;
|
|
|
|
vec2 animatedPos = samplePos.xz + windDirection/4.0 * Cloud_Speed;
|
|
|
|
largeCloud = texture(noisetex, animatedPos/5000.0 * CloudLayer0_scale).b;
|
|
smallCloud = 1.0-texture(noisetex,animatedPos/500.0 * CloudLayer0_scale).r;
|
|
smallCloud = abs(largeCloud-0.6) + smallCloud*smallCloud;
|
|
|
|
shape = min(max(coverage - smallCloud,0.0)/(1e-6+sqrt(coverage)),1.0) ;
|
|
break; }
|
|
|
|
case LARGECUMULUS_LAYER: {
|
|
coverage = parameters.largeCumulus.x;
|
|
|
|
vec2 animatedPos = samplePos.zx + windDirection* Cloud_Speed;
|
|
largeCloud = texture(noisetex, animatedPos/10000.0 * CloudLayer1_scale).b;
|
|
smallCloud = texture(noisetex, animatedPos/2500.0 * CloudLayer1_scale).b;
|
|
smallCloud = abs(largeCloud* -0.7) + smallCloud;
|
|
|
|
shape = min(max(coverage - smallCloud,0.0)/(1e-6+sqrt(coverage)),1.0) ;
|
|
break; }
|
|
|
|
case ALTOSTRATUS_LAYER: {
|
|
coverage = parameters.altostratus.x;
|
|
|
|
vec2 animatedPos = samplePos.xz + windDirection * 10.0 * Cloud_Speed;
|
|
largeCloud = texture(noisetex, animatedPos/100000. * CloudLayer2_scale).b;
|
|
smallCloud = 1.0 - texture(noisetex, (animatedPos/7500. - vec2(1.0-largeCloud, -largeCloud)/5.0) * CloudLayer2_scale).b;
|
|
smallCloud = largeCloud + smallCloud * 0.4 * clamp(1.5-largeCloud,0.0,1.0);
|
|
|
|
shape = min(max(coverage - smallCloud,0.0) / (1e-6+sqrt(coverage)),1.0);
|
|
shape *= shape;
|
|
break; }
|
|
}
|
|
|
|
if(LayerIndex == ALTOSTRATUS_LAYER) return shape;
|
|
|
|
// clamp density of the cloud within its upper/lower bounds
|
|
shape = min(min(shape, clamp(maxHeight - position.y,0,1)), 1.0 - clamp(minHeight - position.y,0,1));
|
|
|
|
// carve out the upper part of clouds. make sure it rounds out at its upper bound
|
|
float topShape = min(max(maxHeight-position.y,0.0) / max(maxHeight-minHeight,1.0),1.0);
|
|
topShape = min(exp(-0.5 * (1.0-topShape)), 1.0-pow(1.0-topShape,5.0));
|
|
|
|
// round out the bottom part slightly
|
|
float bottomShape = 1.0-pow(1.0-min(max(position.y-minHeight,0.0) / 25.0, 1.0), 5.0);
|
|
shape = max((shape - 1.0) + topShape * bottomShape,0.0);
|
|
|
|
/// erosion noise
|
|
if(shape > 0.001){
|
|
|
|
float erodeAmount = 0.5;
|
|
// shrink the coverage slightly so it is a similar shape to clouds with erosion. this helps cloud lighting and cloud shadows.
|
|
if (LOD < 1) return max(shape - 0.27*erodeAmount,0.0);
|
|
|
|
float erosion = 0.0;
|
|
|
|
switch (LayerIndex){
|
|
default : { break; }
|
|
|
|
case SMALLCUMULUS_LAYER: {
|
|
|
|
samplePos.xz += windDirection/2.0* Cloud_Speed;
|
|
samplePos.xz += (-vec2(-windDirection.y, windDirection.x)/1500.0)*pow( max(position.y - (minHeight+20.0), 0.0) / (max(maxHeight-minHeight,1.0)*0.20), 1.5);
|
|
|
|
erosion += (1.0-densityAtPos(samplePos * 200.0 * CloudLayer0_scale)) * sqrt(1.0-shape);
|
|
|
|
float falloff = 1.0 - clamp((maxHeight - position.y)/100.0,0.0,1.0);
|
|
erosion += abs(densityAtPos(samplePos * 600.0 * CloudLayer0_scale) - falloff) * 0.75 * (1.0-shape) * (1.0-falloff*0.25);
|
|
|
|
erosion = erosion*erosion*erosion*erosion;
|
|
break; }
|
|
|
|
case LARGECUMULUS_LAYER: {
|
|
|
|
samplePos.zx += windDirection*1.74* Cloud_Speed;
|
|
samplePos.zx += (-vec2(-windDirection.y, windDirection.x)/1500.0)*pow( abs(position.y - (minHeight+20.0)) / (max(maxHeight-minHeight,1.0)*0.20), 1.75);
|
|
|
|
erosion += (1.0 - densityAtPos(samplePos * 70.0 * CloudLayer1_scale)) * sqrt(1.0-shape);
|
|
|
|
float falloff = 1.0 - clamp((maxHeight - position.y)/200.0,0.0,1.0);
|
|
erosion += abs(densityAtPos(samplePos * 250.0 * CloudLayer1_scale) - falloff) * 0.75 * (1.0-shape) * (1.0-falloff*0.5);
|
|
|
|
erosion = erosion*erosion*erosion*erosion;
|
|
break; }
|
|
}
|
|
|
|
return max(shape - erosion*erodeAmount,0.0);
|
|
|
|
} else return 0.0;
|
|
|
|
}
|
|
|
|
float getPlanetShadow(vec3 playerPos, vec3 WsunVec){
|
|
#ifdef FAKE_PLANET
|
|
return 1.0;
|
|
#endif
|
|
|
|
float planetShadow = min(max(playerPos.y - (FAKE_PLANET_START_HEIGHT + 1.0 / abs(WsunVec.y*0.1)),0.0) / 100.0, 1.0);
|
|
|
|
planetShadow = mix(pow(1.0-pow(1.0-planetShadow,2.0),2.0), 1.0, pow(abs(WsunVec.y),2.0));
|
|
|
|
return planetShadow;
|
|
}
|
|
|
|
float GetCloudShadow(vec3 playerPos, vec3 sunVector){
|
|
|
|
float totalShadow = getPlanetShadow(playerPos, sunVector);
|
|
|
|
vec3 startPosition = playerPos;
|
|
vec3 startOffset = sunVector / abs(sunVector.y);
|
|
|
|
#if defined OVERWORLD_SHADER && defined AETHER_FLAG
|
|
float layer0Height = CloudLayer0_height - 350.0;
|
|
float layer1Height = CloudLayer1_height - 350.0;
|
|
float layer2Height = CloudLayer2_height - 350.0;
|
|
#else
|
|
float layer0Height = CloudLayer0_height;
|
|
float layer1Height = CloudLayer1_height;
|
|
float layer2Height = CloudLayer2_height;
|
|
#endif
|
|
|
|
#if CLOUD_SHADOW_AMOUNT > 0
|
|
float cloudShadows = 0.0;
|
|
|
|
#ifdef CloudLayer0
|
|
startPosition = playerPos + startOffset * max((layer0Height + 20.0) - playerPos.y, 0.0);
|
|
cloudShadows = getCloudShape(SMALLCUMULUS_LAYER, 0, startPosition, layer0Height, CloudLayer0_tallness / CloudLayer0_scale + layer0Height)*parameters.smallCumulus.y;
|
|
#endif
|
|
#ifdef CloudLayer1
|
|
startPosition = playerPos + startOffset * max((layer1Height + 20.0) - playerPos.y, 0.0);
|
|
cloudShadows += getCloudShape(LARGECUMULUS_LAYER, 0, startPosition, layer1Height, CloudLayer1_tallness / CloudLayer1_scale + layer1Height)*parameters.largeCumulus.y;
|
|
#endif
|
|
#ifdef CloudLayer2
|
|
startPosition = playerPos + startOffset * max(layer2Height - playerPos.y, 0.0);
|
|
cloudShadows += getCloudShape(ALTOSTRATUS_LAYER, 0, startPosition, layer2Height, layer2Height)*parameters.altostratus.y * (1.0-abs(WsunVec.y));
|
|
#endif
|
|
|
|
cloudShadows *= float(CLOUD_SHADOW_AMOUNT)/100.0;
|
|
|
|
#if defined CloudLayer0 || defined CloudLayer1 || defined CloudLayer2
|
|
totalShadow *= exp((cloudShadows*cloudShadows) * -200.0);
|
|
#endif
|
|
#endif
|
|
|
|
return totalShadow;
|
|
}
|
|
|
|
#ifndef CLOUDSHADOWSONLY
|
|
uniform sampler2D colortex4;
|
|
|
|
float phaseCloud(float x, float g){
|
|
float gg = g * g;
|
|
return (gg * -0.25 + 0.25) * pow(-2.0 * (g * x) + (gg + 1.0), -1.5) / 3.14;
|
|
}
|
|
|
|
float getCloudScattering(
|
|
int LayerIndex,
|
|
vec3 rayPosition,
|
|
vec3 sunVector,
|
|
float dither,
|
|
float minHeight,
|
|
float maxHeight,
|
|
float density
|
|
){
|
|
int samples = 3;
|
|
int LOD = 0;
|
|
// crazy mode
|
|
// samples = 10;
|
|
// LOD = 1;
|
|
|
|
if(LayerIndex == ALTOSTRATUS_LAYER) samples = 2;
|
|
|
|
float shadow = 0.0;
|
|
vec3 shadowRayPosition = vec3(0.0);
|
|
|
|
for (int i = 0; i < samples; i++){
|
|
|
|
if(LayerIndex == ALTOSTRATUS_LAYER){
|
|
// shadowRayPosition = rayPosition + sunVector * (1.0 + i * dither) / (pow(abs(sunVector.y*0.5),3.0) * 0.995 + 0.005);
|
|
shadowRayPosition = rayPosition + sunVector * (0.25 + i * dither) * 200.0;
|
|
}else{
|
|
// shadowRayPosition = rayPosition + sunVector * (1.0 + i + dither)*20.0;
|
|
shadowRayPosition = rayPosition + sunVector * (0.05 + i + dither)*20.0;
|
|
}
|
|
|
|
// float fadeddensity = density * pow(clamp((shadowRayPosition.y - minHeight)/(max(maxHeight-minHeight,1.0)*0.25),0.0,1.0),2.0);
|
|
|
|
shadow += getCloudShape(LayerIndex, LOD, shadowRayPosition, minHeight, maxHeight) * density;
|
|
}
|
|
|
|
return shadow;
|
|
}
|
|
|
|
vec3 getCloudLighting(
|
|
float shape,
|
|
float shapeFaded,
|
|
|
|
float sunShadowMask,
|
|
vec3 directLightCol,
|
|
|
|
float indirectShadowMask,
|
|
vec3 indirectLightCol
|
|
,float backScatterPhase
|
|
,vec4 phaseLevels
|
|
){
|
|
float beerCoef = -4.0;
|
|
float powder = min(exp(beerCoef*exp(beerCoef*shapeFaded)) * 3.5, 1);
|
|
float backscatter = powder * backScatterPhase;
|
|
float forwardscatter = mix(mix(phaseLevels.x, phaseLevels.y, powder), mix(phaseLevels.z, phaseLevels.w, powder), powder);
|
|
|
|
// backscatter = powder * phaseCloud(-backScatterPhase, 0.25) * 2.0;
|
|
// forwardscatter = phaseCloud(backScatterPhase, mix(0.9,0.1,powder));
|
|
|
|
vec3 directScattering = 6.28 * directLightCol * exp((beerCoef-1.0)*sunShadowMask) * (forwardscatter + backscatter);
|
|
vec3 indirectScattering = indirectLightCol * mix(1.0, exp2(-5.0*shape), indirectShadowMask*indirectShadowMask);
|
|
|
|
// return indirectScattering;
|
|
// return directScattering;
|
|
return indirectScattering + directScattering;
|
|
}
|
|
|
|
vec4 raymarchCloud(
|
|
int LayerIndex,
|
|
int samples,
|
|
vec3 rayPosition,
|
|
vec3 rayDirection,
|
|
float dither,
|
|
|
|
float minHeight,
|
|
float maxHeight,
|
|
|
|
vec3 sunVector,
|
|
vec3 sunScattering,
|
|
vec3 skyScattering,
|
|
|
|
float referenceDistance,
|
|
vec3 sampledSkyCol,
|
|
|
|
inout vec2 cloudPlaneDistance
|
|
,float backScatterPhase
|
|
,vec4 phaseLevels
|
|
){
|
|
vec3 color = vec3(0.0);
|
|
float totalAbsorbance = 1.0;
|
|
float planetShadow = getPlanetShadow(rayPosition, sunVector);
|
|
sunScattering *= planetShadow;
|
|
|
|
#ifdef FAKE_PLANET
|
|
sunScattering = getPlanetAbsorb(rayPosition, WsunVec, colortex4);
|
|
#endif
|
|
|
|
float distanceFactor = length(rayDirection);
|
|
|
|
float densityTresholdCheck = 0.0;
|
|
|
|
if(LayerIndex == SMALLCUMULUS_LAYER) densityTresholdCheck = 0.06;
|
|
if(LayerIndex == LARGECUMULUS_LAYER) densityTresholdCheck = 0.02;
|
|
if(LayerIndex == ALTOSTRATUS_LAYER) densityTresholdCheck = 0.01;
|
|
|
|
densityTresholdCheck = mix(1e-5, densityTresholdCheck, dither);
|
|
|
|
if(LayerIndex == ALTOSTRATUS_LAYER){
|
|
|
|
float density = parameters.altostratus.y;
|
|
|
|
bool ifAboveOrBelowPlane = max(mix(-1.0, 1.0, clamp(cameraPosition.y - minHeight,0.0,1.0)) * normalize(rayDirection).y + 0.0001,0.0) > 0.0;
|
|
|
|
// check if the ray staring position is going farther than the reference distance, if yes, dont begin marching. this is to check for intersections with the world.
|
|
// check if the camera is above or below the cloud plane, so it doesnt waste work on the opposite hemisphere
|
|
#ifndef VL_CLOUDS_DEFERRED
|
|
if(length(rayPosition - cameraPosition) > referenceDistance || ifAboveOrBelowPlane) return vec4(color, totalAbsorbance);
|
|
#else
|
|
if(ifAboveOrBelowPlane) return vec4(color, totalAbsorbance);
|
|
#endif
|
|
|
|
float shape = getCloudShape(LayerIndex, 1, rayPosition, minHeight, maxHeight);
|
|
float shapeWithDensity = shape*density;
|
|
|
|
if(shapeWithDensity > mix(1e-5, 0.06, dither) && cloudPlaneDistance.y > 0.5){
|
|
cloudPlaneDistance.x = length(rayPosition - cameraPosition); cloudPlaneDistance.y = 0.0;
|
|
}
|
|
|
|
// check if the pixel has visible clouds before doing work.
|
|
if(shapeWithDensity > 1e-5){
|
|
|
|
// can add the initial cloud shape sample for a free shadow starting step :D
|
|
float sunShadowMask = getCloudScattering(LayerIndex, rayPosition, sunVector, dither, minHeight, maxHeight, density) * (1.0-abs(WsunVec.y));
|
|
float indirectShadowMask = 0.5;
|
|
|
|
vec3 lighting = getCloudLighting(shapeWithDensity, shapeWithDensity, sunShadowMask, sunScattering, indirectShadowMask, skyScattering, backScatterPhase, phaseLevels);
|
|
|
|
vec3 newPos = rayPosition - cameraPosition;
|
|
|
|
#ifdef AERIAL_PERSPECTIVE_TEST
|
|
float skydensity = exp(-0.00035*length(newPos));
|
|
float ifAboveOrBelowPlane = mix(-1.0, 1.0, clamp(cameraPosition.y - minHeight,0.0,1.0)) ;
|
|
|
|
vec3 samplesky = skyFromTex(clamp(normalize(vec3(newPos.x,ifAboveOrBelowPlane*newPos.y,newPos.z)) - vec3(0,curvatureoffset - 0.005,0),-1,1) , colortex4).rgb/1200.0;
|
|
|
|
lighting = lighting * skydensity + (samplesky - samplesky * skydensity);
|
|
#else
|
|
newPos.xz /= max(newPos.y,0.0)*0.0025 + 1.0;
|
|
newPos.y = min(newPos.y,0.0);
|
|
|
|
float distancefog = exp(-(0.00035 + rainStrength * 0.0015) * length(newPos));
|
|
vec3 atmosphereHaze = (sampledSkyCol - sampledSkyCol * distancefog);
|
|
lighting = lighting * distancefog + atmosphereHaze;
|
|
#endif
|
|
|
|
float densityCoeff = exp(-distanceFactor*shapeWithDensity);
|
|
color += (lighting - lighting * densityCoeff) * totalAbsorbance;
|
|
totalAbsorbance *= densityCoeff;
|
|
}
|
|
|
|
return vec4(color, totalAbsorbance);
|
|
}
|
|
|
|
if(LayerIndex < ALTOSTRATUS_LAYER){
|
|
|
|
float density = parameters.smallCumulus.y;
|
|
if(LayerIndex == LARGECUMULUS_LAYER) density = parameters.largeCumulus.y;
|
|
|
|
float skylightOcclusion = 1.0;
|
|
#if defined CloudLayer1 && defined CloudLayer0
|
|
if(LayerIndex == SMALLCUMULUS_LAYER) {
|
|
float upperLayerOcclusion = getCloudShape(LARGECUMULUS_LAYER, 0, rayPosition + vec3(0.0,1.0,0.0) * max((CloudLayer1_height+20) - rayPosition.y,0.0), CloudLayer1_height, CloudLayer1_height+100.0);
|
|
skylightOcclusion = mix(mix(0.0,0.2,parameters.largeCumulus.y), 1.0, pow(1.0 - upperLayerOcclusion*parameters.largeCumulus.y,2));
|
|
}
|
|
#endif
|
|
|
|
for(int i = 0; i < samples; i++) {
|
|
|
|
// check if the ray staring position is going farther than the reference distance, if yes, dont begin marching. this is to check for intersections with the world.
|
|
#ifndef VL_CLOUDS_DEFERRED
|
|
if(length(rayPosition - cameraPosition) > referenceDistance) break;
|
|
#endif
|
|
|
|
// check if the pixel is in the bounding box before doing work.
|
|
if(clamp(rayPosition.y - maxHeight,0.0,1.0) < 1.0 && clamp(rayPosition.y - minHeight,0.0,1.0) > 0.0){
|
|
|
|
float shape = getCloudShape(LayerIndex, 1, rayPosition, minHeight, maxHeight);
|
|
float shapeWithDensity = shape*density;
|
|
float shapeWithDensityFaded = shape*density * pow(clamp((rayPosition.y - minHeight)/(max(maxHeight-minHeight,1.0)*0.25),0.0,1.0),2.0);
|
|
|
|
if(shapeWithDensityFaded > densityTresholdCheck && cloudPlaneDistance.y > 0.5){
|
|
cloudPlaneDistance.x = length(rayPosition - cameraPosition); cloudPlaneDistance.y = 0.0;
|
|
}
|
|
|
|
// check if the pixel has visible clouds before doing work.
|
|
if(shapeWithDensityFaded > 1e-5){
|
|
// can add the initial cloud shape sample for a free shadow starting step :D
|
|
float indirectShadowMask = 1.0 - min(max(rayPosition.y - minHeight,0.0) / max(maxHeight-minHeight,1.0), 1.0);
|
|
float sunShadowMask = getCloudScattering(LayerIndex, rayPosition, sunVector, dither, minHeight, maxHeight, density);
|
|
|
|
// do cloud shadows from one layer to another
|
|
// large cumulus layer -> small cumulus layer
|
|
#if defined CloudLayer0 && defined CloudLayer1
|
|
if(LayerIndex == SMALLCUMULUS_LAYER){
|
|
vec3 shadowStartPos = rayPosition + sunVector / abs(sunVector.y) * max((CloudLayer1_height + 20.0) - rayPosition.y, 0.0);
|
|
sunShadowMask += 3.0 * getCloudShape(LARGECUMULUS_LAYER, 0, shadowStartPos, CloudLayer1_height, CloudLayer1_height+100.0)*parameters.largeCumulus.y;
|
|
}
|
|
#endif
|
|
|
|
// altostratus layer -> all cumulus layers
|
|
#if defined CloudLayer2
|
|
vec3 shadowStartPos = rayPosition + sunVector / abs(sunVector.y) * max(CloudLayer2_height - rayPosition.y, 0.0);
|
|
sunShadowMask += getCloudShape(ALTOSTRATUS_LAYER, 0, shadowStartPos, CloudLayer2_height, CloudLayer2_height) * parameters.altostratus.y * (1.0-abs(sunVector.y));
|
|
#endif
|
|
|
|
vec3 lighting = getCloudLighting(shapeWithDensity, shapeWithDensityFaded, sunShadowMask, sunScattering, indirectShadowMask, skyScattering * skylightOcclusion, backScatterPhase, phaseLevels);
|
|
|
|
#if defined LIGHTNING_FLASH && defined LIGHTNINGFLASH_VL
|
|
lighting += createLightningPointLight(rayPosition - cameraPosition, lightningBoltPosition.xyz, shapeWithDensity, indirectShadowMask);
|
|
#endif
|
|
|
|
vec3 newPos = rayPosition - cameraPosition;
|
|
|
|
#ifdef AERIAL_PERSPECTIVE_TEST
|
|
float skydensity = exp(-0.00035*length(newPos));
|
|
float ifAboveOrBelowPlane = mix(-1.0, 1.0, clamp(cameraPosition.y - minHeight,0.0,1.0)) ;
|
|
|
|
vec3 samplesky = skyFromTex(clamp(normalize(vec3(newPos.x,ifAboveOrBelowPlane*newPos.y,newPos.z)) - vec3(0,curvatureoffset - 0.005,0),-1,1) , colortex4).rgb/1200.0;
|
|
|
|
lighting = lighting * skydensity + (samplesky - samplesky * skydensity);
|
|
#else
|
|
newPos.xz /= max(newPos.y,0.0)*0.0025 + 1.0;
|
|
newPos.y = min(newPos.y,0.0);
|
|
|
|
float distancefog = exp(-(0.00035 + rainStrength * 0.0015) * length(newPos));
|
|
vec3 atmosphereHaze = (sampledSkyCol - sampledSkyCol * distancefog);
|
|
lighting = lighting * distancefog + atmosphereHaze;
|
|
#endif
|
|
|
|
|
|
float densityCoeff = exp(-distanceFactor*shapeWithDensityFaded);
|
|
color += (lighting - lighting * densityCoeff) * totalAbsorbance;
|
|
totalAbsorbance *= densityCoeff;
|
|
|
|
// check if you can see through the cloud on the pixel before doing the next iteration
|
|
if (totalAbsorbance < 1e-5) break;
|
|
|
|
}
|
|
}
|
|
|
|
rayPosition += rayDirection;
|
|
|
|
}
|
|
return vec4(color, totalAbsorbance);
|
|
}
|
|
|
|
}
|
|
|
|
vec3 getRayOrigin(
|
|
vec3 rayStartPos,
|
|
vec3 cameraPos,
|
|
float dither,
|
|
|
|
float minHeight,
|
|
float maxHeight
|
|
,int samples
|
|
){
|
|
|
|
vec3 cloudDist = vec3(1.0);
|
|
cloudDist.xz = mix(vec2(255.0), vec2(5.0), clamp(cameraPos.y - minHeight ,0.0,clamp((maxHeight-15)-cameraPosition.y ,0.0,1.0)));
|
|
// allow passing through/above/below the plane without limits
|
|
float flip = mix(max(cameraPos.y - maxHeight,0.0), max(minHeight - cameraPos.y,0.0), clamp(rayStartPos.y,0.0,1.0));
|
|
|
|
// orient the ray to be a flat plane facing up/down
|
|
// vec3 position = rayStartPos*dither + cameraPos + (rayStartPos/abs(rayStartPos.y/cloudDist)) * flip;
|
|
vec3 position = rayStartPos*dither + cameraPos + (rayStartPos/length(rayStartPos/cloudDist)) * flip;
|
|
|
|
return position;
|
|
}
|
|
|
|
vec4 getCorrectBlendOrder(int viewIndex, vec4 smallCumulusColor,vec4 largeCumulusColor,vec4 altostratusColor){
|
|
|
|
// swap order of blending based on where the camera position is relative to the cloud plane altitude.
|
|
// viewIndex = 0 is below small cumulus layer
|
|
// viewIndex = 1 is above large cumulus layer
|
|
// viewIndex = 2 is above altostratus layer
|
|
|
|
vec4 blendedCloudsColor = vec4(0.0,0.0,0.0,1.0);
|
|
|
|
switch (viewIndex){
|
|
default : { break; }
|
|
|
|
case 0: {
|
|
#ifdef CloudLayer2
|
|
blendedCloudsColor = altostratusColor;
|
|
#endif
|
|
#ifdef CloudLayer1
|
|
blendedCloudsColor.rgb = blendedCloudsColor.rgb * largeCumulusColor.a + largeCumulusColor.rgb;
|
|
blendedCloudsColor.a *= largeCumulusColor.a;
|
|
#endif
|
|
#ifdef CloudLayer0
|
|
blendedCloudsColor.rgb = blendedCloudsColor.rgb * smallCumulusColor.a + smallCumulusColor.rgb;
|
|
blendedCloudsColor.a *= smallCumulusColor.a;
|
|
#endif
|
|
break; }
|
|
|
|
case 1: {
|
|
#ifdef CloudLayer2
|
|
blendedCloudsColor = altostratusColor;
|
|
#endif
|
|
#ifdef CloudLayer0
|
|
blendedCloudsColor.rgb = blendedCloudsColor.rgb * smallCumulusColor.a + smallCumulusColor.rgb;
|
|
blendedCloudsColor.a *= smallCumulusColor.a;
|
|
#endif
|
|
#ifdef CloudLayer1
|
|
blendedCloudsColor.rgb = blendedCloudsColor.rgb * largeCumulusColor.a + largeCumulusColor.rgb;
|
|
blendedCloudsColor.a *= largeCumulusColor.a;
|
|
#endif
|
|
break; }
|
|
|
|
case 2: {
|
|
#ifdef CloudLayer0
|
|
blendedCloudsColor = smallCumulusColor;
|
|
#endif
|
|
#ifdef CloudLayer1
|
|
blendedCloudsColor.rgb = blendedCloudsColor.rgb * largeCumulusColor.a + largeCumulusColor.rgb;
|
|
blendedCloudsColor.a *= largeCumulusColor.a;
|
|
#endif
|
|
#ifdef CloudLayer2
|
|
blendedCloudsColor.rgb = blendedCloudsColor.rgb * altostratusColor.a + altostratusColor.rgb;
|
|
blendedCloudsColor.a *= altostratusColor.a;
|
|
#endif
|
|
break; }
|
|
}
|
|
|
|
return blendedCloudsColor;
|
|
|
|
}
|
|
|
|
vec4 GetVolumetricClouds(
|
|
vec3 viewPos,
|
|
vec2 dither,
|
|
vec3 sunVector,
|
|
vec3 directLightCol,
|
|
vec3 indirectLightCol,
|
|
|
|
inout float cloudPlaneDistance
|
|
,in vec4 phaseLevels
|
|
,in float backScatterPhase
|
|
){
|
|
#if !(defined CloudLayer0 || defined CloudLayer1 || defined CloudLayer2)
|
|
return vec4(0.0,0.0,0.0,1.0);
|
|
#endif
|
|
|
|
vec3 color = vec3(0.0);
|
|
float totalAbsorbance = 1.0;
|
|
vec4 cloudColor = vec4(color, totalAbsorbance);
|
|
|
|
float cloudheight = CloudLayer0_tallness / CloudLayer0_scale;
|
|
float minHeight = CloudLayer0_height;
|
|
float maxHeight = cloudheight + minHeight;
|
|
|
|
#if defined OVERWORLD_SHADER && defined AETHER_FLAG
|
|
minHeight = CloudLayer0_height - 350.0;
|
|
maxHeight = cloudheight + minHeight;
|
|
#endif
|
|
|
|
int layerViewIndex = 0;
|
|
#if defined CloudLayer1
|
|
if(CloudLayer1_height < cameraPosition.y) layerViewIndex = 1;
|
|
#endif
|
|
#if defined CloudLayer2
|
|
if(CloudLayer2_height < cameraPosition.y) layerViewIndex = 2;
|
|
#endif
|
|
|
|
float heightRelativeToClouds = clamp(1.0 - max(cameraPosition.y - minHeight,0.0) / 100.0 ,0.0,1.0);
|
|
|
|
#ifdef USING_LOD_MOD
|
|
float maxdist = LOD_RENDERDISTANCE;
|
|
#else
|
|
float maxdist = far + 16.0*5.0;
|
|
#endif
|
|
|
|
float lViewPosM = length(viewPos) < maxdist ? length(viewPos) - 1.0 : 100000000.0;
|
|
vec4 NormPlayerPos = normalize(gbufferModelViewInverse * vec4(viewPos, 1.0) + vec4(gbufferModelViewInverse[3].xyz,0.0));
|
|
|
|
vec3 signedSunVec = sunVector;
|
|
vec3 unignedSunVec = sunVector;// * (float(sunElevation > 1e-5)*2.0-1.0);
|
|
float SdotV = dot(unignedSunVec, NormPlayerPos.xyz);
|
|
|
|
// #ifdef SKY_GROUND
|
|
NormPlayerPos.y += curvatureoffset;
|
|
// #endif
|
|
|
|
float maxSamples = 15.0;
|
|
float minSamples = 10.0;
|
|
int samples = int(clamp(maxSamples / sqrt(exp2(NormPlayerPos.y)), 1.0, minSamples));
|
|
// samples = 200;
|
|
|
|
///------- setup the ray
|
|
vec3 cloudDist = vec3(1.0);
|
|
cloudDist.xz = mix(vec2(255.0), vec2(5.0), clamp(cameraPosition.y - minHeight,0.0,clamp((maxHeight-5) - cameraPosition.y ,0.0,1.0)));
|
|
|
|
vec3 rayDirection = NormPlayerPos.xyz * (cloudheight/length(NormPlayerPos.xyz/cloudDist)/samples);
|
|
vec3 rayPosition = getRayOrigin(rayDirection, cameraPosition, dither.y, minHeight, maxHeight,samples);
|
|
|
|
#ifdef SKY_GROUND
|
|
vec3 sampledSkyCol = mix(skyFromTex(normalize(rayPosition - cameraPosition), colortex4)/1200.0 * Sky_Brightness, indirectLightCol, 1.0);
|
|
#else
|
|
vec3 sampledSkyCol = skyFromTex(normalize(rayPosition - cameraPosition), colortex4)/1200.0 * Sky_Brightness;
|
|
#endif
|
|
// setup for getting distance
|
|
vec3 playerPos = mat3(gbufferModelViewInverse) * viewPos;
|
|
|
|
#ifdef USING_LOD_MOD
|
|
float maxLength = min(length(playerPos), max(far, LOD_RENDERDISTANCE))/length(playerPos);
|
|
#else
|
|
float maxLength = min(length(playerPos), far)/length(playerPos);
|
|
#endif
|
|
|
|
playerPos *= maxLength;
|
|
|
|
float startDistance = length(playerPos);
|
|
|
|
#if defined EXCLUDE_WRITE_TO_LUT && defined USE_CUSTOM_CLOUD_LIGHTING_COLORS
|
|
directLightCol = dot(directLightCol,vec3(0.21, 0.72, 0.07)) * vec3(DIRECTLIGHT_CLOUDS_R,DIRECTLIGHT_CLOUDS_G,DIRECTLIGHT_CLOUDS_B);
|
|
indirectLightCol = dot(indirectLightCol,vec3(0.21, 0.72, 0.07)) * vec3(INDIRECTLIGHT_CLOUDS_R,INDIRECTLIGHT_CLOUDS_G,INDIRECTLIGHT_CLOUDS_B);
|
|
#endif
|
|
|
|
///------- do color stuff outside of the raymarcher loop
|
|
// the idea is to interpolate between 4 HG function calls with different G parameters
|
|
// float backScatterPhase = phaseCloud(-SdotV, 0.25) * 2.0;
|
|
// vec4 phaseLevels = vec4(phaseCloud(SdotV, 0.80), phaseCloud(SdotV, 0.55), phaseCloud(SdotV, 0.35), phaseCloud(SdotV, 0.10));
|
|
|
|
// backScatterPhase = SdotV;
|
|
|
|
vec3 sunScattering = directLightCol;
|
|
vec3 skyScattering = indirectLightCol * (1.0 + pow(1.0-pow(1.0-clamp(sunVector.y,0.0,1.0),5.0),5.0));
|
|
|
|
bool occlusionCheck = true;
|
|
////------- RENDER SMALL CUMULUS CLOUDS
|
|
vec4 smallCumulusClouds = cloudColor;
|
|
|
|
vec2 cloudLayer0_Distance = vec2(startDistance, 1.0);
|
|
#ifdef CloudLayer0
|
|
smallCumulusClouds = raymarchCloud(SMALLCUMULUS_LAYER, samples, rayPosition, rayDirection, dither.x, minHeight, maxHeight, unignedSunVec, sunScattering, skyScattering, lViewPosM, sampledSkyCol, cloudLayer0_Distance, backScatterPhase, phaseLevels);
|
|
#endif
|
|
|
|
////------- RENDER LARGE CUMULUS CLOUDS
|
|
vec4 largeCumulusClouds = cloudColor;
|
|
|
|
#ifdef CloudLayer1
|
|
cloudheight = CloudLayer1_tallness/CloudLayer1_scale;
|
|
minHeight = CloudLayer1_height;
|
|
maxHeight = cloudheight + minHeight;
|
|
|
|
cloudDist.xz = mix(vec2(255.0), vec2(5.0), clamp(cameraPosition.y - minHeight,0.0,clamp((maxHeight-15) - cameraPosition.y ,0.0,1.0)));
|
|
rayDirection = NormPlayerPos.xyz * (cloudheight/length(NormPlayerPos.xyz/cloudDist)/samples);
|
|
rayPosition = getRayOrigin(rayDirection, cameraPosition, dither.y, minHeight, maxHeight,samples);
|
|
|
|
vec2 cloudLayer1_Distance = vec2(startDistance, 1.0);
|
|
|
|
occlusionCheck = layerViewIndex < 1 ? smallCumulusClouds.a > 1e-5 : true;
|
|
if(occlusionCheck) largeCumulusClouds = raymarchCloud(LARGECUMULUS_LAYER, samples, rayPosition, rayDirection, dither.x, minHeight, maxHeight, unignedSunVec, sunScattering, skyScattering, lViewPosM, sampledSkyCol, cloudLayer1_Distance, backScatterPhase, phaseLevels);
|
|
#endif
|
|
|
|
////------- RENDER ALTOSTRATUS CLOUDS
|
|
vec4 altoStratusClouds = cloudColor;
|
|
|
|
#ifdef CloudLayer2
|
|
cloudheight = 5.0;
|
|
minHeight = CloudLayer2_height;
|
|
maxHeight = cloudheight + minHeight;
|
|
|
|
cloudDist.xz = mix(vec2(255.0), vec2(5.0), clamp(cameraPosition.y - minHeight,0.0,clamp((maxHeight-15) - cameraPosition.y ,0.0,1.0)));
|
|
rayDirection = NormPlayerPos.xyz * (cloudheight/length(NormPlayerPos.xyz/cloudDist));
|
|
rayPosition = getRayOrigin(rayDirection, cameraPosition, dither.y, minHeight, maxHeight,1);
|
|
|
|
vec2 cloudLayer2_Distance = vec2(startDistance, 1.0);
|
|
|
|
occlusionCheck = layerViewIndex < 2 ? (smallCumulusClouds.a > 1e-5 || largeCumulusClouds.a > 1e-5) : true;
|
|
if(occlusionCheck) altoStratusClouds = raymarchCloud(ALTOSTRATUS_LAYER, samples, rayPosition, rayDirection, dither.x, minHeight, maxHeight, unignedSunVec, sunScattering, skyScattering, lViewPosM, sampledSkyCol, cloudLayer2_Distance, backScatterPhase, phaseLevels);
|
|
#endif
|
|
|
|
////------- BLEND LAYERS
|
|
|
|
// this is for an intersection check for VL fog, so that fog cannot march beyond a cloud.
|
|
#if defined CloudLayer0 && defined CloudLayer1 && defined CloudLayer2
|
|
cloudPlaneDistance = mix(cloudLayer1_Distance.x, cloudLayer2_Distance.x, cloudLayer1_Distance.y);
|
|
cloudPlaneDistance = mix(cloudLayer0_Distance.x, cloudPlaneDistance, cloudLayer0_Distance.y);
|
|
#endif
|
|
#if defined CloudLayer0 && !defined CloudLayer1 && !defined CloudLayer2
|
|
cloudPlaneDistance = cloudLayer0_Distance.x;
|
|
#endif
|
|
#if defined CloudLayer0 && defined CloudLayer1 && !defined CloudLayer2
|
|
cloudPlaneDistance = mix(cloudLayer0_Distance.x, cloudLayer1_Distance.x, cloudLayer0_Distance.y);
|
|
#endif
|
|
#if !defined CloudLayer0 && defined CloudLayer1 && defined CloudLayer2
|
|
cloudPlaneDistance = mix(cloudLayer2_Distance.x, cloudLayer1_Distance.x, cloudLayer2_Distance.y);
|
|
#endif
|
|
#if defined CloudLayer0 && !defined CloudLayer1 && !defined CloudLayer2
|
|
cloudPlaneDistance = cloudLayer0_Distance.x;
|
|
#endif
|
|
#if defined CloudLayer0 && !defined CloudLayer1 && defined CloudLayer2
|
|
cloudPlaneDistance = cloudLayer0_Distance.x;
|
|
#endif
|
|
#if !defined CloudLayer0 && defined CloudLayer1 && !defined CloudLayer2
|
|
cloudPlaneDistance = cloudLayer1_Distance.x;
|
|
#endif
|
|
#if !defined CloudLayer0 && !defined CloudLayer1 && defined CloudLayer2
|
|
cloudPlaneDistance = cloudLayer2_Distance.x;
|
|
#endif
|
|
|
|
vec4 blendedCloudColor = getCorrectBlendOrder(layerViewIndex, smallCumulusClouds, largeCumulusClouds, altoStratusClouds);
|
|
|
|
color = blendedCloudColor.rgb;
|
|
totalAbsorbance = blendedCloudColor.a;
|
|
|
|
// return vec4(vec3(cloudPlaneDistance/1500.0), 0.0);
|
|
|
|
return vec4(color, totalAbsorbance);
|
|
}
|
|
#endif |