From 9e80ac2c96f5bb2541eeeb31dc3705068d19e565 Mon Sep 17 00:00:00 2001 From: vHawk <48140945+vHawk@users.noreply.github.com> Date: Thu, 30 Jun 2022 00:35:59 +0300 Subject: [PATCH] Resize FBs and recompile materials after config change --- src/App.ts | 2 +- src/Config.ts | 4 +-- src/GUI.ts | 4 +-- src/Renderer.ts | 49 ++++++++++++++++++++++++------- src/scenes/AbstractScene.ts | 4 +-- src/scenes/AntsComputeScene.ts | 12 ++++---- src/scenes/AntsDiscretizeScene.ts | 36 +++++++++++++---------- src/scenes/DrawScene.ts | 9 ++---- src/scenes/ScreenScene.ts | 44 +++++++++++++++++++-------- src/scenes/WorldBlurScene.ts | 11 +++---- src/scenes/WorldComputeScene.ts | 9 ++---- src/shaders/antsDiscretize.vert | 4 +-- src/shaders/world.frag | 4 +-- 13 files changed, 120 insertions(+), 72 deletions(-) diff --git a/src/App.ts b/src/App.ts index 0b31c45..18d9ddd 100644 --- a/src/App.ts +++ b/src/App.ts @@ -53,7 +53,7 @@ export default new class App { } private resetRenderer() { - this.renderer.reset(); + this.renderer.reset(this.scenes); } private updateSimulationInterval() { diff --git a/src/Config.ts b/src/Config.ts index 51562f9..cdd9b01 100644 --- a/src/Config.ts +++ b/src/Config.ts @@ -3,10 +3,10 @@ export default { antsCount: 64 ** 2, simulationStepsPerSecond: 60, scentThreshold: 0.01, - scentFadeOutFactor: 0.999, + scentFadeOutFactor: 0.998, scentBlurRadius: 0.1, scentMaxStorage: 1e6, - scentPerMarker: 1000, + scentPerMarker: 300, antSpeed: 1, antRotationAngle: Math.PI / 50, brushRadius: 20, diff --git a/src/GUI.ts b/src/GUI.ts index 91c7268..e657a7c 100644 --- a/src/GUI.ts +++ b/src/GUI.ts @@ -12,13 +12,13 @@ export default class GUI extends EventEmitter { const simFolder = this.gui.addFolder('Simulation'); - simFolder.add(Config, 'worldSize', 256, 8096).onChange(() => { + simFolder.add(Config, 'worldSize', 256, 4096).onChange(() => { this.emit('worldSize'); }); simFolder.add(Config, 'antsCount', 1, 1e6).onChange(() => { this.emit('antsCount'); }); - simFolder.add(Config, 'simulationStepsPerSecond', 1, 300).onChange(() => { + simFolder.add(Config, 'simulationStepsPerSecond', 1, 500).onChange(() => { this.emit('simulationStepsPerSecond'); }); diff --git a/src/Renderer.ts b/src/Renderer.ts index 8289986..2393408 100644 --- a/src/Renderer.ts +++ b/src/Renderer.ts @@ -1,6 +1,7 @@ import * as THREE from 'three'; import {SceneCollection} from "./App"; import Config from "./Config"; +import {WebGLRenderTarget} from "three"; interface Resources { worldRenderTarget: THREE.WebGLRenderTarget; @@ -73,28 +74,24 @@ export default class Renderer { public renderSimulation(scenes: SceneCollection) { const [antsComputeSource, antsComputeTarget] = scenes.ants.getRenderTargets(); - this.renderer.setViewport(0, 0, scenes.worldBlur.renderWidth, scenes.worldBlur.renderHeight); - + this.setViewportFromRT(this.resources.worldBlurredRenderTarget); this.renderer.setRenderTarget(this.resources.worldBlurredRenderTarget); scenes.worldBlur.material.uniforms.tWorld.value = this.resources.worldRenderTarget.texture; this.renderer.render(scenes.worldBlur, scenes.worldBlur.camera); - this.renderer.setViewport(0, 0, scenes.ants.renderWidth, scenes.ants.renderHeight); - + this.setViewportFromRT(antsComputeTarget); this.renderer.setRenderTarget(antsComputeTarget); scenes.ants.material.uniforms.tLastState.value = antsComputeSource.texture; scenes.ants.material.uniforms.tWorld.value = this.resources.worldBlurredRenderTarget.texture; this.renderer.render(scenes.ants, scenes.ants.camera); - this.renderer.setViewport(0, 0, scenes.discretize.renderWidth, scenes.discretize.renderHeight); - + this.setViewportFromRT(this.resources.antsDiscreteRenderTarget); this.renderer.setRenderTarget(this.resources.antsDiscreteRenderTarget); scenes.discretize.material.uniforms.tDataCurrent.value = antsComputeTarget.texture; scenes.discretize.material.uniforms.tDataLast.value = antsComputeSource.texture; this.renderer.render(scenes.discretize, scenes.discretize.camera); - this.renderer.setViewport(0, 0, scenes.world.renderWidth, scenes.world.renderHeight); - + this.setViewportFromRT(this.resources.worldRenderTarget); this.renderer.setRenderTarget(this.resources.worldRenderTarget); scenes.world.material.uniforms.tLastState.value = this.resources.worldBlurredRenderTarget.texture; scenes.world.material.uniforms.tDiscreteAnts.value = this.resources.antsDiscreteRenderTarget.texture; @@ -104,8 +101,12 @@ export default class Renderer { scenes.screen.groundMaterial.uniforms.map.value = this.resources.worldRenderTargetCopy.texture; } + private setViewportFromRT(rt: WebGLRenderTarget) { + this.renderer.setViewport(0, 0, rt.width, rt.height); + } + public renderToScreen(scenes: SceneCollection) { - this.renderer.setViewport(0, 0, scenes.draw.renderWidth, scenes.draw.renderHeight); + this.setViewportFromRT(this.resources.worldRenderTargetCopy); this.renderer.setRenderTarget(this.resources.worldRenderTargetCopy); scenes.draw.material.uniforms.tWorld.value = this.resources.worldRenderTarget.texture; scenes.draw.material.uniforms.pointerPosition.value = scenes.screen.pointerPosition; @@ -137,8 +138,36 @@ export default class Renderer { }; } - public reset() { + public reset(scenes: SceneCollection) { + const antTextureSize = Math.ceil(Math.sqrt(Config.antsCount)); + this.resources.worldRenderTarget.setSize(Config.worldSize, Config.worldSize) + this.renderer.setRenderTarget(this.resources.worldRenderTarget); + this.renderer.clear(); + + this.resources.worldRenderTargetCopy.setSize(Config.worldSize, Config.worldSize) + this.renderer.setRenderTarget(this.resources.worldRenderTargetCopy); + this.renderer.clear(); + + this.resources.worldBlurredRenderTarget.setSize(Config.worldSize, Config.worldSize) + this.renderer.setRenderTarget(this.resources.worldBlurredRenderTarget); + this.renderer.clear(); + + this.resources.antsDataRenderTarget0.setSize(antTextureSize, antTextureSize) + this.renderer.setRenderTarget(this.resources.antsDataRenderTarget0); + this.renderer.clear(); + + this.resources.antsDataRenderTarget1.setSize(antTextureSize, antTextureSize) + this.renderer.setRenderTarget(this.resources.antsDataRenderTarget1); + this.renderer.clear(); + + this.resources.antsDiscreteRenderTarget.setSize(Config.worldSize, Config.worldSize) + this.renderer.setRenderTarget(this.resources.antsDiscreteRenderTarget); + this.renderer.clear(); + + for (const scene of Object.values(scenes)) { + scene.recompileMaterials(); + } } static convertNumberToFloatString(n: number): string { diff --git a/src/scenes/AbstractScene.ts b/src/scenes/AbstractScene.ts index ffddcfb..01eae6c 100644 --- a/src/scenes/AbstractScene.ts +++ b/src/scenes/AbstractScene.ts @@ -4,8 +4,6 @@ import Renderer from "../Renderer"; export default abstract class AbstractScene extends THREE.Scene { protected readonly renderer: Renderer; public readonly camera: THREE.Camera; - public renderWidth: number = 1; - public renderHeight: number = 1; protected constructor(renderer: Renderer) { super(); @@ -13,6 +11,8 @@ export default abstract class AbstractScene extends THREE.Scene { this.renderer = renderer; } + public abstract recompileMaterials(): void; + public abstract resize(width: number, height: number): void; public abstract update(deltaTime: number): void; diff --git a/src/scenes/AntsComputeScene.ts b/src/scenes/AntsComputeScene.ts index 7d34a69..f658224 100644 --- a/src/scenes/AntsComputeScene.ts +++ b/src/scenes/AntsComputeScene.ts @@ -18,8 +18,8 @@ export default class AntsComputeScene extends AbstractScene { const material = new THREE.RawShaderMaterial({ uniforms: { uTime: {value: 0}, - tLastState: {value: this.renderer.resources.antsDataRenderTarget0.texture}, - tWorld: {value: this.renderer.resources.worldRenderTarget.texture}, + tLastState: {value: null}, + tWorld: {value: null}, }, vertexShader, fragmentShader, @@ -35,9 +35,6 @@ export default class AntsComputeScene extends AbstractScene { this.renderer.resources.antsDataRenderTarget0, this.renderer.resources.antsDataRenderTarget1 ]; - - this.renderWidth = this.renderer.resources.antsDataRenderTarget0.width; - this.renderHeight = this.renderer.resources.antsDataRenderTarget0.height; } public getRenderTargets(): [WebGLRenderTarget, WebGLRenderTarget] { @@ -46,6 +43,11 @@ export default class AntsComputeScene extends AbstractScene { return this.renderTargets; } + public recompileMaterials() { + this.material.defines = this.renderer.getCommonMaterialDefines(); + this.material.needsUpdate = true; + } + public resize(width: number, height: number) { } diff --git a/src/scenes/AntsDiscretizeScene.ts b/src/scenes/AntsDiscretizeScene.ts index ed9793e..3e95bce 100644 --- a/src/scenes/AntsDiscretizeScene.ts +++ b/src/scenes/AntsDiscretizeScene.ts @@ -1,5 +1,4 @@ import * as THREE from 'three'; -import {WebGLRenderTarget} from 'three'; import Renderer from "../Renderer"; import AbstractScene from "./AbstractScene"; import fragmentShader from '../shaders/antsDiscretize.frag'; @@ -8,12 +7,12 @@ import vertexShader from '../shaders/antsDiscretize.vert'; export default class AntsDiscretizeScene extends AbstractScene { public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); public readonly material: THREE.RawShaderMaterial; + public mesh: THREE.InstancedMesh; constructor(renderer: Renderer) { super(renderer); - const geometry = new THREE.BoxBufferGeometry(1, 1, 1); - const material = new THREE.RawShaderMaterial({ + this.material = new THREE.RawShaderMaterial({ uniforms: { tDataCurrent: {value: null}, tDataLast: {value: null}, @@ -23,21 +22,28 @@ export default class AntsDiscretizeScene extends AbstractScene { defines: this.renderer.getCommonMaterialDefines(), glslVersion: THREE.GLSL3 }); - const mesh = new THREE.InstancedMesh( - geometry, - material, - this.renderer.resources.antsDataRenderTarget0.width * this.renderer.resources.antsDataRenderTarget0.height - ); - this.add(mesh); - this.material = material; - - this.renderWidth = this.renderer.resources.worldRenderTarget.width; - this.renderHeight = this.renderer.resources.worldRenderTarget.height; + this.createMesh(); } - public getRenderTarget(): WebGLRenderTarget { - return this.renderer.resources.antsDiscreteRenderTarget; + private createMesh() { + if (this.mesh) { + this.remove(this.mesh); + this.mesh.dispose(); + } + + this.mesh = new THREE.InstancedMesh( + new THREE.BoxBufferGeometry(1, 1, 1), + this.material, + this.renderer.resources.antsDataRenderTarget0.width * this.renderer.resources.antsDataRenderTarget0.height + ); + this.add(this.mesh); + } + + public recompileMaterials() { + this.material.defines = this.renderer.getCommonMaterialDefines(); + this.material.needsUpdate = true; + this.createMesh(); } public resize(width: number, height: number) { diff --git a/src/scenes/DrawScene.ts b/src/scenes/DrawScene.ts index b0a9ad2..d0d9f9e 100644 --- a/src/scenes/DrawScene.ts +++ b/src/scenes/DrawScene.ts @@ -4,7 +4,6 @@ import AbstractScene from "./AbstractScene"; import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry"; import fragmentShader from '../shaders/draw.frag'; import vertexShader from '../shaders/draw.vert'; -import {WebGLRenderTarget} from "three"; export default class DrawScene extends AbstractScene { public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); @@ -30,13 +29,11 @@ export default class DrawScene extends AbstractScene { this.add(mesh); this.material = material; - - this.renderWidth = this.renderer.resources.worldRenderTarget.width; - this.renderHeight = this.renderer.resources.worldRenderTarget.height; } - public getRenderTarget(): WebGLRenderTarget { - return this.renderer.resources.worldRenderTarget; + public recompileMaterials() { + this.material.defines = this.renderer.getCommonMaterialDefines(); + this.material.needsUpdate = true; } public resize(width: number, height: number) { diff --git a/src/scenes/ScreenScene.ts b/src/scenes/ScreenScene.ts index 969bebe..f376fbd 100644 --- a/src/scenes/ScreenScene.ts +++ b/src/scenes/ScreenScene.ts @@ -5,6 +5,7 @@ import vertexShaderAnts from "../shaders/ants.vert"; import fragmentShaderAnts from "../shaders/ants.frag"; import vertexShaderGround from "../shaders/screenWorld.vert"; import fragmentShaderGround from "../shaders/screenWorld.frag"; +import Config from "../Config"; enum PointerState { None, @@ -16,11 +17,14 @@ enum PointerState { export default class ScreenScene extends AbstractScene { public readonly camera: THREE.OrthographicCamera; public readonly material: THREE.ShaderMaterial; + public ants: THREE.InstancedMesh; public readonly groundMaterial: THREE.ShaderMaterial; public readonly pointerPosition: THREE.Vector2 = new THREE.Vector2(); public drawMode: PointerState = PointerState.None; private cameraZoomLinear: number = 0; private isPointerDown: boolean = false; + public renderWidth: number = 1; + public renderHeight: number = 1; constructor(renderer: Renderer) { super(renderer); @@ -40,9 +44,6 @@ export default class ScreenScene extends AbstractScene { this.groundMaterial = ground.material; - //ground.position.x = 0.5; - //ground.position.y = 0.5; - this.add(ground); const antTexture = new THREE.TextureLoader().load('textures/ant.png'); @@ -62,15 +63,7 @@ export default class ScreenScene extends AbstractScene { transparent: true }); - const ants = new THREE.InstancedMesh( - new THREE.PlaneBufferGeometry(0.015, 0.015), - this.material, - this.renderer.resources.antsDataRenderTarget0.width * this.renderer.resources.antsDataRenderTarget0.height - ) - - ants.position.x = ants.position.y = -0.5; - - this.add(ants); + this.createInstancedAntsMesh(); this.camera = new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5); @@ -164,6 +157,33 @@ export default class ScreenScene extends AbstractScene { this.camera.updateProjectionMatrix(); } + private createInstancedAntsMesh() { + if (this.ants) { + this.remove(this.ants); + this.ants.dispose(); + } + + const scale = 8 / Config.worldSize; + + const ants = new THREE.InstancedMesh( + new THREE.PlaneBufferGeometry(scale, scale), + this.material, + this.renderer.resources.antsDataRenderTarget0.width * this.renderer.resources.antsDataRenderTarget0.height + ) + + ants.position.x = ants.position.y = -0.5; + + this.add(ants); + + this.ants = ants; + } + + public recompileMaterials() { + this.groundMaterial.defines = this.renderer.getCommonMaterialDefines(); + this.groundMaterial.needsUpdate = true; + this.createInstancedAntsMesh(); + } + public resize(width: number, height: number) { const aspect = width / height; diff --git a/src/scenes/WorldBlurScene.ts b/src/scenes/WorldBlurScene.ts index ff20fed..71136ac 100644 --- a/src/scenes/WorldBlurScene.ts +++ b/src/scenes/WorldBlurScene.ts @@ -4,7 +4,6 @@ import AbstractScene from "./AbstractScene"; import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry"; import fragmentShader from '../shaders/worldBlur.frag'; import vertexShader from '../shaders/worldBlur.vert'; -import {WebGLRenderTarget} from "three"; export default class WorldBlurScene extends AbstractScene { public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); @@ -16,7 +15,7 @@ export default class WorldBlurScene extends AbstractScene { const geometry = new FullScreenTriangleGeometry(); const material = new THREE.RawShaderMaterial({ uniforms: { - tWorld: {value: this.renderer.resources.worldRenderTarget.texture}, + tWorld: {value: null}, }, vertexShader, fragmentShader, @@ -27,13 +26,11 @@ export default class WorldBlurScene extends AbstractScene { this.add(mesh); this.material = material; - - this.renderWidth = this.renderer.resources.worldRenderTarget.width; - this.renderHeight = this.renderer.resources.worldRenderTarget.height; } - public getRenderTarget(): WebGLRenderTarget { - return this.renderer.resources.worldBlurredRenderTarget; + public recompileMaterials() { + this.material.defines = this.renderer.getCommonMaterialDefines(); + this.material.needsUpdate = true; } public resize(width: number, height: number) { diff --git a/src/scenes/WorldComputeScene.ts b/src/scenes/WorldComputeScene.ts index 4ff9194..a463d62 100644 --- a/src/scenes/WorldComputeScene.ts +++ b/src/scenes/WorldComputeScene.ts @@ -4,7 +4,6 @@ import AbstractScene from "./AbstractScene"; import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry"; import fragmentShader from '../shaders/world.frag'; import vertexShader from '../shaders/world.vert'; -import {WebGLRenderTarget} from "three"; export default class WorldComputeScene extends AbstractScene { public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); @@ -28,13 +27,11 @@ export default class WorldComputeScene extends AbstractScene { this.add(mesh); this.material = material; - - this.renderWidth = this.renderer.resources.worldRenderTarget.width; - this.renderHeight = this.renderer.resources.worldRenderTarget.height; } - public getRenderTarget(): WebGLRenderTarget { - return this.renderer.resources.worldRenderTarget; + public recompileMaterials() { + this.material.defines = this.renderer.getCommonMaterialDefines(); + this.material.needsUpdate = true; } public resize(width: number, height: number) { diff --git a/src/shaders/antsDiscretize.vert b/src/shaders/antsDiscretize.vert index 6df4eca..90747a9 100644 --- a/src/shaders/antsDiscretize.vert +++ b/src/shaders/antsDiscretize.vert @@ -34,11 +34,11 @@ void main() { float storage = float(int(dataSampleCurrent.w) >> 1); vIsCarryingFood = isCarrying; - vScentFactor = storage / 1000000.; + vScentFactor = storage / SCENT_MAX_STORAGE; vIsCellCleared = isCellCleared; gl_Position = vec4( - (position.xy * cellSize * 0.01 + floor(offset * WORLD_SIZE) / WORLD_SIZE + cellSize * 0.5) * 2. - 1., + (position.xy * cellSize + floor(offset * WORLD_SIZE) / WORLD_SIZE + cellSize * 0.5) * 2. - 1., 0, 1 ); diff --git a/src/shaders/world.frag b/src/shaders/world.frag index 8de54f6..2bf799f 100644 --- a/src/shaders/world.frag +++ b/src/shaders/world.frag @@ -16,8 +16,8 @@ void main() { int isFood = cellData & 1; int isHome = (cellData & 2) >> 1; int isObstacle = (cellData & 4) >> 2; - float scentToHome = lastState.y + discreteAnts.x * 10.; - float scentToFood = lastState.z + discreteAnts.y * 10.; + float scentToHome = min(10., lastState.y + discreteAnts.x); + float scentToFood = min(10., lastState.z + discreteAnts.y); if (discreteAnts.z == 1.) { isFood = 0;