A Pen by Marie Hekel on CodePen.
Created
December 13, 2024 16:20
-
-
Save mhekel/f1c85ebb88d7b1fc4ff9e6ad27628d21 to your computer and use it in GitHub Desktop.
a trip
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <div class="container"> | |
| <canvas id="glcanvas"></canvas> | |
| <div class="top-container"> | |
| <h2 id="text3d">Because, Donut</h2> | |
| </div> | |
| <div id="navbar"> | |
| <a href="home.html">Home</a> | |
| <a href="https://becausedonut-shop.fourthwall.com">Products</a> | |
| <a href="iracing.html">iRacing</a> | |
| </div> | |
| <script id="vertex-shader" type="x-shader/x-vertex"> | |
| attribute vec2 a_position; | |
| void main() { | |
| gl_Position = vec4(a_position, 0.0, 1.0); | |
| } | |
| </script> | |
| <script id="fragment-shader" type="x-shader/x-fragment"> | |
| #ifdef GL_ES | |
| precision mediump float; | |
| #endif | |
| uniform vec2 u_resolution; | |
| uniform vec2 u_mouse; | |
| uniform float u_time; | |
| uniform float u_terrainHeight; | |
| uniform float u_terrainNoise; | |
| uniform float u_camPosY; | |
| uniform float u_lookAtY; | |
| uniform float u_speedZ; | |
| vec3 palette( float t ) { | |
| vec3 a = vec3(0.5, 0.5, 0.0); | |
| vec3 b = vec3(0.5, 0.5, 0.5); | |
| vec3 c = vec3(1.0, 1.0, 1.0); | |
| vec3 d = vec3(0.25,0.4,0.55); | |
| return a + b*cos( 6.28318*(c*t+d) ); | |
| } | |
| vec3 palette2( float t ) { | |
| vec3 a = vec3(0.5, 0.5, 0.5); | |
| vec3 b = vec3(0.5, 0.5, 0.5); | |
| vec3 c = vec3(1.0, 1.0, 1.0); | |
| vec3 d = vec3(0.25,0.4,0.55); | |
| return a + b*cos( 6.28318*(c*t+d) ); | |
| } | |
| float hash(vec3 p) { | |
| p = 50.0 * fract(p * 0.3183099 + vec3(0.71, 0.113, 0.5)); | |
| return -1.0 + 2.0 * fract(p.x * p.y * p.z * (p.x + p.y + p.z)); | |
| } | |
| float noiseF(vec3 p) { | |
| vec3 i = floor(p); | |
| vec3 f = fract(p); | |
| float a = hash(i); | |
| float b = hash(i + vec3(1.0, 0.0, 0.0)); | |
| float c = hash(i + vec3(0.0, 1.0, 0.0)); | |
| float d = hash(i + vec3(1.0, 1.0, 0.0)); | |
| float e = hash(i + vec3(0.0, 0.0, 1.0)); | |
| float f1 = hash(i + vec3(1.0, 0.0, 1.0)); | |
| float g = hash(i + vec3(0.0, 1.0, 1.0)); | |
| float h = hash(i + vec3(1.0, 1.0, 1.0)); | |
| vec3 u = f * f * (3.0 - 2.0 * f); | |
| return mix(mix(mix(a, b, u.x), mix(c, d, u.x), u.y), mix(mix(e, f1, u.x), mix(g, h, u.x), u.y), u.z); | |
| } | |
| vec3 warp(vec3 p) { | |
| vec3 offset = vec3(noiseF(p + u_time)); | |
| return p + offset; | |
| } | |
| vec2 rotate(vec2 pos, float angle) { | |
| float cosAngle = cos(angle); | |
| float sinAngle = sin(angle); | |
| mat2 rotationMatrix = mat2(cosAngle, -sinAngle, sinAngle, cosAngle); | |
| return rotationMatrix * pos; | |
| } | |
| vec3 getRayDirection(vec2 uv, vec3 camPos, vec3 camForward, vec3 camRight, vec3 camUp, float fov) { | |
| vec3 dir = camForward + uv.x * camRight * fov + uv.y * camUp * fov; | |
| return normalize(dir); | |
| } | |
| float oscillate(float time, float minVal, float maxVal) { | |
| float sineWave = sin(time); | |
| float normalizedSine = (sineWave + 1.0) / 2.0; | |
| return mix(minVal, maxVal, normalizedSine); | |
| } | |
| float waveX(float z){ | |
| return sin((z + u_time) * 0.1) * 10.0; | |
| } | |
| float waveXOffset(float z, float zOffset) { | |
| return sin((z + zOffset + u_time) * 0.1) * 10.0; | |
| } | |
| float boxSDF(vec3 p, vec3 bsize) { | |
| p.x += waveX(p.z); | |
| vec3 d = vec3(mod(p.x, 3.0) - 1.5, p.y * 2.0, mod(p.z, 3.0) - 1.5); | |
| d = abs(d) - bsize; | |
| if (p.x < -3.0 || p.x > 3.0) { | |
| return 100.0; | |
| } | |
| return length(max(d, 0.0)) + min(max(d.x, max(d.y, d.z)), 0.0); | |
| } | |
| float roadSDF(vec3 p, vec3 bsize) { | |
| p.x += waveX(p.z); | |
| vec3 repeatedPos = vec3(p.x, p.y, mod(p.z, 6.0) - 3.0); | |
| vec3 d = abs(repeatedPos) - bsize; | |
| return length(max(d, 0.0)) + min(max(d.x, max(d.y, d.z)), 0.0); | |
| } | |
| float sphereSDF( vec3 p, float size ) | |
| { | |
| float noise = noiseF(p + u_time); | |
| p = mod(p + noise, 100.0) - 50.0 * 0.5; | |
| return length(p)-size; | |
| } | |
| float terrainSDF(vec3 p) { | |
| float scale = u_terrainNoise; | |
| float height = u_terrainHeight; | |
| p.x += waveX(p.z); | |
| float baseTerrainHeight = max(0.0, noiseF(vec3(p.x * scale, 0.0, p.z * scale)) * height); | |
| float detailNoise1 = max(0.0, noiseF(vec3(p.x * scale * 2.0, 0.0, p.z * scale * 2.0)) * height * 0.5); | |
| float detailNoise2 = max(0.0, noiseF(vec3(p.x * scale * 4.0, 0.0, p.z * scale * 4.0)) * height * 0.25); | |
| float detailNoise3 = max(0.0, noiseF(vec3(p.x * scale * 6.0, 0.0, p.z * scale * 6.0)) * height * 0.1); | |
| float totalHeight = baseTerrainHeight + detailNoise1 + detailNoise2 + detailNoise3; | |
| float distanceFromCenter = abs(p.x) - 20.0; | |
| float terrain = p.y - totalHeight; | |
| return max(terrain, -distanceFromCenter); | |
| } | |
| vec3 sky(vec2 uv) { | |
| uv.x += noiseF(vec3(uv.x * 0.5, uv.y * 0.5, uv.y * 0.5)); | |
| uv.y += noiseF(vec3(uv.x * 0.5, uv.y * 0.5, uv.y * 0.5)); | |
| float angle = atan(uv.y, uv.x); | |
| float radius = length(uv); | |
| float spiral = sin(radius * u_time * 0.001 - u_time * 2.0 + angle * 4.0); | |
| float t = 5.0 + 0.1 * spiral + u_time * 0.1 + radius * 0.4; | |
| vec3 color = palette2(t + u_time/200.0) * 0.2; | |
| return color; | |
| } | |
| float sceneSDF(vec3 p, vec3 camPos) { | |
| float road = roadSDF(p, vec3(20.0, 0.0, 10.0)); | |
| float stripes = boxSDF(p, vec3(0.2, 0.0, 1.0)); | |
| float terrain = terrainSDF(p); | |
| return min(road, min(terrain, stripes)); | |
| } | |
| vec3 getColor(vec3 worldPos, vec3 camPos) { | |
| float roadDist = roadSDF(worldPos, vec3(10.0, 0.0, 10.0)); | |
| float stripeDist = boxSDF(worldPos, vec3(0.2, 1.0, 1.0)); | |
| float reverseWave = waveXOffset(camPos.z, -25.0); | |
| float terrainDist = terrainSDF(worldPos); | |
| float closestDist = min(terrainDist, min(roadDist, stripeDist)); | |
| if(closestDist == terrainDist){ | |
| return palette(exp(length((worldPos.y)* 0.02)) + u_time/50.0); | |
| } | |
| if (closestDist == roadDist) { | |
| worldPos.x += waveX(worldPos.z); | |
| return palette(exp(-length((worldPos.x )* 0.011)) + u_time/20.0); | |
| } else if (closestDist == stripeDist) { | |
| worldPos.x += waveX(worldPos.z); | |
| return palette(exp(length((worldPos.x )* 0.11)) + u_time/20.0); | |
| } | |
| return vec3(0.0); | |
| } | |
| float rayMarch(vec3 ro, vec3 rd) { | |
| float t = 0.0; | |
| float maxDist = 400.0; | |
| float hitThreshold = 0.003; | |
| for (int i = 0; i < 400; i++) { | |
| vec3 p = ro + rd * t; | |
| float dist = sceneSDF(p, ro); | |
| if (dist < hitThreshold) { | |
| return t; | |
| } | |
| t += dist * 0.5; | |
| if (t > maxDist) break; | |
| } | |
| return maxDist; | |
| } | |
| void main() { | |
| vec2 uv = (gl_FragCoord.xy / u_resolution.xy) * 2.0 - 1.0; | |
| uv.x *= u_resolution.x / u_resolution.y; | |
| vec3 camPos = vec3(0.0 , u_camPosY, u_time * u_speedZ); | |
| camPos.x = waveXOffset(camPos.z, -25.0); | |
| vec3 camForward = normalize(vec3(waveXOffset(camPos.z, -5.0), u_lookAtY, 20.0)); | |
| vec3 camRight = normalize(cross(camForward, vec3(0.0, 1.0, 0.0))); | |
| vec3 camUp = cross(camRight, camForward); | |
| vec3 rayDir = getRayDirection(uv, camPos, camForward, camRight, camUp, 1.5); | |
| float dist = rayMarch(camPos, rayDir); | |
| vec3 color = vec3(0.0); | |
| if (dist < 200.0) { | |
| vec3 worldPos = camPos + rayDir * dist; | |
| color += getColor(worldPos, camPos); | |
| }else { | |
| color += sky(uv); | |
| } | |
| gl_FragColor = vec4(color, 1.0); | |
| } | |
| </script> | |
| <canvas id="text" width="400" height="300">this is a test</canvas> | |
| </div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { GUI } from "https://cdn.skypack.dev/dat.gui"; | |
| const canvas = document.getElementById("glcanvas"); | |
| const gl = canvas.getContext("webgl2") || canvas.getContext("webgl"); | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| function getShaderSource(id) { | |
| return document.getElementById(id).textContent; | |
| } | |
| const vertexShaderSource = getShaderSource("vertex-shader"); | |
| const fragmentShaderSource = getShaderSource("fragment-shader"); | |
| function createShader(gl, type, source) { | |
| const shader = gl.createShader(type); | |
| gl.shaderSource(shader, source); | |
| gl.compileShader(shader); | |
| if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { | |
| console.error( | |
| "An error occurred compiling the shaders:", | |
| gl.getShaderInfoLog(shader) | |
| ); | |
| gl.deleteShader(shader); | |
| return null; | |
| } | |
| return shader; | |
| } | |
| function createProgram(gl, vertexShader, fragmentShader) { | |
| const program = gl.createProgram(); | |
| gl.attachShader(program, vertexShader); | |
| gl.attachShader(program, fragmentShader); | |
| gl.linkProgram(program); | |
| if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { | |
| console.error( | |
| "Unable to initialize the shader program:", | |
| gl.getProgramInfoLog(program) | |
| ); | |
| return null; | |
| } | |
| return program; | |
| } | |
| const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource); | |
| const fragmentShader = createShader( | |
| gl, | |
| gl.FRAGMENT_SHADER, | |
| fragmentShaderSource | |
| ); | |
| const program = createProgram(gl, vertexShader, fragmentShader); | |
| const positionBuffer = gl.createBuffer(); | |
| gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); | |
| const positions = [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0]; | |
| gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); | |
| const positionLocation = gl.getAttribLocation(program, "a_position"); | |
| gl.enableVertexAttribArray(positionLocation); | |
| gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); | |
| gl.useProgram(program); | |
| const u_terrainHeight = gl.getUniformLocation(program, 'u_terrainHeight'); | |
| const u_terrainNoise = gl.getUniformLocation(program, 'u_terrainNoise'); | |
| const u_camPosY = gl.getUniformLocation(program, 'u_camPosY'); | |
| const u_lookAtY = gl.getUniformLocation(program, 'u_lookAtY'); | |
| const u_speedZ = gl.getUniformLocation(program, 'u_speedZ'); | |
| const resolutionLocation = gl.getUniformLocation(program, "u_resolution"); | |
| const timeLocation = gl.getUniformLocation(program, "u_time"); | |
| const gui = new GUI({ width: "100%", closeOnTop: true, closed: true }); | |
| const guiParams = { | |
| terrainHeight: 20.0, | |
| terrainNoise: 0.033, | |
| camPosY: 5.0, | |
| lookAtY: 0.0, | |
| speedZ: 4.0, | |
| }; | |
| gui.domElement.querySelector(".close-button").innerHTML = "Open Controls"; | |
| gl.uniform1f(u_terrainHeight, guiParams.terrainHeight); | |
| gl.uniform1f(u_terrainNoise, guiParams.terrainNoise); | |
| gl.uniform1f(u_camPosY, guiParams.camPosY); | |
| gl.uniform1f(u_lookAtY, guiParams.lookAtY); | |
| gl.uniform1f(u_speedZ, guiParams.speedZ); | |
| gui.add(guiParams, 'terrainHeight', 0.0, 20.0).onChange((value) => { | |
| gl.uniform1f(u_terrainHeight, value); | |
| }); | |
| gui.add(guiParams, 'terrainNoise', 0.0, 1.0).onChange((value) => { | |
| gl.uniform1f(u_terrainNoise, value); | |
| }); | |
| gui.add(guiParams, 'camPosY', 1.0, 50.0).onChange((value) => { | |
| gl.uniform1f(u_camPosY, value); | |
| }); | |
| gui.add(guiParams, 'lookAtY', -200.0, 0.0).onChange((value) => { | |
| gl.uniform1f(u_lookAtY, value); | |
| }); | |
| gui.add(guiParams, 'speedZ', 0.0, 100.0).onChange((value) => { | |
| gl.uniform1f(u_speedZ, value); | |
| }); | |
| gl.clearColor(0, 0, 0, 1); | |
| gl.clear(gl.COLOR_BUFFER_BIT); | |
| let startTime = null; | |
| function render(time) { | |
| if (!startTime) startTime = time; // Set the start time only once. | |
| const elapsedTime = (time - startTime) * 0.001; // Convert time to seconds. | |
| gl.uniform2f(resolutionLocation, canvas.width, canvas.height); | |
| gl.uniform1f(timeLocation, elapsedTime); // Pass elapsed time to the shader. | |
| gl.clear(gl.COLOR_BUFFER_BIT); | |
| gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); | |
| requestAnimationFrame(render); | |
| } | |
| resize(); | |
| function resize() { | |
| const devicePixelRatio = Math.min(window.devicePixelRatio || 1, 2); | |
| canvas.width = Math.floor(window.innerWidth * devicePixelRatio); | |
| canvas.height = Math.floor(window.innerHeight * devicePixelRatio); | |
| gl.viewport(0, 0, canvas.width, canvas.height); | |
| canvas.style.width = window.innerWidth + "px"; | |
| canvas.style.height = window.innerHeight + "px"; | |
| document.documentElement.requestFullscreen(); | |
| } | |
| window.addEventListener("resize", resize); | |
| requestAnimationFrame(render); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @import url('https://fonts.googleapis.com/css2?family=Lobster&display=swap'); | |
| @import url('https://fonts.googleapis.com/css2?family=Permanent+Marker&display=swap'); | |
| body { | |
| height`: 100vh; | |
| margin: 0; | |
| } | |
| body, html, #glcanvas { | |
| overflow: hidden | |
| } | |
| .container { | |
| position: relative; | |
| } | |
| canvas { | |
| display: block; | |
| width: 100vw; | |
| height: 100vh; | |
| z-index: -1; | |
| position: fixed; | |
| } | |
| .top-container { | |
| background-color: rgba(0,0,0,0.5); | |
| padding: 75px 0; | |
| text-align: center; | |
| min-width: 100vw; | |
| max-height: 100vh; | |
| } | |
| .header { | |
| background-color: rgba(0,0,0,0.4); | |
| } | |
| .content { | |
| padding: 5px; | |
| } | |
| .sticky { | |
| position: fixed; | |
| top: 0; | |
| width: 100%; | |
| } | |
| .sticky + .content { | |
| padding-top: 102px; | |
| } | |
| @import url("https://fonts.googleapis.com/css2?family=Lobster&display=swap"); | |
| h2 { | |
| font-family: "Lobster", cursive; | |
| font-size: 7em; | |
| text-align: center; | |
| } | |
| h4 { | |
| font-family: "Permanent Marker", cursive; | |
| font-weight: 400; | |
| font-style: normal; | |
| font-size: 3em; | |
| } | |
| #text3d { | |
| font-family: Lobster; | |
| text-transform: lowercase; | |
| text-align: center; | |
| color: #000000; | |
| -webkit-text-stroke: 3px orange; | |
| text-shadow: #f9f9f9 3px 5px 5px, #999 16px 8px 3px, #ccc 9px 11px 10px; | |
| } | |
| /* Style the navbar */ | |
| #navbar { | |
| position: sticky; | |
| top: 0; | |
| overflow: hidden; | |
| background-color: rgba(100,100,100,0.5); | |
| } | |
| /* Navbar links */ | |
| #navbar a { | |
| float: left; | |
| display: block; | |
| color: #f2f2f2; | |
| text-align: center; | |
| padding: 14px; | |
| text-decoration: none; | |
| font-family: "Permanent Marker"; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment