Browse Source

added missing raytrace example

pull/1/head
mntmn 1 year ago
parent
commit
19e3dac4e3
3 changed files with 468 additions and 0 deletions
  1. +22
    -0
      examples/raytrace/build-raytrace.sh
  2. +50
    -0
      examples/raytrace/zz9ktrace-main.c
  3. +396
    -0
      examples/raytrace/zz9ktrace.cpp

+ 22
- 0
examples/raytrace/build-raytrace.sh View File

@@ -0,0 +1,22 @@
COMPILE="arm-none-eabi-gcc -std=gnu99 -nostdlib -O2 -c -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -I../../lib -I../../include"
LINK="arm-none-eabi-gcc -T ../../link.ld -std=gnu99 -nostdlib -O2 -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard"
NAME=raytrace

mkdir build

#$COMPILE -S -fverbose-asm -ftree-vectorize -fno-threadsafe-statics -fno-stack-protector -fno-rtti -fno-exceptions -I. -o build/zz9ktrace.asm zz9ktrace.cpp
$COMPILE -ftree-vectorize -fno-threadsafe-statics -fno-stack-protector -fno-rtti -fno-exceptions -o build/zz9ktrace.o zz9ktrace.cpp

$COMPILE -o build/idiv.o ../../lib/div/idiv.S
$COMPILE -o build/idivmod.o ../../lib/div/idivmod.S
$COMPILE -o build/ldivmod.o ../../lib/div/ldivmod.S
$COMPILE -o build/printf.o ../../lib/printf/printf.c
$COMPILE -o build/zz9ktrace-main.o zz9ktrace-main.c

$LINK -o $NAME build/zz9ktrace-main.o build/zz9ktrace.o build/printf.o build/*div*.o -L. -lm

arm-none-eabi-objcopy -O binary $NAME $NAME.bin

rm $NAME.lha
lha a0 $NAME.lha $NAME.bin


+ 50
- 0
examples/raytrace/zz9ktrace-main.c View File

@@ -0,0 +1,50 @@
#include <stdint.h>
#include "printf/printf.h"
#include "zz9k_env.h"

int _Z10trace_mainPmm(uint32_t* fb, uint32_t stride);

struct ZZ9K_ENV* _zz9k_env;

#define ZZ9K_APP_DATASPACE 0x05000000

void _putchar(char c) {
_zz9k_env->putchar(c);
};

void __aeabi_idiv0(int r) {
printf("__aeabi_idiv0()!\n");
while (1) {
}
}
void __aeabi_ldiv0(int r) {
printf("__aeabi_idiv0()!\n");
while (1) {
}
}

int errno_var = 0;

int* __errno() {
return &errno_var;
}

int __attribute__ ((section (".binstart"))) main(struct ZZ9K_ENV* env) {
_zz9k_env = env;

if (!env) {
return 1;
}
if (env->argc<2) {
return 1;
}

printf("zz9ktrace! framebuffer: %lx stride: %d\n",env->argv[0],env->argv[1]);

// arg0: framebuffer pointer
// arg1: screen width
_Z10trace_mainPmm((uint32_t*)env->argv[0],env->argv[1]);
return 0;
}

+ 396
- 0
examples/raytrace/zz9ktrace.cpp View File

@@ -0,0 +1,396 @@
// vim: set et sw=2:

// compile with:
// c++ -O5 -ffast-math -funsafe-math-optimizations [-lm]

// adapted freely from http://fabiensanglard.net/postcard_pathtracer/index.html
// modifications by PoroCYon/K2^TiTAN under CC0, see https://creativecommons.org/publicdomain/zero/1.0/legalcode

//#define PATHTRACE /* don't define if you want simple raytracing */
#define USE_FRAMEBUFFER /* don't define to export a PPM to stdout */
//#define RAYMARCH_NAIVE /* you most likely don't want this */

#define CANVAS_WIDTH (320)
#define CANVAS_HEIGHT (240)
#define PATHTRACE_SAMPLE_COUNT (4) /* you want at least 16 for a good result,
but anything above 4 is horribly slow */
#define CAMERA_POS_X (-22)
#define CAMERA_POS_Y ( 5)
#define CAMERA_POS_Z ( 25)
#define CAMERA_LOOKAT_X (-3)
#define CAMERA_LOOKAT_Y ( 4)
#define CAMERA_LOOKAT_Z ( 0)
#define MNTMN_LOGO_X (0)
#define MNTMN_LOGO_Y (0)
#define MNTMN_LOGO_Z (0)
#define BALL_X ( 0)
#define BALL_Y ( 5)
#define BALL_Z (-5)

