2023-06-23 21:27:34 +02:00
|
|
|
Shader "CrispyPin/Lenia"
|
|
|
|
{
|
|
|
|
Properties
|
|
|
|
{
|
|
|
|
_LastFrame ("Texture", 2D) = "white" {}
|
|
|
|
_GrowtCenter ("Growth fn center (mu)", Range(0, 1)) = 0.2
|
|
|
|
_GrowthWidth ("Growth fn width (sigma / std deviation)", Range(0, 1)) = 0.07
|
2023-06-25 18:53:48 +02:00
|
|
|
_Speed ("Speed factor", Range(0.3, 5)) = 1
|
2023-06-23 21:27:34 +02:00
|
|
|
}
|
|
|
|
SubShader
|
|
|
|
{
|
|
|
|
Tags { "RenderType"="Opaque" }
|
|
|
|
LOD 100
|
|
|
|
|
|
|
|
Pass
|
|
|
|
{
|
|
|
|
CGPROGRAM
|
|
|
|
#pragma vertex vert
|
|
|
|
#pragma fragment frag
|
|
|
|
|
|
|
|
#include "UnityCG.cginc"
|
|
|
|
|
2023-07-22 20:24:12 +02:00
|
|
|
#define WIDTH 512
|
|
|
|
|
2023-06-23 21:27:34 +02:00
|
|
|
struct appdata
|
|
|
|
{
|
|
|
|
float4 vertex : POSITION;
|
|
|
|
float2 uv : TEXCOORD0;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct v2f
|
|
|
|
{
|
|
|
|
float2 uv : TEXCOORD0;
|
|
|
|
|
|
|
|
float4 vertex : SV_POSITION;
|
|
|
|
};
|
|
|
|
|
2023-07-22 20:24:12 +02:00
|
|
|
texture2D _LastFrame;
|
2023-06-23 21:27:34 +02:00
|
|
|
float _GrowtCenter;
|
|
|
|
float _GrowthWidth;
|
|
|
|
float _Speed;
|
|
|
|
|
|
|
|
v2f vert (appdata v)
|
|
|
|
{
|
|
|
|
v2f o;
|
|
|
|
o.vertex = UnityObjectToClipPos(v.vertex);
|
|
|
|
o.uv = v.uv;
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
|
|
|
float kernel(float p) {
|
|
|
|
float r = p / 8.0;
|
2023-06-24 16:30:53 +02:00
|
|
|
float k_sharpness = 28;
|
|
|
|
float k_offset = 0.435;
|
2023-06-23 21:27:34 +02:00
|
|
|
return exp(-((r - k_offset) * (r - k_offset)) * k_sharpness);
|
|
|
|
// float r = p / _Radius;
|
|
|
|
// // -- normal
|
|
|
|
// return exp(-((r - _KOffset)*(r - _KOffset)) * _KSharpness);
|
|
|
|
|
|
|
|
// -- example from the lenia paper
|
|
|
|
// if (r >= 1) return 0;
|
|
|
|
// const float alpha = 4;
|
|
|
|
// return exp(alpha - alpha/(4.0 * r * (1.0 - r)));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline float activation(float neighbors) {
|
|
|
|
// return old_state * (neighbors > 2 && neighbors < 5) +
|
|
|
|
// ((1 - old_state) * neighbors == 3);
|
|
|
|
|
|
|
|
// const float sharpness = 1000.0;
|
|
|
|
// float x = neighbors - _GrowtCenter;
|
|
|
|
// return exp(-(x*x) * _GrowthWidth) * 2.0 - 1.0;
|
|
|
|
const float mu = _GrowtCenter;
|
|
|
|
const float sigma = _GrowthWidth;
|
|
|
|
const float u = neighbors;
|
|
|
|
return exp(-((u-mu) * (u-mu)) / (2 * sigma * sigma)) * 2.0 - 1.0;
|
|
|
|
}
|
|
|
|
|
2023-07-22 20:24:12 +02:00
|
|
|
inline float value(uint2 p, int dx, int dy) {
|
|
|
|
const uint x = (p.x + dx) % WIDTH;
|
|
|
|
const uint y = (p.y + dy) % WIDTH;
|
|
|
|
return _LastFrame[uint2(x, y)].r;
|
2023-06-23 21:27:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fixed4 frag (v2f i) : SV_Target
|
|
|
|
{
|
|
|
|
if(_ProjectionParams.z > 1) discard;
|
|
|
|
|
2023-06-25 18:33:37 +02:00
|
|
|
// Defines RADIUS Kernel total_max
|
2023-06-24 16:30:53 +02:00
|
|
|
#include "lenia_generated_kernel.cginc"
|
2023-07-22 20:24:12 +02:00
|
|
|
const uint2 p = i.uv * WIDTH;
|
2023-06-23 21:27:34 +02:00
|
|
|
|
|
|
|
float total = 0.0;
|
|
|
|
[unroll(RADIUS)]
|
2023-06-25 18:33:37 +02:00
|
|
|
for (int y = 0; y < RADIUS; y++) {
|
2023-06-23 21:27:34 +02:00
|
|
|
[unroll(RADIUS)]
|
2023-06-25 18:33:37 +02:00
|
|
|
for (int x = 1; x <= RADIUS; x++) {
|
2023-07-22 20:24:12 +02:00
|
|
|
total += value(p, x, y) * Kernel[y][x - 1];
|
|
|
|
total += value(p, -y, x) * Kernel[y][x - 1];
|
|
|
|
total += value(p, -x, -y) * Kernel[y][x - 1];
|
|
|
|
total += value(p, y, -x) * Kernel[y][x - 1];
|
2023-06-23 21:27:34 +02:00
|
|
|
}
|
|
|
|
}
|
2023-07-22 20:24:12 +02:00
|
|
|
float old_state = value(p, 0, 0) ;
|
2023-06-23 21:27:34 +02:00
|
|
|
float count = total / total_max;
|
|
|
|
|
2023-06-25 18:53:48 +02:00
|
|
|
const float step = _Speed * unity_DeltaTime.x;
|
2023-06-25 18:33:37 +02:00
|
|
|
float state = activation(count) * step + old_state;
|
2023-06-23 21:27:34 +02:00
|
|
|
state = clamp(state, 0, 1);
|
|
|
|
|
2023-07-22 20:24:12 +02:00
|
|
|
// kernel visualization: lookup table (VERY SLOW)
|
2023-06-23 21:27:34 +02:00
|
|
|
// float k = 0;
|
|
|
|
// {
|
2023-07-22 20:24:12 +02:00
|
|
|
// float2 p = (i.uv - 0.5 ) * WIDTH;
|
2023-06-23 21:27:34 +02:00
|
|
|
// p = floor(p);
|
|
|
|
// if (p.x > 0 && p.y >= 0) {
|
|
|
|
// k = Kernel[p.y][p.x-1];
|
|
|
|
// } else if (p.x <= 0 && p.y > 0) {
|
|
|
|
// k = Kernel[-p.x][p.y-1];
|
|
|
|
// } else if (p.x < 0 && p.y <= 0) {
|
|
|
|
// k = Kernel[-p.y][-p.x-1];
|
|
|
|
// } else if (p.x >= 0 && p.y < 0) {
|
|
|
|
// k = Kernel[p.x][-p.y-1];
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
2023-06-24 16:30:53 +02:00
|
|
|
// kernel visualisation: real size
|
2023-07-22 20:24:12 +02:00
|
|
|
// float2 p = (i.uv - 0.5) * WIDTH;
|
2023-06-25 18:33:37 +02:00
|
|
|
// float k = kernel(length(p)) * (max(abs(p.x), abs(p.y)) <= RADIUS);
|
2023-06-24 16:30:53 +02:00
|
|
|
|
|
|
|
// kernel visualisation: fill square
|
2023-06-25 18:33:37 +02:00
|
|
|
// float k = kernel(length(i.uv - 0.5) * RADIUS * 2);
|
2023-06-23 21:27:34 +02:00
|
|
|
|
|
|
|
// float a = activation(i.uv.x);
|
|
|
|
// float4 col = float4(state, k, a, 1);
|
|
|
|
|
2023-06-24 16:30:53 +02:00
|
|
|
float4 col = float4(state, i.uv.x * state, i.uv.y * state, 1);
|
2023-06-23 21:27:34 +02:00
|
|
|
return col;
|
|
|
|
}
|
|
|
|
ENDCG
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|