#version 310 es
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
uniform vec2 resolution;
uniform float time;
//uniform sampler2D ironblock;
uniform vec2 touch;
uniform sampler2D ironblock;///min:n;mag:n;s:r;t:r;
float rainy = 0.0;
#define u_resolution resolution
#define u_mouse touch
#define u_time time
#define CAM_FOV 2.5
#define MOUSE_SENSITIVITY 6.3
#define SPHERE_SIZE 0.04
#define saturate(x) clamp(x,0.0,1.0)
out vec4 outCol;
/* CONFIG */
// PLAY AROUND WITH THE CODE
// Search learnopengl to learn about this
#define ROUGHNESS 0.2
#define METALLIC 0.0 // I set it zero it darkens texture
#define SUNCOLOR vec3(1.0, 0.5, 0.1)
#define RESOLUTION 102 // in Rd you will set it to [128,256,512,1024]
const float PI = 3.141592653589793;
// --- Tone mapping ---
vec3 ACESFilm(vec3 x) {
return clamp((x * (2.51 * x + 0.03)) /
(x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);
}
// --- Atmospheric scattering ---
float rayleighPhase(float cosTheta) {
return (3.0 / (16.0 * PI)) * (1.0 + cosTheta * cosTheta);
}
float miePhase(float cosTheta, float g) {
float g2 = g * g;
return (1.0 - g2) /
(4.0 * PI * pow(1.0 + g2 - 2.0 * g * cosTheta, 1.5));
}
float opticaldepth(float k, float Vy) {
return 1.0 - exp(-k * max(Vy, 0.0));
}
vec3 scattering(vec3 viewDir, vec3 sunDir) {
float cosTheta = dot(viewDir, sunDir);
float depth = opticaldepth(2.0, viewDir.y);
float r = rayleighPhase(cosTheta);
float m = miePhase(cosTheta, 0.8);
vec3 rayleighColor = vec3(0.8, 0.8, 1.0) * r * depth;
vec3 mieColor = vec3(1.0, 0.5, 0.1) * m * depth;
return rayleighColor + mieColor;
}
// --- Sky color ---
vec3 skyColor(vec3 viewDir, vec3 sunDir,bool sky) {
float y = smoothstep(0.0, 1.0, viewDir.y);
vec3 dayhorizon = vec3(0.7, 0.85, 1.0);
vec3 dayzenith = vec3(0.2, 0.5, 0.9);
vec3 duskzenith = vec3(0.1, 0.15, 0.3);
vec3 duskhorizon = vec3(1.0, 0.35, 0.1);
vec3 nightzenith = vec3(0.03, 0.05, 0.15);
vec3 nighthorizon = vec3(0.07, 0.1, 0.25);
vec3 up = vec3(0.0, 1.0, 0.0);
float sunHeight = dot(normalize(sunDir), up);
float dayThreshold = 0.8;
float duskThreshold = 0.7;
vec3 skyCol;
if (sunHeight > dayThreshold) {
skyCol = mix(dayhorizon, dayzenith, y);
} else if (sunHeight > duskThreshold) {
float t = smoothstep(duskThreshold, dayThreshold, sunHeight);
vec3 dayColor = mix(dayhorizon, dayzenith, y);
vec3 duskColor = mix(duskhorizon, duskzenith, y);
skyCol = mix(duskColor, dayColor, t);
} else if (sunHeight > -duskThreshold) {
float t = smoothstep(-duskThreshold, duskThreshold, sunHeight);
vec3 duskColor = mix(duskhorizon, duskzenith, y);
vec3 nightColor = mix(nighthorizon, nightzenith, y);
skyCol = mix(nightColor, duskColor, t);
} else {
skyCol = mix(nighthorizon, nightzenith, y);
}
if(sky){
skyCol += scattering(viewDir, sunDir);
}else{
}
return skyCol;
}
// === ACES Tonemapping ===
vec3 RRTAndODTFit(vec3 v) {
vec3 a = v * (v + 0.0245786) - 0.000090537;
vec3 b = v * (0.983729 * v + 0.4329510) + 0.238081;
return a / b;
}
vec3 ACESFittedTonemap(vec3 color) {
return saturate(RRTAndODTFit(color));
}
// === Saturation ===
vec3 AdjustSaturation(vec3 color, float saturation) {
float avg = (color.r + color.g + color.b) / 3.0;
return mix(vec3(avg), color, saturation);
}
vec3 colorPost(vec3 col){
col = ACESFittedTonemap(col);
col = saturate(col);
col = AdjustSaturation(col,1.15);
return col;
}
float hash13(vec3 p) {
p = fract(p * 0.1031);
p += dot(p, p.yzx + 33.33);
return fract((p.x + p.y) * p.z);
}
vec4 sampleClouds(vec3 rayOrigin, vec3 viewDir, float cloudBase, float cloudTop,
float cellSize, float time,vec2 sp) {
int steps = 4;
float stepSize = (cloudTop - cloudBase) / float(steps);
vec3 cloudAccum = vec3(0.0);
float alphaAccum = 0.0;
float viewLift = smoothstep(0.01, 0.1, viewDir.y);
for (int i = 0; i < steps; i++) {
float height = cloudBase + stepSize * float(i);
float t = height / max(viewDir.y, 0.001);
float jitterX = fract(sin(dot(sp.xy + vec2(float(i), height),
vec2(12.9898,78.233))) * 43758.5453) - 0.5;
float jitterZ = fract(sin(dot(sp.xy + vec2(height, float(i)),
vec2(93.9898,67.345))) * 43758.5453) - 0.5;
vec3 pos = rayOrigin + viewDir * t;
float base = hash13(floor(pos/0.9));
float density = step(0.55, base);
float heightNorm = (height - cloudBase) / (cloudTop - cloudBase);
float heightFactor =
step(0.2, heightNorm) *
(1.0 - step(0.6, heightNorm));
density = pow(density,2.2) * heightFactor;
float alpha = density * (1.0 - alphaAccum) * viewLift;
float scattering = smoothstep(0.0, 0.5, heightNorm);
cloudAccum += vec3(1.0) * (scattering)*alpha;
alphaAccum += alpha;
if (alphaAccum > 0.98 && viewDir.y < 0.9) break;
}
return vec4(cloudAccum, alphaAccum);
}
float fresnelSchlickRoughness(vec3 N, vec3 V, float F0, float roughness)
{
float cosTheta = clamp(dot(normalize(N), normalize(V)), 0.0, 1.0);
return F0 + (max(1.0 - roughness, F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
float luminance601(vec3 color) {
return color.r * 0.299 + color.g * 0.587 + color.b * 0.114;
}
vec3 getNormal(sampler2D TEXTURE_0, vec2 coord) {
float offsets = 1.0 / float(RESOLUTION);
//float offsets = 0.00125;
float lumR = luminance601(texture(TEXTURE_0, coord + vec2(offsets, 0.0)).rgb);
float lumL = luminance601(texture(TEXTURE_0, coord - vec2(offsets, 0.0)).rgb);
float lumD = luminance601(texture(TEXTURE_0, coord + vec2(0.0, offsets)).rgb);
float lumU = luminance601(texture(TEXTURE_0, coord - vec2(0.0, offsets)).rgb);
vec2 gradient = vec2(lumR - lumL, lumD - lumU);
float lenSq = dot(gradient, gradient);
vec3 normal = normalize(vec3(gradient, sqrt(max(0.0,1.0-lenSq))));
return normalize(normal);
}
mat3 getTBN(vec3 normal) {
vec3 T = vec3(abs(normal.y) + normal.z, 0.0, normal.x);
vec3 B = vec3(0.0, -abs(normal).x - abs(normal).z, abs(normal).y);
vec3 N = normal;
return transpose(mat3(T, B, N));
}
vec3 brdf(vec3 lightDir, vec3 viewDir, float roughness, vec3 normal, vec3 albedo,
float metallic, vec3 reflectance, vec3 sunCol) {
float alpha = pow(roughness,2.0);
vec3 H = normalize(lightDir + viewDir);
//dot products
float NdotV = clamp(dot(normal, viewDir), 0.5,1.0);
float NdotL = clamp(dot(normal, lightDir), 0.5,1.0);
float NdotH = clamp(dot(normal,H), 0.5,1.0);
float VdotH = clamp(dot(viewDir, H), 0.5,1.0);
// Fresnel
vec3 F0 = reflectance;
vec3 fresnelReflectance = F0 + (1.0 - F0) * pow(1.0 - VdotH, 5.0); //Schlick's
Approximation
//phong diffuse
vec3 rhoD = albedo;
rhoD *= (vec3(1.0)- fresnelReflectance); //energy conservation - light that
doesn't reflect adds to diffuse
rhoD *= (1.0-metallic); //diffuse is 0 for metals
// Geometric attenuation
float k = alpha/2.0;
float geometry = (NdotL / (NdotL*(1.0-k)+k)) * (NdotV / ((NdotV*(1.0-k)+k)));
// Distribution of Microfacets
float lowerTerm = pow(NdotH,2.0) * (pow(alpha,2.0) - 1.0) + 1.0;
float normalDistributionFunctionGGX = pow(alpha,2.0) / (3.14159 *
pow(lowerTerm,2.0));
vec3 phongDiffuse = rhoD; //
vec3 cookTorrance =
(fresnelReflectance*normalDistributionFunctionGGX*geometry)/(4.0*NdotL*NdotV);
vec3 BRDF = (phongDiffuse+cookTorrance*sunCol)*NdotL;
vec3 diffFunction = BRDF;
return BRDF;
}
// --- World rendering ---
vec3 renderWorld(vec3 vdir, vec3 wpos, float t) {
vec3 sunDir = normalize(vec3(cos(t * 0.05), sin(t * 0.05), 0.2));
vec3 s = skyColor(vdir, sunDir,true);
if (wpos.y > 0.0) {
vec3 rayOrigin = vec3(0.0);
float cloudBase = 1.5;
float cloudTop = 2.0;
float cellSize = 0.9;
vec2 screenpos = gl_FragCoord.xy;
vec4 clouds = sampleClouds(rayOrigin, vdir, cloudBase, cloudTop, cellSize,
time, screenpos);
s = mix(s, clouds.rgb, clouds.a*0.3);
// Sky
} else {
// Reflection
vec2 waterUV = vdir.xz/vdir.y ;
vec2 blockuv = wpos.xz/wpos.y;
vec3 blockNormal = getNormal(ironblock, waterUV)*2.0;
vec3 worldPos = wpos.xyz; // in Rd use v_position = a_position in vertex
vec3 dpx = dFdx(worldPos);
vec3 dpy = dFdy(worldPos);
vec3 Ngeo = normalize(cross(dpx, dpy));
mat3 TBN = getTBN(Ngeo);
// in Rd you will use mul(Tex/position,mat3)
// example : vec3 worldNormal = normalize(mul(blockNormal,TBN)).xyz;
vec3 worldNormal = normalize(TBN*blockNormal).xyz;
vec3 V = normalize(-wpos);//viewDir used by normals
vdir = reflect(vdir, worldNormal);//distortion using worldNormal
vec3 rayOrigin = vec3(0.0);
float cloudBase = 1.5;
float cloudTop = 2.0;
float cellSize = 0.9;
vec2 screenpos = gl_FragCoord.xy;
vec4 clouds = sampleClouds(rayOrigin, vdir, cloudBase, cloudTop, cellSize,
time, screenpos);
//won't explain this since it doesn't work as expected but good enough
float fresnel = fresnelSchlickRoughness(worldNormal,V, 0.6, 0.2);
s = skyColor(vdir, sunDir,false);
s = mix(s, clouds.rgb, clouds.a*0.2);
vec3 base = texture(ironblock,blockuv).rgb;
//albedo = base.rgb = texture without shading
// s is skyColor
base.rgb = base.rgb * (1.0 - fresnel) + s * fresnel;
vec3 F0_Iron = vec3(0.56, 0.57, 0.58);//reflectance
vec3 specular = brdf(sunDir,V,ROUGHNESS, worldNormal, base.rgb,
METALLIC,F0_Iron,SUNCOLOR);
s = 1.9 * specular;
}
return s;
}
// --- Main ---
void main() {
vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / u_resolution.x;
vec2 an = MOUSE_SENSITIVITY * ((u_mouse.yx / u_resolution.yx) - 0.5);
an.x = clamp(-an.x, -1.57, 1.57);
vec2 s = sin(an), c = cos(an);
mat3 r = mat3(
c.y, s.x * s.y, c.x * s.y,
0.0, c.x, -s.x,
-s.y, s.x * c.y, c.x * c.y
);
vec3 vdir = normalize(vec3(uv, 1.0 / tan(0.5 * CAM_FOV))) * r;
vec2 st = gl_FragCoord.xy / (u_resolution.x * SPHERE_SIZE) - 1.4;
bool f = st.x > 1.0; if (f) st.x -= 2.3;
float d = dot(st, st);
if (d < 1.0) {
vdir = vec3(st, sqrt(1.0 - d));
if (f) vdir = vdir.xzy;
}
vec3 wpos = vdir / abs(vdir.y);
vec3 color = renderWorld(vdir, wpos, u_time);
outCol = vec4(colorPost(color), 1.0);
}