mirror of
https://github.com/lavafroth/lavafroth.github.io.git
synced 2026-05-30 03:01:16 -03:00
feat: remove float render targets altogether
This commit is contained in:
@@ -99,24 +99,19 @@ void main() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
import * as THREE from 'three';
|
||||
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
|
||||
|
||||
var rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||
var renderer = new THREE.WebGLRenderer({antialias: true});
|
||||
var width = document.querySelector('#stage').offsetWidth;
|
||||
var height = Math.round(9/16 * width);
|
||||
|
||||
var renderer = new THREE.WebGLRenderer({antialias: true});
|
||||
|
||||
renderer.setSize(width, height);
|
||||
document.querySelector('#stage').appendChild(renderer.domElement);
|
||||
const gl = renderer.getContext();
|
||||
gl.getExtension("EXT_color_buffer_float");
|
||||
gl.getExtension("EXT_float_blend");
|
||||
|
||||
function get(path) {
|
||||
return document.querySelector(path).textContent;
|
||||
return document.querySelector('#' + path).textContent;
|
||||
};
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
@@ -132,8 +127,8 @@ void main() {
|
||||
solidScene.add(light);
|
||||
camera.position.set(6,8,14);
|
||||
|
||||
var buffer = new THREE.WebGLRenderTarget(width, height, {format: THREE.RGBAFormat, type: THREE.FloatType})
|
||||
var outlineBuffer = new THREE.WebGLRenderTarget(width, height, {format: THREE.RGBAFormat, type: THREE.FloatType})
|
||||
var buffer = new THREE.WebGLRenderTarget(width, height, {format: THREE.RGBAFormat})
|
||||
var outlineBuffer = new THREE.WebGLRenderTarget(width, height, {format: THREE.RGBAFormat})
|
||||
|
||||
const orbit = new OrbitControls(camera, renderer.domElement);
|
||||
orbit.update();
|
||||
@@ -143,30 +138,30 @@ void main() {
|
||||
|
||||
const shadowMesh = new THREE.Mesh(geometry);
|
||||
const uniforms = {
|
||||
gbufferMask: { value: buffer.texture },
|
||||
viewportSize: { value: new THREE.Vector2(width, height) },
|
||||
gbufferMask: { value: buffer.texture },
|
||||
viewportSize: { value: new THREE.Vector2(width, height) },
|
||||
}
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
vertexShader: get('#outline-vert'),
|
||||
fragmentShader: get('#outline-frag'),
|
||||
uniforms,
|
||||
transparent: true,
|
||||
vertexShader: get('outline-vert'),
|
||||
fragmentShader: get('outline-frag'),
|
||||
uniforms,
|
||||
transparent: true,
|
||||
});
|
||||
|
||||
const solidMesh = new THREE.Mesh(
|
||||
geometry,
|
||||
new THREE.MeshPhysicalMaterial({
|
||||
color: 0xaaeadb,
|
||||
metalness: 0.8,
|
||||
clearcoat: 0.4,
|
||||
clearcoatRoughness: 0.1,
|
||||
}),
|
||||
geometry,
|
||||
new THREE.MeshPhysicalMaterial({
|
||||
color: 0xaaeadb,
|
||||
metalness: 0.8,
|
||||
clearcoat: 0.4,
|
||||
clearcoatRoughness: 0.1,
|
||||
}),
|
||||
);
|
||||
|
||||
const mesh = new THREE.Mesh(
|
||||
geometry,
|
||||
material
|
||||
geometry,
|
||||
material
|
||||
);
|
||||
solidScene.add(solidMesh);
|
||||
scene.add(mesh);
|
||||
@@ -174,95 +169,95 @@ void main() {
|
||||
|
||||
|
||||
const evoUniforms = {
|
||||
initBufferMask: { value: null },
|
||||
initBufferMask: { value: null },
|
||||
}
|
||||
|
||||
const evoMaterial = new THREE.ShaderMaterial({
|
||||
// this is a copy shader
|
||||
vertexShader: get('#evolve-vert'),
|
||||
fragmentShader: get('#evolve-frag'),
|
||||
uniforms: evoUniforms,
|
||||
transparent: true,
|
||||
// this is a copy shader
|
||||
vertexShader: get('evolve-vert'),
|
||||
fragmentShader: get('evolve-frag'),
|
||||
uniforms: evoUniforms,
|
||||
transparent: true,
|
||||
});
|
||||
|
||||
const evolveMesh = new THREE.Mesh(
|
||||
geometry,
|
||||
evoMaterial
|
||||
geometry,
|
||||
evoMaterial
|
||||
);
|
||||
|
||||
evolveScene.add(evolveMesh)
|
||||
|
||||
function continuity(bitmap, width, height) {
|
||||
const visited = Array.from({length: height}, () => Array(width).fill(false));
|
||||
const visited = Array.from({length: height}, () => Array(width).fill(false));
|
||||
|
||||
function valid(row, col) {
|
||||
return row >= 0 && row < height && col >= 0 && col <= width
|
||||
}
|
||||
function valid(row, col) {
|
||||
return row >= 0 && row < height && col >= 0 && col <= width
|
||||
}
|
||||
|
||||
// for a point to be on the screen edge, it must have at least three
|
||||
// of its neighbors invalid
|
||||
function isSentinel(row, col) {
|
||||
return (
|
||||
Number(valid(row - 1, col - 1)) +
|
||||
Number(valid(row - 1, col)) +
|
||||
Number(valid(row - 1, col + 1)) +
|
||||
// for a point to be on the screen edge, it must have at least three
|
||||
// of its neighbors invalid
|
||||
function isSentinel(row, col) {
|
||||
return (
|
||||
Number(valid(row - 1, col - 1)) +
|
||||
Number(valid(row - 1, col)) +
|
||||
Number(valid(row - 1, col + 1)) +
|
||||
|
||||
Number(valid(row, col - 1)) +
|
||||
Number(valid(row, col + 1)) +
|
||||
Number(valid(row, col - 1)) +
|
||||
Number(valid(row, col + 1)) +
|
||||
|
||||
Number(valid(row + 1, col - 1)) +
|
||||
Number(valid(row + 1, col)) +
|
||||
Number(valid(row + 1, col + 1))
|
||||
) < 6
|
||||
}
|
||||
Number(valid(row + 1, col - 1)) +
|
||||
Number(valid(row + 1, col)) +
|
||||
Number(valid(row + 1, col + 1))
|
||||
) < 6
|
||||
}
|
||||
|
||||
var sentinels = [];
|
||||
var cyclic = [];
|
||||
var sentinels = [];
|
||||
var cyclic = [];
|
||||
|
||||
function dfs(row, col, rootRow, rootCol, steps) {
|
||||
function dfs(row, col, rootRow, rootCol, steps) {
|
||||
|
||||
const pointIsRoot = row == rootRow && col == rootCol;
|
||||
const pointIsRoot = row == rootRow && col == rootCol;
|
||||
|
||||
if (steps != 0 && pointIsRoot) {
|
||||
cyclic.push([row, col]);
|
||||
return;
|
||||
}
|
||||
if (steps != 0 && pointIsRoot) {
|
||||
cyclic.push([row, col]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!valid(row, col) || visited[row][col]) {
|
||||
return;
|
||||
}
|
||||
if (!valid(row, col) || visited[row][col]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// is this point switched off?
|
||||
var point = 4 * (row * width + col);
|
||||
if (bitmap[point+0] == 0 && bitmap[point+1] == 0 && bitmap[point+2] == 0) {
|
||||
return;
|
||||
}
|
||||
// is this point switched off?
|
||||
var point = 4 * (row * width + col);
|
||||
if (bitmap[point+0] == 0 && bitmap[point+1] == 0 && bitmap[point+2] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
visited[row][col] = true;
|
||||
dfs(row - 1, col, rootRow, rootCol, steps + 1)
|
||||
dfs(row + 1, col, rootRow, rootCol, steps + 1)
|
||||
dfs(row, col - 1, rootRow, rootCol, steps + 1)
|
||||
dfs(row, col + 1, rootRow, rootCol, steps + 1)
|
||||
visited[row][col] = true;
|
||||
dfs(row - 1, col, rootRow, rootCol, steps + 1)
|
||||
dfs(row + 1, col, rootRow, rootCol, steps + 1)
|
||||
dfs(row, col - 1, rootRow, rootCol, steps + 1)
|
||||
dfs(row, col + 1, rootRow, rootCol, steps + 1)
|
||||
|
||||
if (isSentinel(row, col) && steps != 0) {
|
||||
sentinels.push([row, col]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isSentinel(row, col) && steps != 0) {
|
||||
sentinels.push([row, col]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (let row = 0; row < height; row++) {
|
||||
for(let col=0; col < width; col++) {
|
||||
if (visited[row][col]) {
|
||||
continue;
|
||||
}
|
||||
var point = 4 * (row * width + col);
|
||||
if (bitmap[point] == 1 && bitmap[point+1] == 1 && bitmap[point+2] == 1) {
|
||||
dfs(row, col, row, col, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let row = 0; row < height; row++) {
|
||||
for(let col=0; col < width; col++) {
|
||||
if (visited[row][col]) {
|
||||
continue;
|
||||
}
|
||||
var point = 4 * (row * width + col);
|
||||
if (bitmap[point] == 1 && bitmap[point+1] == 1 && bitmap[point+2] == 1) {
|
||||
dfs(row, col, row, col, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cyclic.concat(sentinels);
|
||||
return cyclic.concat(sentinels);
|
||||
}
|
||||
|
||||
var durationInSeconds = 5;
|
||||
@@ -275,103 +270,116 @@ void main() {
|
||||
//
|
||||
// This way we can quickly zero out all the pixels below a
|
||||
// threshold when timing the animation.
|
||||
function dijkstraNumber(points, allThePixels) {
|
||||
points.forEach((point) => {
|
||||
dijkstraPropagate(point, allThePixels, 2)
|
||||
})
|
||||
function dijkstraNumber(points, buf) {
|
||||
points.forEach((point) => {
|
||||
dijkstraPropagate(point, buf, 2)
|
||||
})
|
||||
}
|
||||
|
||||
function dijkstraPropagate(point, allThePixels, value) {
|
||||
let row = point[0]
|
||||
let col = point[1]
|
||||
let pos = 4 * (row * buffer.width + col);
|
||||
if (allThePixels[pos] != 1) {
|
||||
return
|
||||
}
|
||||
// propagate
|
||||
allThePixels[pos] = value;
|
||||
allThePixels[pos+1] = value;
|
||||
allThePixels[pos+2] = value;
|
||||
allThePixels[pos+3] = value;
|
||||
function dijkstraPropagate(point, buf, value) {
|
||||
let row = point[0]
|
||||
let col = point[1]
|
||||
let pos = 4 * (row * buffer.width + col);
|
||||
if (buf[pos] != 1) {
|
||||
return
|
||||
}
|
||||
// propagate
|
||||
buf[pos] = value;
|
||||
buf[pos+1] = value;
|
||||
buf[pos+2] = value;
|
||||
buf[pos+3] = value;
|
||||
|
||||
dijkstraPropagate([row - 1, col], allThePixels, value+1);
|
||||
dijkstraPropagate([row + 1, col], allThePixels, value+1);
|
||||
dijkstraPropagate([row, col - 1], allThePixels, value+1);
|
||||
dijkstraPropagate([row, col + 1], allThePixels, value+1);
|
||||
dijkstraPropagate([row - 1, col - 1], allThePixels, value+1);
|
||||
dijkstraPropagate([row + 1, col + 1], allThePixels, value+1);
|
||||
dijkstraPropagate([row + 1, col - 1], allThePixels, value+1);
|
||||
dijkstraPropagate([row - 1, col + 1], allThePixels, value+1);
|
||||
dijkstraPropagate([row - 1, col], buf, value+1);
|
||||
dijkstraPropagate([row + 1, col], buf, value+1);
|
||||
dijkstraPropagate([row, col - 1], buf, value+1);
|
||||
dijkstraPropagate([row, col + 1], buf, value+1);
|
||||
dijkstraPropagate([row - 1, col - 1], buf, value+1);
|
||||
dijkstraPropagate([row + 1, col + 1], buf, value+1);
|
||||
dijkstraPropagate([row + 1, col - 1], buf, value+1);
|
||||
dijkstraPropagate([row - 1, col + 1], buf, value+1);
|
||||
}
|
||||
|
||||
const clock = new THREE.Clock();
|
||||
var longestPixelStrand = 0;
|
||||
var init = true;
|
||||
const allThePixels = new Float32Array( buffer.width * buffer.height * 4);
|
||||
const allThePixels = new Uint8Array(buffer.width * buffer.height * 4);
|
||||
const dijkstraBuffer = new Float32Array(buffer.width * buffer.height * 4);
|
||||
function animate() {
|
||||
|
||||
if (init) {
|
||||
renderer.setRenderTarget(buffer);
|
||||
renderer.render(maskScene, camera);
|
||||
if (init) {
|
||||
renderer.setRenderTarget(buffer);
|
||||
renderer.render(maskScene, camera);
|
||||
|
||||
renderer.setRenderTarget(outlineBuffer);
|
||||
renderer.render(scene, camera);
|
||||
renderer.setRenderTarget(outlineBuffer);
|
||||
renderer.render(scene, camera);
|
||||
|
||||
renderer.readRenderTargetPixels(outlineBuffer, 0, 0, buffer.width, buffer.height, allThePixels);
|
||||
renderer.readRenderTargetPixels(outlineBuffer, 0, 0, buffer.width, buffer.height, allThePixels);
|
||||
for (let i = 0; i < allThePixels.length; i++) {
|
||||
dijkstraBuffer[i] = allThePixels[i] == 255 ? 1 : 0;
|
||||
}
|
||||
|
||||
let points = continuity(allThePixels, width, height)
|
||||
let dijkstraBuffer = dijkstraNumber(points, allThePixels)
|
||||
let points = continuity(dijkstraBuffer, width, height)
|
||||
dijkstraNumber(points, dijkstraBuffer)
|
||||
|
||||
for (let row = 0; row < height; row++) {
|
||||
for(let col = 0; col<width; col++) {
|
||||
var point = 4 * (row * width + col);
|
||||
longestPixelStrand = Math.max(longestPixelStrand, dijkstraBuffer[point])
|
||||
}
|
||||
}
|
||||
|
||||
for (let row = 0; row < height; row++) {
|
||||
for(let col = 0; col<width; col++) {
|
||||
var point = 4 * (row * width + col);
|
||||
longestPixelStrand = Math.max(longestPixelStrand, allThePixels[point])
|
||||
}
|
||||
}
|
||||
let initBuffer = new Uint8Array(buffer.width * buffer.height * 4);
|
||||
points.forEach((point) => {
|
||||
let pos = 4 * (point[0] * buffer.width + point[1]);
|
||||
initBuffer[pos] = 255;
|
||||
initBuffer[pos+1] = 255;
|
||||
initBuffer[pos+2] = 255;
|
||||
initBuffer[pos+3] = 255;
|
||||
})
|
||||
|
||||
let initBuffer = new Uint8Array(buffer.width * buffer.height * 4);
|
||||
points.forEach((point) => {
|
||||
let pos = 4 * (point[0] * buffer.width + point[1]);
|
||||
initBuffer[pos] = 255;
|
||||
initBuffer[pos+1] = 255;
|
||||
initBuffer[pos+2] = 255;
|
||||
initBuffer[pos+3] = 255;
|
||||
})
|
||||
let ephemeralTex = new THREE.DataTexture(initBuffer, width, height);
|
||||
ephemeralTex.needsUpdate = true;
|
||||
evoUniforms.initBufferMask.value = ephemeralTex;
|
||||
init = false;
|
||||
}
|
||||
let fractionAnimated = (clock.getElapsedTime() % durationInSeconds) / durationInSeconds;
|
||||
let pixelsAnimated = Math.round(longestPixelStrand * fractionAnimated);
|
||||
let initBuffer = new Uint8Array(buffer.width * buffer.height * 4);
|
||||
for (let row = 0; row < height; row++) {
|
||||
for(let col=0; col<width; col++) {
|
||||
var point = 4 * (row * width + col);
|
||||
if (dijkstraBuffer[point] > 1 && dijkstraBuffer[point] < pixelsAnimated) {
|
||||
initBuffer[point] = 255;
|
||||
initBuffer[point+1] = 255;
|
||||
initBuffer[point+2] = 255;
|
||||
initBuffer[point+3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
let ephemeralTex = new THREE.DataTexture(initBuffer, width, height);
|
||||
ephemeralTex.needsUpdate = true;
|
||||
evoUniforms.initBufferMask.value = ephemeralTex;
|
||||
|
||||
let ephemeralTex = new THREE.DataTexture(initBuffer, width, height);
|
||||
ephemeralTex.needsUpdate = true;
|
||||
evoUniforms.initBufferMask.value = ephemeralTex;
|
||||
init = false;
|
||||
}
|
||||
let fractionAnimated = (clock.getElapsedTime() % durationInSeconds) / durationInSeconds;
|
||||
let pixelsAnimated = Math.round(longestPixelStrand * fractionAnimated);
|
||||
let initBuffer = new Uint8Array(buffer.width * buffer.height * 4);
|
||||
for (let row = 0; row < height; row++) {
|
||||
for(let col=0; col<width; col++) {
|
||||
var point = 4 * (row * width + col);
|
||||
if (allThePixels[point] > 1 && allThePixels[point] < pixelsAnimated) {
|
||||
initBuffer[point] = 255;
|
||||
initBuffer[point+1] = 255;
|
||||
initBuffer[point+2] = 255;
|
||||
initBuffer[point+3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
let ephemeralTex = new THREE.DataTexture(initBuffer, width, height);
|
||||
ephemeralTex.needsUpdate = true;
|
||||
evoUniforms.initBufferMask.value = ephemeralTex;
|
||||
|
||||
renderer.setRenderTarget(null);
|
||||
renderer.render(solidScene, camera);
|
||||
renderer.autoClear = false;
|
||||
renderer.clearDepth();
|
||||
renderer.render(evolveScene, camera);
|
||||
renderer.autoClear = true;
|
||||
renderer.setRenderTarget(null);
|
||||
renderer.render(solidScene, camera);
|
||||
renderer.autoClear = false;
|
||||
renderer.clearDepth();
|
||||
renderer.render(evolveScene, camera);
|
||||
renderer.autoClear = true;
|
||||
}
|
||||
|
||||
renderer.setAnimationLoop(animate);
|
||||
|
||||
window.addEventListener('resize', function() {
|
||||
width = window.innerWidth
|
||||
height = window.innerHeight
|
||||
camera.aspect = width / height;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(width, height);
|
||||
init = true;
|
||||
});
|
||||
|
||||
orbit.addEventListener('change', function() {
|
||||
init = true;
|
||||
init = true;
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user