Godot4ShadersPlayground/RayMarching.gdshader

139 lines
3.2 KiB
Plaintext

shader_type spatial;
uniform bool disable_threshold = false;
uniform float threshold = 0.001f;
uniform int max_iterations = 300;
uniform vec4 colour : source_color;
uniform float epsilon = 0.1f;
uniform vec3 sphere_pos = vec3(0.2f, -0.2f, -0.1f);
uniform vec3 cube_pos = vec3(0.3f, 0, 0);
uniform vec3 plane_dir = vec3(0, 1.0f, 0);
// polynomial smooth min
float sminCubic( float a, float b, float k )
{
float h = max( k-abs(a-b), 0.0 )/k;
return min( a, b ) - h*h*h*k*(1.0/6.0);
}
float intersectSDF(float distA, float distB) {
return max(distA, distB);
}
float unionSDF(float distA, float distB) {
return min(distA, distB);
}
float differenceSDF(float distA, float distB) {
return max(distA, -distB);
}
void vertex() {
//POSITION = vec4(VERTEX, 1.0);
}
float sdEllipsoid( vec3 p, vec3 r ) {
float k0 = length(p/r);
float k1 = length(p/(r*r));
return (k0<1.0) ? (k0-1.0)*min(min(r.x,r.y),r.z) : k0*(k0-1.0)/k1;
}
float sdPlane(vec3 p, vec3 n, float h) {
return dot(n, p) + h;
}
float sdBox( vec3 b, vec3 p ) {
vec3 q = abs(p) - b;
return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}
float dstFromSphere(vec3 target, vec3 pos, float r) {
return distance(target, pos) - r;
}
float sdVerticalCapsule( vec3 p, float h, float r ) {
p.y -= clamp( p.y, 0.0, h );
return length( p ) - r;
}
float get_distance(vec3 p) {
//return sdEllipsoid(p + sphere_pos, vec3(0.01f, 0.01f, 0.015f));
//return sdVerticalCapsule(p + sphere_pos, 0.015f, 0.01f);
return sminCubic(
sdVerticalCapsule(p + sphere_pos, 0.015f, 0.01f),
sdVerticalCapsule(p + cube_pos, 0.015f, 0.01f),
epsilon
);
/*return sminCubic(
sdBox(vec3(0.2f), p + cube_pos),
//sdPlane(p + cube_pos, plane_dir, 0),
//dstFromSphere(p + sphere_pos, vec3(0), 0.199f),
sdEllipsoid(p + sphere_pos, vec3(0.01f, 0.01f, 0.015f)),
epsilon
);*/
/*return sminCubic(
sdEllipsoid(p + cube_pos, vec3(0.01f, 0.01f, 0.015f)),
sdEllipsoid(p + sphere_pos, vec3(0.01f, 0.01f, 0.015f)),
0.1
);*/
}
vec3 get_normal(vec3 p) {
vec2 e = vec2(1e-5, 0);
vec3 n = get_distance(p) - vec3(
get_distance(p - e.xyy),
get_distance(p - e.yxy),
get_distance(p - e.yyx)
);
return normalize(n);
}
vec4 rayMarch(vec3 orig, vec3 dir) {
vec3 sphere_orig = vec3(0, 0, 0);
float dst = get_distance(orig);
int inc = 0;
for (int i = 0; i < max_iterations; i++) {
if (dst <= threshold && !disable_threshold) {
break;
}
orig += dst * dir;
dst = get_distance(orig);
}
if (dst <= threshold) {
// We touched a shape
return vec4(orig, 1.0f);
}
else {
// Render some white transparent background so we can still see the quad
return vec4(vec3(1), 0);
}
}
void fragment() {
vec2 uv = SCREEN_UV * 2.0 - 1.0;
vec4 camera = INV_VIEW_MATRIX * INV_PROJECTION_MATRIX * vec4(uv, 1, 1);
vec3 orig = INV_VIEW_MATRIX[3].xyz;
vec3 dir = normalize(camera.xyz);
vec4 point = rayMarch(orig, dir);
// Render white transparent background by default
vec4 col = vec4(1, 1, 1, 0.1f);
// The w member of the vec4 is used to convey that we hit something.
// Let put a colour on that and compute the normal
if (point.w > 0.0f) {
col = colour;
col.a = point.w;
NORMAL = (vec4(get_normal(point.xyz), 1) * INV_VIEW_MATRIX).xyz;
}
ALPHA = col.a;
ALBEDO = col.rgb;
}