From ebe3bfdcff8a4eec5e68a7edcb7f0b59d6dd3e89 Mon Sep 17 00:00:00 2001 From: James Martin Date: Thu, 20 May 2021 20:25:48 -0700 Subject: [PATCH] New marcher calculates instead of approximating normals; fix artifacts in old marcher. --- ray_march 2021-05-14.frag | 12 +-- ray_march 2021-05-20.frag | 200 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 10 deletions(-) create mode 100644 ray_march 2021-05-20.frag diff --git a/ray_march 2021-05-14.frag b/ray_march 2021-05-14.frag index acdf4b1..ce577e4 100644 --- a/ray_march 2021-05-14.frag +++ b/ray_march 2021-05-14.frag @@ -216,15 +216,7 @@ float df_cube_minus_sphere(mat4 trans, float sphere_radius, vec3 pos) { // we're inside the cube vec3 sphere_pos = (trans * vec4(0., 0., 0., 1.)).xyz; float sdsphere = sdf_sphere(sphere_pos, sphere_radius, pos); - if (sdsphere >= MIN_DISTANCE) { - // inside the cube but outside the sphere - return 0.; - } - - // we're inside the sphere subtracted from the cube, - // so our distance is the distance to the sphere. - - return abs(sdsphere); + return max(sdcube, -sdsphere); } float distance_to_objects(out int material, vec3 pos) { @@ -331,7 +323,7 @@ float ray_march(out int mat, vec3 ray_origin, vec3 ray_direction) { vec3 normal(vec3 pos) { // Magic number determined by tinkering with it until stuff worked. - vec2 delta = vec2(0.00025, 0.); + vec2 delta = vec2(0.0001, 0.); int _mat = -1; float dist = distance_to_objects(_mat, pos); vec3 dq = (dist - vec3( diff --git a/ray_march 2021-05-20.frag b/ray_march 2021-05-20.frag new file mode 100644 index 0000000..008b014 --- /dev/null +++ b/ray_march 2021-05-20.frag @@ -0,0 +1,200 @@ +// https://www.shadertoy.com/view/7ts3z8 + +// 4x supersampling antialiasing. +#define SUPERSAMPLE + +// The minimum distance between two points before they are considered intersecting. +#define MIN_DISTANCE 0.001 +// The maximum distance from the camera objects are visible. +#define MAX_DISTANCE 15.000 +// The maximum number of steps we're allowed to take during ray marching. +#define MAX_STEPS 150 +// The width of the field of view. (180 degrees / FOV) +// Larger numbers suffer from less distortion at the edges of the image, +// but make the scene appear more zoomed-in. +#define FOV 2. +#define CAMERA_ORIGIN vec3(0., 0., -2.) + +// The minimum lighting intensity for objects which would otherwise +// be in total darkness. +#define MIN_LIGHT 0.01 + +#define INF 1./0. +#define NaN sqrt(-1.) + +struct NearPoint { + vec3 point; + vec3 normal; +}; + +struct NearObject { + NearPoint near; + int material; +}; + +NearObject nearestNearObject(NearObject a, NearObject b, vec3 pos) { + if (distance(a.near.point, pos) < distance(b.near.point, pos)) { + return a; + } + return b; +} + +NearPoint nearPoint(vec3 pPos, vec3 pos) { + vec3 normal = normalize(pos - pPos); + return NearPoint(pPos, normal); +} + +NearPoint nearSegment(vec3 pPos, vec3 pVec, vec3 pos) { + float len = length(pVec); + float h = clamp(dot(normalize(pVec), pos - pPos), 0., len); + vec3 point = pPos + h * normalize(pVec); + return NearPoint(point, normalize(pos - point)); +} + +NearPoint nearPlane(float height, vec3 pos) { + vec3 normal = vec3(0., 1., 0.); + vec3 point = vec3(pos.x, height, pos.z); + return NearPoint(point, normal); +} + +NearPoint inflate(float radius, NearPoint near) { + return NearPoint(near.normal * radius + near.point, near.normal); +} + +NearPoint nearSphere(vec3 sPos, float sRadius, vec3 pos) { + return inflate(sRadius, nearPoint(sPos, pos)); +} + +NearPoint nearPill(vec3 pPos, vec3 pVec, float pRadius, vec3 pos) { + return inflate(pRadius, nearSegment(pPos, pVec, pos)); +} + +NearObject nearestObject(vec3 pos) { + NearObject ground = NearObject(nearPlane(-1., pos), 1); + + NearObject sphere1 = NearObject(nearSphere( + 5. * vec3(sin(iTime), 0., cos(iTime)), + 1., pos + ), 2); + + NearObject pill1 = NearObject(nearPill( + vec3(-0.7, -0.8, 2.), + vec3(1.3, 0.1, -0.1), + 0.1, pos + ), 3); + + return nearestNearObject(nearestNearObject(ground, sphere1, pos), pill1, pos); +} + +vec3 srgb2linear(vec3 srgb) { + // approximation + return vec3(pow(srgb.x, 1./2.2), pow(srgb.y, 1./2.2), pow(srgb.z, 1./2.2)); +} + +vec3 materialColor(int material) { + if (material == 1) { + return vec3(1.); + } + if (material == 2) { + return srgb2linear(vec3(0., 0.19, 0.56)); + } + if (material == 3) { + return srgb2linear(vec3(0.50, 1.00, 0.40)); + } + if (material == 4) { + return srgb2linear(vec3(0.83, 0.69, 0.22)); + } + // missing material placeholder color + return vec3(1., 0., 1.); +} + +struct Ray { + vec3 origin; + vec3 direction; +}; + +NearObject raymarch(Ray ray) { + vec3 pos = ray.origin; + NearObject nanObject = NearObject(NearPoint(vec3(INF), vec3(NaN)), -1); + NearObject object = nanObject; + for (int i = 0; i < MAX_STEPS && distance(pos, ray.origin) < MAX_DISTANCE; i++) { + object = nearestObject(pos); + float delta = distance(pos, object.near.point); + if (delta < MIN_DISTANCE) { + return object; + } + pos += ray.direction * delta; + } + return nanObject; +} + +Ray projectSphere(vec2 uv) { + vec3 direction = vec3(uv, sqrt(1. - uv.x*uv.x - uv.y*uv.y)); + return Ray(CAMERA_ORIGIN + direction, direction); +} + +float light(vec3 sPos, NearPoint near) { + vec3 sDirection = normalize(sPos - near.point); + float intensity = dot(sDirection, near.normal); + float marchDist = distance(raymarch(Ray(sPos, -sDirection)).near.point, sPos); + if (marchDist < distance(sPos, near.point) - 10. * MIN_DISTANCE) { + // The object is in the shadow. + intensity *= 0.1; + } + if (intensity < MIN_LIGHT && length(near.point) < INF) { + return MIN_LIGHT; + } + return intensity; +} + +vec3 rayColor(Ray ray) { + NearObject object = raymarch(ray); + if (object.material == -1) { + return vec3(0.); + } + + vec3 color = materialColor(object.material); + float lightIntensity = light(vec3(1.5, 1.5, 6.), object.near); + return color * lightIntensity; +} + +vec3 linear2srgb(vec3 linear_rgb) { + // copied from somewhere on the internet + bvec3 cutoff = lessThan(linear_rgb, vec3(0.0031308)); + vec3 higher = vec3(1.055)*pow(linear_rgb, vec3(1.0/2.4)) - vec3(0.055); + vec3 lower = linear_rgb * vec3(12.92); + + return mix(higher, lower, cutoff); +} + +vec3 uvColor(vec2 uv) { + return rayColor(projectSphere(uv)); +} + +vec3 uvSupersample(vec2 uv) { + vec2 off = vec2(0.5, -0.5) / (FOV * iResolution.x); + return ( uvColor(uv + off.xx) + + uvColor(uv + off.xy) + + uvColor(uv + off.yx) + + uvColor(uv + off.yy) + ) / 4.; +} + +void mainImage(out vec4 fragColor, in vec2 fragCoord) { + vec2 uv = (fragCoord / iResolution.xy - 0.5) * 2.; + // Cut off the part of the scene which doesn't fit due to the aspect ratio. + if (iResolution.x > iResolution.y) { + uv = vec2(uv.x, uv.y * iResolution.y / iResolution.x); + } else { + uv = vec2(uv.x * iResolution.x / iResolution.y, uv.y); + } + uv /= FOV; + + #ifndef SUPERSAMPLE + vec3 color = uvColor(uv); + #else + vec3 color = uvSupersample(uv); + #endif + //vec3 color = raymarch(projectSphere(uv)).near.normal / 2. + 0.5; + fragColor = vec4(linear2srgb(color), 0.); +} \ No newline at end of file