void ambient_occlusion(vec *col, const Isect *isect) { int i, j; int ntheta = AOBENCH_NAO_SAMPLES; int nphi = AOBENCH_NAO_SAMPLES; aobfloat eps = 0.0001; vec p; p.x = isect->p.x + eps * isect->n.x; p.y = isect->p.y + eps * isect->n.y; p.z = isect->p.z + eps * isect->n.z; vec basis[3]; orthoBasis(basis, isect->n); aobfloat occlusion = 0.0; for (j = 0; j < ntheta; j++) { for (i = 0; i < nphi; i++) { aobfloat theta = sqrt(drand48()); aobfloat phi = 2.0 * M_PI * drand48(); aobfloat x = cos(phi) * theta; aobfloat y = sin(phi) * theta; aobfloat z = sqrt(1.0 - theta * theta); // local -> global aobfloat rx = x * basis[0].x + y * basis[1].x + z * basis[2].x; aobfloat ry = x * basis[0].y + y * basis[1].y + z * basis[2].y; aobfloat rz = x * basis[0].z + y * basis[1].z + z * basis[2].z; Ray ray; ray.org = p; ray.dir.x = rx; ray.dir.y = ry; ray.dir.z = rz; Isect occIsect; occIsect.t = 1.0e+17; occIsect.hit = 0; ray_sphere_intersect(&occIsect, &ray, &spheres[0]); ray_sphere_intersect(&occIsect, &ray, &spheres[1]); ray_sphere_intersect(&occIsect, &ray, &spheres[2]); ray_plane_intersect (&occIsect, &ray, &plane); if (occIsect.hit) occlusion += 1.0; } } occlusion = (ntheta * nphi - occlusion) / (aobfloat)(ntheta * nphi); col->x = occlusion; col->y = occlusion; col->z = occlusion; }
static void render_tile(unsigned char *img, int comps, float *fimg, int w, int h, int nsubsamples, int tilex, int tiley, int tilew, int tileh) { int endx = tilex + min_i(tilew, w - tilex); int endy = tiley + min_i(tileh, h - tiley); int x, y; int u, v; for (y = tiley; y < endy; y++) { for (x = tilex; x < endx; x++) { for (v = 0; v < nsubsamples; v++) { for (u = 0; u < nsubsamples; u++) { float px = (x + (u / (float)nsubsamples) - (w / 2.0)) / (w / 2.0); float py = -(y + (v / (float)nsubsamples) - (h / 2.0)) / (h / 2.0); Ray ray; ray.org.x = 0.0; ray.org.y = 0.0; ray.org.z = 0.0; ray.dir.x = px; ray.dir.y = py; ray.dir.z = -1.0; vnormalize(&(ray.dir)); Isect isect; isect.t = 1.0e+17; isect.hit = 0; ray_sphere_intersect(&isect, &ray, &spheres[0]); ray_sphere_intersect(&isect, &ray, &spheres[1]); ray_sphere_intersect(&isect, &ray, &spheres[2]); ray_plane_intersect (&isect, &ray, &plane); if (isect.hit) { vec col; ambient_occlusion(&col, &isect); fimg[3 * (y * w + x) + 0] += col.x; fimg[3 * (y * w + x) + 1] += col.y; fimg[3 * (y * w + x) + 2] += col.z; } } } fimg[3 * (y * w + x) + 0] /= (float)(nsubsamples * nsubsamples); fimg[3 * (y * w + x) + 1] /= (float)(nsubsamples * nsubsamples); fimg[3 * (y * w + x) + 2] /= (float)(nsubsamples * nsubsamples); img[comps * (y * w + x) + 0] = clamp(fimg[3 *(y * w + x) + 0]); img[comps * (y * w + x) + 1] = clamp(fimg[3 *(y * w + x) + 1]); img[comps * (y * w + x) + 2] = clamp(fimg[3 *(y * w + x) + 2]); } } }
/* Compute the image for the scanlines from [y0,y1), for an overall image of width w and height h. */ static void ao_scanlines(int y0, int y1, int w, int h, int nsubsamples, float image[]) { static Plane plane = { vec(0.0f, -0.5f, 0.0f), vec(0.f, 1.f, 0.f) }; static Sphere spheres[3] = { { vec(-2.0f, 0.0f, -3.5f), 0.5f }, { vec(-0.5f, 0.0f, -3.0f), 0.5f }, { vec(1.0f, 0.0f, -2.2f), 0.5f } }; srand48(y0); for (int y = y0; y < y1; ++y) { for (int x = 0; x < w; ++x) { int offset = 3 * (y * w + x); for (int u = 0; u < nsubsamples; ++u) { for (int v = 0; v < nsubsamples; ++v) { float px = (x + (u / (float)nsubsamples) - (w / 2.0f)) / (w / 2.0f); float py = -(y + (v / (float)nsubsamples) - (h / 2.0f)) / (h / 2.0f); // Scale NDC based on width/height ratio, supporting non-square image output px *= (float)w / (float)h; float ret = 0.f; Ray ray; Isect isect; ray.org = vec(0.f, 0.f, 0.f); ray.dir.x = px; ray.dir.y = py; ray.dir.z = -1.0f; vnormalize(ray.dir); isect.t = 1.0e+17f; isect.hit = 0; for (int snum = 0; snum < 3; ++snum) ray_sphere_intersect(isect, ray, spheres[snum]); ray_plane_intersect(isect, ray, plane); if (isect.hit) ret = ambient_occlusion(isect, plane, spheres); // Update image for AO for this ray image[offset+0] += ret; image[offset+1] += ret; image[offset+2] += ret; } } // Normalize image pixels by number of samples taken per pixel image[offset+0] /= nsubsamples * nsubsamples; image[offset+1] /= nsubsamples * nsubsamples; image[offset+2] /= nsubsamples * nsubsamples; } } }
// modified to only render one row (specified by parameter y) per function call void aobench_render(unsigned char *img, int w, int h, int nsubsamples, int y) { int x, u, v; for (x = 0; x < w; x++) { aobfloat r = 0.0, g = 0.0, b = 0.0; for (v = 0; v < nsubsamples; v++) { for (u = 0; u < nsubsamples; u++) { aobfloat px = (x + (u / (aobfloat)nsubsamples) - (w / 2.0)) / (w / 2.0); aobfloat py = -(y + (v / (aobfloat)nsubsamples) - (h / 2.0)) / (h / 2.0); Ray ray; ray.org.x = 0.0; ray.org.y = 0.0; ray.org.z = 0.0; ray.dir.x = px; ray.dir.y = py; ray.dir.z = -1.0; vnormalize(&(ray.dir)); Isect isect; isect.t = 1.0e+17; isect.hit = 0; ray_sphere_intersect(&isect, &ray, &spheres[0]); ray_sphere_intersect(&isect, &ray, &spheres[1]); ray_sphere_intersect(&isect, &ray, &spheres[2]); ray_plane_intersect (&isect, &ray, &plane); if (isect.hit) { vec col; ambient_occlusion(&col, &isect); r += col.x; g += col.y; b += col.z; } } } r /= (aobfloat)(nsubsamples * nsubsamples); g /= (aobfloat)(nsubsamples * nsubsamples); b /= (aobfloat)(nsubsamples * nsubsamples); img[3 * (y * w + x) + 0] = clamp(r); img[3 * (y * w + x) + 1] = clamp(g); img[3 * (y * w + x) + 2] = clamp(b); } }
static float ambient_occlusion(Isect &isect, Plane &plane, Sphere spheres[3]) { float eps = 0.0001f; vec p, n; vec basis[3]; float occlusion = 0.0; p = isect.p + eps * isect.n; orthoBasis(basis, isect.n); static const int ntheta = NAO_SAMPLES; static const int nphi = NAO_SAMPLES; for (int j = 0; j < ntheta; j++) { for (int i = 0; i < nphi; i++) { Ray ray; Isect occIsect; float theta = sqrtf(drand48()); float phi = 2.0f * M_PI * drand48(); float x = cosf(phi) * theta; float y = sinf(phi) * theta; float z = sqrtf(1.0f - theta * theta); // local . global float rx = x * basis[0].x + y * basis[1].x + z * basis[2].x; float ry = x * basis[0].y + y * basis[1].y + z * basis[2].y; float rz = x * basis[0].z + y * basis[1].z + z * basis[2].z; ray.org = p; ray.dir.x = rx; ray.dir.y = ry; ray.dir.z = rz; occIsect.t = 1.0e+17f; occIsect.hit = 0; for (int snum = 0; snum < 3; ++snum) ray_sphere_intersect(occIsect, ray, spheres[snum]); ray_plane_intersect (occIsect, ray, plane); if (occIsect.hit) occlusion += 1.f; } } occlusion = (ntheta * nphi - occlusion) / (float)(ntheta * nphi); return occlusion; }
static void render(unsigned char *img, int comps, int w, int h, int nsubsamples) { int x, y; int u, v; //float *fimg = (float *)malloc(sizeof(float) * w * h * 3); vec *fimg = (vec *)malloc(sizeof(vec) * w * h); memset((void *)fimg, 0, sizeof(vec) * w * h); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { for (v = 0; v < nsubsamples; v++) { for (u = 0; u < nsubsamples; u++) { float px = (x + (u / (float)nsubsamples) - (w / 2.0)) / (w / 2.0); float py = -(y + (v / (float)nsubsamples) - (h / 2.0)) / (h / 2.0); Ray ray; ray.org.x = 0.0; ray.org.y = 0.0; ray.org.z = 0.0; ray.dir.x = px; ray.dir.y = py; ray.dir.z = -1.0; vnormalize(&(ray.dir)); Isect isect; isect.t = 1.0e+17; isect.hit = 0; ray_sphere_intersect(&isect, &ray, &spheres[0]); ray_sphere_intersect(&isect, &ray, &spheres[1]); ray_sphere_intersect(&isect, &ray, &spheres[2]); ray_plane_intersect (&isect, &ray, &plane); if (isect.hit) { vec col; ambient_occlusion(&col, &isect); vadd(&fimg[y * w + x], fimg[y * w + x], col); /* fimg[y * w + x].x += col.x; fimg[y * w + x].y += col.y; fimg[y * w + x].z += col.z; */ } } } vdivs(&fimg[y * w + x], fimg[y * w + x], (float)(nsubsamples * nsubsamples)); /* fimg[y * w + x].x /= (float)(nsubsamples * nsubsamples); fimg[y * w + x].y /= (float)(nsubsamples * nsubsamples); fimg[y * w + x].z /= (float)(nsubsamples * nsubsamples); */ img[comps * (y * w + x) + 0] = clamp(fimg[y * w + x].x); img[comps * (y * w + x) + 1] = clamp(fimg[y * w + x].y); img[comps * (y * w + x) + 2] = clamp(fimg[y * w + x].z); } } }
static bool ray_surface_intersect(Ray ray, const Surface *surf, Hit *hit) { float ts[2] = {-HUGE_VAL, -HUGE_VAL}, t; Vec3 tnormals[2] = {{0,0,0},{0,0,0}}, tnormal; Ray tray; Mat4 normal_matrix; Shape *shape = surf->shape; int hits; mat4_copy(normal_matrix, surf->world_to_model); mat4_transpose(normal_matrix); tray.origin = mat4_transform3_homo(surf->world_to_model, ray.origin); tray.direction = mat4_transform3_hetero(surf->world_to_model, ray.direction); tray.near = ray.near; tray.far = ray.far; switch(shape->type) { case SHAPE_PLANE: hits = ray_plane_intersect(tray, shape->u.plane, ts, tnormals); break; case SHAPE_DISK: hits = ray_disk_intersect(tray, shape->u.disk, ts, tnormals); break; case SHAPE_SPHERE: hits = ray_sphere_intersect(tray, shape->u.sphere, ts, tnormals); break; case SHAPE_CYLINDER: hits = ray_cylinder_intersect(tray, shape->u.cylinder, ts, tnormals); break; case SHAPE_CONE: hits = ray_cone_intersect(tray, shape->u.cone, ts, tnormals); break; case SHAPE_MESH: hits = ray_mesh_intersect(tray, shape->u.mesh, ts, tnormals); break; default: printf("Unknown shape\n"); return false; break; } /* We're looking for the smallest hit that is between the near and far * planes of the ray. */ if (hits == 0) return false; if (hits == 1) { if (ts[0] < ray.near || ts[0] > ray.far) return false; t = ts[0]; tnormal = tnormals[0]; } else if (hits == 2) { bool t0_ok = ts[0] >= ray.near && ts[0] <= ray.far; bool t1_ok = ts[1] >= ray.near && ts[1] <= ray.far; if (!t0_ok && !t1_ok) { return false; } else if (t0_ok && !t1_ok) { t = ts[0]; tnormal = tnormals[0]; } else if (!t0_ok && t1_ok) { t = ts[1]; tnormal = tnormals[1]; } else { if (ts[0] < ts[1]) { t = ts[0]; tnormal = tnormals[0]; } else { t = ts[1]; tnormal = tnormals[1]; } } } else { printf("General t finding code unimplemented\n"); return false; } hit->t = t; hit->position = vec3_add(ray.origin, vec3_scale(t, ray.direction)); hit->normal = vec3_normalize(mat4_transform3_hetero(normal_matrix, tnormal)); return true; }
static void ambient_occlusion(vec *col, const Isect *isect) { int i, j; int ntheta = NAO_SAMPLES; int nphi = NAO_SAMPLES; double eps = 0.0001; vec p; ao_vmultsadd(&p, isect->n, eps, isect->p); /* p.x = isect->p.x + eps * isect->n.x; p.y = isect->p.y + eps * isect->n.y; p.z = isect->p.z + eps * isect->n.z; */ vec basis[3]; orthoBasis(basis, isect->n); double occlusion = 0.0; for (j = 0; j < ntheta; j++) { for (i = 0; i < nphi; i++) { double theta = sqrt(drand48()); double phi = 2.0 * M_PI * drand48(); double x = cos(phi) * theta; double y = sin(phi) * theta; double z = sqrt(1.0 - theta * theta); // TODO // local -> global double rx = x * basis[0].x + y * basis[1].x + z * basis[2].x; double ry = x * basis[0].y + y * basis[1].y + z * basis[2].y; double rz = x * basis[0].z + y * basis[1].z + z * basis[2].z; Ray ray; ray.org = p; ray.dir.x = rx; ray.dir.y = ry; ray.dir.z = rz; Isect occIsect; occIsect.t = 1.0e+17; occIsect.hit = 0; ray_sphere_intersect(&occIsect, &ray, &spheres[0]); ray_sphere_intersect(&occIsect, &ray, &spheres[1]); ray_sphere_intersect(&occIsect, &ray, &spheres[2]); ray_plane_intersect (&occIsect, &ray, &plane); if (occIsect.hit) occlusion += 1.0; } } occlusion = (ntheta * nphi - occlusion) / (double)(ntheta * nphi); col->x = occlusion; col->y = occlusion; col->z = occlusion; }