Figured out something. Apparently colors just work differently. Both videos are produced using the *exact* same shader code.
Code: Select all
const float maxDist = 100.; // Maximum distance to trace a ray
#define PI 3.14159265359
// Generative palettes
const vec3 a = vec3(0.5);
const vec3 b = vec3(0.2, 0.5, 1.0);
const vec3 c = vec3(1.0, 0.4, 0.5);
const vec3 d = vec3(0.5);
vec3 palette(float t) {
return a + b*cos(6.28318 * (c * t + d));
}
// Geomtru
struct Sphere {
vec3 c;
float r;
};
struct RidgePlane {
vec3 n;
vec3 o;
float rh;
};
struct Material {
vec3 color;
float albedo;
};
struct Shape {
Material mat;
int tag; // Indicates which member to look at
// Tag 0 [invalid]
// Tag 1 [sphere]
Sphere sphere;
// Tag 2 [ridge plane]
RidgePlane rplane;
};
struct Camera {
vec3 origin;
vec3 target;
float fov;
};
struct Ray {
vec3 o;
vec3 d;
};
struct SurfaceInteraction {
bool isHit;
vec3 n;
float d;
vec3 dpdu, dpdv; // Dunno
vec3 dndu, dndv; // Dunno
Shape shape;
};
// Lighting
struct PointLight {
vec3 p;
};
struct DirectionalLight {
vec3 d;
};
struct Light {
vec3 color;
float brightness;
int tag;
// Tag 0 [invalid]
// Tag 1 [point]
PointLight point;
// Tag 2 [point NO ISL] (just use the point member)
// Tag 3 [directional]
DirectionalLight dir;
};
struct Scene {
Shape shapes[256]; // Support up to 256 shapes
int shapeCount; // How many shapes were *actually* used?
Light lights[256]; // Support up to 256 lights
int lightCount; // How many lights were *actually* used?
float shadow_bias;
};
// Functions
Shape Sphere_new(float x, float y, float z, float r) {
Sphere sphere;
sphere.c = vec3(x, y, z); sphere.r = r;
Shape shape;
shape.sphere = sphere;
shape.tag = 1;
return shape;
}
Shape RidgePlane_new(vec3 n, vec3 o, float rh) {
RidgePlane rplane;
rplane.n = n; rplane.o = o; rplane.rh = rh;
Shape shape;
shape.rplane = rplane;
shape.tag = 2;
return shape;
}
Light PointLight_new(vec3 point) {
PointLight plight;
plight.p = point;
Light light;
light.point = plight;
light.tag = 1;
return light;
}
Light DirectionalLight_new(vec3 dir) {
DirectionalLight dlight;
dlight.d = normalize(dir);
Light light;
light.dir = dlight;
light.tag = 3;
return light;
}
float hit_sphere(Sphere sphere, Ray r) {
vec3 oc = r.o - sphere.c;
float a = dot(r.d, r.d);
float b = 2.0 * dot(oc, r.d);
float c = dot(oc, oc) - sphere.r * sphere.r;
float discriminant = b*b - 4.0 * a * c;
if(discriminant < 0.) {
return -1.0;
} else {
return (-b - sqrt(discriminant)) / (2.0 * a);
}
}
float hit_plane(RidgePlane rplane, Ray r) {
float denom = dot(r.d, rplane.n);
if(denom > 0.0001) {
vec3 p0l0 = rplane.o - r.o;
float t = dot(p0l0, rplane.n) / denom;
if(t > 0.0001)
return t;
}
return -1.0;
}
SurfaceInteraction intersectScene(Scene scene, Ray r) {
SurfaceInteraction si;
float dist = maxDist; // The closest a shape has come
for(int i = 0; i < scene.shapeCount; i++) {
Shape s = scene.shapes[i];
switch(s.tag)
{
case 1:
if(hit_sphere(s.sphere, r) > 0.0 && hit_sphere(s.sphere, r) < dist) {
// Populate the surface interaction
dist = hit_sphere(s.sphere, r);
si.isHit = true;
si.n = normalize((r.o + r.d * dist) - s.sphere.c);
si.shape = s;
si.d = dist;
}
break;
case 2:
if(hit_plane(s.rplane, r) > 0.0 && hit_plane(s.rplane, r) < dist) {
// Populate the surface interaction
dist = hit_plane(s.rplane, r);
si.isHit = true;
// TODO Fake out the normal calc to make the ridges
si.n = -s.rplane.n;
si.shape = s;
si.d = dist;
}
break;
case 0:
default:
break;
}
}
return si;
}
vec3 lightScene(Scene scene, SurfaceInteraction si, Ray r) {
vec3 color = vec3(0.);
vec3 p = r.o + r.d * si.d;
Material mat = si.shape.mat;
float reflected = mat.albedo / PI;
for(int i = 0; i < scene.lightCount; i++) {
Light l = scene.lights[i];
Ray ra;
ra.o = p + (si.n * scene.shadow_bias);
switch(l.tag) {
case 1:
ra.d = normalize(l.point.p - ra.o);
if(!intersectScene(scene, ra).isHit && si.isHit && dot(r.d, si.n) < 0.0) {
color += dot(normalize(l.point.p - p), si.n) * (l.brightness / (4. * PI * dot(l.point.p - p, l.point.p - p))) * l.color * reflected * mat.color;
}
break;
case 2:
ra.d = normalize(l.point.p - ra.o);
if(!intersectScene(scene, ra).isHit && si.isHit && dot(r.d, si.n) < 0.0) {
color += dot(normalize(l.point.p - p), si.n) * l.brightness * l.color * reflected * mat.color;
}
break;
case 3:
ra.d = -l.dir.d;
// if(!intersectScene(scene, ra).isHit && si.isHit && dot(r.d, si.n) < 0.0) {
color += dot(-l.dir.d, si.n) * l.brightness * l.color * reflected * mat.color;
// }
break;
case 0:
default:
break;
}
}
return pow(color, vec3(0.7));
}
Scene buildScene() {
Scene scene;
scene.shapeCount = 2;
scene.lightCount = 2;
scene.shadow_bias = 0.01;
// A sphere at (around) the origin with radius 5
scene.shapes[0] = Sphere_new(3. * sin(iTime * 4.), cos(iTime) + 2.0, 0., 1.);
scene.shapes[0].mat.color = palette(0.3 * sin(iTime / 2.) + 0.3);
scene.shapes[0].mat.albedo = 5.0;
//scene.shapes[0].mat.color = vec3(0., 1., 0.);
//scene.shapes[1] = Sphere_new(0., -1000., 0., 1000.);
//scene.shapes[1].mat.color = vec3(1.);
// A ridged plane at the origin, pointing up, with some ridges
scene.shapes[1] = RidgePlane_new(vec3(0., -1., 0.), vec3(0., 0., 0.), 2.);
scene.shapes[1].mat.color = vec3(1.);
scene.shapes[1].mat.albedo = 2.0;
// A light that moves around
scene.lights[0] = PointLight_new(vec3(2. * sin(iTime), 5., 5.));
scene.lights[0].color = vec3(1.);
scene.lights[0].brightness = 300.;
// A directional light
scene.lights[1] = DirectionalLight_new(vec3(0., -1., 0.));
scene.lights[1].color = vec3(0.5, 0.4, 0.);
scene.lights[1].brightness = 0.4;
return scene;
}
vec3 lookAt(vec2 uv, Camera cam) {
// Get the z axis same way as direction vector
vec3 zAxis = normalize(cam.target - cam.origin);
vec3 up = vec3(0., 1., 0.);
// cross prd of 2 vec prod. 3 vec that is orthog to first 2
// non-communicative so order matters
vec3 xAxis = normalize(cross(up, zAxis));
vec3 yAxis = normalize(cross(zAxis, xAxis));
float fov = cam.fov;
// scale unit v by ray origin
// one for x one for y, no z vector, so just add it
// then scale by fov
vec3 dir = (normalize((uv.x * xAxis) + (uv.y * yAxis) + (zAxis * fov)));
return dir;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// Normalized pixel coordinates (from -1 to 1)
vec2 uv = 2.0 * fragCoord/iResolution.xy - vec2(1.0);
// Create the scene
Scene scene = buildScene();
// Create the camera object
Camera camera;
camera.origin = vec3(20. * sin(iTime / 2.), 10. * sin(iTime) + 12., 20. * cos(iTime / 2.));
//camera.origin = vec3(0., 1., -5.);
camera.target = vec3(0., 0., 0.);
camera.fov = 2.;
// Create Ray object
Ray ray;
ray.o = vec3(uv + camera.origin.xy, camera.origin.z + 1.);
ray.d = lookAt(uv, camera);
SurfaceInteraction si = intersectScene(scene, ray);
// fragColor = vec4(mix(lightScene(scene, si, ray), 1.0 * (float)si.isHit - (vec3(si.d * (float)si.isHit) / maxDist), 0.2), 1.0);
fragColor = vec4(lightScene(scene, si, ray), 1.0);
}
EDIT: How does one switch GPUs in Magic? My computer has 2, and ShaderToy uses 1 (integrated Intel) while Magic uses the other (an RTX 2070 Max-Q), so the comparison might be apples to oranges.