cvr-props/Assets/automata/lenia.shader
2023-06-19 23:17:48 +02:00

141 lines
3.3 KiB
Text

Shader "CrispyPin/Lenia"
{
Properties
{
_LastFrame ("Texture", 2D) = "white" {}
_Radius ("Radius", Range(1,100)) = 10
_GrowtCenter ("Growth fn center (mu)", Range(0, 1)) = 0.2
_GrowthWidth ("Growth fn width (sigma / std deviation)", Range(0, 1)) = 0.07
_KSharpness ("Kernel sharpness", Range(1, 100)) = 2
_KOffset ("Kernel offset", Range(0, 1)) = 0.2
_Speed ("Speed factor", Range(0.001, 1)) = 0.1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _LastFrame;
int _Radius;
float _GrowtCenter;
float _GrowthWidth;
float _KSharpness;
float _KOffset;
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 / _Radius;
// -- example from the lenia paper
// if (r >= 1) return 0;
// const float alpha = 4;
// return exp(alpha - alpha/(4.0 * r * (1.0 - r)));
// -- forgor
// return exp(-((r-0.5)*(r-0.5))*25);
// -- circle :)
// return r < 1 && r > 0.2;
// -- adjustable hard circle
// float a = _KOffset+_KSharpness;
// float b =_KOffset -_KSharpness;
// return r < a && r > b;
// -- normal
return exp(-((r - _KOffset)*(r - _KOffset)) * _KSharpness);
}
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;
float mu = _GrowtCenter;
float sigma = _GrowthWidth;
float u = neighbors;
// float u = neighbors - _GrowtCenter;
return exp(-((u-mu) * (u-mu)) / (2 * sigma * sigma)) * 2.0 - 1.0;
}
half value(float2 center, float x, float y) {
return tex2D(_LastFrame, center + float2(x, y)).r;
}
fixed4 frag (v2f i) : SV_Target
{
const float resolution = 256.0;
const float d = 1.0 / resolution;
float total_max = 0.0; // TODO replace with const?
float total = 0.0;
for (int x = -_Radius; x <= _Radius; x++) {
for (int y = -_Radius; y <= _Radius; y++) {
float dist = sqrt(x*x+y*y);
float kval = kernel(dist);
total_max += kval;
total += value(i.uv, x*d, y*d) * kval;
}
}
float old_state = value(i.uv, 0.0, 0.0) ;
float count = total / total_max;
float state = activation(count) * _Speed + old_state;
state = clamp(state, 0, 1);
// float2 p = (i.uv - 0.5) * resolution;
// float k = kernel(length(p)) * (max(abs(p.x), abs(p.y)) <= _Radius); // kernel visualisation: real size
// // float k = kernel(length(i.uv - 0.5) * _Radius * 2); // kernel visualisation: fill square
// float a = activation(i.uv.x);
// float4 col = float4(state, k, a, 1);
// float4 col = float4(state, state * 0.5, 0, 1);
float4 col = float4(state, i.uv.x * state, i.uv.y * state, 1);
// float4 col = state;
// col.a = 1;
return col;
}
ENDCG
}
}
}