You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
5.6 KiB
GLSL
200 lines
5.6 KiB
GLSL
// 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.);
|
|
} |