#version 450 // Wavescape - dr2 - 2015-01-05 // https://www.shadertoy.com/view/lls3z7 // Another wave renderer, from both above and below the waterline. See the source for where it all comes from. // "Wavescape" by dr2 - 2015 // License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License // Water waves, including what the fish sees. // Acknowledgments: thanks for the following - // Dave_H's multilayered clouds with nimitz's variable layer spacing // (but depends on elevation rather than distance). // Wave shapes from TDM; they seem a little more "energetic" than TekF's. // Raymarching with binary subdivision, as used by Dave_H for mountains; // TekF and TDM use one or the other, not both. // Buoy based on TekF's, but raymarched for generality; shows aging effects // below waterline. // Foam idea from TekF. // Noise functions from iq. layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; vec4 OutputSize; vec4 OriginalSize; vec4 SourceSize; uint FrameCount; } global; #pragma stage vertex layout(location = 0) in vec4 Position; layout(location = 1) in vec2 TexCoord; layout(location = 0) out vec2 vTexCoord; const vec2 madd = vec2(0.5, 0.5); void main() { gl_Position = global.MVP * Position; vTexCoord = gl_Position.xy; } #pragma stage fragment layout(location = 0) in vec2 vTexCoord; layout(location = 0) out vec4 FragColor; float iGlobalTime = float(global.FrameCount)*0.025; vec2 iResolution = global.OutputSize.xy; const float pi = 3.14159; const vec4 cHashA4 = vec4 (0., 1., 57., 58.); const vec3 cHashA3 = vec3 (1., 57., 113.); const float cHashM = 43758.54; vec2 Hashv2f (float p) { return fract (sin (p + cHashA4.xy) * cHashM); } vec4 Hashv4f (float p) { return fract (sin (p + cHashA4) * cHashM); } vec4 Hashv4v3 (vec3 p) { const vec3 cHashVA3 = vec3 (37.1, 61.7, 12.4); const vec3 e = vec3 (1., 0., 0.); return fract (sin (vec4 (dot (p + e.yyy, cHashVA3), dot (p + e.xyy, cHashVA3), dot (p + e.yxy, cHashVA3), dot (p + e.xxy, cHashVA3))) * cHashM); } float Noiseff (float p) { float i, f; i = floor (p); f = fract (p); f = f * f * (3. - 2. * f); vec2 t = Hashv2f (i); return mix (t.x, t.y, f); } float Noisefv2 (vec2 p) { vec2 i, f; i = floor (p); f = fract (p); f = f * f * (3. - 2. * f); vec4 t = Hashv4f (dot (i, cHashA3.xy)); return mix (mix (t.x, t.y, f.x), mix (t.z, t.w, f.x), f.y); } float Noisefv3a (vec3 p) { vec3 i, f; i = floor (p); f = fract (p); f *= f * (3. - 2. * f); vec4 t1 = Hashv4v3 (i); vec4 t2 = Hashv4v3 (i + vec3 (0., 0., 1.)); return mix (mix (mix (t1.x, t1.y, f.x), mix (t1.z, t1.w, f.x), f.y), mix (mix (t2.x, t2.y, f.x), mix (t2.z, t2.w, f.x), f.y), f.z); } float Fbm1 (float p) { float f, a; f = 0.; a = 1.; for (int i = 0; i < 5; i ++) { f += a * Noiseff (p); a *= 0.5; p *= 2.; } return f; } float Fbm3 (vec3 p) { const mat3 mr = mat3 (0., 0.8, 0.6, -0.8, 0.36, -0.48, -0.6, -0.48, 0.64); float f, a, am, ap; f = 0.; a = 0.5; am = 0.5; ap = 4.; p *= 0.5; for (int i = 0; i < 6; i ++) { f += a * Noisefv3a (p); p *= mr * ap; a *= am; } return f; } float Fbmn (vec3 p, vec3 n) { vec3 f = vec3 (0.); float a = 1.; for (int i = 0; i < 5; i ++) { f += a * vec3 (Noisefv2 (p.yz), Noisefv2 (p.zx), Noisefv2 (p.xy)); a *= 0.5; p *= 2.; } return dot (f, abs (n)); } vec3 VaryNf (vec3 p, vec3 n, float f) { vec3 e = vec3 (0.2, 0., 0.); float s = Fbmn (p, n); vec3 g = vec3 (Fbmn (p + e.xyy, n) - s, Fbmn (p + e.yxy, n) - s, Fbmn (p + e.yyx, n) - s); return normalize (n + f * (g - n * dot (n, g))); } float SmoothBump (float lo, float hi, float w, float x) { return (1. - smoothstep (hi - w, hi + w, x)) * smoothstep (lo - w, lo + w, x); } float PrSphDf (vec3 p, float s) { return length (p) - s; } float PrCylDf (vec3 p, float r, float h) { return max (length (p.xy) - r, abs (p.z) - h); } int idObj; mat3 ballMat; vec3 qHit, ballPos, sunCol, sunDir, cloudDisp, waterDisp; float tCur, fCloud; const float dstFar = 300.; vec3 SkyGrndCol (vec3 ro, vec3 rd) { vec3 p, q, cSun, skyBg, clCol, col; float colSum, attSum, s, att, a, dDotS, ds; const vec3 cCol1 = 0.5 * vec3 (0.15, 0.2, 0.4), cCol2 = 0.5 * vec3 (0.25, 0.5, 0.7), gCol = 1.3 * vec3 (0.05, 0.08, 0.05); const float cloudLo = 100., cloudRngI = 1./50., atFac = 0.06; const int nLay = 30; if (rd.y < 0.015 * Fbm1 (16. * rd.x)) col = gCol * (0.5 + 0.5 * Noisefv2 (1000. * vec2 (5. * atan (rd.x, rd.z), rd.y))); else { fCloud = clamp (fCloud, 0., 1.); dDotS = max (dot (rd, sunDir), 0.); ro += cloudDisp; p = ro; p.xz += (cloudLo - p.y) * rd.xz / rd.y; p.y = cloudLo; ds = 1. / (cloudRngI * rd.y * (2. - rd.y) * float (nLay)); colSum = 0.; attSum = 0.; s = 0.; att = 0.; for (int j = 0; j < nLay; j ++) { q = p + rd * s; q.z *= 0.7; att += atFac * max (fCloud - Fbm3 (0.02 * q), 0.); a = (1. - attSum) * att; colSum += a * (q.y - cloudLo) * cloudRngI; attSum += a; s += ds; if (attSum >= 1.) break; } colSum += 0.5 * min ((1. - attSum) * pow (dDotS, 3.), 1.); clCol = vec3 (1.) * colSum + 0.05 * sunCol; cSun = sunCol * clamp ((min (pow (dDotS, 1500.) * 2., 1.) + min (pow (dDotS, 10.) * 0.75, 1.)), 0., 1.); skyBg = mix (cCol1, cCol2, 1. - rd.y); col = clamp (mix (skyBg + cSun, 1.6 * clCol, attSum), 0., 1.); } return col; } vec3 SeaFloorCol (vec3 rd) { vec2 p; float w, f; p = 5. * rd.xz / rd.y; w = 1.; f = 0.; for (int j = 0; j < 4; j ++) { f += w * Noisefv2 (p); w *= 0.5; p *= 2.; } return mix (vec3 (0.01, 0.04, 0.02), vec3 (0, 0.05, 0.05), smoothstep (0.4, 0.7, f)); } float WaveHt (vec3 p) { const mat2 qRot = mat2 (1.6, -1.2, 1.2, 1.6); vec4 t4, ta4, v4; vec2 q2, t2, v2; float wFreq, wAmp, pRough, ht; wFreq = 0.16; wAmp = 0.6; pRough = 5.; q2 = p.xz + waterDisp.xz; ht = 0.; for (int j = 0; j < 5; j ++) { t2 = 1.1 * tCur * vec2 (1., -1.); t4 = vec4 (q2 + t2.xx, q2 + t2.yy) * wFreq; t2 = vec2 (Noisefv2 (t4.xy), Noisefv2 (t4.zw)); t4 += 2. * vec4 (t2.xx, t2.yy) - 1.; ta4 = abs (sin (t4)); v4 = (1. - ta4) * (ta4 + abs (cos (t4))); v2 = pow (1. - pow (v4.xz * v4.yw, vec2 (0.65)), vec2 (pRough)); ht += (v2.x + v2.y) * wAmp; q2 *= qRot; wFreq *= 1.9; wAmp *= 0.22; pRough = 0.8 * pRough + 0.2; } return ht; } float WaveRay (vec3 ro, vec3 rd) { vec3 p; float dHit, h, s, sLo, sHi; s = 0.; sLo = 0.; dHit = dstFar; for (int j = 0; j < 150; j ++) { p = ro + s * rd; h = p.y - WaveHt (p); if (h < 0.) break; sLo = s; s += max (0.2, h) + 0.005 * s; if (s > dstFar) break; } if (h < 0.) { sHi = s; for (int j = 0; j < 7; j ++) { s = 0.5 * (sLo + sHi); p = ro + s * rd; h = step (0., p.y - WaveHt (p)); sLo += h * (s - sLo); sHi += (1. - h) * (s - sHi); } dHit = sHi; } return dHit; } float WaveOutRay (vec3 ro, vec3 rd) { vec3 p; float dHit, h, s, sLo, sHi; s = 0.; sLo = 0.; dHit = dstFar; ro.y *= -1.; rd.y *= -1.; for (int j = 0; j < 150; j ++) { p = ro + s * rd; h = p.y + WaveHt (p); if (h < 0.) break; sLo = s; s += max (0.2, h) + 0.005 * s; if (s > dstFar) break; } if (h < 0.) { sHi = s; for (int j = 0; j < 7; j ++) { s = 0.5 * (sLo + sHi); p = ro + s * rd; h = step (0., p.y + WaveHt (p)); sLo += h * (s - sLo); sHi += (1. - h) * (s - sHi); } dHit = sHi; } return dHit; } vec3 WaveNf (vec3 p, float d) { vec2 e = vec2 (max (0.1, 5e-5 * d * d), 0.); float h = WaveHt (p); return normalize (vec3 (h - WaveHt (p + e.xyy), e.x, h - WaveHt (p + e.yyx))); } float ObjDf (vec3 p) { vec3 q; float dHit, d; dHit = dstFar; q = p; q -= ballPos; q *= ballMat; d = PrSphDf (q, 2.); if (d < dHit) { dHit = d; idObj = 1; qHit = q; } q.y -= 3.; d = PrCylDf (q.xzy, 0.05, 1.); if (d < dHit) { dHit = d; idObj = 2; qHit = q; } q.y -= 1.3; d = PrCylDf (q.xzy, 0.15, 0.3); if (d < dHit) { dHit = d; idObj = 3; qHit = q; } return dHit; } float ObjRay (in vec3 ro, in vec3 rd) { float d, dHit; dHit = 0.; for (int j = 0; j < 150; j ++) { d = ObjDf (ro + dHit * rd); dHit += d; if (d < 0.002 || dHit > dstFar) break; } return dHit; } vec3 ObjNf (vec3 p) { const vec3 e = vec3 (0.001, -0.001, 0.); vec4 v = vec4 (ObjDf (p + e.xxx), ObjDf (p + e.xyy), ObjDf (p + e.yxy), ObjDf (p + e.yyx)); return normalize (vec3 (v.x - v.y - v.z - v.w) + 2. * v.yzw); } vec3 ObjCol (vec3 n) { vec3 col; col = vec3 (0.); if (idObj == 1) { col = vec3 (1., 0.01, 0.); if (abs (qHit.y) < 0.21) col = (mod (floor (7. * (atan (qHit.x, qHit.z) / pi + 1.)), 2.) == 0.) ? vec3 (1., 0.8, 0.08) : vec3 (0.04); else if (qHit.y > 1.93) col = vec3 (0.15, 0.05, 0.); else if (abs (qHit.y) < 0.25) col = vec3 (1., 0.8, 0.08); else if (abs (abs (qHit.y) - 0.55) < 0.05) col = vec3 (1.); else if (abs (abs (qHit.y) - 0.65) < 0.05) col = vec3 (0.04); if (qHit.y < 0.) col = mix (col, vec3 (0.05, 0.2, 0.05), min (- 2. * qHit.y, 0.9)); } else if (idObj == 2) { col = vec3 (0.6, 0.4, 0.2); } else if (idObj == 3) { if (abs (qHit.y) < 0.2) col = vec3 (0., 1., 0.) * (2. + 1.5 * cos (10. * tCur)); else col = vec3 (0.6, 0.4, 0.2); } return col; } float ObjSShadow (vec3 ro, vec3 rd) { float sh, d, h; sh = 1.; d = 0.1; for (int j = 0; j < 40; j ++) { h = ObjDf (ro + rd * d); sh = min (sh, 20. * h / d); d += 0.1; if (h < 0.001) break; } return clamp (sh, 0., 1.); } vec3 ObjRender (vec3 ro, vec3 rd) { vec3 col, vn; float dif, bk, sh, cc; int idObjT; idObjT = idObj; vn = ObjNf (ro); idObj = idObjT; if (idObj == 1) { vn = VaryNf (20. * qHit, vn, 0.3); if (qHit.y < 0.) vn = mix (vn, VaryNf (12. * qHit, vn, 2.), min (- 5. * qHit.y, 1.)); } col = ObjCol (rd); cc = 1. - smoothstep (0.3, 0.6, fCloud); sh = ObjSShadow (ro, sunDir); bk = max (dot (vn, - normalize (vec3 (sunDir.x, 0., sunDir.z))), 0.); dif = max (dot (vn, sunDir), 0.); return col * (0.2 + 0.1 * bk + max (0., dif) * (0.7 + 0.3 * cc * sh)) + 0.3 * cc * sh * sunCol * pow (max (0., dot (sunDir, reflect (rd, vn))), 32.); } vec3 ShowScene (vec3 ro, vec3 rd) { vec3 col, vn, rdd, refCol, uwatCol; float dstHit, dstWat, dif, bk, sh, foamFac; const float eta = 0.75, att = 0.5; int idObjT; bool doReflect; if (ro.y > WaveHt (ro)) { dstWat = WaveRay (ro, rd); idObj = -1; dstHit = ObjRay (ro, rd); if (idObj < 0) dstHit = dstFar; doReflect = (dstWat < dstFar && dstWat < dstHit); if (doReflect) { ro += rd * dstWat; vn = WaveNf (ro, dstWat); rdd = rd; rd = reflect (rd, vn); idObj = -1; dstHit = ObjRay (ro, rd); if (idObj < 0) dstHit = dstFar; } col = (dstHit < dstFar) ? ObjRender (ro + rd * dstHit, rd) : SkyGrndCol (ro, rd); if (doReflect) { refCol = col; rd = refract (rdd, vn, eta); idObj = -1; dstHit = ObjRay (ro, rd); if (idObj < 0) dstHit = dstFar; col = (dstHit < dstFar) ? ObjRender (ro + rd * dstHit, rd) * exp (- att * dstHit) : SeaFloorCol (rd); col = mix (col, 0.8 * refCol, pow (1. - abs (dot (rdd, vn)), 5.)); foamFac = pow (clamp (WaveHt (ro) + 0.004 * Fbm3 (256. * ro) - 0.65, 0., 1.), 8.); col = mix (col, vec3 (1.), foamFac); } } else { uwatCol = vec3 (0., 0.05, 0.05) + step (0.4, Fbm1 (20. * tCur)) * vec3 (0.02, 0.02, 0.03); col = uwatCol; dstWat = WaveOutRay (ro, rd); idObj = -1; dstHit = ObjRay (ro, rd); if (idObj < 0) dstHit = dstFar; if (dstWat < dstFar && dstWat < dstHit) { ro += rd * dstWat; vn = - WaveNf (ro, dstWat); rdd = refract (rd, vn, 1. / eta); if (length (rdd) > 0.) rd = rdd; else rd = reflect (rd, vn); idObj = -1; dstHit = ObjRay (ro, rd); if (idObj < 0) dstHit = dstFar; if (dstHit < dstFar) col = 0.9 * ObjRender (ro + rd * dstHit, rd); else if (rd.y > 0.) col = mix (uwatCol, 0.9 * SkyGrndCol (ro, rd), exp (- 0.07 * att * dstWat)); } else if (dstHit < dstFar) col = mix (uwatCol, ObjRender (ro + rd * dstHit, rd), exp (- 0.07 * att * dstHit)); } return col; } void BallPM () { const vec3 e = vec3 (1., 0., 0.); float h[5], b; ballPos = vec3 (0., 0., 0.); h[0] = WaveHt (ballPos); h[1] = WaveHt (ballPos + e.yyx); h[2] = WaveHt (ballPos - e.yyx); h[3] = WaveHt (ballPos + e); h[4] = WaveHt (ballPos - e); ballPos.y = 0.5 + (2. * h[0] + h[1] + h[2] + h[3] + h[4]) / 9.; b = (h[1] - h[2]) / (4. * e.x); ballMat[2] = normalize (vec3 (0., b, 1.)); b = (h[3] - h[4]) / (4. * e.x); ballMat[1] = normalize (cross (ballMat[2], vec3 (1., b, 0.))); ballMat[0] = cross (ballMat[1], ballMat[2]); } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = 2. * fragCoord.xy / iResolution.xy - 1.; uv.x *= iResolution.x / iResolution.y; tCur = iGlobalTime; #ifdef MOUSE vec4 mPtr = iMouse; mPtr.xy = mPtr.xy / iResolution.xy - 0.5; #endif mat3 vuMat; vec3 col, ro, rd; vec2 vEl, vAz; float el, az, zmFac, a, tPer, tSeq; cloudDisp = 10. * tCur * vec3 (1., 0., 1.); waterDisp = 0.5 * tCur * vec3 (-1., 0., 1.); sunDir = normalize (vec3 (0.2, 0.5, 0.5)); sunCol = vec3 (1., 0.4, 0.3) + vec3 (0., 0.5, 0.2) * sunDir.y; fCloud = 0.5 + 0.2 * sin (0.022 * 2. * pi * tCur); zmFac = 3.5; tPer = 35.; #ifdef MOUSE if (mPtr.z <= 0.) { az = 0.01 * tCur; el = 0.2 * pi; tSeq = mod (tCur, tPer); if (mod (floor (tCur / tPer), 2.) == 0.) { a = SmoothBump (10., 30., 5., tSeq); zmFac -= 0.1 * a; el -= 0.19 * pi * a; } else { a = SmoothBump (8., 26., 8., tSeq); zmFac -= 0.05 * a; el -= 0.55 * pi * a; } } else { az = 1.1 * pi * mPtr.x; el = 0.02 * pi - 0.7 * pi * mPtr.y; } #else { az = 0.01 * tCur; el = 0.2 * pi; tSeq = mod (tCur, tPer); if (mod (floor (tCur / tPer), 2.) == 0.) { a = SmoothBump (10., 30., 5., tSeq); zmFac -= 0.1 * a; el -= 0.19 * pi * a; } else { a = SmoothBump (8., 26., 8., tSeq); zmFac -= 0.05 * a; el -= 0.55 * pi * a; } } #endif vEl = vec2 (cos (el), sin (el)); vAz = vec2 (cos (az), sin (az)); rd = normalize (vec3 (uv, zmFac)); vuMat = mat3 (1., 0., 0., 0., vEl.x, - vEl.y, 0., vEl.y, vEl.x) * mat3 (vAz.x, 0., vAz.y, 0., 1., 0., - vAz.y, 0., vAz.x); rd = rd * vuMat; ro = vec3 (0., 0., -20.) * vuMat; ro.y += 2.; BallPM (); col = ShowScene (ro, rd); fragColor = vec4 (sqrt (clamp (col, 0., 1.)), 1.); } void main(void) { //just some shit to wrap shadertoy's stuff vec2 FragmentCoord = vTexCoord.xy*global.OutputSize.xy; FragmentCoord.y = -FragmentCoord.y; mainImage(FragColor,FragmentCoord); }