#include <stddef.h>
#include <stdint.h>
#include <math.h>
#ifndef USE_FRAMEBUFFER
#include <stdio.h>
#endif

struct Vec {
float x, y, z;

Vec(float v = 0) { x = y = z = v; }
Vec(float a, float b, float c = 0) { x = a; y = b; z = c;}

Vec operator+(Vec r) { return Vec(x + r.x, y + r.y, z + r.z); }
Vec operator*(Vec r) { return Vec(x * r.x, y * r.y, z * r.z); }
// dot product
float operator%(Vec r) { return x * r.x + y * r.y + z * r.z; }
// inverse square root
Vec operator!() {return *this * (1 / sqrtf(*this % *this) );}
};

float max(float l, float r) { return l > r ? l : r; }
float min(float l, float r) { return l < r ? l : r; }
#ifdef PATHTRACE
float randomVal() { return (float) rand() / RAND_MAX; } // TODO: use a different PRNG
#endif

// Rectangle CSG equation. Returns minimum signed distance from
// space carved by lowerLeft vertex and opposite rectangle
// vertex upperRight.
float BoxTest(Vec position, Vec lowerLeft, Vec upperRight) {
lowerLeft = position + lowerLeft * -1;
upperRight = upperRight + position * -1;
return -min(
min(
min(lowerLeft.x, upperRight.x),
min(lowerLeft.y, upperRight.y)
),
min(lowerLeft.z, upperRight.z));
}

#define HIT_NONE 0
#define HIT_LETTER 1
#define HIT_WALL 2
#define HIT_SUN 3
#define HIT_AMIGARED 4
#define HIT_LETTER_CONT 5

char letters[] = // line strip
"2K2Q7I7aNYNaSYS_X_";

char get(char* x, int i) {
return x[i]-((i<7)?(70+(i&1)):(88-(i&1)));
}
// Sample the world using Signed Distance Fields.
float sdf(Vec position, int *hitType) {
float distance = 1e9;
Vec f = position; // Flattened position (z=0)
f.x -= MNTMN_LOGO_X;
f.y -= MNTMN_LOGO_Y;
f.z = MNTMN_LOGO_Z;
for (size_t i = 0; i < sizeof(letters)-3; i += 2) {
Vec begin = Vec(get(letters,i), get(letters,i + 1)) * .5;
Vec e = Vec(get(letters,i + 2), get(letters,i + 3)) * .5 + begin * -1;
Vec o = f + (begin + e * min(-min((begin + f * -1) % e / (e % e),
0),
1)
) * -1;
distance = min(distance, o % o); // compare squared distance.
}
distance = sqrtf(distance); // Get real distance, not square distance.
distance = powf(powf(distance, 8) + powf(position.z, 8), .125) - .5;
*hitType = HIT_LETTER;

{
// amiiiiiiiiigaaaaaaaaaaaaah
Vec fp = position;
fp.x -= BALL_X;
fp.y -= BALL_Y;
fp.z -= BALL_Z;
const float rad = 5;
const int quant = 9;
// tf to sph. coord
float th = acosf(fp.y/sqrtf(fp%fp)),
ph = atanf(fp.z / fp.x)-M_PI/2;
int thi = (int)(th*quant/M_PI), phi = (int)(ph*quant/M_PI);
float dd = sqrtf(fp.x*fp.x+fp.y*fp.y+fp.z*fp.z) - rad;
if (dd < distance) {
*hitType = ((phi ^ thi) & 1) ? HIT_AMIGARED : HIT_LETTER_CONT;
distance = dd;
}
}

float roomDist;
roomDist = min(// min(A,B) = Union with Constructive solid geometry
//-min carves an empty space
-min(// Lower room
BoxTest(position, Vec(-30, -.5, -30), Vec(30, 18, 30)),
// Upper room
BoxTest(position, Vec(-25, 17, -25), Vec(25, 20, 25))
),
BoxTest( // Ceiling "planks" spaced 8 units apart.
Vec(fmodf(fabsf(position.x), 8),
position.y,
position.z),
Vec(1.5, 18.5, -25),
Vec(6.5, 20, 25)
)
);
if (roomDist < distance) {distance = roomDist; *hitType = HIT_WALL;}

float sun = 19.9 - position.y ; // Everything above 19.9 is light source.
if (sun < distance){distance = sun; *hitType = HIT_SUN;}

return distance;
}

