Skip to content

Instantly share code, notes, and snippets.

@jesterswilde
Created October 7, 2025 20:22
Show Gist options
  • Select an option

  • Save jesterswilde/88741820a5938e243a444b516f4047c3 to your computer and use it in GitHub Desktop.

Select an option

Save jesterswilde/88741820a5938e243a444b516f4047c3 to your computer and use it in GitHub Desktop.
Raymarcher
#pragma kernel CSMain
Texture2D<float4> Source;
RWTexture2D<float4> Destination;
float4x4 _CameraToWorld;
float4x4 _CameraInverseProjection;
float3 _Light;
bool positionLight;
static const float maxDst = 80;
static const float epsilon = 0.001f;
static const float shadowBias = epsilon * 50;
struct Shape {
float3 position;
float3 size;
float3 colour;
int shapeType;
int operation;
float blendStrength;
int numChildren;
};
StructuredBuffer<Shape> shapes;
Buffer<float> data;
Buffer<int> instructions;
RWBuffer<float> debug;
int numShapes;
struct Ray {
float3 origin;
float3 direction;
};
Ray CreateRay(float3 origin, float3 direction) {
Ray ray;
ray.origin = origin;
ray.direction = direction;
return ray;
}
Ray CreateCameraRay(float2 uv) {
float3 origin = mul(_CameraToWorld, float4(0,0,0,1)).xyz;
float3 direction = mul(_CameraInverseProjection, float4(uv,0,1)).xyz;
direction = mul(_CameraToWorld, float4(direction,0)).xyz;
direction = normalize(direction);
return CreateRay(origin,direction);
}
float4x4 makeMatrix(inout int i){
float4x4 m;
m[0] = float4(data[i++],data[i++],data[i++],data[i++]);
m[1] = float4(data[i++],data[i++],data[i++],data[i++]);
m[2] = float4(data[i++],data[i++],data[i++],data[i++]);
m[3] = float4(0,0,0,1);
return m;
}
/*
Operations Reference:
Push - 0,
Push3 - 1,
Push_N - 2,
CopyPosition - 3,
Translate 4,
Matrix 5,
Union - 6,
Subtract - 7,
Intersect - 8,
Blend - 9,
Sphere - 10,
Box - 11,
Torus - 12,
InfiniteCylinder - 13
Mandlebulb - 14
*/
const int OpLength[9] = {-1,-1,-1,-2,1,0,-3,-5,3};
const int stackSize = 64;
int numInstructions;
float Evaluate(float3 pos, int id){
int stackIndex = -1;
int dataIndex = 0;
float stack[32];
bool hasMatrix = false;
float4x4 curMatrix;
for(int i = 0; i < 32; i++){
stack[i] = 0;
}
for(int instI = 0; instI < numInstructions; instI++){
int operation = instructions[instI];
//ToPush Val
if (operation == 0) { //Push
stack[++stackIndex] = data[dataIndex++];
}
else if(operation == 1){//Push3
stack[++stackIndex] = data[dataIndex++];
stack[++stackIndex] = data[dataIndex++];
stack[++stackIndex] = data[dataIndex++];
}
//N Val
else if (operation == 2) { //PushN
for(int i = 0; i < data[dataIndex++]; i++) {
stack[++stackIndex] = data[dataIndex++];
}
}
else if (operation == 3){//CopyPosition
stack[++stackIndex] = pos.x;
stack[++stackIndex] = pos.y;
stack[++stackIndex] = pos.z;
}
else if(operation == 4){//Translate
stack[stackIndex-3] -= stack[stackIndex];
stack[stackIndex-4] -= stack[stackIndex-1];
stack[stackIndex-5] -= stack[stackIndex-2];
stackIndex -= 3;
}
else if(operation == 5){//Matrix
hasMatrix = true;
curMatrix = makeMatrix(dataIndex);
}
//2 Val1 Val2
else if (operation == 6) { //Union
stack[stackIndex-1] = min(stack[stackIndex], stack[stackIndex-1]);
stackIndex -= 1;
}
//2 Val1 Val2
else if (operation == 7) { // Subtract
stack[stackIndex-1] = max(stack[stackIndex-1], -stack[stackIndex]);
stackIndex -= 1;
}
//2 Val1 Val2
else if (operation == 8) { //Intersect
stack[stackIndex-1] = max(stack[stackIndex-1], stack[stackIndex]);
stackIndex -= 1;
}
//3 Val1 Val2 BlendStrength
else if (operation == 9) { //Blend
float a = stack[stackIndex - 2];
float b = stack[stackIndex - 1];
float k = stack[stackIndex];
float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 );
stackIndex -= 2;
stack[stackIndex] = lerp( b, a, h ) - k*h*(1.0-h);
}
//4 PosX PosY PosZ Radius
else if (operation == 10) { //Sphere
float3 p = float3(stack[stackIndex-3], stack[stackIndex-2], stack[stackIndex-1]);
if(hasMatrix)
p = mul(curMatrix, float4(p,1)).xyz;
stack[stackIndex - 3] = length(p) - stack[stackIndex];
stackIndex -= 3;
//stack[stackIndex] = 0.5f;
}
//[1,2,3,2,2,3]
// ^
//6 PointX PointY PointZ BoundsX BoudnsY BoundsZ
else if (operation == 11) { //Box
float3 p = float3(stack[stackIndex-5], stack[stackIndex-4], stack[stackIndex-3]);
if(hasMatrix)
p = mul(curMatrix, float4(p,1)).xyz;
float3 b = float3(stack[stackIndex-2], stack[stackIndex-1], stack[stackIndex]);
float3 q = abs(p) - b;
stack[stackIndex - 5] = length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0);
stackIndex -= 5;
}
//5 PosX PosY PosZ InnerRadius OuterRadius
else if (operation == 12) { //Torus
float3 p = float3(stack[stackIndex-4], stack[stackIndex-3], stack[stackIndex-2]);
if(hasMatrix)
p = mul(curMatrix, float4(p,1)).xyz;
stack[stackIndex-4] = length(float2(length(p.xz) - stack[stackIndex-1], p.y)) - stack[stackIndex];
stackIndex -= 4;
}
else if (operation == 13) { //InfiniteCylinder
float3 p = float3(stack[stackIndex-5], stack[stackIndex-4], stack[stackIndex-3]);
if(hasMatrix)
p = mul(curMatrix, float4(p,1)).xyz;
float3 c = float3(stack[stackIndex-2], stack[stackIndex-1], stack[stackIndex]);
stack[stackIndex-5] = length(p.xz-c.xy)-c.z;
stackIndex -= 5;
} else if (operation == 14){ //SmoothSubtract
float a = stack[stackIndex - 2];
float b = stack[stackIndex - 1];
float k = stack[stackIndex];
float h = clamp( 0.5-0.5*(b+a)/k, 0.0, 1.0 );
stackIndex -= 2;
stack[stackIndex] = lerp(b, -a, h) + k*h*(1.0-h);
}else if (operation == 15){//SmoothIntersect
float a = stack[stackIndex - 2];
float b = stack[stackIndex - 1];
float k = stack[stackIndex];
float h = clamp( 0.5-0.5*(b-a)/k, 0.0, 1.0 );
stackIndex -= 2;
stack[stackIndex] = lerp(b, a, h) + k*h*(1.0-h);
}
else if(operation == 16){// NullMatrix
hasMatrix = false;
}
else if (operation == 17){ // Capsule
float3 p = float3(stack[stackIndex-4], stack[stackIndex-3], stack[stackIndex-2]);
if(hasMatrix)
p = mul(curMatrix, float4(p,1)).xyz;
p.y -= clamp(p.y, 0.0, stack[stackIndex-1]);
stack[stackIndex-4] = length(p) - stack[stackIndex];
stackIndex -= 4;
}
}
debug[id*10] = stackIndex;
for(int i = 0; i < 9; i++){
debug[id*10 + i + 1] = stack[i];
}
return stack[stackIndex];
}
float3 EstimateNormal(float3 p) {
float x = Evaluate(float3(p.x+epsilon,p.y,p.z),0) - Evaluate(float3(p.x-epsilon,p.y,p.z),0);
float y = Evaluate(float3(p.x,p.y+epsilon,p.z),0) - Evaluate(float3(p.x,p.y-epsilon,p.z),0);
float z = Evaluate(float3(p.x,p.y,p.z+epsilon),0) - Evaluate(float3(p.x,p.y,p.z-epsilon),0);
return normalize(float3(x,y,z));
}
[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
uint width,height;
Destination.GetDimensions(width, height);
int index = id.x + id.y * width;
Destination[id.xy] = Source[id.xy];
float2 uv = id.xy / float2(width,height) * 2 - 1;
float rayDst = 0;
Ray ray = CreateCameraRay(uv);
int maxMarchSteps = 64;
int marchSteps = 0;
while (rayDst < maxDst && marchSteps < maxMarchSteps) {
marchSteps ++;
float dst = Evaluate(ray.origin,index);
if (dst <= epsilon) {
float3 pointOnSurface = ray.origin + ray.direction * dst;
float3 normal = EstimateNormal(pointOnSurface - ray.direction * epsilon);
float3 lightDir = (positionLight)?normalize(_Light-ray.origin):-_Light;
float val = dot(normal,lightDir);
Destination[id.xy] = float4(val,val,val,1);
break;
}
ray.origin += ray.direction * dst;
rayDst += dst;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment