ants/src/shaders/antsCompute.frag

217 lines
No EOL
6.4 KiB
GLSL

precision highp float;
precision highp int;
#define PI 3.1415926535897932384626433832795
in vec2 vUv;
layout(location = 0) out vec4 FragColor;
layout(location = 1) out vec4 FragColorExt;
uniform float uTime;
uniform sampler2D tLastState;
uniform sampler2D tLastExtState;
uniform sampler2D tWorld;
const float sampleDistance = 20.;
const float cellSize = 1. / WORLD_SIZE;
float rand(vec2 co) {
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 + cellSize * 0.5;
}
bool tryGetFood(vec2 pos) {
float value = texture(tWorld, roundUvToCellCenter(pos)).x;
return (int(value) & 1) == 1;
}
bool tryDropFood(vec2 pos) {
float value = texture(tWorld, roundUvToCellCenter(pos)).x;
return ((int(value) & 2) >> 1) == 1;
}
bool isObstacle(vec2 pos) {
float value = texture(tWorld, roundUvToCellCenter(pos)).x;
return ((int(value) & 4) >> 2) == 1;
}
float smell(vec2 pos, float isCarrying) {
vec2 value = texture(tWorld, pos).yz;
if (isCarrying > 0.5) {
return value.y;
}
return value.x;
}
vec2 applyOffsetToPos(vec2 pos, vec2 offsetDirectaion) {
vec2 newPos = clamp(pos + offsetDirectaion * cellSize, 0., 1.);
if (!isObstacle(pos) && isObstacle(newPos)) {
return pos;
}
return newPos;
}
float getMaxScentStorage(vec2 antDataUv) {
float factor = 0.8 + rand(antDataUv * 100.) * 0.2;
return SCENT_MAX_STORAGE * factor;
}
void main() {
vec4 lastState = texture(tLastState, vUv);
vec4 lastExtState = texture(tLastExtState, vUv);
float noise = rand(vUv * 1000. + fract(uTime / 1000.));
vec2 pos = lastState.xy;
float angle = lastState.z;
float isCarrying = float(int(lastState.w) & 1);
float storage = float(int(lastState.w) >> 1);
bool wasObstacle = isObstacle(pos);
float personality = lastExtState.r;
float cargoQuality = lastExtState.g;
float pathIntDx = lastExtState.b;
float pathIntDy = lastExtState.a;
bool movementProcessed = false;
if (pos == vec2(0)) { // init new ant
pos = vec2(0.5);
angle = rand(vUv * 10000.) * 2. * PI;
isCarrying = 0.;
storage = 0.;
personality = rand(vUv * 42069.); // 0.0 = pure follower, 1.0 = pure explorer
cargoQuality = 0.;
pathIntDx = 0.;
pathIntDy = 0.;
}
if (isCarrying == 0.) {
if (noise < 0.33) {
vec2 offset = vec2(cos(angle), sin(angle)) * sampleDistance;
vec2 point = applyOffsetToPos(pos, offset);
if (tryGetFood(point)) {
movementProcessed = true;
}
} else if (noise < 0.66) {
float newAngle = angle - ANT_ROTATION_ANGLE;
vec2 offset = vec2(cos(newAngle), sin(newAngle)) * sampleDistance;
vec2 point = applyOffsetToPos(pos, offset);
if (tryGetFood(point)) {
movementProcessed = true;
angle = newAngle;
}
} else {
float newAngle = angle + ANT_ROTATION_ANGLE;
vec2 offset = vec2(cos(newAngle), sin(newAngle)) * sampleDistance;
vec2 point = applyOffsetToPos(pos, offset);
if (tryGetFood(point)) {
movementProcessed = true;
angle = newAngle;
}
}
} else if (isCarrying == 1.) {
if (noise < 0.33) {
vec2 offset = vec2(cos(angle), sin(angle)) * sampleDistance;
vec2 point = applyOffsetToPos(pos, offset);
if (tryDropFood(point)) {
movementProcessed = true;
}
} else if (noise < 0.66) {
float newAngle = angle - ANT_ROTATION_ANGLE;
vec2 offset = vec2(cos(newAngle), sin(newAngle)) * sampleDistance;
vec2 point = applyOffsetToPos(pos, offset);
if (tryDropFood(point)) {
movementProcessed = true;
angle = newAngle;
}
} else {
float newAngle = angle + ANT_ROTATION_ANGLE;
vec2 offset = vec2(cos(newAngle), sin(newAngle)) * sampleDistance;
vec2 point = applyOffsetToPos(pos, offset);
if (tryDropFood(point)) {
movementProcessed = true;
angle = newAngle;
}
}
}
if (!movementProcessed) {
float noise2 = rand(vUv * 1000. + fract(uTime / 1000.) + 0.2);
float sampleAhead = smell(applyOffsetToPos(pos, vec2(cos(angle), sin(angle)) * 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 + ANT_ROTATION_ANGLE), sin(angle + ANT_ROTATION_ANGLE)) * sampleDistance), isCarrying);
if (sampleAhead > sampleLeft && sampleAhead > sampleRight) {
// don't change direction
} else if (sampleLeft > sampleAhead && sampleLeft > sampleRight) {
angle -= ANT_ROTATION_ANGLE; // steer left
} else if (sampleRight > sampleAhead && sampleRight > sampleLeft) {
angle += ANT_ROTATION_ANGLE; // steer right
} else if (noise < 0.33) {
angle += ANT_ROTATION_ANGLE; // no smell detected, do random movement
} else if (noise < 0.66) {
angle -= ANT_ROTATION_ANGLE;
}
if (noise2 > 0.5) {
angle += ANT_ROTATION_ANGLE * 2.;
} else {
angle -= ANT_ROTATION_ANGLE * 2.;
}
}
vec2 offset = vec2(cos(angle), sin(angle));
pos = applyOffsetToPos(pos, offset);
if (fract(pos.x) == 0. || fract(pos.y) == 0. || (!wasObstacle && isObstacle(pos + offset * cellSize))) {
angle += PI * (noise - 0.5);
}
if (tryGetFood(pos) && isCarrying == 0.) {
isCarrying = 1.;
angle += PI;
storage = getMaxScentStorage(vUv);
}
if (tryDropFood(pos)) {
storage = getMaxScentStorage(vUv);
if (isCarrying == 1.) {
isCarrying = 0.;
angle += PI;
}
}
FragColor = vec4(
pos.x,
pos.y,
angle,
float((uint(max(storage - SCENT_PER_MARKER, 0.)) << 1) + uint(isCarrying))
);
FragColorExt = vec4(personality, cargoQuality, pathIntDx, pathIntDy);
}