// Perform signed sphere marching
// Returns hitType 0, 1, 2, or 3 and update hit position/normal
int RayMarching(Vec origin, Vec direction, Vec &hitPos, Vec &hitNorm) {
int hitType = HIT_NONE;

#ifdef RAYMARCH_NAIVE
// raymarching, naive implementation
const float eps = 0.01;
int noHitCount = 0;
float d; // distance from closest object in world.
// Signed distance marching
for (float total_d=0; total_d < 100; total_d += d)
if ((d = sdf(hitPos = origin + direction * total_d, &hitType)) < eps
|| noHitCount > 99) {
hitNorm =
!Vec(sdf(hitPos + Vec(.01, 0), &noHitCount) - d,
sdf(hitPos + Vec(0, .01), &noHitCount) - d,
sdf(hitPos + Vec(0, 0, .01), &noHitCount) - d);
return hitType; // Weird return statement where a variable is also updated.
} else ++noHitCount;
#else
// "enhanced sphere tracing", keinert et al (aka Mercury)
float O = 1.2, cer = 1e9, t = 0, /*ct = 0,*/ sl = 0, sg, prad = 0;
sg = sdf(origin, &hitType);
sg = (sg < 0) ? -1 : 1;
const float pxrad = 0.5 / sqrtf(CANVAS_WIDTH*CANVAS_WIDTH+CANVAS_HEIGHT*CANVAS_HEIGHT);

for (int i = 0; i < 100; ++i) {
hitPos = origin + direction * t;
float sr = sg * sdf(hitPos, &hitType);
float rr = fabsf(sr);

bool sfail = O > 1 && (rr + prad) < sl;
if (sfail) {
sl -= O*sl;
O = 1;
} else {
sl = sr * O;
}

prad = rr;
float err = rr / t;
if (!sfail && err < cer) {
//ct = t;
cer = err;
}
if ((!sfail && err < pxrad) || t > 100) {
int _;
hitNorm =
!Vec(sdf(hitPos + Vec(.01, 0), &_) - sr,
sdf(hitPos + Vec(0, .01), &_) - sr,
sdf(hitPos + Vec(0, 0, .01), &_) - sr);
return (t > 100) ? 0 : hitType;
}
t += sl;
}
#endif

return 0;
}

Vec Trace(Vec origin, Vec direction) {
Vec sampledPosition=origin, normal(1,0,0), color(0,0,0), attenuation = 1;
Vec lightDirection(!Vec(.6, .6, 1)); // Directional light

for (int bounceCount = 3; bounceCount--;) {
#ifdef PATHTRACE
int hitType = RayMarching(origin, direction, sampledPosition, normal);
if (hitType == HIT_NONE) break; // No hit. This is over, return color.
else if (hitType == HIT_LETTER) { // Specular bounce on a letter. No color acc.
//return Vec(1,0,0);
direction = direction + normal * ( normal % direction * -2);
origin = sampledPosition + direction * 0.1;
attenuation = attenuation * 0.2; // Attenuation via distance traveled.
}
else if (hitType == HIT_WALL) { // Wall hit uses color yellow?
//return Vec(0,1,0);
float incidence = normal % lightDirection;
float p = 6.283185 * randomVal();
float c = randomVal();
float s = sqrtf(1 - c);
float g = normal.z < 0 ? -1 : 1;
float u = -1 / (g + normal.z);
float v = normal.x * normal.y * u;
direction = Vec(v,
g + normal.y * normal.y * u,
-normal.y) * (cosf(p) * s)
+
Vec(1 + g * normal.x * normal.x * u,
g * v,
-g * normal.x) * (sinf(p) * s) + normal * sqrtf(c);
origin = sampledPosition + direction * .1;
attenuation = attenuation * 0.2;
if (incidence > 0 &&
RayMarching(sampledPosition + normal * .1,
lightDirection,
sampledPosition,
normal) == HIT_SUN)
color = color + attenuation * Vec(500, 400, 100) * incidence;
} else if (hitType == HIT_AMIGARED) {
color = color + attenuation * Vec(80, 0, 0); break;
}
else {//if (hitType == HIT_SUN) { //
//return Vec(0,0,1);
color = color + attenuation * Vec(50, 80, 100); break; // Sun Color
}
#else

Vec lights[] = {
Vec(BALL_X,BALL_Y,BALL_Z), Vec(80,0,0), // amigaball
Vec(0,19.9,0), Vec(50,80,100), // sun
//Vec(3,6,3), Vec(0.1)
};
int hitType = RayMarching(origin, direction, sampledPosition, normal);
if (hitType == HIT_NONE) return color; // No hit. This is over, return color.
else if (hitType == HIT_LETTER || hitType == HIT_WALL || hitType==HIT_LETTER_CONT) {
bool ltr = hitType != HIT_WALL;
// obj color
color = ltr ? Vec() : Vec(0.9,0.85,0.8);

// I'm so sorry
for (size_t i = 0; i < sizeof(lights)/(sizeof(lights[0])); i+=2) {
Vec ld = lights[i] + sampledPosition*(-1), mld = ld*(-1);
Vec lc = lights[i+1];
float len = sqrtf(ld%ld);
ld = ld*(1/len);
float latt = min(1/(4*len*len),1);
// I, N --> I - 2.0 * dot(N, I) * N
Vec refl = mld+normal*(-2*(normal%mld));

float amb = 0.1;
float spp = 1;
float dif = max(0,normal%ld);
double spc = max(0,refl%!(origin+sampledPosition*(-1)));
spc = pow(spc, spp);
double spcc = spc*((i == 2 && ltr)?0.0000001:0.5);
Vec coeff = color*(dif*0.8f+amb);
if (hitType!=HIT_LETTER_CONT)coeff=coeff+Vec((float)spcc*0.5f);
color = color+coeff*(lc*latt);
}

float incidence = normal % lightDirection;

if (hitType == HIT_WALL && incidence > 0 &&
RayMarching(sampledPosition + normal * .1,
lightDirection,
sampledPosition,
normal) == HIT_SUN)
color = color + Vec(0.2,0.2,0.2)* Vec(500, 400, 100) * incidence;

if (hitType!=HIT_LETTER_CONT) return color;
else {
float p = 6.283185 * 0.5f;
float c = 0.5;
float s = sqrtf(1 - c);
float g = normal.z < 0 ? -1 : 1;
float u = -1 / (g + normal.z);
float v = normal.x * normal.y * u;
direction = Vec(v,
g + normal.y * normal.y * u,
-normal.y) * (cosf(p) * s)
+
Vec(1 + g * normal.x * normal.x * u,
g * v,
-g * normal.x) * (sinf(p) * s) + normal * sqrtf(c);
origin = sampledPosition + direction * .1;
attenuation=attenuation*0.02;
}
} else if (hitType == HIT_AMIGARED) {
return Vec(80, 0, 0) * attenuation + color;
}
else {//if (hitType == HIT_SUN) { //
return Vec(50, 80, 100) * attenuation + color; // Sun Color
}
#endif
}
return color;
}

