ascii-raytracer
v0.0.154
Published
Explore infinite 3d worlds from the comfort of your terminal
Downloads
25
Maintainers
Readme
ascii-raytracer
Explore infinite 3d worlds from the comfort of your terminal!
Simple utility takes a signed distance function and lets you explore it with realtime raytracing (marching spheres) or alternatively by using a user-provided raytracing function. No GPU needed!
Why?
For retro fun, to experiment with distance functions / 3d algorithms / ray-tracing / data-stuctures / optimization without dealing with browser, GPU, ect.
Table of Contents
- Installation
- Controls
- Example - Blob World
- Example - Custom Raycaster - Maze
- Example - View STL file
- Build a scene from boxes
- Build a scene from triangles
- UV Texture Mapping
- 3D Texture Mapping
- Taking high-res screenshots
- All Config Options
- See Also
Installation
npm i ascii-raytracer
Controls
A/S/W/D - move camera
Arrow Keys - rotate camera
Q/E - rise/fall
R/F - change field of view
C/X - change camera mode
H - Toggle fast anti-aliasing (EPX + anti-aliasing simultaneous)
V - Toggle naive anti-aliasing (2x render size, runs 4x slower)
U - Toggle EPX Upscaling (1/2 render size + 2x upscale)
P - Take screenshot (saves PNG with 4x resolution)
Hold shift to move/rotate faster
Example - Blob World
Simplest pre-built example, using only a distance function.
//here just a distance function - raytracing is done with marching spheres
//distance function format:
//funtion(x,y,z){return signedDistanceToScene;}
//see src of dfBlobWorld for more details
var art = require('ascii-raytracer');
var config = {
distanceFunction: art.distanceFunctions.dfBlobWorld
}
art.runScene(config);
Example - Custom Raycaster - Maze
It's usually a good idea to use a custom raytracing / raycasting function if you can organize your scene with an efficent data structure.
This pre-built example renders a maze which internally is stored in an R-Tree.
Note - the scene will still render if raytraceFunction
is removed, but may be very slow!
You can also build scenes using just a list of boxes (see below).
//here we additionally include a custom raytracing function [much faster than naive marching spheres]
//raytrace function format:
// function(ray) {
// var o = ray.point; //{x,y,z} origin of ray
// var v = ray.vector; //{x,y,z} direction of ray
// return distance;
// }
//example with a maze build from axis aligned bounding blocks
var art = require('ascii-raytracer');
var config = {
distanceFunction: art.distanceFunctions.dfMaze,
raytraceFunction: art.distanceFunctions.dfMazeTrace, //raytracer uses RTree
resolution: 64,
aspectRatio: 1.0
}
art.runScene(config);
View STL file
By specifying stl
in the config, the program will automatically produce a distance function and raycaster for your STL model.
You can also build scenes using just a list of triangles (see below).
//example with polygon mesh, 5000 triangles
//raycaster uses bounding volume hierarchy
var art = require('ascii-raytracer');
var config = {
stl: "./Bitey_Reconstructed_5k.stl"
}
art.runScene(config);
Build a scene from boxes
Instead of dealing with distance functions, you can specify your scene with a list of axis-aligned bounding boxes.
The program will automatically create an appropriate distance function+raycaster.
In this example we create a bunch of random boxes and render them.
var art = require('ascii-raytracer');
//format of box: [lower-point-3d, upper-point-3d]
var randomBox3d = function(){
var range = 32;
var boxSizeMax = 8;
var p0 = [Math.random()*range,Math.random()*range,Math.random()*range];
var p1 = [p0[0]+Math.random()*boxSizeMax,p0[1]+Math.random()*boxSizeMax,p0[2]+Math.random()*boxSizeMax];
return [p0,p1];
}
var randomBoxes = [];
for(var i=0; i<100; i++){
randomBoxes.push(randomBox3d());
}
var config = {
boxes: randomBoxes, //or bricks, or blocks
resolution: 64,
aspectRatio: 1.0
}
art.runScene(config);
Build a scene from triangles
Instead of dealing with distance functions, you can specify your scene with a list of triangles.
The program will automatically create an appropriate distance function+raycaster.
In this example we create a bunch of random triangles and render them.
var art = require('ascii-raytracer');
var randomPoint3d = function(){
var S = 32;
return [Math.random()*S,Math.random()*S,Math.random()*S];
}
var randomTriangles = [];
for(var i=0; i<100; i++){
var randomTri = [ randomPoint3d(), randomPoint3d(), randomPoint3d() ];
randomTriangles.push(randomTri);
}
var config = {
triangles: randomTriangles,
resolution: 64,
aspectRatio: 1.0
}
art.runScene(config);
UV Texture Mapping
specify config.uvFunction
and config.textureFunction
to render a 2D texture onto the scene.
uvFunction
takes (x,y,z) as input and returns[u,v]
texture coordinates in range 0...1textureFunction
takes (u,v) as inputs and returns[r,g,b]
color data in range 0...1
in this example we load a PNG file with the readimage
module and then specify textureFunction to pull color values from the image, and uvFunction for tiling the image.
the scene is the same maze from the earlier example.
var art = require('ascii-raytracer');
var image = require('image-sync').read("./cat.png");
var w = image.width;
var h = image.height;
var d = image.data; // [r,g,b,a, r,g,b,a ...]
var myTextureFunction = function(u,v){ //pull colors from texture data
var xc = Math.floor(u*w);
var yc = Math.floor(v*h);
var o = (yc*h+xc)*4;
return [d[o]/255, d[o+1]/255, d[o+2]/255] //return [r,g,b] in range 0...1
}
var myUvFunction = function(x,y,z){ //tile texture across XZ plane
return [Math.abs(x/1.0)%1.0, Math.abs(z/1.0)%1.0]; //return u,v coords
}
var config = {
distanceFunction: art.distanceFunctions.dfMaze,
raytraceFunction: art.distanceFunctions.dfMazeTrace,
uvFunction: myUvFunction,
textureFunction: myTextureFunction,
resolution: 64,
aspectRatio: 1.0
}
art.runScene(config);
3D Texture Mapping
Specify textureFunction3d
instead of textureFunction
if you want to define surface colors directly from a 3D function.
In this example we apply 3D perlin noise [from the blob world example] as the surface texture for our maze.
Because the blob world distance function varies over time, the texture is animated!
var art = require('ascii-raytracer');
var my3dTextureFunction = function(x,y,z){
var d = Math.abs(art.distanceFunctions.dfBlobWorld(x*15,y*15,z*15))/2.0; //using 3d perlin noise to define color
return [d,d,d] //return [r,g,b]
}
var config = {
distanceFunction: art.distanceFunctions.dfMaze,
raytraceFunction: art.distanceFunctions.dfMazeTrace,
textureFunction3d: my3dTextureFunction,
resolution: 64,
aspectRatio: 1.0
}
art.runScene(config);
Taking high-res screenshots
Press P to save a screenshot [png file]. By default, screenshots are 4x higher resolution than the ascii render.
To make screenshots bigger or smaller, specify screenShotScaleUp
in the config [default 4]
Warning - depending on your scene, taking a screenshot may be very slow!
var art = require('ascii-raytracer');
var config = {
distanceFunction: art.distanceFunctions.dfSkull,
raytraceFunction: art.distanceFunctions.dfSkullTrace,
resolution: 64,
aspectRatio: 1.0,
screenShotScaleUp: 8, //default is 4
screenShotDir: '/Users/user/Desktop/' //default is ./
}
art.runScene(config);
All Config Options
Note that the options distanceFunction
,triangles
, boxes
, lines
, points
, stl
are mutually exclusive - you should only declare one of these at a time.
{
triangles: [], //list of tris [pt0,pt1,pt2]
boxes: [], //list of aabb's [minPt,maxPt]
lines: [], //list of line segments [startPt,endPt]
points: [], //list of pts [x,y,z] -- rendered as small cubes
stl: "something.stl",
distanceFunction: myDistFunc,
raytraceFunction: myTraceFunc,
uvFunction: myUvFunction,
textureFunction: myTextureFunction,
cameraPos: [-17.96,8.02,14.72], //camera position [x,y,z]
cameraRot: [1.82,-0.40], //camera rotation, radians [phi, theta]
aspectRatio: 4/3,
screenShotScaleUp: 8, //default is 4
screenShotDir: '/Users/user/Desktop/' //default is ./
}
See Also
- ascii-data-image - render data to ascii
- pixel-scale-epx - EPX scaling
- glsl-imager - render GLSL in terminal
- triangles-mesh-renderer - CPU mesh renderer
- triangles-mesh-viewer-sdl-regl - GPU mesh viewer using regl+SDL