i'm an idiot: let threads consume working queue!
[raymarch.git] / raytracer.d
blobe83952fbc650a36a6b1114f82eecfade5067c859
1 module raytracer;
3 private:
4 import objfn;
5 import vecs;
6 import world;
9 // ////////////////////////////////////////////////////////////////////////// //
10 public struct LightInfo {
11 bool active;
12 Vec3 origin;
13 Vec3 color;
17 // ////////////////////////////////////////////////////////////////////////// //
18 // raymarch global vars
19 public __gshared RmFloat worldtime = 0; // seconds
20 public __gshared Vec3 vrp; // view reference point
21 public __gshared Vec3 vuv; // view up vector
22 public __gshared Vec3 prp; // camera position
25 immutable Vec3 eps = Vec3(rmFloat!0.001, rmFloat!0.0, rmFloat!0.0);
26 immutable Vec3 exyy = eps.xyy;
27 immutable Vec3 eyxy = eps.yxy;
28 immutable Vec3 eyyx = eps.yyx;
31 // ////////////////////////////////////////////////////////////////////////// //
32 // main raycaster routine
33 struct RayCastRes {
34 nothrow @safe @nogc:
35 int obj; // obj id or -1 if we doesn't hit anything
36 // the following fields are undefined if obj is -1
37 RmFloat objdist; // in teh world, from ray origin to nearest object
38 RmFloat dist; // how much we traveled?
39 Vec3 hitp; // hitpoint
41 @property bool hit () const pure { pragma(inline, true); return (obj >= 0); }
45 void castRay() (ref RayCastRes res, in auto ref Vec3 ro, in auto ref Vec3 rd, RmFloat tmin=rmFloat!1.0, RmFloat tmax=rmFloat!20.0) {
46 enum MaxSteps = 1024; // arbitrary limit, it will be culled by precision
47 enum precis = rmFloat!0.002;
48 RmFloat t = tmin;
49 int obj = 0;
50 Vec3 rp = void;
51 RmFloat odst = void;
52 int i = 0;
53 for (; i < MaxSteps; ++i) {
54 rp = ro+rd*t;
55 odst = mapWorld(rp, obj);
56 if (odst < precis || t > tmax) break;
57 t += odst;
59 //if (i >= MaxSteps) t -= odst;
60 //if (i >= MaxSteps) t = tmax+rmFloat!1;
61 if (i >= MaxSteps || t > tmax) {
62 res.obj = -1;
63 //res.dist = tmax+rmFloat!1; // just in case
64 } else {
65 res.obj = (obj < 0 ? 0 : obj);
66 res.objdist = odst;
67 res.dist = t;
68 res.hitp = rp;
73 // ////////////////////////////////////////////////////////////////////////// //
74 // calc normal to the nearest hitpoint
75 Vec3 calcNormal() (in auto ref Vec3 pos) {
76 return Vec3(
77 mapWorld(pos+exyy)-mapWorld(pos-exyy),
78 mapWorld(pos+eyxy)-mapWorld(pos-eyxy),
79 mapWorld(pos+eyyx)-mapWorld(pos-eyyx)
80 ).normalize;
84 // calc ambient occlusion
85 RmFloat calcAO() (in auto ref Vec3 pos, in auto ref Vec3 nor) {
86 RmFloat occ = rmFloat!0.0;
87 RmFloat sca = rmFloat!1.0;
88 for (int i = 0; i < 5; ++i) {
89 RmFloat hr = rmFloat!0.01+rmFloat!0.12*cast(RmFloat)i/rmFloat!4.0;
90 Vec3 aopos = nor*hr+pos;
91 RmFloat dd = mapWorld(aopos);
92 occ += -(dd-hr)*sca;
93 sca *= rmFloat!0.95;
95 return clamp(rmFloat!1.0-rmFloat!3.0*occ, rmFloat!0.0, rmFloat!1.0);
99 // trace hard shadow
100 RmFloat shadow() (in auto ref Vec3 ro, in auto ref Vec3 rd, RmFloat mint, RmFloat tmax) {
101 import std.algorithm : min;
102 RmFloat res = rmFloat!1.0;
103 RmFloat t = mint;
104 for (int i = 0; i < 16; ++i) {
105 RmFloat h = mapWorld(ro+rd*t);
106 t += clamp(h, rmFloat!0.02, rmFloat!0.10);
107 if (h < rmFloat!0.001 || t > tmax) return rmFloat!0.0;
109 return rmFloat!1.0;
113 // trace soft shadow
114 RmFloat softshadow() (in auto ref Vec3 ro, in auto ref Vec3 rd, RmFloat mint, RmFloat tmax, RmFloat k=rmFloat!8.0) {
115 import std.algorithm : min;
116 RmFloat res = rmFloat!1.0;
117 RmFloat t = mint;
118 for (int i = 0; i < 16; ++i) {
119 RmFloat h = mapWorld(ro+rd*t);
120 res = min(res, k*h/t);
121 t += clamp(h, rmFloat!0.02, rmFloat!0.10);
122 if (h < rmFloat!0.001 || t > tmax) break;
124 return clamp(res, rmFloat!0.0, rmFloat!1.0);
128 // ////////////////////////////////////////////////////////////////////////// //
129 // compute screen space derivatives of positions analytically without dPdx()
130 void calcDpDxy() (in auto ref Vec3 ro, in auto ref Vec3 rd, in auto ref Vec3 rdx, in auto ref Vec3 rdy, RmFloat t, in auto ref Vec3 nor, ref Vec3 dpdx, ref Vec3 dpdy) {
131 dpdx = (rdx*rd.dot(nor)/rdx.dot(nor)-rd)*t;
132 dpdy = (rdy*rd.dot(nor)/rdy.dot(nor)-rd)*t;
135 // ////////////////////////////////////////////////////////////////////////// //
136 // raymarching
137 // light 0: fog
138 public void raymarch() (/*RmFloat x, RmFloat y,*/ in auto ref Vec3 rd, const(LightInfo)[] lights, ref Vec3 color, in auto ref Vec3 rdx, in auto ref Vec3 rdy) @nogc {
139 enum maxd = rmFloat!100.0; // max depth
141 RayCastRes rcres = void;
143 castRay(rcres, prp, rd, rmFloat!1.0, maxd);
145 // did we hit something?
146 if (rcres.hit) {
147 import std.math : exp, pow;
149 Vec3 nor = calcNormal(rcres.hitp);
150 Vec3 refl = rd.reflect(nor);
152 Vec3 dposdx = void, dposdy = void;
153 calcDpDxy(prp, rd, rdx, rdy, rcres.dist, nor, dposdx, dposdy);
154 // get primitive color
155 auto col = getObjColor(rcres.hitp, rcres.obj);
157 // old
158 //vec2 uv = textureMapping( pos, oid );
159 //Vec3 sur = texture( sampler, uv );
160 // new
161 //vec2 (uv,duvdx,duvdy) = textureMapping( pos, dposdx, dposdy, oid );
162 //Vec3 sur = textureGrad( sampler, uv, dudvx, dudvy );
164 // lighting
165 RmFloat occ = calcAO(rcres.hitp, nor);
166 foreach (immutable lidx; 1..lights.length) {
167 auto lt = lights.ptr+lidx;
168 if (lt.active) {
169 //Vec3 lig = Vec3(-rmFloat!0.6, rmFloat!0.7, -rmFloat!0.5).normalize;
170 Vec3 lig = lt.origin.normalized;
171 RmFloat amb = clamp(rmFloat!0.5+rmFloat!0.5*nor.y, rmFloat!0.0, rmFloat!1.0);
172 RmFloat dif = clamp(nor.dot(lig), rmFloat!0.0, rmFloat!1.0);
173 RmFloat bac = clamp(nor.dot(Vec3(-lig.x, rmFloat!0.0, -lig.z).normalize), rmFloat!0.0, rmFloat!1.0)*clamp(rmFloat!1.0-rcres.hitp.y, rmFloat!0.0, rmFloat!1.0);
174 RmFloat dom = smoothstep(-rmFloat!0.1, rmFloat!0.1, refl.y);
175 RmFloat fre = pow(clamp(rmFloat!1.0+nor.dot(rd), rmFloat!0.0, rmFloat!1.0), rmFloat!2.0);
176 RmFloat spe = pow(clamp(refl.dot(lig), rmFloat!0.0, rmFloat!1.0), rmFloat!16.0);
178 dif *= softshadow(rcres.hitp, lig, rmFloat!0.02, maxd);
179 dom *= softshadow(rcres.hitp, refl, rmFloat!0.02, maxd);
181 // light color
182 //immutable lc = Vec3(rmFloat!1.00, rmFloat!0.85, rmFloat!0.55);
183 //immutable lc = Vec3(rmFloat!0.0, rmFloat!0.0, rmFloat!0.0);
184 // calculate light
185 auto lc = &lt.color;
186 Vec3 lin = Vec3(rmFloat!0.0, rmFloat!0.0, rmFloat!0.0);
187 lin += (*lc)*(rmFloat!1.20*dif);
188 lin += (*lc)*(rmFloat!1.20*spe)*dif;
189 // ambient occlusion
190 lin += Vec3(rmFloat!0.50, rmFloat!0.70, rmFloat!1.00)*(rmFloat!0.20*amb)*occ;
191 lin += Vec3(rmFloat!0.50, rmFloat!0.70, rmFloat!1.00)*(rmFloat!0.30*dom)*occ;
192 lin += Vec3(rmFloat!0.25, rmFloat!0.25, rmFloat!0.25)*(rmFloat!0.30*bac)*occ;
193 lin += Vec3(rmFloat!1.00, rmFloat!1.00, rmFloat!1.00)*(rmFloat!0.40*fre)*occ;
194 col *= lin;
197 if (lights.length && lights.ptr[0].active) {
198 // fog
199 col = mix(col, lights.ptr[0].color, rmFloat!1.0-exp(-rmFloat!0.002*rcres.dist*rcres.dist));
201 // done
202 color = col;
203 } else {
204 // background color
205 color = Vec3(rmFloat!0.7, rmFloat!0.9, rmFloat!1.0)+rd.y*rmFloat!0.8;