190 lines
6.6 KiB
Text
190 lines
6.6 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, 0.5)) = 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 / 8.0;
|
||
|
float k_sharpness=28;
|
||
|
float k_offset =0.435;
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
inline half value(float2 center, float x, float y) {
|
||
|
return tex2D(_LastFrame, center + float2(x, y)).r;
|
||
|
}
|
||
|
|
||
|
fixed4 frag (v2f i) : SV_Target
|
||
|
{
|
||
|
if(_ProjectionParams.z > 1) discard;
|
||
|
|
||
|
const float resolution = 512.0;
|
||
|
const float d = 1.0 / resolution;
|
||
|
// generated by the rust program
|
||
|
const int Radius = 16;
|
||
|
// /*
|
||
|
#define RADIUS 16
|
||
|
// kernel LUT size is 544 bytes
|
||
|
const half Kernel[17][16] = {
|
||
|
{0.0205, 0.0678, 0.1799, 0.3835, 0.6569, 0.9041, 0.9998, 0.8884, 0.6343, 0.3639, 0.1678, 0.0621, 0.0185, 0.0044, 0.0009, 0.0001, },
|
||
|
{0.0346, 0.0871, 0.2065, 0.4147, 0.6847, 0.9193, 0.9987, 0.8756, 0.6186, 0.3520, 0.1611, 0.0593, 0.0176, 0.0042, 0.0008, 0.0001, },
|
||
|
{0.0871, 0.1546, 0.2921, 0.5082, 0.7624, 0.9568, 0.9889, 0.8345, 0.5721, 0.3177, 0.1425, 0.0516, 0.0151, 0.0035, 0.0007, 0.0001, },
|
||
|
{0.2065, 0.2921, 0.4459, 0.6569, 0.8699, 0.9931, 0.9541, 0.7600, 0.4974, 0.2659, 0.1156, 0.0407, 0.0116, 0.0027, 0.0005, 0.0001, },
|
||
|
{0.4147, 0.5082, 0.6569, 0.8305, 0.9667, 0.9931, 0.8756, 0.6501, 0.4014, 0.2043, 0.0852, 0.0290, 0.0080, 0.0018, 0.0003, 0.0000, },
|
||
|
{0.6847, 0.7624, 0.8699, 0.9667, 0.9987, 0.9240, 0.7445, 0.5120, 0.2961, 0.1425, 0.0567, 0.0185, 0.0049, 0.0011, 0.0002, 0.0000, },
|
||
|
{0.9193, 0.9568, 0.9931, 0.9931, 0.9240, 0.7753, 0.5721, 0.3639, 0.1966, 0.0891, 0.0336, 0.0105, 0.0027, 0.0006, 0.0001, 0.0000, },
|
||
|
{0.9987, 0.9889, 0.9541, 0.8756, 0.7445, 0.5721, 0.3887, 0.2292, 0.1156, 0.0493, 0.0176, 0.0052, 0.0013, 0.0003, 0.0000, 0.0000, },
|
||
|
{0.8756, 0.8345, 0.7600, 0.6501, 0.5120, 0.3639, 0.2292, 0.1258, 0.0593, 0.0238, 0.0080, 0.0023, 0.0005, 0.0001, 0.0000, 0.0000, },
|
||
|
{0.6186, 0.5721, 0.4974, 0.4014, 0.2961, 0.1966, 0.1156, 0.0593, 0.0263, 0.0099, 0.0032, 0.0009, 0.0002, 0.0000, 0.0000, 0.0000, },
|
||
|
{0.3520, 0.3177, 0.2659, 0.2043, 0.1425, 0.0891, 0.0493, 0.0238, 0.0099, 0.0035, 0.0011, 0.0003, 0.0001, 0.0000, 0.0000, 0.0000, },
|
||
|
{0.1611, 0.1425, 0.1156, 0.0852, 0.0567, 0.0336, 0.0176, 0.0080, 0.0032, 0.0011, 0.0003, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000, },
|
||
|
{0.0593, 0.0516, 0.0407, 0.0290, 0.0185, 0.0105, 0.0052, 0.0023, 0.0009, 0.0003, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, },
|
||
|
{0.0176, 0.0151, 0.0116, 0.0080, 0.0049, 0.0027, 0.0013, 0.0005, 0.0002, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, },
|
||
|
{0.0042, 0.0035, 0.0027, 0.0018, 0.0011, 0.0006, 0.0003, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, },
|
||
|
{0.0008, 0.0007, 0.0005, 0.0003, 0.0002, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, },
|
||
|
{0.0001, 0.0001, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, },
|
||
|
};
|
||
|
const float total_max = 234.37535;
|
||
|
|
||
|
float total = 0.0;
|
||
|
[unroll(RADIUS)]
|
||
|
for (int y = 0; y < Radius; y++) {
|
||
|
[unroll(RADIUS)]
|
||
|
for (int x = 1; x <= Radius; x++) {
|
||
|
const float xx = (float)x * d;
|
||
|
const float yy = (float)y * d;
|
||
|
total += value(i.uv, xx, yy) * Kernel[y][x-1];
|
||
|
total += value(i.uv, -yy, xx) * Kernel[y][x-1];
|
||
|
total += value(i.uv, -xx, -yy) * Kernel[y][x-1];
|
||
|
total += value(i.uv, yy, -xx) * Kernel[y][x-1];
|
||
|
}
|
||
|
}
|
||
|
// */
|
||
|
/*
|
||
|
float total_max = 0;
|
||
|
float total = 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);
|
||
|
|
||
|
// // kernel visualization: lookup table
|
||
|
// float k = 0;
|
||
|
// {
|
||
|
// float2 p = (i.uv - 0.5 ) * resolution;
|
||
|
// 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];
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// 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, k, k2, 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
|
||
|
}
|
||
|
}
|
||
|
}
|