smallpt в 50 строк
ray tracing, path tracing, computer graphics
Наглядным примером стохастического алгоритма path tracing является smallpt. В smallpt базовый алгоритм реализован во всего 99 строках кода. Минимальный размер позволяет легко изучать реализацию. Но он может быть ещё меньше, например, 49 строк — как в данной статье. Такой код правда проигрывает в читабельности (и не только), но представляет эзотерический интерес]
weert.cpp
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
struct V {
double x,y,z;
V(double u=0, double v=0, double w=0):x(u),y(v),z(w){};
V operator+(V b){return V(x+b.x,y+b.y,z+b.z);}
V operator-(V b){return V(x-b.x,y-b.y,z-b.z);}
V operator*(double k){return V(k*x,k*y,k*z);}
V operator^(V v){return V(x*v.x,y*v.y,z*v.z);}
double operator*(V b){return x*b.x+y*b.y+z*b.z;}
V c(V b){return V(y*b.z-z*b.y,z*b.x-x*b.z,x*b.y-y*b.x);}
} Z=V(),M,O=V(50,52,285),*o,ro,D,l,f,dd,sd,gd,n,N,L,c,u,v;
V h(V v){return v*(1/sqrt(v.x*v.x+v.y*v.y+v.z*v.z));}
double A=1e5,B=.999,C=.7,X=81.6,Y=50,Q[]={Y,691.33,X,600,0,0,
12,12,12,0,0,0,1,0,0,-A+1,Y,X,A,0,0,0,0,0,C,.25,.25,1,0,0,
A+99,Y,X,A,0,0,0,0,0,.25,.25,C,1,0,0,Y,50.8,-A-25,A,0,0,0,0,
0,C,C,C,1,0,0,Y,-A+10,X,A,0,0,0,0,0,C,C,C,1,0,0,Y,A+91.6,X,A,
0,0,0,0,0,C,C,C,1,0,0,27,26.5,47,16.5,0,0,0,0,0,B,B,B,0,1,0,
73,26.5,78,16.5,0,0,0,0,0,B,B,B,0,0,1},t,tt,d,b,W,K=0.04,F,J;
double r(){return (double)rand()/(double)RAND_MAX;};
double ppm(double x){return pow(x<0?0:x>1?1:x,1/2.2);}
main(int I, char** argv) {
printf("P3\n480 360\n255\n", F=(1-K),J=1/1.5,W=2*M_PI);
for (int sw=480,sh=360,S=16384,i=0,s=0,j;i<sw*sh;i++) {
fprintf(stderr,"\r%.2f%%",(100.*i)/(sw*sh));
X=sw*.5135/sh,Y=-.5135,A=B=C=0,o=(V*)Q,M=V(1,1,1);
for (double a=0,U=i%sw,E,P,k,e;a<S;a++,b=r()-r(),U=i%sw+b) {
ro=O,l=Z,f=M,D=h(V(X*(U/sw-.5),Y*((i*1./sw+b)/sh-.5),-1));
for (double t=1e3,m=2,r1,r2,q,H,j=0,G;(r2=r())<=m;t=1e3) {
for (V*ss=(V*)Q;j<8;ss+=5,q=sqrt(r2)) {
u=ss[0]-ro,b=D*u,d=b*b-u*u+ss[1].x*ss[1].x,r1=r();j++;
if ((tt=b-sqrt(d>=0?d:1e9))>1e-4 && tt<t) t=tt,o=ss;
if ((tt=b+sqrt(d>=0?d:1e9))>1e-4 && tt<t) t=tt,o=ss;
}
n=h((ro=ro+D*t)-*o),N=(n*D)<0?n:n*-1,l=l+f^o[2],c=o[3];
m=fmax(c.x,fmax(c.y,c.z)),r1*=W,c=c*(1/m),f=f^c,d=D*N,
u=h((fabs(N.x)>.1?V(0,1):V(1)).c(N)),k=(G=n*N>0)?J:1.5,
dd=h(u*cos(r1)*q+N.c(u)*sin(r1)*q+N*sqrt(1-r2)),H=d*d,
H=1-k*k*(1-H),v=h(D*k-n*((G?1:-1)*(d*k+sqrt(H)))),
gd=sd=D-n*2*(n*D),I=H>=0,E=K+F*pow(1-(G?-d:v*n),5),
P=.25+E/2,f=I?r2<P?f*E*(1/P):f*((1-E)/(1-P)):f,L=o[4],
gd=I?r2<P?sd:v:gd,D=dd*L.x+sd*L.y+gd*L.z;j=0;
}
l=l*(1./S),A+=fmin(l.x,1),B+=fmin(l.y,1),C+=fmin(l.z,1);
}
printf("%.0f %.0f %.0f ", V(ppm(A),ppm(B),ppm(C))*255);
}
}
Синтезируемое изображение
$ g++ -O3 weert.cpp; ./a.out > r.ppm
Наблюдения
Код вмещается в 64-колоночное окно без прокрутки и насчитывает 49 строк:
- примерно четверть кода занимает векторная алгебра
- другую четверть — описание сцены
- оставшиеся две четверти занимают вычисления
Вычислительная часть кода напоминает brainfuck]
Пожалуй, компактификация выполнена не очень творчески, поскольку не было предложено качественно новых компактных или аппроксимирующих альтернатив относительно исходника, но были лишь оптимизированы существующие. Возможно, что либо размер может быть ещё уменьшен, либо код может быть упрощён.
Ссылки
- smallpt — миниатюрный path tracer (на странице проекта есть много хороших ссылок)
- minilight — другой маленький path tracer с MLT
- Paul S. Heckbert. A Minimal Ray Tracer — статья о контесте по компактным рей-трейсерам из книги Graphics Gems, том IV
- Christophe Schlick. A Customizable Reectance Model for Everyday Rendering — хорошая статья по моделям материалов aka BRDF, которые используются в PT
До встречи в виртуальной реальности, визуализированной с помощью лучших алгоритмов растеризации!
shitpoet@gmail.com