Skip to content

Instantly share code, notes, and snippets.

@Stagyrite
Last active May 17, 2024 10:53
Show Gist options
  • Save Stagyrite/3d99f58594527572a95560dabe92059e to your computer and use it in GitHub Desktop.
Save Stagyrite/3d99f58594527572a95560dabe92059e to your computer and use it in GitHub Desktop.
a raytracer inspired by small-raytracer
/*
* a raytracer inspired by small-raytracer
* https://github.com/FrontierPsychiatrist/small-raytracer/
*/
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* A very small and very incapable raytracer.
* It contains a hard-coded scene (Cornell box without spheres) and outputs to a file called "test.bmp".
*/
class SmallRayTracer {
private static final double EPSILON = 0.000000001;
public static void main(String[] args) throws IOException {
var rayTracing = new RayTracing();
var img = new BufferedImage(640, 480, BufferedImage.TYPE_INT_RGB);
for (var x = 0; x < img.getWidth(); x++) {
for (var y = 0; y < img.getHeight(); y++) {
img.setRGB(x, y, rayTracing.render(x, y));
}
}
File file = new File("test.bmp");
ImageIO.write(img, "bmp", file);
}
private static class Color {
double red;
double green;
double blue;
public Color(Color other) {
super();
this.red = other.red;
this.green = other.green;
this.blue = other.blue;
}
public Color(double red, double green, double blue) {
super();
this.red = red;
this.green = green;
this.blue = blue;
}
void normalizeColor() {
var factor = 255 / Math.max(Math.max(red, green), blue);
if (factor < 1) {
red *= factor;
green *= factor;
blue *= factor;
}
}
int getRGB() {
var redChar = (char) red;
var greenChar = (char) green;
var blueChar = (char) blue;
return (0xFF << 24) |
((redChar & 0xFF) << 16) |
((greenChar & 0xFF) << 8) |
(blueChar & 0xFF);
}
void scale(double scale) {
red *= scale;
green *= scale;
blue *= scale;
}
}
private static class Intersection {
Point point;
double lambda;
final Plane plane;
Intersection(Point point, Plane plane) {
super();
this.point = point;
this.plane = plane;
}
}
private static class Plane {
final Vector normal;
final double distance = 5;
final Color color;
Plane(Vector normal, Color color) {
super();
this.normal = normal;
this.color = color;
}
}
private static class Point extends Vector {
Point(double x, double y, double z) {
super(x, y, z);
}
}
private static class Ray {
final Point origin;
final Vector direction;
Ray(int x, int y) {
this(new Point(0, 0, 0),
new Vector(-320 + x + 0.5, 240 - y + 0.5, -120));
}
Ray(Point origin, Vector direction) {
super();
this.origin = origin;
this.direction = direction;
}
Intersection getFirstIntersection(Plane[] box) {
Intersection nearestIntersect = null;
for (var plane : box) {
var intersect = rayHitsPlane(plane);
var found = nearestIntersect == null || nearestIntersect.lambda > intersect.lambda;
if (intersect.lambda > EPSILON && found) {
nearestIntersect = intersect;
}
}
return nearestIntersect;
}
Intersection rayHitsPlane(Plane p) {
var gamma = direction.scalarProduct(p.normal);
var side = p.distance - p.normal.scalarProduct(origin);
var intersect = new Intersection(new Point(0, 0, 0), p);
if (gamma * side > EPSILON) {
var lambda = side / gamma;
intersect.point = new Point(
origin.x + direction.x * lambda,
origin.y + direction.y * lambda,
origin.z + direction.z * lambda
);
intersect.lambda = lambda;
}
return intersect;
}
Color traceRay(Plane[] box) {
var color = new Color(0, 0, 0);
var light = new Point(0.5, 2, -3.5);
var intersect = getFirstIntersection(box);
if (intersect.lambda > EPSILON) {
var directionToLight = light.pointDifference(intersect.point);
var rayToLight = new Ray(intersect.point, directionToLight);
var tempIntersect = rayToLight.getFirstIntersection(box);
if (tempIntersect.lambda >= 1 || tempIntersect.lambda == 0) {
// This condition is always true.
var length = directionToLight.vectorLength();
var scale = 5 / (length * length);
color = new Color(intersect.plane.color);
color.scale(scale);
}
}
return color;
}
}
private static class RayTracing {
/**
* a Cornell box
*/
final Plane[] box = {
new Plane(new Vector(-1, 0, 0), new Color(255, 0, 100)), // links
new Plane(new Vector(1, 0, 0), new Color(0, 255, 0)), // rechts
new Plane(new Vector(0, 0, -1), new Color(255, 255, 255)), // hinten
new Plane(new Vector(0, 1, 0), new Color(255, 255, 255)), // oben
new Plane(new Vector(0, -1, 0), new Color(255, 255, 255)) // unten
};
/**
* Renders an sRGB color.
*
* @param x the x position
* @param y the y position
* @return the sRGB color
*/
int render(int x, int y) {
var r = new Ray(x, y);
var c = r.traceRay(box);
c.normalizeColor();
return c.getRGB();
}
}
private static class Vector {
final double x;
final double y;
final double z;
Vector(double x, double y, double z) {
super();
this.x = x;
this.y = y;
this.z = z;
}
Vector pointDifference(Point p2) {
return new Vector(x - p2.x, y - p2.y, z - p2.z);
}
double scalarProduct(Vector v2) {
return x * v2.x + y * v2.y + z * v2.z;
}
double vectorLength() {
return Math.sqrt(x * x + y * y + z * z);
}
}
}
@Stagyrite
Copy link
Author

Here's an output image converted to PNG.
test

Check out a GitHub repository for the source code in C.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment