Cleanup; make ants bounce off the world bounds
This commit is contained in:
parent
fb248c33ee
commit
fb772db717
21 changed files with 265 additions and 197 deletions
26
README.md
26
README.md
|
|
@ -1,2 +1,26 @@
|
||||||
# ants-simulation
|
# Ants simulation
|
||||||
|
|
||||||
|
A simple ant colony GPU-accelerated simulation made with Three.js.
|
||||||
|
|
||||||
|
**[Live demo](https://vhawk.github.io/ants-simulation/)**
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
Ants can emit two types of pheromones: to-home pheromone and to-food pheromone. To-home pheromones are emitted by those ants searching for food and to-food pheromones are emitted by those carrying food. Ants searching for food are attracted to to-food pheromones, while ants searching for home are attracted to to-home pheromones.
|
||||||
|
|
||||||
|
If an ant searching for food detects a food cell nearby, then it picks it up, and drops it after reaching home.
|
||||||
|
|
||||||
|
If an ant senses the desirable pheromones nearby, then it turns to the direction of these pheromones, and if no pheromones are detected nearby or the ant can't decide at which direction pheromones are stronger, then is moves randomly.
|
||||||
|
|
||||||
|
It is important to prevent ants from following pheromone trails left by those ants who wandered too far from home or a food source. Each individual ant has an inventory for storing pheromones. Each time an ant leaves a pheromone marker anywhere on the map a small portion of the stored pheromones is used. And each time it picks up food or reaches home its inventory gets fully refilled.
|
||||||
|
|
||||||
|
Pheromone trails left by ants evaporate and diffuse over time.
|
||||||
|
|
||||||
|
## Compute pipeline overview
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- https://softologyblog.wordpress.com/2020/03/21/ant-colony-simulations/
|
||||||
|
- https://github.com/johnBuffer/AntSimulator
|
||||||
43
src/App.ts
43
src/App.ts
|
|
@ -4,6 +4,8 @@ import Renderer from "./Renderer";
|
||||||
import WorldComputeScene from "./scenes/WorldComputeScene";
|
import WorldComputeScene from "./scenes/WorldComputeScene";
|
||||||
import AntsDiscretizeScene from "./scenes/AntsDiscretizeScene";
|
import AntsDiscretizeScene from "./scenes/AntsDiscretizeScene";
|
||||||
import WorldBlurScene from "./scenes/WorldBlurScene";
|
import WorldBlurScene from "./scenes/WorldBlurScene";
|
||||||
|
import Config from "./Config";
|
||||||
|
import GUI from "./GUI";
|
||||||
|
|
||||||
export interface SceneCollection {
|
export interface SceneCollection {
|
||||||
ants: AntsComputeScene;
|
ants: AntsComputeScene;
|
||||||
|
|
@ -16,7 +18,10 @@ export interface SceneCollection {
|
||||||
export default new class App {
|
export default new class App {
|
||||||
private renderer: Renderer = new Renderer(<HTMLCanvasElement>document.getElementById('canvas'));
|
private renderer: Renderer = new Renderer(<HTMLCanvasElement>document.getElementById('canvas'));
|
||||||
private scenes: SceneCollection;
|
private scenes: SceneCollection;
|
||||||
private loop = (deltaTime: number): void => this.update(deltaTime);
|
private gui: GUI = new GUI();
|
||||||
|
private renderLoop = (deltaTime: number): void => this.render(deltaTime);
|
||||||
|
private simInterval: NodeJS.Timer;
|
||||||
|
private simulationStepsPerSecond: number = 0;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.initScenes();
|
this.initScenes();
|
||||||
|
|
@ -25,7 +30,24 @@ export default new class App {
|
||||||
|
|
||||||
this.resize();
|
this.resize();
|
||||||
|
|
||||||
this.update(0);
|
this.renderLoop(0);
|
||||||
|
|
||||||
|
this.simulationStepsPerSecond = Config.simulationStepsPerSecond;
|
||||||
|
this.updateSimulationInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateSimulationInterval() {
|
||||||
|
clearInterval(this.simInterval);
|
||||||
|
|
||||||
|
this.simInterval = setInterval(() => {
|
||||||
|
if (Config.simulationStepsPerSecond !== this.simulationStepsPerSecond) {
|
||||||
|
this.simulationStepsPerSecond = Config.simulationStepsPerSecond;
|
||||||
|
this.updateSimulationInterval();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.simulationStep();
|
||||||
|
}, 1000 / this.simulationStepsPerSecond);
|
||||||
}
|
}
|
||||||
|
|
||||||
private initScenes() {
|
private initScenes() {
|
||||||
|
|
@ -49,14 +71,17 @@ export default new class App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private update(deltaTime: number) {
|
private simulationStep() {
|
||||||
requestAnimationFrame(this.loop);
|
|
||||||
|
|
||||||
for (let i = 0; i < 3; i++) {
|
|
||||||
for (const scene of Object.values(this.scenes)) {
|
for (const scene of Object.values(this.scenes)) {
|
||||||
scene.update(deltaTime);
|
scene.update(0);
|
||||||
}
|
|
||||||
this.renderer.renderScenes(this.scenes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.renderer.renderSimulation(this.scenes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private render(deltaTime: number) {
|
||||||
|
requestAnimationFrame(this.renderLoop);
|
||||||
|
|
||||||
|
this.renderer.renderToScreen(this.scenes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
12
src/Config.ts
Normal file
12
src/Config.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
export default {
|
||||||
|
worldSize: 1024,
|
||||||
|
antsCount: 64 ** 2,
|
||||||
|
simulationStepsPerSecond: 60,
|
||||||
|
scentThreshold: 0.01,
|
||||||
|
scentFadeOutFactor: 0.998,
|
||||||
|
scentBlurRadius: 0.2,
|
||||||
|
scentMaxStorage: 1e6,
|
||||||
|
scentPerMarker: 1000,
|
||||||
|
antSpeed: 1,
|
||||||
|
antRotationAngle: Math.PI / 50
|
||||||
|
};
|
||||||
21
src/GUI.ts
Normal file
21
src/GUI.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import * as dat from 'dat.gui';
|
||||||
|
import Config from "./Config";
|
||||||
|
|
||||||
|
export default class GUI {
|
||||||
|
private gui: dat.GUI = new dat.GUI({
|
||||||
|
width: 400
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
const simFolder = this.gui.addFolder('Simulation');
|
||||||
|
|
||||||
|
simFolder.add(Config, 'worldSize', 256, 8096);
|
||||||
|
simFolder.add(Config, 'antsCount', 1, 1e6);
|
||||||
|
simFolder.add(Config, 'simulationStepsPerSecond', 1, 300);
|
||||||
|
|
||||||
|
const controlsFolder = this.gui.addFolder('Controls');
|
||||||
|
|
||||||
|
simFolder.open();
|
||||||
|
controlsFolder.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,9 @@
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import {SceneCollection} from "./App";
|
import {SceneCollection} from "./App";
|
||||||
|
import Config from "./Config";
|
||||||
const Config = {
|
|
||||||
worldSize: 1024,
|
|
||||||
antsCount: 64 ** 2
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Resources {
|
interface Resources {
|
||||||
worldRenderTarget0: THREE.WebGLRenderTarget;
|
worldRenderTarget: THREE.WebGLRenderTarget;
|
||||||
worldRenderTarget1: THREE.WebGLRenderTarget;
|
|
||||||
worldBlurredRenderTarget: THREE.WebGLRenderTarget;
|
worldBlurredRenderTarget: THREE.WebGLRenderTarget;
|
||||||
antsDataRenderTarget0: THREE.WebGLRenderTarget;
|
antsDataRenderTarget0: THREE.WebGLRenderTarget;
|
||||||
antsDataRenderTarget1: THREE.WebGLRenderTarget;
|
antsDataRenderTarget1: THREE.WebGLRenderTarget;
|
||||||
|
|
@ -29,14 +24,7 @@ export default class Renderer {
|
||||||
const antTextureSize = Math.round(Math.sqrt(Config.antsCount));
|
const antTextureSize = Math.round(Math.sqrt(Config.antsCount));
|
||||||
|
|
||||||
this.resources = {
|
this.resources = {
|
||||||
worldRenderTarget0: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, {
|
worldRenderTarget: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, {
|
||||||
format: THREE.RGBAFormat,
|
|
||||||
type: THREE.FloatType,
|
|
||||||
depthBuffer: false,
|
|
||||||
magFilter: THREE.LinearFilter,
|
|
||||||
minFilter: THREE.LinearFilter,
|
|
||||||
}),
|
|
||||||
worldRenderTarget1: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, {
|
|
||||||
format: THREE.RGBAFormat,
|
format: THREE.RGBAFormat,
|
||||||
type: THREE.FloatType,
|
type: THREE.FloatType,
|
||||||
depthBuffer: false,
|
depthBuffer: false,
|
||||||
|
|
@ -74,15 +62,14 @@ export default class Renderer {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderScenes(scenes: SceneCollection) {
|
public renderSimulation(scenes: SceneCollection) {
|
||||||
const [antsComputeSource, antsComputeTarget] = scenes.ants.getRenderTargets();
|
const [antsComputeSource, antsComputeTarget] = scenes.ants.getRenderTargets();
|
||||||
const [worldComputeSource, worldComputeTarget] = scenes.world.getRenderTargets();
|
|
||||||
|
|
||||||
this.renderer.setViewport(0, 0, scenes.ants.renderWidth, scenes.ants.renderHeight);
|
this.renderer.setViewport(0, 0, scenes.ants.renderWidth, scenes.ants.renderHeight);
|
||||||
|
|
||||||
this.renderer.setRenderTarget(antsComputeTarget);
|
this.renderer.setRenderTarget(antsComputeTarget);
|
||||||
scenes.ants.material.uniforms.tLastState.value = antsComputeSource.texture;
|
scenes.ants.material.uniforms.tLastState.value = antsComputeSource.texture;
|
||||||
scenes.ants.material.uniforms.tWorld.value = worldComputeSource.texture;
|
scenes.ants.material.uniforms.tWorld.value = scenes.worldBlur.getRenderTarget().texture;
|
||||||
this.renderer.render(scenes.ants, scenes.ants.camera);
|
this.renderer.render(scenes.ants, scenes.ants.camera);
|
||||||
|
|
||||||
this.renderer.setViewport(0, 0, scenes.discretize.renderWidth, scenes.discretize.renderHeight);
|
this.renderer.setViewport(0, 0, scenes.discretize.renderWidth, scenes.discretize.renderHeight);
|
||||||
|
|
@ -94,7 +81,7 @@ export default class Renderer {
|
||||||
|
|
||||||
this.renderer.setViewport(0, 0, scenes.world.renderWidth, scenes.world.renderHeight);
|
this.renderer.setViewport(0, 0, scenes.world.renderWidth, scenes.world.renderHeight);
|
||||||
|
|
||||||
this.renderer.setRenderTarget(worldComputeTarget);
|
this.renderer.setRenderTarget(scenes.world.getRenderTarget());
|
||||||
scenes.world.material.uniforms.tLastState.value = scenes.worldBlur.getRenderTarget().texture;
|
scenes.world.material.uniforms.tLastState.value = scenes.worldBlur.getRenderTarget().texture;
|
||||||
scenes.world.material.uniforms.tDiscreteAnts.value = scenes.discretize.getRenderTarget().texture;
|
scenes.world.material.uniforms.tDiscreteAnts.value = scenes.discretize.getRenderTarget().texture;
|
||||||
scenes.world.material.uniforms.pointerData.value = scenes.screen.getPointerData();
|
scenes.world.material.uniforms.pointerData.value = scenes.screen.getPointerData();
|
||||||
|
|
@ -103,14 +90,16 @@ export default class Renderer {
|
||||||
this.renderer.setViewport(0, 0, scenes.worldBlur.renderWidth, scenes.worldBlur.renderHeight);
|
this.renderer.setViewport(0, 0, scenes.worldBlur.renderWidth, scenes.worldBlur.renderHeight);
|
||||||
|
|
||||||
this.renderer.setRenderTarget(scenes.worldBlur.getRenderTarget());
|
this.renderer.setRenderTarget(scenes.worldBlur.getRenderTarget());
|
||||||
scenes.worldBlur.material.uniforms.tWorld.value = worldComputeTarget.texture;
|
scenes.worldBlur.material.uniforms.tWorld.value = scenes.world.getRenderTarget().texture;
|
||||||
this.renderer.render(scenes.worldBlur, scenes.worldBlur.camera);
|
this.renderer.render(scenes.worldBlur, scenes.worldBlur.camera);
|
||||||
|
|
||||||
this.renderer.setViewport(0, 0, scenes.screen.renderWidth, scenes.screen.renderHeight);
|
|
||||||
|
|
||||||
this.renderer.setRenderTarget(null);
|
|
||||||
scenes.screen.material.uniforms.tData.value = antsComputeTarget.texture;
|
scenes.screen.material.uniforms.tData.value = antsComputeTarget.texture;
|
||||||
scenes.screen.groundMaterial.uniforms.map.value = scenes.worldBlur.getRenderTarget().texture;
|
scenes.screen.groundMaterial.uniforms.map.value = scenes.worldBlur.getRenderTarget().texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderToScreen(scenes: SceneCollection) {
|
||||||
|
this.renderer.setViewport(0, 0, scenes.screen.renderWidth, scenes.screen.renderHeight);
|
||||||
|
this.renderer.setRenderTarget(null);
|
||||||
this.renderer.render(scenes.screen, scenes.screen.camera);
|
this.renderer.render(scenes.screen, scenes.screen.camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,4 +107,25 @@ export default class Renderer {
|
||||||
this.canvas.width = width;
|
this.canvas.width = width;
|
||||||
this.canvas.height = height;
|
this.canvas.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getCommonMaterialDefines(): Record<string, string> {
|
||||||
|
return {
|
||||||
|
WORLD_SIZE: Renderer.convertNumberToFloatString(Config.worldSize),
|
||||||
|
SCENT_THRESHOLD: Renderer.convertNumberToFloatString(Config.scentThreshold),
|
||||||
|
SCENT_FADE_OUT_FACTOR: Renderer.convertNumberToFloatString(Config.scentFadeOutFactor),
|
||||||
|
SCENT_BLUR_RADIUS: Renderer.convertNumberToFloatString(Config.scentBlurRadius),
|
||||||
|
SCENT_MAX_STORAGE: Renderer.convertNumberToFloatString(Config.scentMaxStorage),
|
||||||
|
SCENT_PER_MARKER: Renderer.convertNumberToFloatString(Config.scentPerMarker),
|
||||||
|
ANT_SPEED: Renderer.convertNumberToFloatString(Config.antSpeed),
|
||||||
|
ANT_ROTATION_ANGLE: Renderer.convertNumberToFloatString(Config.antRotationAngle)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static convertNumberToFloatString(n: number): string {
|
||||||
|
return n.toFixed(8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
|
import {WebGLRenderTarget} from 'three';
|
||||||
import Renderer from "../Renderer";
|
import Renderer from "../Renderer";
|
||||||
import AbstractScene from "./AbstractScene";
|
import AbstractScene from "./AbstractScene";
|
||||||
import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry";
|
import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry";
|
||||||
import fragmentShader from '../shaders/antsCompute.frag';
|
import fragmentShader from '../shaders/antsCompute.frag';
|
||||||
import vertexShader from '../shaders/antsCompute.vert';
|
import vertexShader from '../shaders/antsCompute.vert';
|
||||||
import {WebGLRenderTarget} from "three";
|
|
||||||
|
|
||||||
export default class AntsComputeScene extends AbstractScene {
|
export default class AntsComputeScene extends AbstractScene {
|
||||||
public camera: THREE.OrthographicCamera = new THREE.OrthographicCamera();
|
public camera: THREE.OrthographicCamera = new THREE.OrthographicCamera();
|
||||||
|
|
@ -19,10 +19,12 @@ export default class AntsComputeScene extends AbstractScene {
|
||||||
uniforms: {
|
uniforms: {
|
||||||
uTime: {value: 0},
|
uTime: {value: 0},
|
||||||
tLastState: {value: this.renderer.resources.antsDataRenderTarget0.texture},
|
tLastState: {value: this.renderer.resources.antsDataRenderTarget0.texture},
|
||||||
tWorld: {value: this.renderer.resources.worldRenderTarget0.texture},
|
tWorld: {value: this.renderer.resources.worldRenderTarget.texture},
|
||||||
},
|
},
|
||||||
vertexShader,
|
vertexShader,
|
||||||
fragmentShader
|
fragmentShader,
|
||||||
|
defines: this.renderer.getCommonMaterialDefines(),
|
||||||
|
glslVersion: THREE.GLSL3
|
||||||
});
|
});
|
||||||
const mesh = new THREE.Mesh(geometry, material);
|
const mesh = new THREE.Mesh(geometry, material);
|
||||||
this.add(mesh);
|
this.add(mesh);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import vertexShader from '../shaders/antsDiscretize.vert';
|
||||||
export default class AntsDiscretizeScene extends AbstractScene {
|
export default class AntsDiscretizeScene extends AbstractScene {
|
||||||
public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera();
|
public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera();
|
||||||
public readonly material: THREE.RawShaderMaterial;
|
public readonly material: THREE.RawShaderMaterial;
|
||||||
private readonly renderTarget: WebGLRenderTarget;
|
|
||||||
|
|
||||||
constructor(renderer: Renderer) {
|
constructor(renderer: Renderer) {
|
||||||
super(renderer);
|
super(renderer);
|
||||||
|
|
@ -20,7 +19,9 @@ export default class AntsDiscretizeScene extends AbstractScene {
|
||||||
tDataLast: {value: null},
|
tDataLast: {value: null},
|
||||||
},
|
},
|
||||||
vertexShader,
|
vertexShader,
|
||||||
fragmentShader
|
fragmentShader,
|
||||||
|
defines: this.renderer.getCommonMaterialDefines(),
|
||||||
|
glslVersion: THREE.GLSL3
|
||||||
});
|
});
|
||||||
const mesh = new THREE.InstancedMesh(
|
const mesh = new THREE.InstancedMesh(
|
||||||
geometry,
|
geometry,
|
||||||
|
|
@ -31,14 +32,12 @@ export default class AntsDiscretizeScene extends AbstractScene {
|
||||||
|
|
||||||
this.material = material;
|
this.material = material;
|
||||||
|
|
||||||
this.renderTarget = this.renderer.resources.antsDiscreteRenderTarget;
|
this.renderWidth = this.renderer.resources.worldRenderTarget.width;
|
||||||
|
this.renderHeight = this.renderer.resources.worldRenderTarget.height;
|
||||||
this.renderWidth = this.renderer.resources.worldRenderTarget0.width;
|
|
||||||
this.renderHeight = this.renderer.resources.worldRenderTarget0.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getRenderTarget(): WebGLRenderTarget {
|
public getRenderTarget(): WebGLRenderTarget {
|
||||||
return this.renderTarget;
|
return this.renderer.resources.antsDiscreteRenderTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(width: number, height: number) {
|
public resize(width: number, height: number) {
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,12 @@ export default class ScreenScene extends AbstractScene {
|
||||||
new THREE.PlaneBufferGeometry(10, 10),
|
new THREE.PlaneBufferGeometry(10, 10),
|
||||||
new THREE.ShaderMaterial({
|
new THREE.ShaderMaterial({
|
||||||
uniforms: {
|
uniforms: {
|
||||||
map: {value: this.renderer.resources.worldRenderTarget0.texture},
|
map: {value: this.renderer.resources.worldRenderTarget.texture},
|
||||||
},
|
},
|
||||||
vertexShader: vertexShaderGround,
|
vertexShader: vertexShaderGround,
|
||||||
fragmentShader: fragmentShaderGround
|
fragmentShader: fragmentShaderGround,
|
||||||
|
defines: this.renderer.getCommonMaterialDefines(),
|
||||||
|
glslVersion: THREE.GLSL3
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import {WebGLRenderTarget} from "three";
|
||||||
export default class WorldBlurScene extends AbstractScene {
|
export default class WorldBlurScene extends AbstractScene {
|
||||||
public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera();
|
public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera();
|
||||||
public readonly material: THREE.RawShaderMaterial;
|
public readonly material: THREE.RawShaderMaterial;
|
||||||
private readonly renderTarget: WebGLRenderTarget;
|
|
||||||
|
|
||||||
constructor(renderer: Renderer) {
|
constructor(renderer: Renderer) {
|
||||||
super(renderer);
|
super(renderer);
|
||||||
|
|
@ -17,23 +16,24 @@ export default class WorldBlurScene extends AbstractScene {
|
||||||
const geometry = new FullScreenTriangleGeometry();
|
const geometry = new FullScreenTriangleGeometry();
|
||||||
const material = new THREE.RawShaderMaterial({
|
const material = new THREE.RawShaderMaterial({
|
||||||
uniforms: {
|
uniforms: {
|
||||||
tWorld: {value: this.renderer.resources.worldRenderTarget0.texture},
|
tWorld: {value: this.renderer.resources.worldRenderTarget.texture},
|
||||||
},
|
},
|
||||||
vertexShader,
|
vertexShader,
|
||||||
fragmentShader
|
fragmentShader,
|
||||||
|
defines: this.renderer.getCommonMaterialDefines(),
|
||||||
|
glslVersion: THREE.GLSL3
|
||||||
});
|
});
|
||||||
const mesh = new THREE.Mesh(geometry, material);
|
const mesh = new THREE.Mesh(geometry, material);
|
||||||
this.add(mesh);
|
this.add(mesh);
|
||||||
|
|
||||||
this.material = material;
|
this.material = material;
|
||||||
|
|
||||||
this.renderTarget = this.renderer.resources.worldBlurredRenderTarget;
|
this.renderWidth = this.renderer.resources.worldRenderTarget.width;
|
||||||
this.renderWidth = this.renderer.resources.worldRenderTarget0.width;
|
this.renderHeight = this.renderer.resources.worldRenderTarget.height;
|
||||||
this.renderHeight = this.renderer.resources.worldRenderTarget0.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getRenderTarget(): WebGLRenderTarget {
|
public getRenderTarget(): WebGLRenderTarget {
|
||||||
return this.renderTarget;
|
return this.renderer.resources.worldBlurredRenderTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(width: number, height: number) {
|
public resize(width: number, height: number) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import {WebGLRenderTarget} from "three";
|
||||||
export default class WorldComputeScene extends AbstractScene {
|
export default class WorldComputeScene extends AbstractScene {
|
||||||
public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera();
|
public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera();
|
||||||
public readonly material: THREE.RawShaderMaterial;
|
public readonly material: THREE.RawShaderMaterial;
|
||||||
private readonly renderTargets: [WebGLRenderTarget, WebGLRenderTarget];
|
|
||||||
|
|
||||||
constructor(renderer: Renderer) {
|
constructor(renderer: Renderer) {
|
||||||
super(renderer);
|
super(renderer);
|
||||||
|
|
@ -17,31 +16,26 @@ export default class WorldComputeScene extends AbstractScene {
|
||||||
const geometry = new FullScreenTriangleGeometry();
|
const geometry = new FullScreenTriangleGeometry();
|
||||||
const material = new THREE.RawShaderMaterial({
|
const material = new THREE.RawShaderMaterial({
|
||||||
uniforms: {
|
uniforms: {
|
||||||
tLastState: {value: this.renderer.resources.worldRenderTarget0.texture},
|
tLastState: {value: this.renderer.resources.worldRenderTarget.texture},
|
||||||
tDiscreteAnts: {value: this.renderer.resources.antsDiscreteRenderTarget.texture},
|
tDiscreteAnts: {value: this.renderer.resources.antsDiscreteRenderTarget.texture},
|
||||||
pointerData: {value: new THREE.Vector4()},
|
pointerData: {value: new THREE.Vector4()},
|
||||||
},
|
},
|
||||||
vertexShader,
|
vertexShader,
|
||||||
fragmentShader
|
fragmentShader,
|
||||||
|
defines: this.renderer.getCommonMaterialDefines(),
|
||||||
|
glslVersion: THREE.GLSL3
|
||||||
});
|
});
|
||||||
const mesh = new THREE.Mesh(geometry, material);
|
const mesh = new THREE.Mesh(geometry, material);
|
||||||
this.add(mesh);
|
this.add(mesh);
|
||||||
|
|
||||||
this.material = material;
|
this.material = material;
|
||||||
|
|
||||||
this.renderTargets = [
|
this.renderWidth = this.renderer.resources.worldRenderTarget.width;
|
||||||
this.renderer.resources.worldRenderTarget0,
|
this.renderHeight = this.renderer.resources.worldRenderTarget.height;
|
||||||
this.renderer.resources.worldRenderTarget1
|
|
||||||
];
|
|
||||||
|
|
||||||
this.renderWidth = this.renderer.resources.worldRenderTarget0.width;
|
|
||||||
this.renderHeight = this.renderer.resources.worldRenderTarget0.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getRenderTargets(): [WebGLRenderTarget, WebGLRenderTarget] {
|
public getRenderTarget(): WebGLRenderTarget {
|
||||||
this.renderTargets.reverse();
|
return this.renderer.resources.worldRenderTarget;
|
||||||
|
|
||||||
return this.renderTargets;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(width: number, height: number) {
|
public resize(width: number, height: number) {
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,15 @@
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp int;
|
precision highp int;
|
||||||
|
|
||||||
varying vec2 vUv;
|
in vec2 vUv;
|
||||||
varying float vIsCarryingFood;
|
in float vIsCarryingFood;
|
||||||
|
|
||||||
uniform sampler2D tAnt;
|
uniform sampler2D tAnt;
|
||||||
uniform sampler2D tFood;
|
uniform sampler2D tFood;
|
||||||
|
|
||||||
vec3 hsv2rgb(vec3 c) {
|
|
||||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
||||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
|
||||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 antColor = texture2D(tAnt, vUv);
|
vec4 antColor = texture(tAnt, vUv);
|
||||||
vec4 foodColor = texture2D(tFood, vUv);
|
vec4 foodColor = texture(tFood, vUv);
|
||||||
|
|
||||||
gl_FragColor = mix(antColor, foodColor, foodColor.a * vIsCarryingFood);
|
pc_fragColor = mix(antColor, foodColor, foodColor.a * vIsCarryingFood);
|
||||||
}
|
}
|
||||||
|
|
@ -24,7 +24,7 @@ void main() {
|
||||||
float sampleY = floor(id / dataTextureSize);
|
float sampleY = floor(id / dataTextureSize);
|
||||||
float sampleX = id - sampleY * dataTextureSize;
|
float sampleX = id - sampleY * dataTextureSize;
|
||||||
|
|
||||||
vec4 dataSample = texture2D(tData, vec2(sampleX, sampleY) / dataTextureSize);
|
vec4 dataSample = texture(tData, vec2(sampleX, sampleY) / dataTextureSize);
|
||||||
|
|
||||||
vec2 offset = dataSample.xy * 10.;
|
vec2 offset = dataSample.xy * 10.;
|
||||||
vec2 rotatedPosition = rotate(position.xy, -dataSample.z + PI * 0.5);
|
vec2 rotatedPosition = rotate(position.xy, -dataSample.z + PI * 0.5);
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,38 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp int;
|
precision highp int;
|
||||||
|
|
||||||
#define PI 3.1415926535897932384626433832795
|
#define PI 3.1415926535897932384626433832795
|
||||||
#define MAX_STORAGE 1000000.
|
|
||||||
|
|
||||||
in vec2 vUv;
|
in vec2 vUv;
|
||||||
|
|
||||||
out highp vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
uniform float uTime;
|
uniform float uTime;
|
||||||
uniform sampler2D tLastState;
|
uniform sampler2D tLastState;
|
||||||
uniform sampler2D tWorld;
|
uniform sampler2D tWorld;
|
||||||
|
|
||||||
const float PHI = 1.61803398874989484820459;
|
|
||||||
|
|
||||||
const float speed = 1.;
|
|
||||||
const float sampleDistance = 20.;
|
const float sampleDistance = 20.;
|
||||||
const float worldSize = 1024.;
|
const float cellSize = 1. / WORLD_SIZE;
|
||||||
const float cellSize = 1. / worldSize;
|
|
||||||
|
|
||||||
float goldNoise(in vec2 xy, in float seed) {
|
float rand(vec2 co) {
|
||||||
return fract(tan(distance(xy*PHI, xy)*seed)*xy.x);
|
float a = 12.9898;
|
||||||
|
float b = 78.233;
|
||||||
|
float c = 43758.5453;
|
||||||
|
float dt = dot(co.xy ,vec2(a,b));
|
||||||
|
float sn = mod(dt, 3.14);
|
||||||
|
return fract(sin(sn) * c);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 roundUvToCellCenter(vec2 uv) {
|
||||||
|
return floor(uv * WORLD_SIZE) / WORLD_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tryGetFood(vec2 pos) {
|
bool tryGetFood(vec2 pos) {
|
||||||
return texture(tWorld, pos).x == 1.;
|
return texture(tWorld, roundUvToCellCenter(pos)).x == 1.;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tryDropFood(vec2 pos) {
|
bool tryDropFood(vec2 pos) {
|
||||||
return texture(tWorld, pos).y == 1.;
|
return texture(tWorld, roundUvToCellCenter(pos)).y == 1.;
|
||||||
}
|
}
|
||||||
|
|
||||||
float smell(vec2 pos, float isCarrying) {
|
float smell(vec2 pos, float isCarrying) {
|
||||||
|
|
@ -45,24 +47,21 @@ float smell(vec2 pos, float isCarrying) {
|
||||||
|
|
||||||
vec2 applyOffsetToPos(vec2 pos, vec2 offsetDirectaion) {
|
vec2 applyOffsetToPos(vec2 pos, vec2 offsetDirectaion) {
|
||||||
return vec2(
|
return vec2(
|
||||||
fract(pos.x + offsetDirectaion.x * cellSize),
|
clamp(pos.x + offsetDirectaion.x * cellSize, 0., 1.),
|
||||||
fract(pos.y + offsetDirectaion.y * cellSize)
|
clamp(pos.y + offsetDirectaion.y * cellSize, 0., 1.)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const float rotAngle = PI / 50.;
|
float getMaxScentStorage(vec2 antDataUv) {
|
||||||
|
float factor = 0.8 + rand(antDataUv * 100.) * 0.2;
|
||||||
|
|
||||||
float constrainAngle(float x){
|
return SCENT_MAX_STORAGE * factor;
|
||||||
x = mod(x,360.);
|
|
||||||
if (x < 0.)
|
|
||||||
x += 360.;
|
|
||||||
return x;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 lastState = texture(tLastState, vUv);
|
vec4 lastState = texture(tLastState, vUv);
|
||||||
|
|
||||||
float rand = goldNoise(vUv * 1000., fract(uTime / 1000.));
|
float noise = rand(vUv * 1000. + fract(uTime / 1000.));
|
||||||
|
|
||||||
vec2 pos = lastState.xy;
|
vec2 pos = lastState.xy;
|
||||||
float angle = lastState.z;
|
float angle = lastState.z;
|
||||||
|
|
@ -73,128 +72,119 @@ void main() {
|
||||||
|
|
||||||
if (pos == vec2(0)) { // init new ant
|
if (pos == vec2(0)) { // init new ant
|
||||||
pos = vec2(0.5);
|
pos = vec2(0.5);
|
||||||
angle = goldNoise(vUv * 10000., 1.) * 2. * PI;
|
angle = rand(vUv * 10000.) * 2. * PI;
|
||||||
isCarrying = 0.;
|
isCarrying = 0.;
|
||||||
storage = MAX_STORAGE;
|
storage = 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tryGetFood(pos) && isCarrying == 0.) {
|
||||||
|
isCarrying = 1.;
|
||||||
|
angle += PI;
|
||||||
|
storage = getMaxScentStorage(vUv);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tryDropFood(pos)) {
|
if (tryDropFood(pos)) {
|
||||||
|
storage = getMaxScentStorage(vUv);
|
||||||
|
|
||||||
if (isCarrying == 1.) {
|
if (isCarrying == 1.) {
|
||||||
isCarrying = 0.;
|
isCarrying = 0.;
|
||||||
angle += PI;
|
angle += PI;
|
||||||
}
|
}
|
||||||
storage = MAX_STORAGE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCarrying == 0.) {
|
if (isCarrying == 0.) {
|
||||||
if (rand < 0.33) {
|
if (noise < 0.33) {
|
||||||
vec2 offset = vec2(cos(angle), sin(angle)) * sampleDistance;
|
vec2 offset = vec2(cos(angle), sin(angle)) * sampleDistance;
|
||||||
vec2 point = applyOffsetToPos(pos, offset);
|
vec2 point = applyOffsetToPos(pos, offset);
|
||||||
|
|
||||||
if (tryGetFood(point)) {
|
if (tryGetFood(point)) {
|
||||||
isCarrying = 1.;
|
|
||||||
storage = MAX_STORAGE;
|
|
||||||
pos = point;
|
|
||||||
movementProcessed = true;
|
movementProcessed = true;
|
||||||
}
|
}
|
||||||
} else if (rand < 0.66) {
|
} else if (noise < 0.66) {
|
||||||
vec2 offset = vec2(cos(angle - rotAngle), sin(angle - rotAngle)) * sampleDistance;
|
float newAngle = angle - ANT_ROTATION_ANGLE;
|
||||||
|
vec2 offset = vec2(cos(newAngle), sin(newAngle)) * sampleDistance;
|
||||||
vec2 point = applyOffsetToPos(pos, offset);
|
vec2 point = applyOffsetToPos(pos, offset);
|
||||||
|
|
||||||
if (tryGetFood(point)) {
|
if (tryGetFood(point)) {
|
||||||
isCarrying = 1.;
|
|
||||||
storage = MAX_STORAGE;
|
|
||||||
pos = point;
|
|
||||||
movementProcessed = true;
|
movementProcessed = true;
|
||||||
angle -= rotAngle;
|
angle = newAngle;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vec2 offset = vec2(cos(angle + rotAngle), sin(angle + rotAngle)) * sampleDistance;
|
float newAngle = angle + ANT_ROTATION_ANGLE;
|
||||||
|
vec2 offset = vec2(cos(newAngle), sin(newAngle)) * sampleDistance;
|
||||||
vec2 point = applyOffsetToPos(pos, offset);
|
vec2 point = applyOffsetToPos(pos, offset);
|
||||||
|
|
||||||
if (tryGetFood(point)) {
|
if (tryGetFood(point)) {
|
||||||
isCarrying = 1.;
|
|
||||||
storage = MAX_STORAGE;
|
|
||||||
pos = point;
|
|
||||||
movementProcessed = true;
|
movementProcessed = true;
|
||||||
angle += rotAngle;
|
angle = newAngle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (isCarrying == 1.) {
|
} else if (isCarrying == 1.) {
|
||||||
if (rand < 0.33) {
|
if (noise < 0.33) {
|
||||||
vec2 offset = vec2(cos(angle), sin(angle)) * 10.;
|
vec2 offset = vec2(cos(angle), sin(angle)) * sampleDistance;
|
||||||
vec2 point = applyOffsetToPos(pos, offset);
|
vec2 point = applyOffsetToPos(pos, offset);
|
||||||
|
|
||||||
if (tryDropFood(point)) {
|
if (tryDropFood(point)) {
|
||||||
isCarrying = 0.;
|
|
||||||
storage = MAX_STORAGE;
|
|
||||||
pos = point;
|
|
||||||
movementProcessed = true;
|
movementProcessed = true;
|
||||||
}
|
}
|
||||||
} else if (rand < 0.66) {
|
} else if (noise < 0.66) {
|
||||||
vec2 offset = vec2(cos(angle - rotAngle), sin(angle - rotAngle)) * sampleDistance;
|
float newAngle = angle - ANT_ROTATION_ANGLE;
|
||||||
|
vec2 offset = vec2(cos(newAngle), sin(newAngle)) * sampleDistance;
|
||||||
vec2 point = applyOffsetToPos(pos, offset);
|
vec2 point = applyOffsetToPos(pos, offset);
|
||||||
|
|
||||||
if (tryDropFood(point)) {
|
if (tryDropFood(point)) {
|
||||||
isCarrying = 0.;
|
|
||||||
storage = MAX_STORAGE;
|
|
||||||
pos = point;
|
|
||||||
movementProcessed = true;
|
movementProcessed = true;
|
||||||
angle -= rotAngle;
|
angle = newAngle;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vec2 offset = vec2(cos(angle + rotAngle), sin(angle + rotAngle)) * sampleDistance;
|
float newAngle = angle + ANT_ROTATION_ANGLE;
|
||||||
|
vec2 offset = vec2(cos(newAngle), sin(newAngle)) * sampleDistance;
|
||||||
vec2 point = applyOffsetToPos(pos, offset);
|
vec2 point = applyOffsetToPos(pos, offset);
|
||||||
|
|
||||||
if (tryDropFood(point)) {
|
if (tryDropFood(point)) {
|
||||||
isCarrying = 0.;
|
|
||||||
storage = MAX_STORAGE;
|
|
||||||
pos = point;
|
|
||||||
movementProcessed = true;
|
movementProcessed = true;
|
||||||
angle += rotAngle;
|
angle = newAngle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!movementProcessed) {
|
if (!movementProcessed) {
|
||||||
float noise2 = goldNoise(vUv * 10000., uTime / 1000. + 0.2);
|
float noise2 = rand(vUv * 1000. + fract(uTime / 1000.) + 0.2);
|
||||||
|
|
||||||
float sampleAhead = smell(applyOffsetToPos(pos, vec2(cos(angle), sin(angle)) * sampleDistance), isCarrying);
|
float sampleAhead = smell(applyOffsetToPos(pos, vec2(cos(angle), sin(angle)) * sampleDistance), isCarrying);
|
||||||
float sampleLeft = smell(applyOffsetToPos(pos, vec2(cos(angle - rotAngle), sin(angle - rotAngle)) * sampleDistance), isCarrying);
|
float sampleLeft = smell(applyOffsetToPos(pos, vec2(cos(angle - ANT_ROTATION_ANGLE), sin(angle - ANT_ROTATION_ANGLE)) * sampleDistance), isCarrying);
|
||||||
float sampleRight = smell(applyOffsetToPos(pos, vec2(cos(angle + rotAngle), sin(angle + rotAngle)) * sampleDistance), isCarrying);
|
float sampleRight = smell(applyOffsetToPos(pos, vec2(cos(angle + ANT_ROTATION_ANGLE), sin(angle + ANT_ROTATION_ANGLE)) * sampleDistance), isCarrying);
|
||||||
|
|
||||||
if (sampleAhead > sampleLeft && sampleAhead > sampleRight) {
|
if (sampleAhead > sampleLeft && sampleAhead > sampleRight) {
|
||||||
// don't change direction
|
// don't change direction
|
||||||
} else if (sampleLeft > sampleAhead && sampleLeft > sampleRight) {
|
} else if (sampleLeft > sampleAhead && sampleLeft > sampleRight) {
|
||||||
angle -= rotAngle; // steer left
|
angle -= ANT_ROTATION_ANGLE; // steer left
|
||||||
} else if (sampleRight > sampleAhead && sampleRight > sampleLeft) {
|
} else if (sampleRight > sampleAhead && sampleRight > sampleLeft) {
|
||||||
angle += rotAngle; // steer right
|
angle += ANT_ROTATION_ANGLE; // steer right
|
||||||
} else if (rand < 0.33) {
|
} else if (noise < 0.33) {
|
||||||
angle += rotAngle; // no smell detected, do random movement
|
angle += ANT_ROTATION_ANGLE; // no smell detected, do random movement
|
||||||
} else if (rand < 0.66) {
|
} else if (noise < 0.66) {
|
||||||
angle -= rotAngle;
|
angle -= ANT_ROTATION_ANGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noise2 > 0.5) {
|
if (noise2 > 0.5) {
|
||||||
angle += PI / 30.;
|
angle += ANT_ROTATION_ANGLE * 2.;
|
||||||
} else {
|
} else {
|
||||||
angle -= PI / 30.;
|
angle -= ANT_ROTATION_ANGLE * 2.;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 offset = vec2(cos(angle), sin(angle));
|
vec2 offset = vec2(cos(angle), sin(angle));
|
||||||
pos = applyOffsetToPos(pos, offset);
|
pos = applyOffsetToPos(pos, offset);
|
||||||
} else {
|
|
||||||
angle += PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fract(pos.x) == 0. || fract(pos.y) == 0.) {
|
if (fract(pos.x) == 0. || fract(pos.y) == 0.) {
|
||||||
//angle += PI / 2.;
|
angle += PI * (noise - 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
FragColor = vec4(
|
FragColor = vec4(
|
||||||
pos.x,
|
pos.x,
|
||||||
pos.y,
|
pos.y,
|
||||||
angle,
|
angle,
|
||||||
float((uint(max(storage - 1000., 0.)) << 1) + uint(isCarrying))
|
float((uint(max(storage - SCENT_PER_MARKER, 0.)) << 1) + uint(isCarrying))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp int;
|
precision highp int;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp int;
|
precision highp int;
|
||||||
|
|
||||||
|
|
@ -8,7 +6,7 @@ in float vIsCarryingFood;
|
||||||
in float vScentFactor;
|
in float vScentFactor;
|
||||||
in float vIsCellCleared;
|
in float vIsCellCleared;
|
||||||
|
|
||||||
out highp vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
FragColor = vec4(vIsCarryingFood * vScentFactor, (1. - vIsCarryingFood) * vScentFactor, vIsCellCleared, 1);
|
FragColor = vec4(vIsCarryingFood * vScentFactor, (1. - vIsCarryingFood) * vScentFactor, vIsCellCleared, 1);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp int;
|
precision highp int;
|
||||||
|
|
||||||
|
|
@ -14,8 +12,7 @@ out float vIsCellCleared;
|
||||||
uniform sampler2D tDataCurrent;
|
uniform sampler2D tDataCurrent;
|
||||||
uniform sampler2D tDataLast;
|
uniform sampler2D tDataLast;
|
||||||
|
|
||||||
const float worldSize = 1024.;
|
const float cellSize = 1. / WORLD_SIZE;
|
||||||
const float cellSize = 1. / worldSize;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vUv = uv;
|
vUv = uv;
|
||||||
|
|
@ -41,7 +38,7 @@ void main() {
|
||||||
vIsCellCleared = isCellCleared;
|
vIsCellCleared = isCellCleared;
|
||||||
|
|
||||||
gl_Position = vec4(
|
gl_Position = vec4(
|
||||||
(position.xy * cellSize * 0.5 + floor(offset * worldSize) / worldSize + cellSize * 0.5) * 2. - 1.,
|
(position.xy * cellSize * 0.1 + floor(offset * WORLD_SIZE) / WORLD_SIZE + cellSize * 0.5) * 2. - 1.,
|
||||||
0,
|
0,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,26 @@
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp int;
|
precision highp int;
|
||||||
|
|
||||||
varying vec2 vUv;
|
in vec2 vUv;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
uniform sampler2D map;
|
uniform sampler2D map;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 value = clamp(texture2D(map, vUv), 0., 1.);
|
vec4 value = clamp(texture(map, vUv), 0., 1.);
|
||||||
vec4 bg = vec4(1);
|
vec3 bg = vec3(0.9);
|
||||||
|
|
||||||
//bg = mix(bg, vec4(0, 0, 1, 1), value.a * 0.7);
|
bg = mix(bg, vec3(0.2, 0.2, 0.8), clamp(value.a, 0., 1.));
|
||||||
//bg = mix(bg, vec4(1, 0, 0, 1), value.b * 0.7);
|
bg = mix(bg, vec3(0.8, 0.2, 0.2), clamp(value.b, 0., 1.));
|
||||||
|
|
||||||
if (value.r == 1.) {
|
if (value.r == 1.) {
|
||||||
bg = vec4(1, 0, 0, 1);
|
bg = vec3(1, 0.2, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.g == 1.) {
|
if (value.g == 1.) {
|
||||||
bg = vec4(0.1, 0.1, 1, 1);
|
bg = vec3(0.2, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
gl_FragColor = bg;
|
FragColor = vec4(bg, 1);
|
||||||
}
|
}
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp int;
|
precision highp int;
|
||||||
|
|
||||||
varying vec2 vUv;
|
in vec2 vUv;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
uniform sampler2D tLastState;
|
uniform sampler2D tLastState;
|
||||||
uniform sampler2D tDiscreteAnts;
|
uniform sampler2D tDiscreteAnts;
|
||||||
uniform vec4 pointerData;
|
uniform vec4 pointerData;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 lastState = texture2D(tLastState, vUv);
|
vec4 lastState = texture(tLastState, vUv);
|
||||||
vec3 discreteAnts = texture2D(tDiscreteAnts, vUv).xyz;
|
vec3 discreteAnts = texture(tDiscreteAnts, vUv).xyz;
|
||||||
|
|
||||||
float isFood = lastState.x;
|
float isFood = lastState.x;
|
||||||
float isHome = lastState.y;
|
float isHome = lastState.y;
|
||||||
float scentToHome = lastState.z + discreteAnts.x * 4.;
|
float scentToHome = lastState.z + discreteAnts.x * 2.;
|
||||||
float scentToFood = lastState.w + discreteAnts.y * 4.;
|
float scentToFood = lastState.w + discreteAnts.y * 2.;
|
||||||
|
|
||||||
if (discreteAnts.z == 1.) {
|
if (discreteAnts.z == 1.) {
|
||||||
isFood = 0.;
|
isFood = 0.;
|
||||||
|
|
@ -25,5 +27,5 @@ void main() {
|
||||||
isHome = max(isHome, pointerData.y);
|
isHome = max(isHome, pointerData.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
gl_FragColor = vec4(isFood, isHome, scentToHome, scentToFood);
|
FragColor = vec4(isFood, isHome, scentToHome, scentToFood);
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp int;
|
precision highp int;
|
||||||
|
|
||||||
attribute vec3 position;
|
in vec3 position;
|
||||||
attribute vec2 uv;
|
in vec2 uv;
|
||||||
|
|
||||||
varying vec2 vUv;
|
out vec2 vUv;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vUv = uv;
|
vUv = uv;
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,28 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp int;
|
precision highp int;
|
||||||
|
|
||||||
in vec2 vUv;
|
in vec2 vUv;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
uniform sampler2D tWorld;
|
uniform sampler2D tWorld;
|
||||||
|
|
||||||
out highp vec4 FragColor;
|
const float offset = 1. / WORLD_SIZE * SCENT_BLUR_RADIUS;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
float cellSize = 1. / vec2(textureSize(tWorld, 0)).x * 0.2;
|
|
||||||
|
|
||||||
vec4 s0 = texture(tWorld, vUv);
|
vec4 s0 = texture(tWorld, vUv);
|
||||||
vec4 s1 = texture(tWorld, vUv + vec2(1, 1) * cellSize);
|
vec4 s1 = texture(tWorld, vUv + vec2(1, 1) * offset);
|
||||||
vec4 s2 = texture(tWorld, vUv + vec2(-1, -1) * cellSize);
|
vec4 s2 = texture(tWorld, vUv + vec2(-1, -1) * offset);
|
||||||
vec4 s3 = texture(tWorld, vUv + vec2(-1, 1) * cellSize);
|
vec4 s3 = texture(tWorld, vUv + vec2(-1, 1) * offset);
|
||||||
vec4 s4 = texture(tWorld, vUv + vec2(1, -1) * cellSize);
|
vec4 s4 = texture(tWorld, vUv + vec2(1, -1) * offset);
|
||||||
|
|
||||||
float scentToHome = (s0.z + s1.z + s2.z + s3.z + s4.z) / 5. * 0.998;
|
float scentToHome = (s0.z + s1.z + s2.z + s3.z + s4.z) / 5. * SCENT_FADE_OUT_FACTOR;
|
||||||
float scentToFood = (s0.w + s1.w + s2.w + s3.w + s4.w) / 5. * 0.998;
|
float scentToFood = (s0.w + s1.w + s2.w + s3.w + s4.w) / 5. * SCENT_FADE_OUT_FACTOR;
|
||||||
|
|
||||||
float threshold = 0.01;
|
FragColor = vec4(
|
||||||
|
s0.x,
|
||||||
FragColor = vec4(s0.x, s0.y, scentToHome < threshold ? 0. : scentToHome, scentToFood < threshold ? 0. : scentToFood);
|
s0.y,
|
||||||
//FragColor = vec4(s0.x, s0.y, s0.z * 0.995, s0.w * 0.995);
|
scentToHome < SCENT_THRESHOLD ? 0. : scentToHome,
|
||||||
|
scentToFood < SCENT_THRESHOLD ? 0. : scentToFood
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp int;
|
precision highp int;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue