A simple ray tracer written in Python.
Without a specific license, this code is the direct intellectual property of the original developer. It may not be used, copied, modified, or shared without explicit permission. Please see GitHub's guide on licensing and choosealicense.com.
For legal reasons, if you choose to contribute to this project, you agree to give up your copyright and hand over full rights to your contribution. However, you will still be attributed for your work on GitHub. Thank you!
This project abides by Semantic Versioning.
To see a changelog for each update, check the description of releases on GitHub.
This is a simple ray-tracer, so not every possible feature has been implemented.
Feature | Implemented |
---|---|
.json scene importing |
✅ |
.obj object importing |
❌ |
Directional light sources | ✅ |
Point/area light sources | ❌ |
Multiple light sources | ❌ |
Spheres | ✅ |
Planes | ✅ |
Polygons | ✅ |
Parameterized surfaces | ❌ |
Phong shading | ✅ |
Shadows | ✅ |
Reflections | ✅ |
Transparency | ❌ |
Refraction | ❌ |
Anti-aliasing | ❌ |
Texture/normal mapping | ❌ |
Multi-threading | ✅ |
Distributed ray-tracing | ❌ |
Install all required packages with:
pip install --requirements requirements.txt
You can run the ray tracer with the following command:
python src
The script has many arguments. Use the --help
command to see a full list. Some have a default values, but you must provide the rest yourself.
Passing all the arguments through the command line can get tedious, so this project has support for dotenv
.
Create a new file called .env
in the root folder and add any arguments you reuse often. These will act as new "default" values. You can find a sample .env
with all the default values in .env.example
.
You may still pass arguments through the command prompt, and your .env
values will be superseded.
This raytracer exports images using Pillow. To see the full list of supported file extensions, see the docs.
Transparency is not supported, but is a future goal.
Scenes are defined in JSON
files. See a few examples in the scenes folder.
Any deviation from the following type definitions will result in assertion errors.
/** Represents an RGB color. All elements must be between 0 and 1. */
type Color = Array<number>[3];
/** Represents a direction vector. Will throw warnings if not normalized. */
type Direction = Array<number>[3];
/** Represents a location in the scene. */
type Position = Array<number>[3];
/** Defines the entire scene. This is the schema you should use for your scene JSON files. */
class Scene {
/** The position the camera is looking towards. */
camera_look_at?: Position = [0, 0, 0];
/** The position the camera is looking from (the camera's position). */
camera_look_from?: Position = [0, 0, 1];
/** The "up" direction for the camera. */
camera_look_up?: Direction = [0, 1, 0];
/** The angle width of the camera's view. Must be between 0 and 359. */
field_of_view?: float = 90;
/** The direction to the directional light source. */
light_direction?: Direction = [0, 1, 0];
/** The color of the directional light. */
light_color?: Color = [1, 1, 1];
/** The color of the global ambient light. */
ambient_light_color?: Color = [1, 1, 1];
/** The color of the scene background. */
background_color?: Color = [0, 0, 0];
/** A list of all objects in the scene. */
objects?: Array<Object>;
};
/** The universal values shared by all objects. */
class Object {
/** The name of the object. Has no affect on the behavior of the object. */
name?: string;
/** The type of the object. Used to differentiate between different classes of objects.
* Must be one of the accepted values. See other object classes below. */
type: string;
/** The coefficient for ambient lighting. Must be between 0 and 1. */
ambient_coefficient?: number = 0;
/** The coefficient for diffuse lighting. Must be between 0 and 1. */
diffuse_coefficient?: number = 1;
/** The coefficient for specular lighting. Must be between 0 and 1. */
specular_coefficient?: number = 0;
/** The color for diffuse lighting. */
diffuse_color?: Color = [1, 1, 1];
/** The color for specular lighting. */
specular_color?: Color = [1, 1, 1];
/** The coefficient for specular glossiness. */
gloss_coefficient?: number = 4;
/** How reflective the object surface should be. Must be between 0 and 1. */
reflectivity?: number = 0;
};
/** The specific values necessary for Planes. */
class Plane extends Object {
/** Defines this object as a Plane. */
type: string = "plane";
/** Any position on the plane. */
position: Position = [0, 0, 0];
/** The direction the plane faces. */
normal: Direction;
};
/** The specific values necessary for Circles. */
class Circle extends Object {
/** Defines this object as a Circle. */
type: string = "circle";
/** The center of the Circle. */
position: Position;
/** The direction the circle faces. */
normal: Direction = [0, 0, 1];
/** The radius of the Circle. Must be greater than 0. */
radius: number;
};
/** The specific values necessary for Polygons. */
class Polygon extends Object {
/** Defines this object as a Polygon. */
type: string = "polygon";
/** A list of vertices in counterclockwise order. Must have at least 3. */
vertices: Array<Position>;
};
/**
* The specific values necessary for Triangles.
* The algorithm for Triangle intersections is slightly faster than Polygons,
* so 3-sided Polygons will be automatically converted to Triangles.
*/
class Triangle extends Polygon {
/** Defines this object as a Triangle. */
type: string = "triangle";
/** The number of vertices must be ONLY 3. */
};
/** The specific values necessary for Spheres. */
class Sphere extends Object {
/** Defines this object as a Sphere. */
type: string = "sphere";
/** The center of the Sphere. */
position: Position;
/** The radius of the Sphere. Must be greater than 0. */
radius: number;
};
// More types of objects can be added later.
// In the meantime, most kinds of objects can be modeled with Polygons.
To run the linter, use
ruff check
To run the type-checker, use
mypy .
The linter and type-checker will run automatically on pull requests, and success is required to merge.