#ifdef USE_FRAMEBUFFER

uint32_t* fb;
uint32_t fb_stride = 640;

void init(int w, int h) {
// ???
}
inline void put(int x, int y, int r, int g, int b) {
//printf("put %d/%d\n",x,y);
fb[y*fb_stride+x] = (r<<16)|(g<<8)|b;
}
#else
void init(int w, int h) {
printf("P6 %d %d 255 ", w, h);
}
void put(int x, int y, int r, int g, int b) {
printf("%c%c%c", r, g, b);
}
#endif

void trace_main(uint32_t* fb_, uint32_t stride_) {
#ifdef USE_FRAMEBUFFER
fb = fb_;
fb_stride = stride_;
#endif
int w = CANVAS_WIDTH, h = CANVAS_HEIGHT, samplesCount = PATHTRACE_SAMPLE_COUNT;
Vec position(CAMERA_POS_X, CAMERA_POS_Y, CAMERA_POS_Z);// ray origin
Vec goal = !(Vec(CAMERA_LOOKAT_X, CAMERA_LOOKAT_Y, CAMERA_LOOKAT_Z)
+ position * -1);// ray target
Vec left = !Vec(goal.z, 0, -goal.x) * (1. / w);// camera left vector (use this to rotate the camera)

// Cross-product to get the up vector
Vec up(goal.y * left.z - goal.z * left.y,
goal.z * left.x - goal.x * left.z,
goal.x * left.y - goal.y * left.x);
init(w, h);
for (int y = 0; y < h; ++y)
for (int x = 0; x < w; ++x) {
int xx = w - x, yy = h - y; // dunno if this is needed for the fb backend
Vec color;
for (int p = samplesCount; p--;)
color = color + Trace(position, !(goal + left * (xx - w / 2
#ifdef PATHTRACE
+ randomVal()
#endif
)+
up * (yy - h / 2
#ifdef PATHTRACE
+ randomVal()
#endif
)));

// Reinhard tone mapping
color = color * (1. / samplesCount) + 14. / 241;
Vec o = color + 1;
color = Vec(color.x / o.x, color.y / o.y, color.z / o.z) * 255;
put(x, y, (int)color.x, (int)color.y, (int)color.z);
}
}

#ifndef USE_FRAMEBUFFER
int main() {
trace_main(NULL);
return 0;
}
#endif


Loading…
Cancel
Save