Created
July 17, 2025 07:36
-
-
Save disini/45de127f7f915175036a016d6cdd1c41 to your computer and use it in GitHub Desktop.
Slime Simulation (mips)
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
| body { | |
| margin: 0; | |
| font-family: monospace; | |
| background: #444; | |
| } | |
| canvas { | |
| display: block; | |
| width: 100vw; | |
| height: 100vh; | |
| } | |
| .dg { | |
| opacity: 0.8; | |
| } | |
| #nowebgl { | |
| background: red; | |
| color: white; | |
| font-family: sans-serif; | |
| font-weight: bold; | |
| font-size: 36pt; | |
| width: 100vw; | |
| height: 100vh; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| } |
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
| <canvas></canvas> | |
| <div id="nowebgl" style="display: none;"> | |
| <div>need WebGL2</div> | |
| </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://threejsfundamentals.org/threejs/../3rdparty/dat.gui.module.js'; | |
| import * as twgl from 'https://twgljs.org/dist/4.x/twgl-full.module.js'; | |
| function main() { | |
| const m4 = twgl.m4; | |
| const v3 = twgl.v3; | |
| const gl = document.querySelector("canvas").getContext("webgl2"); | |
| if (!gl) { | |
| document.querySelector("canvas").style.display = "none"; | |
| document.querySelector("#nowebgl").style.display = ""; | |
| } | |
| const ext = gl.getExtension('EXT_color_buffer_float'); | |
| const ext2 = gl.getExtension('OES_texture_float_linear'); | |
| console.log((ext && ext2) ? 'using floating point textures' : 'using 8bit textures'); | |
| const processVS = `#version 300 es | |
| in vec2 position; | |
| in float angle; | |
| out vec2 v_position; | |
| out float v_angle; | |
| out vec4 v_color; | |
| uniform vec2 resolution; | |
| uniform float moveSpeed; | |
| uniform float turnSpeed; | |
| uniform float trailWeight; | |
| uniform float sensorOffsetDist; | |
| uniform float sensorAngleSpacing; | |
| uniform float sensorSize; | |
| uniform float deltaTime; | |
| uniform sampler2D tex; | |
| #define PI radians(180.0) | |
| uint hash(uint s) { | |
| s ^= 2747636419u; | |
| s *= 2654435769u; | |
| s ^= s >> 16; | |
| s *= 2654435769u; | |
| s ^= s >> 16; | |
| s *= 2654435769u; | |
| return s; | |
| } | |
| float scaleToRange01(uint v) { | |
| return float(v) / 4294967295.0; | |
| } | |
| float sense(vec2 position, float sensorAngle) { | |
| vec2 sensorDir = vec2(cos(sensorAngle), sin(sensorAngle)); | |
| vec2 sensorCenter = position + sensorDir * sensorOffsetDist; | |
| vec2 size = vec2(textureSize(tex, 0)); | |
| vec2 sensorUV = sensorCenter / size; | |
| vec4 s = textureLod(tex, sensorUV, sensorSize); | |
| return s.r; | |
| } | |
| void main() { | |
| uint width = uint(resolution.x); | |
| uint height = uint(resolution.y); | |
| uint random = hash(uint(position.y) * width + uint(position.x) + uint(gl_VertexID)); | |
| v_angle = angle; | |
| float weightForward = sense(position, angle); | |
| float weightLeft = sense(position, angle + sensorAngleSpacing); | |
| float weightRight = sense(position, angle - sensorAngleSpacing); | |
| float randomSteerStrength = scaleToRange01(random); | |
| // continue in same direction | |
| if (weightForward > weightLeft && weightForward > weightRight) { | |
| v_angle += 0.0; // ? | |
| } else if (weightForward < weightLeft && weightForward < weightRight) { | |
| v_angle += (randomSteerStrength - 0.5) * 2.0 * turnSpeed * deltaTime; | |
| } else if (weightRight > weightLeft) { | |
| v_angle -= randomSteerStrength * turnSpeed * deltaTime; | |
| } else if (weightLeft > weightRight) { | |
| v_angle += randomSteerStrength * turnSpeed * deltaTime; | |
| } | |
| // move agent | |
| vec2 direction = vec2(cos(v_angle), sin(v_angle)); | |
| vec2 newPos = position + direction * moveSpeed * deltaTime; | |
| // clamp to boundries and pick new angle if hit boundries | |
| if (newPos.x < 0.0 || newPos.x >= resolution.x || | |
| newPos.y < 0.0 || newPos.y >= resolution.y) { | |
| newPos.x = min(resolution.x - 0.01, max(0.0, newPos.x)); | |
| newPos.y = min(resolution.y - 0.01, max(0.0, newPos.y)); | |
| v_angle = scaleToRange01(random) * 2.0 * PI; | |
| } | |
| v_position = newPos; | |
| // convert to clipspace | |
| gl_Position = vec4(newPos / resolution * 2.0 - 1.0, 0, 1); | |
| gl_PointSize = 1.0; | |
| v_color = vec4(vec3(trailWeight * deltaTime), 1); | |
| } | |
| `; | |
| const processFS = `#version 300 es | |
| precision highp float; | |
| in vec4 v_color; | |
| out vec4 outColor; | |
| void main() { | |
| outColor = v_color; | |
| } | |
| `; | |
| const quadVS = `#version 300 es | |
| layout(location = 0) in vec4 position; | |
| out vec2 v_texcoord; | |
| void main() { | |
| gl_Position = position; | |
| v_texcoord = position.xy * 0.5 + 0.5; | |
| } | |
| `; | |
| const rampFS = `#version 300 es | |
| precision highp float; | |
| uniform sampler2D tex; | |
| uniform sampler2D ramp; | |
| uniform float colorPower; | |
| uniform float colorDivisor; | |
| in vec2 v_texcoord; | |
| out vec4 outputColor; | |
| void main () { | |
| vec4 inputColor = texture(tex, v_texcoord); | |
| outputColor = texture(ramp, vec2(pow(inputColor.r, colorPower) / colorDivisor, 0)); | |
| } | |
| `; | |
| const fadeAndSpreadFS = `#version 300 es | |
| precision highp float; | |
| in vec2 v_texcoord; | |
| uniform float evaporateSpeed; | |
| uniform float diffuseSpeed; | |
| uniform float deltaTime; | |
| uniform sampler2D tex; | |
| out vec4 outColor; | |
| void main() { | |
| vec4 originalValue = texture(tex, v_texcoord); | |
| // Simulate diffuse with a simple 3x3 blur | |
| vec4 sum; | |
| ivec2 size = textureSize(tex, 0); | |
| ivec2 samplePos = ivec2(v_texcoord * vec2(size)); | |
| for (int offsetY = -1; offsetY <= 1; ++offsetY) { | |
| for (int offsetX = -1; offsetX <= 1; ++offsetX) { | |
| ivec2 sampleOff = ivec2(offsetX, offsetY); | |
| sum += texelFetch(tex, samplePos + sampleOff, 0); | |
| } | |
| } | |
| vec4 blurResult = sum / 9.0; | |
| vec4 diffusedValue = mix(originalValue, blurResult, diffuseSpeed * deltaTime); | |
| vec4 diffusedAndEvaporatedValue = max(vec4(0), diffusedValue - evaporateSpeed * deltaTime); | |
| outColor = vec4(diffusedAndEvaporatedValue.rgb, 1); | |
| } | |
| `; | |
| const processProgramInfo = twgl.createProgramInfo(gl, [processVS, processFS], { | |
| transformFeedbackVaryings: ['v_position', 'v_angle'], | |
| }); | |
| const displayProgramInfo = twgl.createProgramInfo(gl, [quadVS, rampFS]); | |
| const fadeAndSpreadProgramInfo = twgl.createProgramInfo(gl, [quadVS, fadeAndSpreadFS]); | |
| const quadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl); | |
| const quadVAI = twgl.createVertexArrayInfo(gl, displayProgramInfo, quadBufferInfo); | |
| function createAgents(positions, angles) { | |
| const bufferInfo = twgl.createBufferInfoFromArrays(gl, { | |
| position: { data: positions, numComponents: 2, }, | |
| angle: { data: angles, numComponents: 1, }, | |
| }); | |
| const vertexArrayInfo = twgl.createVertexArrayInfo(gl, processProgramInfo, bufferInfo); | |
| const transformFeedback = twgl.createTransformFeedback(gl, processProgramInfo, { | |
| v_position: bufferInfo.attribs.position, | |
| v_angle: bufferInfo.attribs.angle, | |
| }); | |
| return { | |
| bufferInfo, | |
| transformFeedback, | |
| vertexArrayInfo, | |
| }; | |
| } | |
| function rand(min, max) { | |
| if (max === undefined) { | |
| max = min; | |
| min = 0; | |
| } | |
| return Math.random() * (max - min) + min; | |
| } | |
| // resize once before initialization | |
| twgl.resizeCanvasToDisplaySize(gl.canvas); | |
| const maxAgents = 1000000; | |
| function createCircleInAgents(maxAgents) { | |
| const positions = []; | |
| const angles = []; | |
| const radius = Math.min(gl.canvas.width, gl.canvas.height) / 2; | |
| for (let i = 0; i < maxAgents; ++i) { | |
| const angle = rand(0, Math.PI * 2); | |
| const r = Math.sqrt(rand(1)) * radius; //Math.sqrt(rand(1)) ∗ radius; | |
| positions.push( | |
| gl.canvas.width / 2 + Math.cos(angle) * r, | |
| gl.canvas.height / 2 + Math.sin(angle) * r, | |
| ); | |
| angles.push(angle + Math.PI); | |
| } | |
| return { | |
| positions, | |
| angles, | |
| } | |
| }; | |
| function createCircleOutAgents(maxAgents) { | |
| const positions = []; | |
| const angles = []; | |
| for (let i = 0; i < maxAgents; ++i) { | |
| positions.push( | |
| gl.canvas.width / 2 + rand(-10, 10), | |
| gl.canvas.height / 2 + rand(-10, 10), | |
| ); | |
| angles.push(rand(0, Math.PI * 2)); | |
| } | |
| return { | |
| positions, | |
| angles, | |
| } | |
| }; | |
| function createRandomAgents(maxAgents) { | |
| const positions = []; | |
| const angles = []; | |
| for (let i = 0; i < maxAgents; ++i) { | |
| positions.push( | |
| rand(0, gl.canvas.width), | |
| rand(0, gl.canvas.height), | |
| ); | |
| angles.push(rand(0, Math.PI * 2)); | |
| } | |
| return { | |
| positions, | |
| angles, | |
| } | |
| }; | |
| const {positions, angles} = createCircleInAgents(maxAgents); | |
| const agents1 = createAgents(positions, angles); | |
| const agents2 = createAgents(positions, angles); | |
| function restart({positions, angles}) { | |
| twgl.setAttribInfoBufferFromArray(gl, agents1.bufferInfo.attribs.position, positions); | |
| twgl.setAttribInfoBufferFromArray(gl, agents2.bufferInfo.attribs.position, positions); | |
| twgl.setAttribInfoBufferFromArray(gl, agents1.bufferInfo.attribs.angle, angles); | |
| twgl.setAttribInfoBufferFromArray(gl, agents2.bufferInfo.attribs.angle, angles); | |
| twgl.bindFramebufferInfo(gl, fbi1); | |
| gl.clear(gl.COLOR_BUFFER_BIT); | |
| twgl.bindFramebufferInfo(gl, fbi2); | |
| gl.clear(gl.COLOR_BUFFER_BIT); | |
| } | |
| function restartCircleIn() { | |
| restart(createCircleInAgents(maxAgents)); | |
| } | |
| function restartCircleOut() { | |
| restart(createCircleOutAgents(maxAgents)); | |
| } | |
| function restartRandom() { | |
| restart(createRandomAgents(maxAgents)); | |
| } | |
| const attachments = [ | |
| { internalFormat: (ext && ext2) ? gl.RGBA32F : gl.RGBA8, min: gl.LINEAR_MIPMAP_LINEAR }, | |
| ]; | |
| const fbi1 = twgl.createFramebufferInfo(gl, attachments); | |
| gl.generateMipmap(gl.TEXTURE_2D); | |
| gl.clearColor(0, 0, 0, 1); | |
| gl.clear(gl.COLOR_BUFFER_BIT); | |
| const fbi2 = twgl.createFramebufferInfo(gl, attachments); | |
| gl.generateMipmap(gl.TEXTURE_2D); | |
| gl.clearColor(0, 0, 0, 1); | |
| gl.clear(gl.COLOR_BUFFER_BIT); | |
| function createRampColors(numColors, _stops) { | |
| // we could assume they are sorted but let's sort | |
| const stops = _stops.slice().sort((a, b) => Math.sign(a[0] - b[0])); | |
| const ramp = []; | |
| fillRampRange([0, stops[0][1]], stops[0], numColors, ramp); | |
| for (let i = 0; i < stops.length - 1; ++i) { | |
| fillRampRange(stops[i], stops[i + 1], numColors, ramp); | |
| } | |
| fillRampRange(stops[stops.length - 1], [1, stops[stops.length - 1][1]], numColors, ramp); | |
| return ramp; | |
| } | |
| const lerpColor = (a, b, t) => a.map((s, i) => lerp(s, b[i], t)); | |
| const lerp = (a, b, t) => a + (b - a) * t; | |
| function fillRampRange(start, end, numColors, ramp) { | |
| const startPos = Math.round(start[0] * numColors); | |
| const startColor = start[1]; | |
| const endPos = Math.round(end[0] * numColors); | |
| const endColor = end[1]; | |
| const range = endPos - startPos - 1; | |
| for (let pos = startPos; pos < endPos; ++pos) { | |
| if (pos < 0 || pos >= numColors) { | |
| continue; | |
| } | |
| const t = range ? (pos - startPos) / range : 0; | |
| const color = lerpColor(startColor, endColor, t); | |
| const off = pos * 4; | |
| ramp[off ] = color[0]; | |
| ramp[off + 1] = color[1]; | |
| ramp[off + 2] = color[2]; | |
| ramp[off + 3] = color[3]; | |
| } | |
| } | |
| function createRampTexture(rampSize, ramp) { | |
| const rampColors = createRampColors(rampSize, ramp).map(a => a * 255); | |
| return twgl.createTexture(gl, { | |
| src: rampColors, | |
| width: rampSize, | |
| minMag: gl.LINEAR, | |
| wrap: gl.CLAMP_TO_EDGE, | |
| }); | |
| } | |
| const rampSize = 256; | |
| const red = [1, 0, 0, 1]; | |
| const green = [0, 1, 0, 1]; | |
| const blue = [0, 0, 1, 1]; | |
| const yellow = [1, 1, 0, 1]; | |
| const cyan = [0, 1, 1, 1]; | |
| const magenta = [1, 0, 1, 1]; | |
| const white = [1, 1, 1, 1]; | |
| const black = [0, 0, 0, 1]; | |
| const purple = [0.5, 0, 1, 1]; | |
| const band = (stop, range, edgeColor, centerColor) => [ | |
| [stop - range, edgeColor ], | |
| [stop , centerColor], | |
| [stop + range, edgeColor ], | |
| ]; | |
| const rampTextures = { | |
| 'bpw': [ | |
| [0.0, black], | |
| [0.9, purple], | |
| [1.0, white], | |
| ], | |
| 'ycmy': [ | |
| [0.0, yellow], | |
| [0.2, cyan], | |
| [0.4, magenta], | |
| [1, yellow], | |
| ], | |
| 'bycmy': [ | |
| [0.0, black], | |
| [0.1, yellow], | |
| [0.2, cyan], | |
| [0.4, magenta], | |
| [1, yellow], | |
| ], | |
| 'band': [ | |
| // [0.045, blue], | |
| // [0.050, white], | |
| // [0.055, blue], | |
| ...band(0.1, 0.01, blue, white), | |
| // [0.1, blue], | |
| // [0.12, yellow], | |
| // [0.14, blue], | |
| ...band(0.24, 0.04, blue, yellow), | |
| // [0.155, blue], | |
| // [0.16, [1, 0.5, 1, 1]], | |
| // [0.165, blue], | |
| ...band(0.32, 0.01, blue, [1, 0.5, 1, 1]), | |
| // [0.2, blue], | |
| // [0.22, green], | |
| // [0.24, blue], | |
| ...band(0.44, 0.04, blue, green, blue), | |
| ], | |
| red: [ | |
| [0.0, black], | |
| [0.5, red], | |
| [1.0, white], | |
| ], | |
| bcr: [ | |
| [0.0, black], | |
| [0.5, cyan], | |
| [1.0, red], | |
| ], | |
| gcm: [ | |
| [0.0, green], | |
| [0.5, cyan], | |
| [1.0, magenta], | |
| ], | |
| fire: [ | |
| [0.0, black], | |
| [0.25, red], | |
| [1.0, yellow], | |
| ], | |
| brybry: [ | |
| [0.0, black], | |
| [0.2, red], | |
| [0.4, yellow], | |
| [0.6, black], | |
| [0.8, red], | |
| [1.0, yellow], | |
| ], | |
| hardred: [ | |
| [0.0, black], | |
| [0.01, red], | |
| [1.0, black], | |
| ], | |
| rainbow: [ | |
| [0.0, black], | |
| [0.1, red], | |
| [0.2, yellow], | |
| [0.3, green], | |
| [0.4, cyan], | |
| [0.5, blue], | |
| [0.6, magenta], | |
| [1.0, white], | |
| ], | |
| identity: [ | |
| [0.0, black], | |
| [1.0, white], | |
| ], | |
| beam: [ | |
| [0.0, black], | |
| [0.25, blue], | |
| [0.5, cyan], | |
| [0.75, blue], | |
| [1.0, black], | |
| ], | |
| }; | |
| Object.values(rampTextures).forEach(ramp => ramp.texture = createRampTexture(rampSize, ramp)); | |
| let currentFBI = fbi1; | |
| let current = agents1; | |
| let then = 0; | |
| const settings = { | |
| moveSpeed: 42.0, | |
| turnSpeed: 12, | |
| trailWeight: 60, | |
| sensorOffsetDist: 11, | |
| sensorAngleSpacing: 35 * Math.PI / 180, | |
| sensorSize: 2, | |
| evaporateSpeed: 2.9, | |
| diffuseSpeed: 44, | |
| colorPower: 1, | |
| colorDivisor: 1, | |
| colorScheme: 'ycmy', | |
| numAgents: 1000000, | |
| startShape: 'circleIn', | |
| }; | |
| const presets = { | |
| blob: { | |
| "moveSpeed": 65.22568599220493, | |
| "turnSpeed": 50.72481828583753, | |
| "trailWeight": 60, | |
| "sensorOffsetDist": 11, | |
| "sensorAngleSpacing": 0.6108652381980153, | |
| "sensorSize": 2, | |
| "evaporateSpeed": 2.9, | |
| "diffuseSpeed": 44, | |
| "colorPower": 1, | |
| "colorDivisor": 1, | |
| "colorScheme": "ycmy", | |
| "numAgents": 1000000, | |
| "startShape": "circleIn", | |
| }, | |
| splat: { | |
| "moveSpeed": 119.82207943566836, | |
| "turnSpeed": 76.73755239691357, | |
| "trailWeight": 60, | |
| "sensorOffsetDist": 11, | |
| "sensorAngleSpacing": 0.6108652381980153, | |
| "sensorSize": 2, | |
| "evaporateSpeed": 0.40102932011007003, | |
| "diffuseSpeed": 13.873444961813131, | |
| "colorPower": 1, | |
| "colorDivisor": 1, | |
| "colorScheme": "bcr", | |
| "numAgents": 1000000, | |
| "startShape": "circleOut", | |
| }, | |
| wik: { | |
| "moveSpeed": 119.82207943566836, | |
| "turnSpeed": 24.712084174761483, | |
| "trailWeight": 64.41252941466865, | |
| "sensorOffsetDist": 11, | |
| "sensorAngleSpacing": 0.6108652381980153, | |
| "sensorSize": 2.8288838422724623, | |
| "evaporateSpeed": 1.5932796335343884, | |
| "diffuseSpeed": 37.7184512302995, | |
| "colorPower": 1, | |
| "colorDivisor": 1, | |
| "colorScheme": "bpw", | |
| "numAgents": 1000000, | |
| "startShape": "random", | |
| }, | |
| shockwave: { | |
| "moveSpeed": 42, | |
| "turnSpeed": 3.03480574886478, | |
| "trailWeight": 21.274745347134232, | |
| "sensorOffsetDist": 7.261884964985205, | |
| "sensorAngleSpacing": 2.367158407185097, | |
| "sensorSize": 2, | |
| "evaporateSpeed": 3.923587064318284, | |
| "diffuseSpeed": 63.73118534137554, | |
| "colorPower": 1, | |
| "colorDivisor": 1, | |
| "colorScheme": "ycmy", | |
| "numAgents": 1000000, | |
| "startShape": "circleIn", | |
| }, | |
| mute: { | |
| "moveSpeed": 173.26707874682472, | |
| "turnSpeed": 77.62912785774768, | |
| "trailWeight": 91.18187976291279, | |
| "sensorOffsetDist": 13.44623200677392, | |
| "sensorAngleSpacing": 1.2233700254022015, | |
| "sensorSize": 1.9204064352243861, | |
| "evaporateSpeed": 10.194750211685012, | |
| "diffuseSpeed": 47.28196443691787, | |
| "colorPower": 1, | |
| "colorDivisor": 1, | |
| "colorScheme": "bcr", | |
| "numAgents": 1000000, | |
| "startShape": "circleOut", | |
| }, | |
| whisps: { | |
| "moveSpeed": 180, | |
| "turnSpeed": 18, | |
| "trailWeight": 69, | |
| "sensorOffsetDist": 11, | |
| "sensorAngleSpacing": 0.61, | |
| "sensorSize": 2, | |
| "evaporateSpeed": 16.6, | |
| "diffuseSpeed": 45, | |
| "colorPower": 1, | |
| "colorDivisor": 1, | |
| "colorScheme": "fire", | |
| "numAgents": 1000000, | |
| "startShape": "circleOut", | |
| }, | |
| }; | |
| const buttons = { | |
| preset: Object.keys(presets).pop(), | |
| restart: _ => startShapes[settings.startShape](), | |
| '8bit': false, | |
| }; | |
| const startShapes = { | |
| circleIn: restartCircleIn, | |
| circleOut: restartCircleOut, | |
| random: restartRandom, | |
| }; | |
| const gui = new GUI(); | |
| gui.add(settings, 'moveSpeed', 0.1, 180).listen(); | |
| gui.add(settings, 'turnSpeed', 0.0, 200).listen(); | |
| gui.add(settings, 'trailWeight', 1, 200).listen(); | |
| gui.add(settings, 'sensorOffsetDist', 0, 50).listen(); | |
| gui.add(settings, 'sensorAngleSpacing', 0, 6).listen(); | |
| gui.add(settings, 'sensorSize', 0, 15).listen(); | |
| gui.add(settings, 'evaporateSpeed', 0, 50).listen(); | |
| gui.add(settings, 'diffuseSpeed', 0, 200).listen(); | |
| gui.add(settings, 'numAgents', 1, 1000000).listen(); | |
| gui.add(settings, 'colorScheme', Object.keys(rampTextures)).listen(); | |
| //gui.add(settings, 'colorPower', 0.001, 10).listen(); | |
| //gui.add(settings, 'colorDivisor', 0.001, 5).listen(); | |
| gui.add(settings, 'startShape', Object.keys(startShapes)).listen(); | |
| gui.add(buttons, 'restart'); | |
| gui.add(buttons, 'preset', Object.keys(presets)).onChange(restartPreset); | |
| if (ext && ext2) { | |
| gui.add(buttons, '8bit'); | |
| } | |
| window.addEventListener('keydown', e => { | |
| if (e.key === 's' && e.ctrlKey) { | |
| e.preventDefault(); | |
| console.log(JSON.stringify(settings, null, 2)); | |
| } | |
| }); | |
| function restartPreset() { | |
| const preset = presets[buttons.preset]; | |
| Object.assign(settings, preset); | |
| startShapes[settings.startShape](); | |
| } | |
| restartPreset(); | |
| function render(time) { | |
| time *= 0.001; | |
| const deltaTime = 1 / 60; // note: we don't really want this to be framerate independent. | |
| const format = (!ext || !ext2 || buttons['8bit']) ? gl.RGBA8 : gl.RGBA32F; | |
| const newFormat = attachments[0].internalFormat !== format; | |
| if (newFormat || twgl.resizeCanvasToDisplaySize(gl.canvas)) { | |
| attachments[0].internalFormat = format; | |
| twgl.resizeFramebufferInfo(gl, fbi1, attachments); | |
| twgl.resizeFramebufferInfo(gl, fbi2, attachments); | |
| } | |
| const dest = current === agents1 ? agents2 : agents1; | |
| const destFBI = currentFBI === fbi1 ? fbi2 : fbi1; | |
| gl.bindBuffer(gl.ARRAY_BUFFER, null); | |
| twgl.bindFramebufferInfo(gl, destFBI); | |
| gl.useProgram(processProgramInfo.program); | |
| gl.bindVertexArray(current.vertexArrayInfo.vertexArrayObject); | |
| gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, dest.transformFeedback); | |
| gl.beginTransformFeedback(gl.POINTS); | |
| twgl.setUniforms(processProgramInfo, { | |
| resolution: [gl.canvas.width, gl.canvas.height], | |
| deltaTime, | |
| tex: currentFBI.attachments[0], | |
| }, settings); | |
| twgl.drawBufferInfo(gl, current.vertexArrayInfo, gl.POINTS, settings.numAgents); | |
| current = dest; | |
| gl.endTransformFeedback(); | |
| gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null); | |
| twgl.bindFramebufferInfo(gl, currentFBI); | |
| gl.useProgram(fadeAndSpreadProgramInfo.program); | |
| gl.bindVertexArray(quadVAI.vertexArrayObject); | |
| twgl.setUniforms(fadeAndSpreadProgramInfo, { | |
| deltaTime, | |
| tex: destFBI.attachments[0], | |
| }, settings); | |
| twgl.drawBufferInfo(gl, quadVAI); | |
| twgl.bindFramebufferInfo(gl, null); | |
| gl.bindTexture(gl.TEXTURE_2D, currentFBI.attachments[0]); | |
| gl.generateMipmap(gl.TEXTURE_2D); | |
| gl.useProgram(displayProgramInfo.program); | |
| gl.bindVertexArray(quadVAI.vertexArrayObject); | |
| twgl.setUniforms(displayProgramInfo, settings, { | |
| tex: currentFBI.attachments[0], | |
| ramp: rampTextures[settings.colorScheme], | |
| colorDivisor: buttons['8bit'] ? 1 : settings.trailWeight * deltaTime, | |
| }); | |
| twgl.drawBufferInfo(gl, quadVAI); | |
| currentFBI = destFBI; | |
| requestAnimationFrame(render); | |
| } | |
| requestAnimationFrame(render); | |
| } | |
| main(); |
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
| {"name":"Slime Simulation (mips)","settings":{},"filenames":["index.html","index.css","index.js"]} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment