Skip to content

Instantly share code, notes, and snippets.

@jftochka
Created October 17, 2023 08:05
Show Gist options
  • Save jftochka/92d6dfa4fd8a8f5c1dc1f68a0e2e82fa to your computer and use it in GitHub Desktop.
Save jftochka/92d6dfa4fd8a8f5c1dc1f68a0e2e82fa to your computer and use it in GitHub Desktop.
Shark!
canvas#canvas
/*********
* made by Matthias Hurrle (@atzedent)
*/
/** @type {HTMLCanvasElement} */
const canvas = window.canvas
const gl = canvas.getContext("webgl2")
const dpr = Math.max(.5, .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 vec2 touch;
uniform float time;
uniform int pointerCount;
#define T (time+12.)
#define S smoothstep
#define P pointerCount
#define mouse (touch/resolution)
#define rot(a) mat2(cos(a),-sin(a),sin(a),cos(a))
#define syl(p,s) (length(p)-s)
float rnd(vec2 p) {
return fract(
sin(
dot(
p,
vec2(12.233, 78.599)
)
)*45876.53521
);
}
float smin(float a, float b, float k) {
float h =
clamp(
.5+.5*(b-a)/k,
.0,
1.
);
return mix(b, a, h)-k*h*(1.-h);
}
float egg(vec3 p, vec3 s) {
return (length(p / s) - 1.) * min(min(s.x, s.y), s.z);
}
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 dsc(vec3 p, vec2 s, float r) {
vec2 e = vec2(
abs(length(p.xy)),
abs(p.z)
)-s;
return length(max(e,.0))+
min(.0, max(e.x, e.y))-r;
}
float fin(vec3 p) {
p.x += exp(-cos(T*.5)*.5)-.25;
vec3 q = p;
q.y -= .5;
q.x += .6;
float o = dsc(p, vec2(1.2,.0125)*1.2,.0),
i = dsc(q, vec2(.8,.5)*1.4,.0);
return max(o, -i*.5);
}
float mat = .0;
float map(vec3 p) {
float t = T*2.;
vec3 q = p+vec3(0, 1.05, 0);
q.yz += .125*sin(t+q.xz*1.5);
vec3 st = p+vec3(0, 1.5+.2*sin(-1.+t), 0);
float d = 5e5,
flr = box(q, vec3(9,.05, 9),.05),
sph = fin(st);
d = min(d, flr);
d = smin(d, -egg(st+vec3(1.85,0,0),vec3(.4,.2,.1)),-.5);
d = min(d, sph);
if (d == sph) mat = 1.;
else mat = .0;
return d;
}
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);
}
float tween(float x) {
return x*x*x*(10. + x*(-15. + 6.*x));
}
void cam(inout vec3 p) {
if (P > 0) {
p.yz *= rot(-.15);
p.xz *= rot((.65 - mouse.x) * acos(-1.) * 2.);
} else {
p.z -= .05*-sin(T*2.+p.y*1.5);
p.yz *= rot(-.1);
p.xz *= rot(.2-.6*sin(T*.6)+tween(.5+.5*sin(T*.2)));
}
}
void main(void) {
vec2 uv = (
gl_FragCoord.xy-.5*resolution
)/min(resolution.x, resolution.y);
vec3 col = vec3(0),
ro = vec3(0, 0, -4),
rd = normalize(vec3(uv, 1));
cam(ro);
cam(rd);
vec3 p = ro;
const float steps = 400., maxd = 20.;
float i = .0,
dd = .0,
at = .0;
for (; i < steps; i++) {
float d = map(p)*.5;
if (d < 1e-3) {
if (mat == 1.) {
col = vec3(3, 2, 1)/3.;
break;
}
d = 1e-2;
vec3 n = norm(p);
rd = reflect(rd, n);
}
if (dd > maxd) {
dd = maxd;
break;
}
p += rd*d;
dd += d;
at += .1*(.1/dd);
}
vec3 tint = vec3(3, 2, 1)*at;
float glow = S(.2,.6, i/steps*1.5);
col += rnd(p.xz)*.31;
col += glow*tint;
col.yz *= rot((P > 0?.0: .5*tween(sin(T*.2)*.5+.5)-.5));
col.xz *= rot(2.75);
col = exp(-col*8.);
col = sqrt(col);
col = exp(-col*10.);
col += uv.x*uv.y*.3;
col *= S(1.,.0, dot(uv, uv));
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()
}

Shark!

Majestically, the shark makes its way through the open sea. The scene is viewed through the lens of a night vision device.

A Pen by Matthias Hurrle on CodePen.

License.

* {
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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment