clray

annotate src/dbgray.cc @ 55:df239a52a091

extensive render stats for the CPU raytracer
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 11 Sep 2010 03:00:21 +0100
parents 6a30f27fa1e6
children
rev   line source
nuclear@54 1 #include <string.h>
nuclear@54 2 #include <assert.h>
nuclear@55 3 #include <limits.h>
nuclear@54 4 #include "rt.h"
nuclear@54 5 #include "ogl.h"
nuclear@54 6 #include "vector.h"
nuclear@54 7 #include "timer.h"
nuclear@54 8
nuclear@54 9 struct SurfPoint {
nuclear@54 10 float t;
nuclear@54 11 Vector3 pos, norm;
nuclear@54 12 const Face *face;
nuclear@54 13 };
nuclear@54 14
nuclear@54 15 static void trace_ray(float *pixel, const Ray &ray, int iter, float energy = 1.0f);
nuclear@54 16 static void shade(float *pixel, const Ray &ray, const SurfPoint &sp, int iter, float energy = 1.0f);
nuclear@54 17 static bool find_intersection(const Ray &ray, const Scene *scn, const KDNode *kd, SurfPoint *spret);
nuclear@54 18 static bool ray_aabb_test(const Ray &ray, const AABBox &aabb);
nuclear@54 19 static bool ray_triangle_test(const Ray &ray, const Face *face, SurfPoint *sp);
nuclear@54 20 static Vector3 calc_bary(const Vector3 &pt, const Face *face, const Vector3 &norm);
nuclear@54 21 static void transform(float *res, const float *v, const float *xform);
nuclear@54 22 static void transform_ray(Ray *ray, const float *xform, const float *invtrans_xform);
nuclear@54 23
nuclear@54 24 static int xsz, ysz;
nuclear@54 25 static float *fb;
nuclear@54 26 static unsigned int tex;
nuclear@54 27 static Scene *scn;
nuclear@54 28 static const Ray *prim_rays;
nuclear@55 29 static int max_iter;
nuclear@54 30
nuclear@55 31 static RenderStats *rstat;
nuclear@55 32 static int cur_ray_aabb_tests, cur_ray_triangle_tests;
nuclear@54 33
nuclear@54 34 bool init_dbg_renderer(int width, int height, Scene *scene, unsigned int texid)
nuclear@54 35 {
nuclear@54 36 try {
nuclear@54 37 fb = new float[3 * width * height];
nuclear@54 38 }
nuclear@54 39 catch(...) {
nuclear@54 40 return false;
nuclear@54 41 }
nuclear@54 42
nuclear@54 43 xsz = width;
nuclear@54 44 ysz = height;
nuclear@54 45 tex = texid;
nuclear@54 46 scn = scene;
nuclear@55 47
nuclear@55 48 rstat = (RenderStats*)get_render_stats();
nuclear@55 49
nuclear@54 50 return true;
nuclear@54 51 }
nuclear@54 52
nuclear@54 53 void destroy_dbg_renderer()
nuclear@54 54 {
nuclear@54 55 delete [] fb;
nuclear@54 56 delete [] prim_rays;
nuclear@54 57 }
nuclear@54 58
nuclear@54 59 void dbg_set_primary_rays(const Ray *rays)
nuclear@54 60 {
nuclear@54 61 prim_rays = rays;
nuclear@54 62 }
nuclear@54 63
nuclear@54 64 void dbg_render(const float *xform, const float *invtrans_xform, int num_threads)
nuclear@54 65 {
nuclear@54 66 unsigned long t0 = get_msec();
nuclear@54 67
nuclear@55 68 max_iter = get_render_option_int(ROPT_ITER);
nuclear@55 69
nuclear@55 70 // initialize render-stats
nuclear@55 71 memset(rstat, 0, sizeof *rstat);
nuclear@55 72 rstat->min_aabb_tests = rstat->min_triangle_tests = INT_MAX;
nuclear@55 73 rstat->max_aabb_tests = rstat->max_triangle_tests = 0;
nuclear@54 74
nuclear@54 75 int offs = 0;
nuclear@54 76 for(int i=0; i<ysz; i++) {
nuclear@54 77 for(int j=0; j<xsz; j++) {
nuclear@54 78 Ray ray = prim_rays[offs];
nuclear@54 79 transform_ray(&ray, xform, invtrans_xform);
nuclear@54 80
nuclear@55 81 cur_ray_aabb_tests = cur_ray_triangle_tests = 0;
nuclear@55 82
nuclear@55 83 trace_ray(fb + offs * 3, ray, max_iter, 1.0);
nuclear@54 84 offs++;
nuclear@55 85
nuclear@55 86 // update stats as needed
nuclear@55 87 if(cur_ray_aabb_tests < rstat->min_aabb_tests) {
nuclear@55 88 rstat->min_aabb_tests = cur_ray_aabb_tests;
nuclear@55 89 }
nuclear@55 90 if(cur_ray_aabb_tests > rstat->max_aabb_tests) {
nuclear@55 91 rstat->max_aabb_tests = cur_ray_aabb_tests;
nuclear@55 92 }
nuclear@55 93 if(cur_ray_triangle_tests < rstat->min_triangle_tests) {
nuclear@55 94 rstat->min_triangle_tests = cur_ray_triangle_tests;
nuclear@55 95 }
nuclear@55 96 if(cur_ray_triangle_tests > rstat->max_triangle_tests) {
nuclear@55 97 rstat->max_triangle_tests = cur_ray_triangle_tests;
nuclear@55 98 }
nuclear@55 99 rstat->prim_rays++;
nuclear@55 100 rstat->aabb_tests += cur_ray_aabb_tests;
nuclear@55 101 rstat->triangle_tests += cur_ray_triangle_tests;
nuclear@54 102 }
nuclear@54 103 }
nuclear@54 104
nuclear@55 105 unsigned long t1 = get_msec();
nuclear@55 106
nuclear@54 107 glPushAttrib(GL_TEXTURE_BIT);
nuclear@54 108 glBindTexture(GL_TEXTURE_2D, tex);
nuclear@54 109 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, xsz, ysz, GL_RGB, GL_FLOAT, fb);
nuclear@54 110 glPopAttrib();
nuclear@55 111 glFinish();
nuclear@54 112
nuclear@55 113 rstat->render_time = t1 - t0;
nuclear@55 114 rstat->tex_update_time = get_msec() - t1;
nuclear@55 115
nuclear@55 116 rstat->rays_cast = rstat->prim_rays + rstat->refl_rays + rstat->shadow_rays;
nuclear@55 117 rstat->rays_per_sec = 1000 * rstat->rays_cast / rstat->render_time;
nuclear@55 118 rstat->avg_aabb_tests = (float)rstat->aabb_tests / (float)rstat->rays_cast;
nuclear@55 119 rstat->avg_triangle_tests = (float)rstat->triangle_tests / (float)rstat->rays_cast;
nuclear@54 120 }
nuclear@54 121
nuclear@54 122 static void trace_ray(float *pixel, const Ray &ray, int iter, float energy)
nuclear@54 123 {
nuclear@54 124 SurfPoint sp;
nuclear@54 125
nuclear@54 126 if(find_intersection(ray, scn, scn->kdtree, &sp)) {
nuclear@54 127 shade(pixel, ray, sp, iter, energy);
nuclear@54 128 } else {
nuclear@54 129 pixel[0] = pixel[1] = pixel[2] = 0.05f;
nuclear@54 130 }
nuclear@54 131 }
nuclear@54 132
nuclear@54 133 #define MAX(a, b) ((a) > (b) ? (a) : (b))
nuclear@54 134
nuclear@54 135 static void shade(float *pixel, const Ray &ray, const SurfPoint &sp, int iter, float energy)
nuclear@54 136 {
nuclear@54 137 const Material *mat = scn->get_materials() + sp.face->matid;
nuclear@54 138
nuclear@54 139 bool cast_shadows = get_render_option_bool(ROPT_SHAD);
nuclear@54 140 Vector3 raydir(ray.dir);
nuclear@54 141 Vector3 norm = sp.norm;
nuclear@54 142
nuclear@54 143 if(dot(raydir, norm) >= 0.0) {
nuclear@54 144 norm = -norm;
nuclear@54 145 }
nuclear@54 146
nuclear@54 147 float dcol[3] = {0, 0, 0};
nuclear@54 148 float scol[3] = {0, 0, 0};
nuclear@54 149
nuclear@54 150 for(int i=0; i<scn->get_num_lights(); i++) {
nuclear@54 151 Vector3 lpos(scn->lights[i].pos);
nuclear@54 152 Vector3 ldir = lpos - sp.pos;
nuclear@54 153
nuclear@54 154 Ray shadowray;
nuclear@54 155 shadowray.origin[0] = sp.pos.x;
nuclear@54 156 shadowray.origin[1] = sp.pos.y;
nuclear@54 157 shadowray.origin[2] = sp.pos.z;
nuclear@54 158 shadowray.dir[0] = ldir.x;
nuclear@54 159 shadowray.dir[1] = ldir.y;
nuclear@54 160 shadowray.dir[2] = ldir.z;
nuclear@54 161
nuclear@54 162 if(!cast_shadows || !find_intersection(shadowray, scn, scn->kdtree, 0)) {
nuclear@55 163 rstat->brdf_evals++;
nuclear@54 164
nuclear@54 165 ldir.normalize();
nuclear@54 166
nuclear@54 167 Vector3 vdir = -raydir / RAY_MAG;
nuclear@54 168 Vector3 vref = reflect(vdir, norm);
nuclear@54 169
nuclear@54 170 float ndotl = dot(ldir, norm);
nuclear@54 171 float diff = MAX(ndotl, 0.0f);
nuclear@54 172
nuclear@54 173 dcol[0] += mat->kd[0] * diff;
nuclear@54 174 dcol[1] += mat->kd[1] * diff;
nuclear@54 175 dcol[2] += mat->kd[2] * diff;
nuclear@54 176
nuclear@54 177 float ldotvr = dot(ldir, vref);
nuclear@54 178 float spec = pow(MAX(ldotvr, 0.0f), mat->spow);
nuclear@54 179
nuclear@54 180 scol[0] += mat->ks[0] * spec;
nuclear@54 181 scol[1] += mat->ks[1] * spec;
nuclear@54 182 scol[2] += mat->ks[2] * spec;
nuclear@54 183 }
nuclear@55 184
nuclear@55 185 if(cast_shadows) {
nuclear@55 186 rstat->shadow_rays++;
nuclear@55 187 }
nuclear@54 188 }
nuclear@54 189
nuclear@54 190 float refl_color[3];
nuclear@54 191 refl_color[0] = mat->ks[0] * mat->kr;
nuclear@54 192 refl_color[1] = mat->ks[1] * mat->kr;
nuclear@54 193 refl_color[2] = mat->ks[2] * mat->kr;
nuclear@54 194
nuclear@54 195 energy *= (refl_color[0] + refl_color[1] + refl_color[2]) / 3.0;
nuclear@54 196 if(iter >= 0 && energy > MIN_ENERGY) {
nuclear@54 197 Vector3 rdir = reflect(-raydir, norm);
nuclear@54 198
nuclear@54 199 Ray refl;
nuclear@54 200 refl.origin[0] = sp.pos.x;
nuclear@54 201 refl.origin[1] = sp.pos.y;
nuclear@54 202 refl.origin[2] = sp.pos.z;
nuclear@54 203 refl.dir[0] = rdir.x;
nuclear@54 204 refl.dir[1] = rdir.y;
nuclear@54 205 refl.dir[2] = rdir.z;
nuclear@54 206
nuclear@54 207 float rcol[3];
nuclear@54 208 trace_ray(rcol, refl, iter - 1, energy);
nuclear@54 209 scol[0] += rcol[0] * mat->ks[0] * mat->kr;
nuclear@54 210 scol[1] += rcol[1] * mat->ks[1] * mat->kr;
nuclear@54 211 scol[2] += rcol[2] * mat->ks[2] * mat->kr;
nuclear@55 212
nuclear@55 213 rstat->refl_rays++;
nuclear@54 214 }
nuclear@54 215
nuclear@54 216 pixel[0] = dcol[0] + scol[0];
nuclear@54 217 pixel[1] = dcol[1] + scol[1];
nuclear@54 218 pixel[2] = dcol[2] + scol[2];
nuclear@54 219 }
nuclear@54 220
nuclear@54 221 static bool find_intersection(const Ray &ray, const Scene *scn, const KDNode *kd, SurfPoint *spret)
nuclear@54 222 {
nuclear@54 223 if(!ray_aabb_test(ray, kd->aabb)) {
nuclear@54 224 return false;
nuclear@54 225 }
nuclear@54 226
nuclear@54 227 SurfPoint sp, sptmp;
nuclear@54 228 if(!spret) {
nuclear@54 229 spret = &sptmp;
nuclear@54 230 }
nuclear@54 231
nuclear@54 232 spret->t = RAY_MAG;
nuclear@54 233 spret->face = 0;
nuclear@54 234
nuclear@54 235 if(kd->left) {
nuclear@54 236 assert(kd->right);
nuclear@54 237
nuclear@54 238 bool found = find_intersection(ray, scn, kd->left, spret);
nuclear@54 239 if(find_intersection(ray, scn, kd->right, &sp)) {
nuclear@54 240 if(!found || sp.t < spret->t) {
nuclear@54 241 *spret = sp;
nuclear@54 242 }
nuclear@54 243 found = true;
nuclear@54 244 }
nuclear@54 245 return found;
nuclear@54 246 }
nuclear@54 247
nuclear@54 248 const Face *faces = scn->get_face_buffer();
nuclear@54 249
nuclear@54 250 for(size_t i=0; i<kd->face_idx.size(); i++) {
nuclear@54 251 if(ray_triangle_test(ray, faces + kd->face_idx[i], &sp) && sp.t < spret->t) {
nuclear@54 252 *spret = sp;
nuclear@54 253 }
nuclear@54 254 }
nuclear@54 255 return spret->face != 0;
nuclear@54 256 }
nuclear@54 257
nuclear@54 258 static bool ray_aabb_test(const Ray &ray, const AABBox &aabb)
nuclear@54 259 {
nuclear@55 260 cur_ray_aabb_tests++;
nuclear@55 261
nuclear@54 262 if(ray.origin[0] >= aabb.min[0] && ray.origin[1] >= aabb.min[1] && ray.origin[2] >= aabb.min[2] &&
nuclear@54 263 ray.origin[0] < aabb.max[0] && ray.origin[1] < aabb.max[1] && ray.origin[2] < aabb.max[2]) {
nuclear@54 264 return true;
nuclear@54 265 }
nuclear@54 266
nuclear@54 267 float bbox[][3] = {
nuclear@54 268 {aabb.min[0], aabb.min[1], aabb.min[2]},
nuclear@54 269 {aabb.max[0], aabb.max[1], aabb.max[2]}
nuclear@54 270 };
nuclear@54 271
nuclear@54 272 int xsign = (int)(ray.dir[0] < 0.0);
nuclear@54 273 float invdirx = 1.0 / ray.dir[0];
nuclear@54 274 float tmin = (bbox[xsign][0] - ray.origin[0]) * invdirx;
nuclear@54 275 float tmax = (bbox[1 - xsign][0] - ray.origin[0]) * invdirx;
nuclear@54 276
nuclear@54 277 int ysign = (int)(ray.dir[1] < 0.0);
nuclear@54 278 float invdiry = 1.0 / ray.dir[1];
nuclear@54 279 float tymin = (bbox[ysign][1] - ray.origin[1]) * invdiry;
nuclear@54 280 float tymax = (bbox[1 - ysign][1] - ray.origin[1]) * invdiry;
nuclear@54 281
nuclear@54 282 if(tmin > tymax || tymin > tmax) {
nuclear@54 283 return false;
nuclear@54 284 }
nuclear@54 285
nuclear@54 286 if(tymin > tmin) tmin = tymin;
nuclear@54 287 if(tymax < tmax) tmax = tymax;
nuclear@54 288
nuclear@54 289 int zsign = (int)(ray.dir[2] < 0.0);
nuclear@54 290 float invdirz = 1.0 / ray.dir[2];
nuclear@54 291 float tzmin = (bbox[zsign][2] - ray.origin[2]) * invdirz;
nuclear@54 292 float tzmax = (bbox[1 - zsign][2] - ray.origin[2]) * invdirz;
nuclear@54 293
nuclear@54 294 if(tmin > tzmax || tzmin > tmax) {
nuclear@54 295 return false;
nuclear@54 296 }
nuclear@54 297
nuclear@54 298 return tmin < 1.0 && tmax > 0.0;
nuclear@54 299
nuclear@54 300 }
nuclear@54 301
nuclear@54 302 static bool ray_triangle_test(const Ray &ray, const Face *face, SurfPoint *sp)
nuclear@54 303 {
nuclear@55 304 cur_ray_triangle_tests++;
nuclear@55 305
nuclear@54 306 Vector3 origin = ray.origin;
nuclear@54 307 Vector3 dir = ray.dir;
nuclear@54 308 Vector3 norm = face->normal;
nuclear@54 309
nuclear@54 310 float ndotdir = dot(dir, norm);
nuclear@54 311
nuclear@54 312 if(fabs(ndotdir) <= EPSILON) {
nuclear@54 313 return false;
nuclear@54 314 }
nuclear@54 315
nuclear@54 316 Vector3 pt = face->v[0].pos;
nuclear@54 317 Vector3 vec = pt - origin;
nuclear@54 318
nuclear@54 319 float ndotvec = dot(norm, vec);
nuclear@54 320 float t = ndotvec / ndotdir;
nuclear@54 321
nuclear@54 322 if(t < EPSILON || t > 1.0) {
nuclear@54 323 return false;
nuclear@54 324 }
nuclear@54 325 pt = origin + dir * t;
nuclear@54 326
nuclear@54 327
nuclear@54 328 Vector3 bc = calc_bary(pt, face, norm);
nuclear@54 329 float bc_sum = bc.x + bc.y + bc.z;
nuclear@54 330
nuclear@54 331 if(bc_sum < 1.0 - EPSILON || bc_sum > 1.0 + EPSILON) {
nuclear@54 332 return false;
nuclear@54 333 }
nuclear@54 334
nuclear@54 335 Vector3 n0(face->v[0].normal);
nuclear@54 336 Vector3 n1(face->v[1].normal);
nuclear@54 337 Vector3 n2(face->v[2].normal);
nuclear@54 338
nuclear@54 339 sp->t = t;
nuclear@54 340 sp->pos = pt;
nuclear@54 341 sp->norm = n0 * bc.x + n1 * bc.y + n2 * bc.z;
nuclear@54 342 sp->norm.normalize();
nuclear@54 343 sp->face = face;
nuclear@54 344 return true;
nuclear@54 345 }
nuclear@54 346
nuclear@54 347 static Vector3 calc_bary(const Vector3 &pt, const Face *face, const Vector3 &norm)
nuclear@54 348 {
nuclear@54 349 Vector3 bc(0.0f, 0.0f, 0.0f);
nuclear@54 350
nuclear@54 351 Vector3 v1 = Vector3(face->v[1].pos) - Vector3(face->v[0].pos);
nuclear@54 352 Vector3 v2 = Vector3(face->v[2].pos) - Vector3(face->v[0].pos);
nuclear@54 353 Vector3 xv1v2 = cross(v1, v2);
nuclear@54 354
nuclear@54 355 float area = fabs(dot(xv1v2, norm)) * 0.5;
nuclear@54 356 if(area < EPSILON) {
nuclear@54 357 return bc;
nuclear@54 358 }
nuclear@54 359
nuclear@54 360 Vector3 pv0 = face->v[0].pos - pt;
nuclear@54 361 Vector3 pv1 = face->v[1].pos - pt;
nuclear@54 362 Vector3 pv2 = face->v[2].pos - pt;
nuclear@54 363
nuclear@54 364 // calculate the area of each sub-triangle
nuclear@54 365 Vector3 x12 = cross(pv1, pv2);
nuclear@54 366 Vector3 x20 = cross(pv2, pv0);
nuclear@54 367 Vector3 x01 = cross(pv0, pv1);
nuclear@54 368
nuclear@54 369 float a0 = fabs(dot(x12, norm)) * 0.5;
nuclear@54 370 float a1 = fabs(dot(x20, norm)) * 0.5;
nuclear@54 371 float a2 = fabs(dot(x01, norm)) * 0.5;
nuclear@54 372
nuclear@54 373 bc.x = a0 / area;
nuclear@54 374 bc.y = a1 / area;
nuclear@54 375 bc.z = a2 / area;
nuclear@54 376 return bc;
nuclear@54 377
nuclear@54 378 }
nuclear@54 379
nuclear@54 380 static void transform(float *res, const float *v, const float *xform)
nuclear@54 381 {
nuclear@54 382 float tmp[3];
nuclear@54 383 tmp[0] = v[0] * xform[0] + v[1] * xform[4] + v[2] * xform[8] + xform[12];
nuclear@54 384 tmp[1] = v[0] * xform[1] + v[1] * xform[5] + v[2] * xform[9] + xform[13];
nuclear@54 385 tmp[2] = v[0] * xform[2] + v[1] * xform[6] + v[2] * xform[10] + xform[14];
nuclear@54 386 memcpy(res, tmp, sizeof tmp);
nuclear@54 387 }
nuclear@54 388
nuclear@54 389 static void transform_ray(Ray *ray, const float *xform, const float *invtrans_xform)
nuclear@54 390 {
nuclear@54 391 transform(ray->origin, ray->origin, xform);
nuclear@54 392 transform(ray->dir, ray->dir, invtrans_xform);
nuclear@54 393 }