65e8417661
This change adds a 3d rendering of the Zuul logo. Change-Id: I56d311c95dac678057a440878ac2f8b24d7be977
397 lines
10 KiB
GLSL
397 lines
10 KiB
GLSL
/* Zuul-ci pyramid logo in 3D
|
|
Copyright © 2019 Tristan De Cacqueray
|
|
SPDX short identifier: MIT & CC-BY-NC-SA-4.0
|
|
|
|
Code is mainly based on
|
|
https://www.shadertoy.com/view/Xds3zN
|
|
Uploaded by iq in 2013-03-25
|
|
|
|
and
|
|
|
|
https://www.shadertoy.com/view/ldKGWW
|
|
Uploaded by wjbgrafx on 2016-02-04
|
|
|
|
More resources:
|
|
http://www.iquilezles.org/www/material/nvscene2008/rwwtt.pdf
|
|
http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
|
|
*/
|
|
|
|
// Set to 1 if too slow
|
|
#define AA 4
|
|
|
|
// Uncomment to add scene elements
|
|
// #define SKY
|
|
// #define MONSTER
|
|
// #define FLOOR
|
|
|
|
// Params
|
|
uniform vec3 iResolution;
|
|
uniform float iTime;
|
|
uniform vec4 iMouse;
|
|
|
|
const vec3 cam = vec3(0., -.2, -2.);
|
|
#define PI 3.141592653589793
|
|
#define PITCH 0.428
|
|
#define YAW -0.8
|
|
#define DEG60 0.866025
|
|
|
|
// Primitives
|
|
const mat3 rotX180 = mat3(1.0, 0.0, 0.0,
|
|
0.0, cos(PI), sin(PI),
|
|
0.0, -sin(PI), cos(PI));
|
|
|
|
float smin(float a, float b, float k) {
|
|
float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
|
|
return mix(b, a, h) - k * h * (1.0 - h);
|
|
}
|
|
|
|
vec2 opU(vec2 d1, vec2 d2) {
|
|
return (d1.x < d2.x) ? d1 : d2;
|
|
}
|
|
|
|
float pMirror(inout float p, float dist) {
|
|
float s = sign(p);
|
|
p = abs(p) - dist;
|
|
return s;
|
|
}
|
|
|
|
float sdPlane(vec3 p) {
|
|
return p.y;
|
|
}
|
|
|
|
float sdTriPrism(vec3 p, vec2 h) {
|
|
vec3 q = abs(p);
|
|
return max(q.z - h.y,
|
|
max(q.x * 0.4 + p.y * 0.5, -p.y) - h.x * 0.5);
|
|
}
|
|
|
|
float sdPrismZ(vec3 p, float angleRads, float height, float depth) {
|
|
vec3 q = abs(p);
|
|
return max(q.z - depth,
|
|
max(q.x * angleRads + p.y * 0.5, -p.y) - height * 0.5);
|
|
}
|
|
|
|
float sdPrismX(vec3 p, float angleRads, float height, float depth) {
|
|
vec3 q = abs(p);
|
|
return max(q.x - depth,
|
|
max(q.z * angleRads + p.y * 0.5, -p.y) - height * 0.5);
|
|
}
|
|
|
|
float sdPyramid(vec3 p, float angleRads, float height, float depth) {
|
|
vec3 q = abs(p);
|
|
return max(sdPrismX(p, angleRads, height, depth),
|
|
sdPrismZ(p, angleRads, height, depth));
|
|
}
|
|
|
|
float sdBox(vec3 p, vec3 b) {
|
|
vec3 d = abs(p) - b;
|
|
return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0));
|
|
}
|
|
|
|
float dot2(vec3 v) {
|
|
return dot(v,v);
|
|
}
|
|
|
|
float udTriangle(vec3 p, vec3 a, vec3 b, vec3 c) {
|
|
vec3 ba = b - a; vec3 pa = p - a;
|
|
vec3 cb = c - b; vec3 pb = p - b;
|
|
vec3 ac = a - c; vec3 pc = p - c;
|
|
vec3 nor = cross(ba, ac);
|
|
|
|
return sqrt(
|
|
(sign(dot(cross(ba,nor),pa)) +
|
|
sign(dot(cross(cb,nor),pb)) +
|
|
sign(dot(cross(ac,nor),pc)) < 2.0)
|
|
?
|
|
min(min(
|
|
dot2(ba*clamp(dot(ba,pa)/dot2(ba),0.0,1.0)-pa),
|
|
dot2(cb*clamp(dot(cb,pb)/dot2(cb),0.0,1.0)-pb)),
|
|
dot2(ac*clamp(dot(ac,pc)/dot2(ac),0.0,1.0)-pc))
|
|
:
|
|
dot(nor,pa)*dot(nor,pa)/dot2(nor));
|
|
}
|
|
|
|
// Zuul logo primitives
|
|
float sdHollowPyramid(vec3 p) {
|
|
float pyramid = sdPyramid(p, DEG60, 1.0, 1.0);
|
|
pyramid = max(pyramid, -sdPyramid(p - vec3(0.0, 0., 0.5), DEG60, .9, .9));
|
|
pyramid = max(pyramid, -sdBox(p, vec3(.45, 0.45, 0.45)));
|
|
return pyramid;
|
|
}
|
|
|
|
float sdHollowBox(vec3 pos, vec3 size, float hole) {
|
|
float box = sdBox(pos, size);
|
|
box = max(box, -sdBox(pos, size * vec3(2., hole, hole)));
|
|
box = max(box, -sdBox(pos, size * vec3(hole, hole, 2.)));
|
|
return box;
|
|
}
|
|
|
|
float sdInnerFrame(vec3 pos) {
|
|
float left = sdHollowBox(pos + vec3(0.06, .1, .0), vec3(.39, .48, .45), 0.87);
|
|
float right = sdHollowBox(pos + vec3(.0, .1, -.06), vec3(.45, .48, .39), 0.87);
|
|
return left < right ? left : right;
|
|
}
|
|
|
|
float sdPilar(vec3 pos) {
|
|
return sdBox(pos + vec3(.000, .15, -.424), vec3(.026, .35, .026));
|
|
}
|
|
|
|
float sdRoof(vec3 pos) {
|
|
return sdTriPrism((pos - vec3(.44, .492, .0)) * rotX180, vec2(.05, .45));
|
|
}
|
|
|
|
float sdZuul(vec3 pos) {
|
|
vec3 p = pos;
|
|
pMirror(p.x, 0.0);
|
|
pMirror(p.z, 0.0);
|
|
float pyramid = sdHollowPyramid(p);
|
|
float mainBox = sdHollowBox(p, vec3(.45, .517, .45), 0.87);
|
|
float innerBox = sdInnerFrame(p);
|
|
float lowerPlateau = sdBox(p - vec3(.0, .21, .0), vec3(.45, .026, .45));
|
|
float pilar = sdPilar(p);
|
|
float entranceWall = udTriangle(
|
|
p, vec3(.78, -.45, .45), vec3(.39, -.45, .451), vec3(.39, .34, .451));
|
|
float roof = sdRoof(p);
|
|
|
|
float closer = pyramid;
|
|
closer = smin(closer, lowerPlateau, .01);
|
|
closer = smin(closer, entranceWall, .01);
|
|
closer = closer < mainBox ? closer : mainBox;
|
|
closer = closer < roof ? closer : roof;
|
|
closer = closer < innerBox ? closer : innerBox;
|
|
closer = closer < pilar ? closer : pilar;
|
|
float socle = sdBox(pos + vec3(.0, .6, .0), vec3(2., .1, 2.));
|
|
return max(closer, -socle);
|
|
}
|
|
|
|
#ifdef MONSTER
|
|
// Julia - Quaternion
|
|
// https://www.shadertoy.com/view/MsfGRr
|
|
const int numIterations = 11;
|
|
|
|
vec4 qsqr(vec4 a) {
|
|
return vec4(a.x*a.x - a.y*a.y - a.z*a.z - a.w*a.w,
|
|
2.0*a.x*a.y,
|
|
2.0*a.x*a.z,
|
|
2.0*a.x*a.w);
|
|
}
|
|
|
|
float juliaQ(vec3 p, vec4 c) {
|
|
vec4 z = vec4(p, 0.0) * 3.6;
|
|
float md2 = 20.0;
|
|
float mz2 = dot(z, z);
|
|
|
|
vec4 trap = vec4(abs(z.xyz), dot(z, z));
|
|
|
|
for (int i=0; i < numIterations; i++) {
|
|
md2 *= 4.0 * mz2;
|
|
z = qsqr(z) + c;
|
|
trap = min(trap, vec4(abs(z.xyz), dot(z, z)));
|
|
mz2 = dot(z, z);
|
|
if (mz2 > 4.0)
|
|
break;
|
|
}
|
|
return 0.25 * sqrt(mz2 / md2) * log(mz2);
|
|
}
|
|
#endif
|
|
|
|
// The scene
|
|
vec2 scene(vec3 pos) {
|
|
vec2 res = vec2(sdZuul(pos), 29.);
|
|
#ifdef FLOOR
|
|
float plane = sdPlane(pos + vec3(.0, 1., .0));
|
|
// Does not work well, but tries to remove the floor when under it...
|
|
if (plane > .0)
|
|
res = opU(res, vec2(plane, 1.));
|
|
#endif
|
|
#ifdef MONSTER
|
|
vec4 c = 0.45*cos(vec4(0.5,3.9,1.4,1.1) + (iTime*1.5)*vec4(1.2,1.7,1.3,2.5)) - vec4(0.3,0.0,0.0,0.0);
|
|
res = opU(res, vec2(juliaQ(pos + vec3(.0, .13, .0), c), 49.));
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
vec3 calcNormal(vec3 pos) {
|
|
vec2 e = vec2(1.0, -1.0) * 0.5773 * 0.0005;
|
|
return normalize(e.xyy * scene(pos + e.xyy).x +
|
|
e.yyx * scene(pos + e.yyx).x +
|
|
e.yxy * scene(pos + e.yxy).x +
|
|
e.xxx * scene(pos + e.xxx).x);
|
|
}
|
|
|
|
float calcAO(vec3 pos, vec3 nor) {
|
|
float occ = 0.0;
|
|
float sca = 1.0;
|
|
for (int i=0; i < 5; i++) {
|
|
float hr = 0.01 + 0.12 * float(i) / 4.0;
|
|
vec3 aopos = nor * hr + pos;
|
|
float dd = scene(aopos).x;
|
|
occ += -(dd - hr) * sca;
|
|
sca *= 0.95;
|
|
}
|
|
return clamp(1.0 - 3.0 * occ, 0.0, 1.0);
|
|
}
|
|
|
|
vec2 castRay(vec3 ro, vec3 rd) {
|
|
const float tmin = 0.2;
|
|
const float tmax = 20.0;
|
|
float t = tmin;
|
|
float m;
|
|
for (int i=0; i < 300; i++) {
|
|
vec2 res = scene(ro + rd * t);
|
|
if (res.x < 0.0001 ||
|
|
t > tmax) break;
|
|
t += res.x;
|
|
m = res.y;
|
|
}
|
|
if (t > tmax) m = -1.0;
|
|
return vec2(t, m);
|
|
}
|
|
|
|
// Improved soft shadow by Sebastian Aaltonen
|
|
// From: http://www.iquilezles.org/www/articles/rmshadows/rmshadows.htm
|
|
float calcSoftshadow(vec3 ro, vec3 rd, float mint, float maxt) {
|
|
float k = 32.;
|
|
float res = 1.0;
|
|
float ph = 1e20;
|
|
for (float t=mint; t < maxt;) {
|
|
float h = scene(ro + rd * t).x;
|
|
if (h < 0.001)
|
|
return 0.0;
|
|
float y = h * h / (2.0 * ph);
|
|
float d = sqrt(h * h - y * y);
|
|
res = min(res, k * d / max(0.0, t-y));
|
|
ph = h;
|
|
t += h;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#ifdef FLOOR
|
|
// http://iquilezles.org/www/articles/checkerfiltering/checkerfiltering.htm
|
|
float checkersGradBox(vec2 p) {
|
|
// filter kernel
|
|
vec2 w = fwidth(p) + 0.001;
|
|
// analytical integral (box filter)
|
|
vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w;
|
|
// xor pattern
|
|
return 0.5 - 0.5*i.x*i.y;
|
|
}
|
|
#endif
|
|
|
|
#ifdef SKY
|
|
vec3 skyColor(vec3 rd) {
|
|
float offset = (.95 - clamp(rd.y, 0.0, 0.2))*.7;
|
|
return vec3(.5)+vec3(.5)*cos(6.28*(vec3(1.)*offset+vec3(0.0,0.10,0.20)));
|
|
}
|
|
#endif
|
|
|
|
vec3 render(vec3 ro, vec3 rd) {
|
|
vec3 col = vec3(.0);
|
|
#ifdef SKY
|
|
col = skyColor(rd);
|
|
#endif
|
|
vec2 res = castRay(ro, rd);
|
|
float t = res.x;
|
|
float m = res.y;
|
|
|
|
if (m > 0.) {
|
|
vec3 pos = ro + t * rd;
|
|
vec3 nor = calcNormal(pos);
|
|
vec3 ref = reflect(rd, nor);
|
|
col = 0.45 + 0.35*sin(vec3(0.0001,0.004,0.04)*(m-1.0));
|
|
#ifdef FLOOR
|
|
if (m < 1.5) {
|
|
float f = checkersGradBox(5.0*pos.xz);
|
|
col = 0.01 + f*vec3(0.1);
|
|
}
|
|
#endif
|
|
// lightning
|
|
float occ = calcAO(pos, nor);
|
|
vec3 lig = normalize(vec3(0.25, 0.7, -1.));
|
|
vec3 hal = normalize(lig - rd);
|
|
float amb = clamp(0.5 + 0.5 * nor.y, 0.0, 1.0);
|
|
float dif = clamp(dot(nor, lig), 0.0, 1.0);
|
|
float bac = clamp(dot(nor, normalize(vec3(-lig.x, 0.0, -lig.z))),
|
|
0.0, 1.0)*clamp(1.0 - pos.y, 0.0, 1.0);
|
|
float dom = smoothstep(-0.2, 0.2, ref.y);
|
|
float fre = pow(clamp(1.0+dot(nor,rd),0.0,1.0), 2.0);
|
|
|
|
dif *= calcSoftshadow(pos, lig, 0.02, 2.5) * 0.2;
|
|
dom *= calcSoftshadow(pos, ref, 0.02, 2.5);
|
|
|
|
float spe = pow(clamp(dot(nor, hal), 0.0, 1.0), 16.0);
|
|
spe *= dif * (0.04 + 0.96 * pow(clamp(1.0 + dot(hal, rd), 0.0, 1.0), 5.0));
|
|
|
|
vec3 lin = vec3(0.0);
|
|
lin += 3.30 * dif * vec3(1.00, 0.80, 0.55);
|
|
lin += 0.40 * amb * vec3(0.40, 0.60, 1.00) * occ;
|
|
lin += 0.40 * dom * vec3(0.40, 0.60, 1.00) * occ;
|
|
lin += 0.50 * bac * vec3(0.25, 0.25, 0.25) * occ;
|
|
lin += 0.25 * fre * vec3(1.00, 1.00, 1.00) * occ;
|
|
col *= lin;
|
|
col += 50.0 * spe * vec3(1.00, 0.90, 0.70);
|
|
col = mix(col, vec3(.0), 1.0 - exp(-0.0002 * t * t * t));
|
|
}
|
|
#ifdef SKY
|
|
// fog
|
|
float rayDist = length(ro + rd * t);
|
|
col = mix(col, skyColor(rd), 1.0 - 1.0 / exp(rayDist * 0.05));
|
|
#endif
|
|
return vec3(clamp(col, 0.0, 1.0));
|
|
}
|
|
|
|
float smoothStair(float frame) {
|
|
return frame - sin(frame) / 1.9;
|
|
}
|
|
|
|
mat3 camRotation() {
|
|
float yaw, pitch;
|
|
if (iMouse.z > 0.0) {
|
|
yaw = (iMouse.x / iResolution.x - 0.5) * 4.;
|
|
pitch = (iMouse.y / iResolution.y - 0.5) * 4.;
|
|
} else {
|
|
yaw = -.35 + smoothStair(iTime * 2.) / 2.;
|
|
pitch = PITCH;
|
|
}
|
|
return mat3(1.0, 0.0, 0.0,
|
|
0.0, cos(pitch), -sin(pitch),
|
|
0.0, sin(pitch), cos(pitch)) *
|
|
mat3(cos(yaw), 0.0, sin(yaw),
|
|
0.0, 1.0, 0.0,
|
|
-sin(yaw), 0.0, cos(yaw));
|
|
}
|
|
|
|
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
mat3 rot = camRotation();
|
|
vec3 tot = vec3(0.0);
|
|
#if AA > 1
|
|
for(int m=0; m < AA; m++)
|
|
for(int n=0; n < AA; n++) {
|
|
vec2 o = vec2(float(m), float(n)) / float(AA) - 0.5;
|
|
vec2 uv = (gl_FragCoord.xy+o) / iResolution.xy*2.-1.;
|
|
#else
|
|
vec2 uv = gl_FragCoord.xy / iResolution.xy*2.-1.;
|
|
#endif
|
|
|
|
uv.y *= iResolution.y / iResolution.x;
|
|
|
|
vec3 dir = normalize(vec3(uv, 1.)) * rot;
|
|
vec3 pos = cam * rot;
|
|
|
|
vec3 col = render(pos, dir);
|
|
// gamma
|
|
col = pow(col, vec3(0.4));
|
|
|
|
tot += col;
|
|
#if AA > 1
|
|
}
|
|
tot /= float(AA * AA);
|
|
#endif
|
|
fragColor = vec4(tot, 1.0);
|
|
}
|
|
|
|
void main(void) {
|
|
mainImage(gl_FragColor, gl_FragCoord.xy);
|
|
}
|