Created
November 30, 2018 03:40
-
-
Save KelsonBall/a43125f88f1d0603c7d487e18003bdf8 to your computer and use it in GitHub Desktop.
Generic Raymarching Fragment Shader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
OpenGL/Glsl result on left, simulated C# result on the right | |
https://media.discordapp.net/attachments/439873163497832449/517899042055782410/unknown.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 430 | |
const uint MAX_ENTITY_COUNT = 30; | |
uniform vec4 BackgroundColor; | |
uniform vec2 Origin; | |
uniform vec2 Size; | |
uniform vec2 Resolution; | |
uniform double DrawDistance; | |
uniform double MinStepLength; | |
uniform double NormalEpsilon; | |
uniform double FieldOfView; | |
uniform double HalfTanFoV; | |
uniform int MarchLimit; | |
struct Node | |
{ | |
uint EntityId; | |
uint Operation; | |
uint Left; | |
uint Right; | |
uint Parent; | |
uint Parameter; | |
}; | |
uniform Node NodeEntities[MAX_ENTITY_COUNT]; | |
uniform mat4 MatrixEntities[MAX_ENTITY_COUNT]; | |
uniform vec3 Vector3Entities[MAX_ENTITY_COUNT]; | |
uniform double DoubleEntities[MAX_ENTITY_COUNT]; | |
const uint DONE = 256; | |
const uint OpType3d_CsgUnion = 1; | |
const uint OpType3d_CsgIntersect = 2; | |
const uint OpType3d_CsgSubtract = 3; | |
const uint OpType3d_ShapeSphere = 11; | |
const uint OpType3d_ShapeBox = 12; | |
const uint OpType3d_SpatialTranslation = 21; | |
const uint OpType3d_SpatialTransform = 22; | |
const uint OpType3d_Color = 31; | |
struct MarchResult | |
{ | |
double Value; | |
vec3 Color; | |
}; | |
struct MarchStep | |
{ | |
uint Index; | |
uint Next; | |
uint State; | |
double Value; | |
double LeftValue; | |
double RightValue; | |
vec3 Color; | |
vec3 LeftColor; | |
vec3 RightColor; | |
vec3 Position; | |
vec3 LeftPosition; | |
vec3 RightPosition; | |
}; | |
MarchStep HandleColorOp(MarchStep step) | |
{ | |
Node node = NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
step.Color = Vector3Entities[node.Parameter]; | |
step.LeftPosition = step.Position; | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.Value = step.LeftValue; | |
step.State = DONE; | |
step.Next = node.Parent; | |
} | |
return step; | |
} | |
MarchStep HandleUnionOp(MarchStep step) | |
{ | |
Node node = NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
step.LeftPosition = step.Position; | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.State = 2; | |
step.RightPosition = step.Position; | |
step.Next = node.Right; | |
} | |
else if (step.State == 2) | |
{ | |
if (step.LeftValue < step.RightValue) | |
{ | |
step.Value = step.LeftValue; | |
step.Color = step.LeftColor; | |
} | |
else | |
{ | |
step.Value = step.RightValue; | |
step.Color = step.RightColor; | |
} | |
step.State = DONE; | |
step.Next = node.Parent; | |
} | |
return step; | |
} | |
MarchStep HandleIntersectionOp(MarchStep step) | |
{ | |
Node node = NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
step.LeftPosition = step.Position; | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.State = 2; | |
step.RightPosition = step.Position; | |
step.Next = node.Right; | |
} | |
else if (step.State == 2) | |
{ | |
if (step.LeftValue > step.RightValue) | |
{ | |
step.Value = step.LeftValue; | |
step.Color = step.LeftColor; | |
} | |
else | |
{ | |
step.Value = step.RightValue; | |
step.Color = step.RightColor; | |
} | |
step.State = DONE; | |
step.Next = node.Parent; | |
} | |
return step; | |
} | |
MarchStep HandleSubtractionOp(MarchStep step) | |
{ | |
Node node = NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
step.LeftPosition = step.Position; | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.State = 2; | |
step.RightPosition = step.Position; | |
step.Next = node.Right; | |
} | |
else if (step.State == 2) | |
{ | |
if (step.LeftValue > -step.RightValue) | |
{ | |
step.Value = step.LeftValue; | |
step.Color = step.LeftColor; | |
} | |
else | |
{ | |
step.Value = -step.RightValue; | |
step.Color = step.RightColor; | |
} | |
step.State = DONE; | |
step.Next = node.Parent; | |
} | |
return step; | |
} | |
MarchStep HandleTransformOp(MarchStep step) | |
{ | |
Node node = NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
step.LeftPosition = (vec4(step.Position, 0) * MatrixEntities[node.Parameter]).xyz; | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.State = DONE; | |
step.Value = step.LeftValue; | |
step.Color = step.LeftColor; | |
step.Next = node.Parent; | |
} | |
return step; | |
} | |
MarchStep HandleTranslationOp(MarchStep step) | |
{ | |
Node node = NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
step.LeftPosition = step.Position + Vector3Entities[node.Parameter]; | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.State = DONE; | |
step.Value = step.LeftValue; | |
step.Color = step.LeftColor; | |
step.Next = node.Parent; | |
} | |
return step; | |
} | |
MarchStep HandleSphereOp(MarchStep step) | |
{ | |
Node node = NodeEntities[step.Index]; | |
step.Value = length(step.Position) - DoubleEntities[node.Parameter]; | |
step.Next = node.Parent; | |
return step; | |
} | |
MarchStep HandleBoxOp(MarchStep step) | |
{ | |
Node node = NodeEntities[step.Index]; | |
double size = DoubleEntities[node.Parameter]; | |
vec3 d = vec3(abs(step.Position.x), abs(step.Position.y), abs(step.Position.z) ) - vec3(size, size, size); | |
double inside = min(max(d.x, max(d.y, d.z)), 0); | |
double outside = length(vec3(max(d.x, 0), max(d.y, 0), max(d.z, 0))); | |
step.Value = inside + outside; | |
step.Next = node.Parent; | |
return step; | |
} | |
MarchResult Sdf_March(vec3 initial) | |
{ | |
MarchStep steps[13]; | |
steps[0].Position = initial; | |
uint index = 0; | |
bool running = true; | |
while (running) | |
{ | |
Node node = NodeEntities[index]; | |
if (node.Left > 0) | |
{ | |
steps[index].LeftValue = steps[node.Left].Value; | |
steps[index].LeftColor = steps[node.Left].Color; | |
} | |
if (node.Right > 0) | |
{ | |
steps[index].RightValue = steps[node.Right].Value; | |
steps[index].RightColor = steps[node.Right].Color; | |
} | |
steps[index].Index = index; | |
MarchStep stepResult; | |
switch (NodeEntities[index].Operation) | |
{ | |
case OpType3d_Color: | |
stepResult = HandleColorOp(steps[index]); | |
break; | |
case OpType3d_CsgUnion: | |
stepResult = HandleUnionOp(steps[index]); | |
break; | |
case OpType3d_CsgIntersect: | |
stepResult = HandleIntersectionOp(steps[index]); | |
break; | |
case OpType3d_CsgSubtract: | |
stepResult = HandleSubtractionOp(steps[index]); | |
break; | |
case OpType3d_SpatialTransform: | |
stepResult = HandleTransformOp(steps[index]); | |
break; | |
case OpType3d_SpatialTranslation: | |
stepResult = HandleTranslationOp(steps[index]); | |
break; | |
case OpType3d_ShapeSphere: | |
stepResult = HandleSphereOp(steps[index]); | |
break; | |
case OpType3d_ShapeBox: | |
stepResult = HandleBoxOp(steps[index]); | |
break; | |
} | |
steps[index] = stepResult; | |
if (node.Left > 0) | |
steps[node.Left].Position = steps[index].LeftPosition; | |
if (node.Right > 0) | |
steps[node.Right].Position = steps[index].RightPosition; | |
index = steps[index].Next; | |
if (index == 0 && steps[index].State == DONE) | |
running = false; | |
} | |
MarchResult result; | |
result.Value = steps[0].Value; | |
result.Color = steps[0].Color; | |
return result; | |
} | |
vec3 getUvRay(vec2 uv, vec2 size) | |
{ | |
vec2 xy = uv - (size / 2); | |
double z = size.y / HalfTanFoV; | |
return normalize(vec3(xy, -z)); | |
} | |
vec3 estimateNormal(vec3 p) | |
{ | |
return normalize( | |
vec3( | |
Sdf_March(vec3(p.x + NormalEpsilon, p.y, p.z)).Value - Sdf_March(vec3(p.x - NormalEpsilon, p.y, p.z)).Value, | |
Sdf_March(vec3(p.x, p.y + NormalEpsilon, p.z)).Value - Sdf_March(vec3(p.x, p.y - NormalEpsilon, p.z)).Value, | |
Sdf_March(vec3(p.x, p.y, p.z + NormalEpsilon)).Value - Sdf_March(vec3(p.x, p.y, p.z - NormalEpsilon)).Value | |
) | |
); | |
} | |
out vec4 outColor; | |
void main() | |
{ | |
vec2 xy = gl_FragCoord.xy - Origin; | |
vec3 ray = getUvRay(xy, Size); | |
vec3 position = vec3(0, 0, 0); | |
MarchResult last_result = Sdf_March(position); | |
double traveled = 0; | |
for (int marches = 0; marches < MarchLimit; marches++) | |
{ | |
if (last_result.Value < MinStepLength) | |
{ | |
// no shading | |
outColor = vec4(last_result.Color, 1.0); | |
// phong shading | |
// vec3 normal = estimateNormal(position); | |
// double value = length(normal - vec3(0, 1, 0)) - 0.8; | |
// | |
// outColor = vec4(last_result.Color * min(max(value, 0.0), 1.0), 1.0); | |
// solid color for debug | |
// outColor = vec4(0.1, 0.3, 0.7, 1.0); | |
return; | |
} | |
if (traveled > DrawDistance) | |
break; | |
float distance = float(last_result.Value); | |
position = position + (ray * distance); | |
traveled += distance; | |
last_result = Sdf_March(position); | |
} | |
outColor = BackgroundColor; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class RayMarchStateMachineShader : IShader | |
{ | |
protected readonly ref struct MarchResult | |
{ | |
public readonly double Value; | |
public readonly vec3 Color; | |
public MarchResult(double value, vec3 color) | |
=> (Value, Color) = (value, color); | |
} | |
protected readonly RaymarchScene scene; | |
public vec3 BackgroundColor { get; set; } | |
public RayMarchStateMachineShader(RaymarchScene scene, vec3 backgroundColor = default) | |
{ | |
this.scene = scene; | |
BackgroundColor = backgroundColor; | |
} | |
protected struct MarchStep | |
{ | |
public uint Index; | |
public uint Next; | |
public uint State; | |
public double Value; | |
public double LeftValue; | |
public double RightValue; | |
public vec3 Color; | |
public vec3 LeftColor; | |
public vec3 RightColor; | |
public vec3 Position; | |
public vec3 LeftPosition; | |
public vec3 RightPosition; | |
} | |
const byte DONE = 0xFF; | |
protected void HandleColorOp(ref MarchStep step) | |
{ | |
var node = scene.NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
step.Color = scene.Vector3Entities[node.Parameter]; | |
step.LeftPosition = step.Position; | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.Value = step.LeftValue; | |
step.State = DONE; | |
step.Next = node.Parent; | |
} | |
} | |
protected void HandleUnionOp(ref MarchStep step) | |
{ | |
var node = scene.NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
step.LeftPosition = step.Position; | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.State = 2; | |
step.RightPosition = step.Position; | |
step.Next = node.Right; | |
} | |
else if (step.State == 2) | |
{ | |
if (step.LeftValue < step.RightValue) | |
{ | |
step.Value = step.LeftValue; | |
step.Color = step.LeftColor; | |
} | |
else | |
{ | |
step.Value = step.RightValue; | |
step.Color = step.RightColor; | |
} | |
step.State = DONE; | |
step.Next = node.Parent; | |
} | |
} | |
protected void HandleIntersectionOp(ref MarchStep step) | |
{ | |
var node = scene.NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
step.LeftPosition = step.Position; | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.State = 2; | |
step.RightPosition = step.Position; | |
step.Next = node.Right; | |
} | |
else if (step.State == 2) | |
{ | |
if (step.LeftValue > step.RightValue) | |
{ | |
step.Value = step.LeftValue; | |
step.Color = step.LeftColor; | |
} | |
else | |
{ | |
step.Value = step.RightValue; | |
step.Color = step.RightColor; | |
} | |
step.State = DONE; | |
step.Next = node.Parent; | |
} | |
} | |
protected void HandleSubtractionOp(ref MarchStep step) | |
{ | |
var node = scene.NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
step.LeftPosition = step.Position; | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.State = 2; | |
step.RightPosition = step.Position; | |
step.Next = node.Right; | |
} | |
else if (step.State == 2) | |
{ | |
if (step.LeftValue > -step.RightValue) | |
{ | |
step.Value = step.LeftValue; | |
step.Color = step.LeftColor; | |
} | |
else | |
{ | |
step.Value = -step.RightValue; | |
step.Color = step.RightColor; | |
} | |
step.State = DONE; | |
step.Next = node.Parent; | |
} | |
} | |
protected void HandleTransformOp(ref MarchStep step) | |
{ | |
var node = scene.NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
var mat = scene.MatrixEntities[node.Parameter]; | |
var pos = step.Position; | |
step.LeftPosition = mat.AppliedTo(pos); | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.State = DONE; | |
step.Value = step.LeftValue; | |
step.Color = step.LeftColor; | |
step.Next = node.Parent; | |
} | |
} | |
protected void HandleTranslationOp(ref MarchStep step) | |
{ | |
var node = scene.NodeEntities[step.Index]; | |
if (step.State == 0) | |
{ | |
step.State = 1; | |
var vec = scene.Vector3Entities[node.Parameter]; | |
var pos = step.Position; | |
step.LeftPosition = pos + vec; | |
step.Next = node.Left; | |
} | |
else if (step.State == 1) | |
{ | |
step.State = DONE; | |
step.Value = step.LeftValue; | |
step.Color = step.LeftColor; | |
step.Next = node.Parent; | |
} | |
} | |
protected void HandleSphereOp(ref MarchStep step) | |
{ | |
var node = scene.NodeEntities[step.Index]; | |
step.Value = step.Position.Magnitude() - scene.DoubleEntities[node.Parameter]; | |
step.Next = node.Parent; | |
} | |
protected void HandleBoxOp(ref MarchStep step) | |
{ | |
var node = scene.NodeEntities[step.Index]; | |
var position = step.Position; | |
var size = scene.DoubleEntities[node.Parameter]; | |
var d = new vec3(Math.Abs(position.X), Math.Abs(position.Y), Math.Abs(position.Z)) - (size, size, size); | |
var inside = Math.Min(Math.Max(d.X, Math.Max(d.Y, d.Z)), 0); | |
var outside = new vec3(Math.Max(d.X, 0), Math.Max(d.Y, 0), Math.Max(d.Z, 0)).Magnitude(); | |
step.Value = inside + outside; | |
step.Next = node.Parent; | |
} | |
protected MarchResult Sdf_March(vec3 initial) | |
{ | |
var steps = new MarchStep[scene.NodeEntities.Length]; | |
steps[0].Position = initial; | |
uint index = 0; | |
while (true) | |
{ | |
var node = scene.NodeEntities[index]; | |
if (node.Left > 0) | |
{ | |
steps[index].LeftValue = steps[node.Left].Value; | |
steps[index].LeftColor = steps[node.Left].Color; | |
} | |
if (node.Right > 0) | |
{ | |
steps[index].RightValue = steps[node.Right].Value; | |
steps[index].RightColor = steps[node.Right].Color; | |
} | |
steps[index].Index = index; | |
switch (scene.NodeEntities[index].Operation) | |
{ | |
case OpType3d.Color: | |
HandleColorOp(ref steps[index]); | |
break; | |
case OpType3d.CsgUnion: | |
HandleUnionOp(ref steps[index]); | |
break; | |
case OpType3d.CsgIntersect: | |
HandleIntersectionOp(ref steps[index]); | |
break; | |
case OpType3d.CsgSubtract: | |
HandleSubtractionOp(ref steps[index]); | |
break; | |
case OpType3d.SpatialTransform: | |
HandleTransformOp(ref steps[index]); | |
break; | |
case OpType3d.SpatialTranslation: | |
HandleTranslationOp(ref steps[index]); | |
break; | |
case OpType3d.ShapeSphere: | |
HandleSphereOp(ref steps[index]); | |
break; | |
case OpType3d.ShapeBox: | |
HandleBoxOp(ref steps[index]); | |
break; | |
default: | |
throw new InvalidOperationException(); | |
} | |
if (node.Left > 0) | |
steps[node.Left].Position = steps[index].LeftPosition; | |
if (node.Right > 0) | |
steps[node.Right].Position = steps[index].RightPosition; | |
index = steps[index].Next; | |
if (index == 0 && steps[index].State == DONE) | |
break; | |
} | |
return new MarchResult(steps[0].Value, steps[0].Color); | |
} | |
protected const double FieldOfView = Math.PI / 3.5; | |
protected const int MarchLimit = 250; | |
protected const double DrawDistance = 30; | |
protected const double MinStepLength = 0.025; | |
protected const double NormalEpsilon = 0.1; | |
protected vec3 getUvRay(double fov, vec2 uv, vec2 size) | |
{ | |
vec2 xy = uv - (size / 2); | |
var z = size.y / (Math.Tan(fov) / 2); | |
return new vec3(xy, -z).Unit(); | |
} | |
protected vec3 estimateNormal(vec3 p) | |
{ | |
var dir = new vec3( | |
Sdf_March(new vec3(p.X + NormalEpsilon, p.Y, p.Z)).Value - Sdf_March(new vec3(p.X - NormalEpsilon, p.Y, p.Z)).Value, | |
Sdf_March(new vec3(p.X, p.Y + NormalEpsilon, p.Z)).Value - Sdf_March(new vec3(p.X, p.Y - NormalEpsilon, p.Z)).Value, | |
Sdf_March(new vec3(p.X, p.Y, p.Z + NormalEpsilon)).Value - Sdf_March(new vec3(p.X, p.Y, p.Z - NormalEpsilon)).Value | |
); | |
var normal = dir.Unit(); | |
return normal; | |
} | |
public virtual vec4 Run(vec2 xy, vec2 wh) | |
{ | |
var ray = getUvRay(FieldOfView, xy, wh); | |
vec3 position = (0, 0, 0); | |
var last_result = Sdf_March(position); | |
double traveled = 0; | |
for (int marches = 0; marches < MarchLimit; marches++) | |
{ | |
if (last_result.Value < MinStepLength) | |
{ | |
var normal = estimateNormal(position); | |
var value = (normal - (0, 1, 0)).Magnitude() - 0.8; | |
return new vec4(last_result.Color * Math.Min(Math.Max(value, 0), 1), 1.0); | |
} | |
if (traveled > DrawDistance) | |
break; | |
var distance = last_result.Value; | |
position += ray * distance; | |
traveled += distance; | |
last_result = Sdf_March(position); | |
} | |
return new vec4(BackgroundColor, 1.0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment