Light_EvalRes AmbientLight_eval(const Light* super, const DifferentialGeometry& dg, const Vec3fa& dir) { AmbientLight* self = (AmbientLight*)super; Light_EvalRes res; res.value = self->radiance; res.dist = inf; res.pdf = cosineSampleHemispherePDF(max(dot(dg.Ns, dir), 0.f)); return res; }
// XXX importance sampling is only done into the positive hemisphere // ==> poor support for translucent materials Light_SampleRes AmbientLight_sample(const Light* super, const DifferentialGeometry& dg, const Vec2f& s) { AmbientLight* self = (AmbientLight*)super; Light_SampleRes res; const Vec3fa localDir = cosineSampleHemisphere(s); res.dir = frame(dg.Ns) * localDir; res.pdf = cosineSampleHemispherePDF(localDir); res.dist = inf; res.weight = self->radiance * rcp(res.pdf); return res; }
Light_SampleRes PointLight_sample(const Light* super, const DifferentialGeometry& dg, const Vec2f& s) { const PointLight* self = (PointLight*)super; Light_SampleRes res; // extant light vector from the hit point const Vec3fa dir = self->position - dg.P; const float dist2 = dot(dir, dir); const float invdist = rsqrt(dist2); // normalized light vector res.dir = dir * invdist; res.dist = dist2 * invdist; res.pdf = inf; // per default we always take this res // convert from power to radiance by attenuating by distance^2 res.weight = self->power * sqr(invdist); const float sinTheta = self->radius * invdist; if ((self->radius > 0.f) & (sinTheta > 0.005f)) { // res surface of sphere as seen by hit point -> cone of directions // for very small cones treat as point light, because float precision is not good enough if (sinTheta < 1.f) { const float cosTheta = sqrt(1.f - sinTheta * sinTheta); const Vec3fa localDir = uniformSampleCone(cosTheta, s); res.dir = frame(res.dir) * localDir; res.pdf = uniformSampleConePDF(cosTheta); const float c = localDir.z; res.dist = c*res.dist - sqrt(sqr(self->radius) - (1.f - c*c) * dist2); // TODO scale radiance by actual distance } else { // inside sphere const Vec3fa localDir = cosineSampleHemisphere(s); res.dir = frame(dg.Ns) * localDir; res.pdf = cosineSampleHemispherePDF(localDir); // TODO: res.weight = self->power * rcp(sqr(self->radius)); res.dist = self->radius; } } return res; }
/* renders a single pixel casting with ambient occlusion */ Vec3fa ambientOcclusionShading(int x, int y, RTCRay& ray) { RTCRay rays[AMBIENT_OCCLUSION_SAMPLES]; Vec3fa Ng = normalize(ray.Ng); if (dot(ray.dir,Ng) > 0.0f) Ng = neg(Ng); Vec3fa col = Vec3fa(min(1.0f,0.3f+0.8f*abs(dot(Ng,normalize(ray.dir))))); /* calculate hit point */ float intensity = 0; Vec3fa hitPos = ray.org + ray.tfar * ray.dir; RandomSampler sampler; RandomSampler_init(sampler,x,y,0); /* enable only valid rays */ for (int i=0; i<AMBIENT_OCCLUSION_SAMPLES; i++) { /* sample random direction */ Vec2f s = RandomSampler_get2D(sampler); Sample3f dir; dir.v = cosineSampleHemisphere(s); dir.pdf = cosineSampleHemispherePDF(dir.v); dir.v = frame(Ng) * dir.v; /* initialize shadow ray */ RTCRay& shadow = rays[i]; shadow.org = hitPos; shadow.dir = dir.v; bool mask = 1; { // invalidate inactive rays shadow.tnear = mask ? 0.001f : (float)(pos_inf); shadow.tfar = mask ? (float)(inf) : (float)(neg_inf); } shadow.geomID = RTC_INVALID_GEOMETRY_ID; shadow.primID = RTC_INVALID_GEOMETRY_ID; shadow.mask = -1; shadow.time = 0; // FIXME: invalidate inactive rays } RTCIntersectContext context; context.flags = RAYN_FLAGS; /* trace occlusion rays */ #if USE_INTERFACE == 0 rtcOccluded1M(g_scene,&context,rays,AMBIENT_OCCLUSION_SAMPLES,sizeof(RTCRay)); #elif USE_INTERFACE == 1 for (size_t i=0; i<AMBIENT_OCCLUSION_SAMPLES; i++) rtcOccluded(g_scene,rays[i]); #else for (size_t i=0; i<AMBIENT_OCCLUSION_SAMPLES; i++) rtcOccluded1M(g_scene,&context,&rays[i],1,sizeof(RTCRay)); #endif /* accumulate illumination */ for (int i=0; i<AMBIENT_OCCLUSION_SAMPLES; i++) { if (rays[i].geomID == RTC_INVALID_GEOMETRY_ID) intensity += 1.0f; } /* shade pixel */ return col * (intensity/AMBIENT_OCCLUSION_SAMPLES); }