canvas#canvas
View Compiled
* {
box-sizing: border-box;
}
html, body {
margin: 0;
min-height: 100vh;
overflow: hidden;
background:
repeating-radial-gradient(
circle at center,
#444 0 10%,
#111 10% 20%
);
touch-action: none;
}
canvas {
width: 100%;
height: auto;
object-fit: contain;
}
/*********
* made by Matthias Hurrle (@atzedent)
*/
/** @type {HTMLCanvasElement} */
const canvas = window.canvas
const gl = canvas.getContext("webgl2")
const dpr = Math.max(1, .5*window.devicePixelRatio)
/** @type {Map<string,PointerEvent>} */
const touches = new Map()
const vertexSource = `#version 300 es
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
in vec2 position;
void main(void) {
gl_Position = vec4(position, 0., 1.);
}
`
const fragmentSource = `#version 300 es
/*********
* made by Matthias Hurrle (@atzedent)
*/
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
out vec4 fragColor;
uniform vec2 resolution;
uniform float time;
uniform int pointerCount;
uniform vec2 touch;
const vec3 roomsize=vec3(6,4,6)*1.25;
const vec3 boxsize=vec3(1,1.25,1);
const float walleps=1e-3;
#define T time
#define S smoothstep
#define mouse (touch/resolution)
#define rot(a) mat2(cos(a),-sin(a),sin(a),cos(a))
float tick(float t,float e) {
return floor(t)+pow(S(.0,1.,S(.0,1.,fract(t))),e);
}
float box(vec3 p,vec3 s,float r) {
p=abs(p)-s;
return length(max(p,.0))+
min(.0,max(max(p.x,p.y),p.z))-r;
}
float mat=.0;
float map(vec3 p) {
float d=1e5,
room=-box(p,roomsize,.05),
bx=box(p-vec3(0,-(roomsize.y-(boxsize.y+.05)),0),boxsize,.05);
d=min(d, room);
d=min(d, bx);
if (d==bx) mat=1.;
else mat=.0;
return d;
}
vec3 creature(vec2 uv) {
vec2
n=vec2(0),
q=vec2(0);
uv*=.875;
float
d=dot(uv,uv),
s=9.,
a=.02,
b=sin(T*4.-d*4.)*.9,
t=T*4.;
mat2 m=mat2(.6,-1.2,1.2,.6)*.9;
for (float i=.0; i<30.; i++) {
uv*=m;
n*=m;
q=uv*s-t+b+i+n;
a+=dot(cos(q)/s,vec2(.2));
n+=sin(q);
s*=1.2;
}
vec3 col=vec3(1,3,4)*(a+.2)+a+a-d*1.5;
col=exp(-col*8.);
col=abs(col);
col=sqrt(col);
col=exp(-col*4.);
return col;
}
vec3 crawler(vec2 uv) {
vec2
n=vec2(0),
q=vec2(0);
uv*=.875;
float
d=dot(uv,uv),
s=9.,
a=.02,
b=sin(T*4.4-d*4.)*.7,
t=T*4.;
mat2 m=mat2(.6,1.2,-1.2,.6);
for (float i=.0; i<30.; i++) {
n*=m;
q=uv*s-t+b+i+n;
a+=dot(sin(q)/s,vec2(.2));
n+=sin(q);
s*=1.2;
}
vec3 col=vec3(1,3,4)*(a+a)+a+a-d;
col=exp(-col*8.);
col=abs(col);
col=sqrt(col);
col=exp(-col*4.);
return col;
}
vec3 mollusc(vec2 uv) {
vec2
n=vec2(0),
q=vec2(0);
uv*=.8;
float
d=dot(uv,uv),
s=9.,
a=.05,
b=sin(T*4.-d*3.)*.9,
t=T*4.;
uv.yx-=T*.1;
mat2 m=mat2(-.6,.8,-.8,-.6)*.9;
for (float i=.0; i<30.; i++) {
uv*=m;
n*=m;
q=uv*s-t+b+i+n;
a+=dot(cos(q)/s,vec2(.2));
n-=sin(q);
s*=1.2;
}
vec3 col=vec3(4,2,1)*(a+.15)+a-d;
col=sqrt(col);
return col;
}
vec3 dflame(vec2 uv) {
vec2
n=vec2(0),
q=vec2(0);
uv*=.875;
float
d=dot(uv,uv),
s=9.,
a=.02,
b=sin(T*.4-d*4.)*.9,
t=T*4.;
uv*=rot(sin(6.+t*.05)*.8-.567);
uv.y-=t*.05;
mat2 m=mat2(.6,1.2,-1.2,.6);
for (float i=.0; i<30.; i++) {
n*=m;
q=uv*s-t+b+i+n;
a+=dot(cos(q)/s,vec2(.2));
n+=sin(q);
s*=1.2;
}
vec3 col=vec3(4,2,1)*(a+.2)+a+a-d;
col=exp(-col*8.);
col=abs(col);
col=sqrt(col);
col=exp(-col*4.);
return col;
}
vec3 flame(vec2 uv) {
vec2
n=vec2(0),
q=vec2(0);
uv*=.6+(sin(T)*.2);
float
d=dot(uv,uv),
s=9.,
a=.02,
b=sin(T*.4-d*4.)*.9,
t=T*4.;
uv*=rot(sin(6.+t*.05)*.8-.567);
uv.y-=t*.05;
mat2 m=mat2(.6,1.2,-1.2,.6);
for (float i=.0; i<30.; i++) {
n*=m;
q=uv*s-t+b+i+n;
a+=dot(cos(q)/s,vec2(.2));
n-=sin(q);
s*=1.2;
}
vec3 col=vec3(1,2,4)*(a+.2)+a+a-d*.5;
col=exp(-col*8.);
col=abs(col);
col=sqrt(col);
return col;
}
vec3 water(vec2 uv) {
vec2
n=vec2(0),
q=vec2(0);
uv*=.875;
float
d=dot(uv,uv),
s=9.,
a=.02,
b=sin(T*4.4-d*90.)*.7,
t=T*4.;
mat2 m=mat2(.6,1.2,-1.2,.6);
for (float i=.0; i<30.; i++) {
n*=m;
q=uv*s-t+b+i+n;
a+=dot(sin(q)/s,vec2(.2));
n-=sin(q);
s*=1.2;
}
vec3 col=vec3(1,3,4)*(a+a)+a+a-d;
col=exp(-col*8.);
col=abs(col);
col=sqrt(col);
col=exp(-col*4.);
return col;
}
vec3 norm(vec3 p) {
vec2 e=vec2(1e-3,0);
float d=map(p);
vec3 n=d-vec3(
map(p-e.xyy),
map(p-e.yxy),
map(p-e.yyx)
);
return normalize(n);
}
void cam(inout vec3 p) {
if(pointerCount>0) {
p.yz*=rot(-clamp(mouse.y,.08,.6)*acos(-1.)+acos(.0));
p.xz*=rot(-mouse.x*acos(-1.)*2.);
} else {
p.yz*=rot(sin(3.+1.0*T*.2)*.25);
p.xz*=rot(tick(3.+1.0*T*.25, 5.)*1.57079);
}
}
void main(void) {
vec2 uv = (
gl_FragCoord.xy-.5*resolution
)/min(resolution.x, resolution.y);
float zoom=pointerCount>0
? .0
: -exp(-cos(T))*.2;
vec3 col=vec3(0),
ro=vec3(0,-roomsize.y*.5,zoom-(roomsize.x-roomsize.x*.225)),
rd=normalize(vec3(uv,1)),
l=normalize(vec3(-6,2.49,-4));
cam(ro);
cam(rd);
vec3 p=ro;
const float steps=180.,maxd=20.;
float dd=.0,side=1.,e=1.;
for (float i=.0; i<steps; i++) {
float d=map(p)*side;
if (d<1e-2) {
vec3 n=norm(p)*side;
if (dot(l,n)<.0) l=-l;
float diff=max(.0,dot(l,n)),
fog=1.-clamp(dd/maxd,.0,1.),
fres=max(.0,dot(-rd,n));
if (mat==.0) {
if (p.x>(roomsize.x-walleps)) {
col+=mix(diff*flame(p.zy*.125)*2.,vec3(fres),fog)*e;
} else if (p.x<-(roomsize.x-walleps)) {
col+=mix(diff*dflame(p.zy*.25)*2.,vec3(fres),fog)*e;
} else if (p.z<-(roomsize.z-walleps)) {
col+=mix(diff*mollusc(p.xy*.25)*2.5,vec3(fres),fog)*e;
} else if (p.z>(roomsize.z-walleps)) {
col+=mix(diff*creature(p.xy*.15)*2.,vec3(fres),fog)*e;
} else if (p.y>(roomsize.y-walleps)) {
col+=mix(diff*water(p.xz*.125)*4.,vec3(fres),fog)*e;
} else if (p.y<-(roomsize.y-walleps)) {
col+=mix(diff*crawler(p.xz*.1)*4.,vec3(fres),fog)*e;
} else {
// no color...
}
col-=diff*fog;
break;
} else {
vec3 h=normalize(l-rd);
col+=e*fog*diff*(
1.88*pow(max(.0, dot(n, h)), 33.) +
.05*pow(max(.0, fres), 33.));
side=-side;
vec3 rdo=refract(rd,n,1.+side*.45);
if (dot(rdo,rdo)==.0) {
rdo=reflect(rd,n);
}
rd=rdo;
d=9e-2;
e*=.925;
}
}
if (dd>maxd) {
dd=maxd;
break;
}
p+=rd*d;
dd+=d;
}
fragColor = vec4(col,1);
}
`
let time
let buffer
let program
let touch
let resolution
let pointerCount
let vertices = []
let touching = false
function resize() {
const { innerWidth: width, innerHeight: height } = window
canvas.width = width * dpr
canvas.height = height * dpr
gl.viewport(0, 0, width * dpr, height * dpr)
}
function compile(shader, source) {
gl.shaderSource(shader, source)
gl.compileShader(shader)
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(shader))
}
}
function setup() {
const vs = gl.createShader(gl.VERTEX_SHADER)
const fs = gl.createShader(gl.FRAGMENT_SHADER)
program = gl.createProgram()
compile(vs, vertexSource)
compile(fs, fragmentSource)
gl.attachShader(program, vs)
gl.attachShader(program, fs)
gl.linkProgram(program)
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(program))
}
vertices = [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0]
buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
const position = gl.getAttribLocation(program, "position")
gl.enableVertexAttribArray(position)
gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0)
time = gl.getUniformLocation(program, "time")
touch = gl.getUniformLocation(program, "touch")
pointerCount = gl.getUniformLocation(program, "pointerCount")
resolution = gl.getUniformLocation(program, "resolution")
}
function draw(now) {
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.useProgram(program)
gl.bindBuffer(gl.ARRAY_BUFFER, null)
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.uniform1f(time, now * 0.001)
gl.uniform2f(touch, ...getTouches())
gl.uniform1i(pointerCount, touches.size)
gl.uniform2f(resolution, canvas.width, canvas.height)
gl.drawArrays(gl.TRIANGLES, 0, vertices.length * 0.5)
}
function getTouches() {
if (!touches.size) {
return [0, 0]
}
for (let [id, t] of touches) {
const result = [dpr * t.clientX, dpr * (innerHeight - t.clientY)]
return result
}
}
function loop(now) {
draw(now)
requestAnimationFrame(loop)
}
function init() {
setup()
resize()
loop(0)
}
document.body.onload = init
window.onresize = resize
canvas.onpointerdown = e => {
touching = true
touches.set(e.pointerId, e)
}
canvas.onpointermove = e => {
if (!touching) return
touches.set(e.pointerId, e)
}
canvas.onpointerup = e => {
touching = false
touches.clear()
}
canvas.onpointerout = e => {
touching = false
touches.clear()
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.