Color Matte::shade(ShadeRecord& sr, const Ray& ray) { Vector3D wo = ray.direction * -1; Color L = ambientBRDF->rho(sr, wo) * LightManager::instance().getAmbientLight(sr); for(LightIter it = LightManager::instance().begin(); it != LightManager::instance().end(); it++) { Color power; Vector3D wis; for(int s = 0; s < (*it)->getNumLightSamples(); s++) { Vector3D wi = (*it)->getLightDirection(sr); wis += wi; float ndotwi = sr.normal.dot(wi); if(ndotwi > 0.0) { Ray shadowRay(sr.hitPoint, wi); bool inShadow = (*it)->inShadow(shadowRay, sr); if(!inShadow) { power += (*it)->L(sr) * (*it)->G(sr) * ndotwi / (*it)->pdf(sr); } } } power = power / (*it)->getNumLightSamples(); wis.normalize(); L += diffuseBRDF->f(sr, wo, wis) * power; } return L; }
bool ShadowCalculator::inShadow(const Point3D& pointToBeTested, const Point3D& lightPoint) const { //Shadow ray calculation. ShadowRay shadowRay(pointToBeTested, lightPoint); //Calculate intersection Intersection *intersectionData = tracer.closestIntersection(*shadowRay.ray); //No intersection or intersection with light object. if(intersectionData == nullptr || intersectionData->object->isLight == true) { return false; } //Extract t (intersection distance) and d (distance from light). float t = intersectionData->t; float d = shadowRay.d; //Clear pointer. delete intersectionData; //If the intersection distance is greater than the distance between intersectionPoint and light then the point //is NOT in shadow. if(t > d) { return false; } //Ok, the point is in shadow. return true; }
RGB TexturedLambertianShader::getDiffuseRadiance(Object3d* p_object, Vector3d p_point, Vector3d p_normal, UVMap p_uv, Vector3d p_viewVec, Scene3d* p_scene) { RGB totalRadiance(0,0,0); Light* light; for(int l = 0; l < p_scene->getLightCount(); l++) { light = p_scene->getLights()[l]; Vector3d lightDirection = -light->getDirectionAt(p_point); float cosThetaSurface = lightDirection.dot(p_normal); if(cosThetaSurface > -EPSILON) { //shadow ray Ray shadowRay(p_point, lightDirection); float distToLight = light->getDistanceAt(p_point); bool intersects = false; if(p_scene->kdTree == NULL) { intersects = shadowRay.intersects(p_scene->getObjects(), p_scene->getObjectCount(), distToLight); } else { intersects = p_scene->kdTree->intersects(&shadowRay, distToLight); } if(!intersects) { totalRadiance = totalRadiance + light->getRadianceAt(p_point) * cosThetaSurface; } } } RGB c = (map != NULL)? map->getTexel(p_uv.u, p_uv.v) : color; return (totalRadiance * c); }
RGBColor Matte::shade(ShadeRec& sr) { Vector3D wo = -sr.ray.d; RGBColor L = ambient_brdf->rho(sr,wo) * sr.w.ambient_ptr->L(sr); int numLights = sr.w.lights.size(); for(int j = 0; j < numLights; j++){ Vector3D wi = sr.w.lights[j] -> get_direction(sr); float ndotwi = sr.normal * wi; if(ndotwi > 0.0) { bool in_shadow = false; if(sr.w.lights[j]->casts_shadows()) { Ray shadowRay(sr.hit_point, wi); in_shadow = sr.w.lights[j]->in_shadow(shadowRay, sr); } if(!in_shadow) L += diffuse_brdf->f(sr,wo,wi) * sr.w.lights[j]->L(sr) * ndotwi; } } return (L); }
RGBColor Phong::shade(ShadeRec& sr) { Vector3D wo = -sr.ray.d; RGBColor L = ambient + emission; int numLights = sr.w.lights.size(); for(int j = 0; j < numLights; j++) { Vector3D wi = sr.w.lights[j]->get_direction(sr); Vector3D h(wi + wo); h.normalize(); float ndotwi = sr.normal * wi; float ndoth = sr.normal * h; bool in_shadow = false; if(sr.w.lights[j]->casts_shadows()) { Ray shadowRay(sr.hit_point, wi); in_shadow = sr.w.lights[j]->in_shadow(shadowRay, sr); } if(!in_shadow) { L += diffuse * sr.w.lights[j]->L(sr) * max(0.0, ndotwi) + specular * sr.w.lights[j]->L(sr) * pow(max(ndoth, 0.0), shininess); } } return(L); }
ColorRGB RayTracer::trace(const Ray& p_ray, const SceneList& p_scene, int p_bounce) { if (p_bounce > m_settings.maxBounces) { return m_settings.clearColor; } std::pair<Shape*, float> closestInfo = getClosestShape(p_ray, p_scene); if (closestInfo.first != nullptr) { Point3 intersectionPoint = p_ray.origin + closestInfo.second * p_ray.direction; Vector3 intersectionNormal = closestInfo.first->getNormal(intersectionPoint); intersectionNormal.normalize(); // Offset to prevent self intersection intersectionPoint += 0.0001f * intersectionNormal; //ColorRGB reflectedColor = ColorRGB(255, 255, 255); if (closestInfo.first->reflective) { // Trace reflection ray Ray reflectionRay( intersectionPoint, reflect(p_ray.direction, intersectionNormal)); return trace(reflectionRay, p_scene, p_bounce + 1); } if (m_settings.lightEnabled == false) { return closestInfo.first->color.rgb(); } // Compute shadow ray const Point3 lightPosition(0, 500, 0); Vector3 shadowVector = lightPosition - intersectionPoint; shadowVector.normalize(); Ray shadowRay(intersectionPoint, shadowVector); std::pair<Shape*, float> shadowInfo = getClosestShape(shadowRay, p_scene); float lightContribution = 0.2f; // Ambient light if (shadowInfo.first == nullptr) { // Compute diffuse light factor float diffuseFactor = dotProduct(intersectionNormal, shadowRay.direction); if (diffuseFactor > 0.0f) { lightContribution += diffuseFactor; } } return std::min(lightContribution, 1.0f) * closestInfo.first->color.rgb(); } return m_settings.clearColor; }
Color3f Li(const Scene *scene, Sampler *sampler, const Ray3f &ray) const { /* Find the surface that is visible in the requested direction */ Intersection its; //check if the ray intersects the scene if (!scene->rayIntersect(ray, its)) { //check if a distant disk light is set const Emitter* distantsDisk = scene->getDistantEmitter(); if(distantsDisk == nullptr ) return Color3f(0.0f); //sample the distant disk light return distantsDisk->sampleL(ray.d); } //get the radiance of hitten object Color3f Le(0.0f, 0.0f, 0.0f); if (its.mesh->isEmitter() ) { const Emitter* areaLightEM = its.mesh->getEmitter(); const areaLight* aEM = static_cast<const areaLight *> (areaLightEM); Le = aEM->sampleL(-ray.d, its.shFrame.n, its); } //get the asigned BSDF const BSDF* curBSDF = its.mesh->getBSDF(); Color3f Ld(0.0f, 0.0f, 0.0f); Color3f f(0.0f, 0.0f, 0.0f); Color3f totalLight(0.0f, 0.0f, 0.0f); //transform to the local frame //create a BRDF Query BSDFQueryRecord query = BSDFQueryRecord(its.toLocal(-ray.d), Vector3f(0.0f), EMeasure::ESolidAngle); //sample the BRDF Color3f mats = curBSDF->sample(query, sampler->next2D()); if(mats.maxCoeff() > 0.0f) { //Check for the light source Vector3f wo = its.toWorld(query.wo); Ray3f shadowRay(its.p, wo); Intersection itsShadow; if (scene->rayIntersect(shadowRay, itsShadow)) { //intersection check if mesh is emitter if(itsShadow.mesh->isEmitter()){ Ld = itsShadow.mesh->getEmitter()->radiance(); } } else { //check for distant disk light const Emitter* distantsDisk = scene->getDistantEmitter(); if(distantsDisk != nullptr ) Ld = distantsDisk->sampleL(wo); } totalLight += Ld * mats; } return Le + totalLight; }
void InstantRadiosityRenderer::processPixel(uint32_t threadID, uint32_t tileID, const Vec2u& pixel) const { PixelSensor pixelSensor(getSensor(), pixel, getFramebufferSize()); RaySample raySample; float positionPdf, directionPdf; auto We = pixelSensor.sampleExitantRay(getScene(), getFloat2(threadID), getFloat2(threadID), raySample, positionPdf, directionPdf); auto L = zero<Vec3f>(); if(We != zero<Vec3f>() && raySample.pdf) { auto I = getScene().intersect(raySample.value); BSDF bsdf(-raySample.value.dir, I, getScene()); if(I) { // Direct illumination for(auto& vpl: m_EmissionVPLBuffer) { RaySample shadowRay; auto Le = vpl.pLight->sampleDirectIllumination(getScene(), vpl.emissionVertexSample, I, shadowRay); if(shadowRay.pdf) { auto contrib = We * Le * bsdf.eval(shadowRay.value.dir) * abs(dot(I.Ns, shadowRay.value.dir)) / (vpl.lightPdf * shadowRay.pdf * m_nLightPathCount * raySample.pdf); if(contrib != zero<Vec3f>()) { if(!getScene().occluded(shadowRay.value)) { L += contrib; } } } } // Indirect illumination for(auto& vpl: m_SurfaceVPLBuffer) { auto dirToVPL = vpl.intersection.P - I.P; auto dist = length(dirToVPL); if(dist > 0.f) { dirToVPL /= dist; auto fs1 = bsdf.eval(dirToVPL); auto fs2 = vpl.bsdf.eval(-dirToVPL); auto geometricFactor = abs(dot(I.Ns, dirToVPL)) * abs(dot(vpl.intersection.Ns, -dirToVPL)) / sqr(dist); auto contrib = We * vpl.power * fs1 * fs2 * geometricFactor / raySample.pdf; if(contrib != zero<Vec3f>()) { Ray shadowRay(I, vpl.intersection, dirToVPL, dist); if(!getScene().occluded(shadowRay)) { L += contrib; } } } } } } accumulate(0, getPixelIndex(pixel.x, pixel.y), Vec4f(L, 1.f)); }
void DirectionalLight::sample(Accelerator *surfaces, Intersection *intersection, Vector *direction, Color *Li) { *direction = d; /* Shadow testing */ Ray shadowRay(intersection->point, d); if(!surfaces->intersectFast(&shadowRay)) *Li = Le; else *Li = Color(0); }
void PathCPURenderThread::DirectLightSampling( const float u0, const float u1, const float u2, const float u3, const float u4, const Spectrum &pathThrouput, const BSDF &bsdf, const int depth, Spectrum *radiance) { PathCPURenderEngine *engine = (PathCPURenderEngine *)renderEngine; Scene *scene = engine->renderConfig->scene; if (!bsdf.IsDelta()) { // Pick a light source to sample float lightPickPdf; const LightSource *light = scene->SampleAllLights(u0, &lightPickPdf); Vector lightRayDir; float distance, directPdfW; Spectrum lightRadiance = light->Illuminate(scene, bsdf.hitPoint, u1, u2, u3, &lightRayDir, &distance, &directPdfW); if (!lightRadiance.Black()) { BSDFEvent event; float bsdfPdfW; Spectrum bsdfEval = bsdf.Evaluate(lightRayDir, &event, &bsdfPdfW); if (!bsdfEval.Black()) { const float epsilon = Max(MachineEpsilon::E(bsdf.hitPoint), MachineEpsilon::E(distance)); Ray shadowRay(bsdf.hitPoint, lightRayDir, epsilon, distance - epsilon); RayHit shadowRayHit; BSDF shadowBsdf; Spectrum connectionThroughput; // Check if the light source is visible if (!scene->Intersect(device, false, u4, &shadowRay, &shadowRayHit, &shadowBsdf, &connectionThroughput)) { const float cosThetaToLight = AbsDot(lightRayDir, bsdf.shadeN); const float directLightSamplingPdfW = directPdfW * lightPickPdf; const float factor = cosThetaToLight / directLightSamplingPdfW; if (depth >= engine->rrDepth) { // Russian Roulette bsdfPdfW *= Max(bsdfEval.Filter(), engine->rrImportanceCap); } // MIS between direct light sampling and BSDF sampling const float weight = PowerHeuristic(directLightSamplingPdfW, bsdfPdfW); *radiance += (weight * factor) * pathThrouput * connectionThroughput * lightRadiance * bsdfEval; } } } } }
static void appendVPL(const Scene *scene, Random *random, VPL &vpl, bool prune, std::deque<VPL> &vpls) { prunedVPLs.incrementBase(); const Sensor *sensor = scene->getSensor(); Float time = sensor->getShutterOpen() + 0.5f * sensor->getShutterOpenTime(); if (prune) { /* Possibly reject VPLs if they are unlikely to be visible from the camera */ int nSuccesses = 0, nSamples = 50; const Shape *shape; Normal n; Point2 uv; Ray ray; Vector2i size = sensor->getFilm()->getCropSize(); for (int i=0; i<nSamples; ++i) { if (sensor->needsTimeSample()) time = sensor->sampleTime(random->nextFloat()); sensor->sampleRay(ray, Point2(random->nextFloat() * size.x, random->nextFloat() * size.y), Point2(0.5f), time); Float t; if (scene->rayIntersect(ray, t, shape, n, uv)) { Point p = ray(t); Vector d = vpl.its.p - p; Float length = d.length(); Ray shadowRay(p, d/length, Epsilon, length*(1-ShadowEpsilon), time); if (!scene->rayIntersect(shadowRay)) ++nSuccesses; } else { ++nSuccesses; // be conservative } } /// Have a small chance of acceptance in any case Float acceptanceProb = (nSuccesses+1) / (Float) (nSamples+1); if (random->nextFloat() < acceptanceProb) { vpl.P /= acceptanceProb; vpls.push_back(vpl); } else { ++prunedVPLs; } } else { vpls.push_back(vpl); } }
Color PointLight::calculateColor(const Scene &scene, const Ray &ray, float distance, const Vector &normal, MaterialPointer material) const { Vector point = ray.getPointAt(distance); Color ambientComponent; Color diffuseComponent; Color specularComponent; ambientComponent = componentwiseProduct(material->ambientColor, mAmbientIntensity); Color result = ambientComponent; Vector shadowRayDirection = mPosition - point; float distanceToLight = shadowRayDirection.length(); float attenuation = 1 / (mConstantAttenutaionCoefficient + mLinearAttenutaionCoefficient * distanceToLight + mQuadraticAttenutaionCoefficient * distanceToLight * distanceToLight); result *= attenuation; shadowRayDirection.normalize(); float shadowRayDotNormal = shadowRayDirection.dotProduct(normal); // If the point is not illuminated if (shadowRayDotNormal <= 0.0) { return result; } Ray shadowRay(point + shadowRayDirection * EPS_FOR_SHADOW_RAYS, shadowRayDirection); RayIntersection shadowRayIntersection = scene.calculateNearestIntersection(shadowRay); // If object is not in shadow if (!shadowRayIntersection.rayIntersectsWithShape || shadowRayIntersection.distanceFromRayOrigin > distanceToLight) { Color diffuseColor = material->diffuseColor; diffuseComponent = componentwiseProduct(diffuseColor, mDiffuseIntensity * attenuation * shadowRayDotNormal); Vector lightReflect = shadowRayDirection - normal * 2 * shadowRayDirection.dotProduct(normal); lightReflect.normalize(); Vector cameraDirection = ray.getOriginPosition() - point; cameraDirection.normalize(); float cosLightReflect = cameraDirection.dotProduct(lightReflect); if (cosLightReflect > 0.0) { Color specularColor = material->specularColor; specularComponent = componentwiseProduct(specularColor, mSpecularIntensity * powf(cosLightReflect, material->specularPower) * attenuation); } } result += (diffuseComponent + specularComponent); return result; }
QVector3D WhittedRenderer::trace(const Ray& primaryRay, int depth, const QList<Shape*>& shapes, const QList<Light*>& lights) { double minDist = std::numeric_limits<double>::max(); Shape* closestShape = nullptr; QVector3D shaded; if (m_grid->intersect(primaryRay, closestShape, minDist)) { auto material = closestShape->material(); shaded = material->ambientReflectivity() * m_ambientLightColor * material->colorVector(); QColor color = material ? material->color() : QColor(40, 40, 40); QVector3D diffuseColor(color.redF(), color.greenF(), color.blueF()); QVector4D hitPoint = primaryRay.along(minDist); QVector4D normalVector = closestShape->surfaceNormal(hitPoint, primaryRay); QVector4D viewVector = -primaryRay.direction(); foreach(Light* light, lights) { QVector3D emittance; QVector4D lightVector; light->sample(lightVector, emittance); lightVector = lightVector - hitPoint; float lightVectorLengthSquared = lightVector.lengthSquared(); float lightVectorLength = sqrt(lightVectorLengthSquared); lightVector.normalize(); Ray shadowRay(hitPoint, lightVector); double t; Shape* blockingShape; bool shadowRayHit = m_grid->intersect(shadowRay, blockingShape, t); if (!shadowRayHit || (shadowRayHit && (lightVectorLengthSquared < t * t))) { // Diffuse float dot = fmax(0.0f, QVector4D::dotProduct(lightVector, normalVector)) * material->diffuseReflectivity(); emittance *= 1 / (1 + 0.2 * lightVectorLength + 0.08 * lightVectorLengthSquared); shaded += dot * QVector3D(emittance.x() * diffuseColor.x(), emittance.y() * diffuseColor.y(), emittance.z() * diffuseColor.z()); // Specular if (material->specularReflectivity() > 0.0) { QVector4D reflectedLightVector = MathUtils::reflect(-lightVector, normalVector); // lightVector and normalVector are already normalized float dot = QVector4D::dotProduct(reflectedLightVector, viewVector); if (dot > 0.0) shaded += material->specularReflectivity() * pow(dot, material->shininess()) * emittance; } } }
Vec3f Leafcutter::EvalutateLight(const DirLight& light, DifferentialGeometry& dp, const Vec3f &wo, Material *ms) { Vec3f wi = -light.normal; BxdfUnion msu; ms->SampleReflectance(dp, msu); Vec3f brdf = msu.EvalSmoothCos(wo, wi, dp); Vec3f estimate = brdf; if(!estimate.IsZero()) { Ray shadowRay(dp.P, wi, RAY_INFINITY, 0.0f); if(!_engine->IntersectAny(shadowRay)) { return light.le * estimate; } } return Vec3f::Zero(); }
// ---------------------------------------------------------------- shade RGBColor SV_Matte::shade(ShadeRec& sr) { //Vector3D wo = -sr.ray.d; // wo is the inverse eye ray direction //RGBColor L = ambient_brdf->rho(sr,wo) * sr.w.ambient_ptr->L(sr); // ambient light //size_t num_lights = sr.w.lights.size(); // //for (size_t j = 0; j < num_lights; j++) //{ // Vector3D wi = sr.w.lights[j]->get_direction(sr); // //wi.normalizeThis(); // double ndotwi = sr.normal * wi; // if(ndotwi> 0.0) // L += ( diffuse_brdf->f(sr, wo, wi) + specula_brdf->f(sr, wo, wi) ) * sr.w.lights[j]->L(sr) * ndotwi; // diffuse and specular //} // //return (L); Vector3D wo = -sr.ray.d; RGBColor L = ambient_brdf->rho(sr,wo) * sr.w.ambient_ptr->L(sr); // ambient light size_t numLights = sr.w.lights.size(); for(size_t j = 0;j < numLights;j++) { Vector3D wi = sr.w.lights[j]->get_direction(sr); //wi.normalizeThis(); double ndotwi = sr.normal * wi; if(ndotwi> 0.0) { bool in_shadow = false; if(sr.w.lights[j]->casts_shadows()) { Point3D new_hit_point = sr.hit_point + Vector3D(sr.normal * EPSILON); Ray shadowRay(new_hit_point,wi); //Ray shadowRay(sr.hit_point,wi); in_shadow = sr.w.lights[j]->in_shadow(shadowRay,sr); } if(!in_shadow) L += ( diffuse_brdf->f(sr,wo,wi) * ndotwi + specula_brdf->f(sr, wo, wi) ) * sr.w.lights[j]->L(sr); } } return L; }
float3 trace(const Ray& ray, int depth) { Hit hit = firstIntersect(ray); // If ray hits nothing, we return the aurora borealis if(hit.t < 0) { return ray.dir * ray.dir * float3(0,1,0.6); } float3 lightSum = {0,0,0}; for(LightSource *l : lightSources) { float3 li = l->getLightDirAt(ray.origin); float dist = l->getDistanceFrom(hit.position); int maxDepth = 5; // Deals with shadows Ray shadowRay(hit.position + (hit.normal*0.1), li); Hit shadowHit = firstIntersect(shadowRay); if(shadowHit.t > 0 && shadowHit.t < dist) continue; // Handles types of materials differently if(depth > maxDepth) return lightSum; if(hit.material->reflective){ // for smooth surface float3 reflectionDir = hit.material->reflect(ray.dir, hit.normal); Ray reflectedRay(hit.position + hit.normal*0.1, reflectionDir ); lightSum += trace(reflectedRay, depth+1) * hit.material->getReflectance(ray.dir, hit.normal); } if(hit.material->refractive) { // for smooth surface float3 refractionDir = hit.material->refract(ray.dir, hit.normal); Ray refractedRay(hit.position - hit.normal*0.1, refractionDir ); lightSum += trace(refractedRay, depth+1) * (float3(1,1,1) - hit.material->getReflectance(ray.dir, hit.normal)); } else { // for rough surface lightSum += hit.material->shade(hit.normal, -ray.dir, l->getLightDirAt(hit.position), l->getPowerDensityAt(hit.position)); } } return lightSum; }
Vector3D PointLight::LightIntensity(Scene& scene, IntersectResult intersect, const PerspectiveCamera& cam) { Vector3D delta = mPosition - intersect.mPosition; float rr = Length2(delta); float r = sqrt(rr); float r1 = 1.0 / r; Vector3D inverseDire = Normalize(delta); if (mIsShadow) { Ray shadowRay(intersect.mPosition, inverseDire); IntersectResult shadowResult = scene.Intersect(shadowRay); if (shadowResult.mIsHit && shadowResult.mDistance <= r) { return -(this->mColor * this->mShadowReducingFactor); } } float diffuseAttenuation = 1.0 / (mAttenuation.x + mAttenuation.y * r + mAttenuation.z * rr); float specularAttenuation = 1.0 / (mAttenuation.x + mAttenuation.y * r + mAttenuation.z * rr); Vector3D colorRes; // Diffuse Material float NdotL = Dot(intersect.mNormal, inverseDire); // std::cout << NdotL << std::endl; if (NdotL > 0) { colorRes = colorRes + intersect.mPrimitive->GetDiffuseColor() * this->mColor * NdotL * diffuseAttenuation; } // Specular Material Vector3D Vo = cam.GetPosition() - intersect.mPosition; Vector3D V = Normalize(Vo); Vector3D h = Normalize(V + inverseDire); float NdotH = Dot(intersect.mNormal, h); if (NdotH > 0) { colorRes = colorRes + intersect.mPrimitive->GetSpecularColor() * this->mColor * powf(NdotH, intersect.mPrimitive->GetShininess()) * specularAttenuation; } return colorRes; }
RGBColor Matte::area_light_shade(ShadeRec& sr){ Vector3D wo = -sr.ray.d; RGBColor L = ambient_brdf->rho(sr, wo) * sr.w.ambient_ptr->L(sr); int numLights = sr.w.lights.size(); for( int j = 0; j < numLights; j++){ Vector3D wi = sr.w.lights[j]->get_direction(sr); float ndotwi = sr.normal * wi; if (ndotwi > 0.0){ bool in_shadow = false; if (sr.w.lights[j]->casts_shadows()){ Ray shadowRay(sr.hit_point, wi); in_shadow = sr.w.lights[j]->in_shadow(shadowRay, sr); } if(!in_shadow){ RGBColor lf, ll, lg; float fpdf; lf = diffuse_brdf->f(sr, wo, wi); ll = sr.w.lights[j]->L(sr); lg = sr.w.lights[j]->G(sr); fpdf = sr.w.lights[j]->pdf(sr); L += lf * ll * lg * ndotwi / fpdf; //L+= diffuse_brdf->f(sr, wo, wi) * sr.w.lights[j]->L(sr) // * sr.w.lights[j]->G(sr) * ndotwi / sr.w.lights[j]->pdf(sr); } } } return L; }
RGBVec World::traceRay(const Ray &ray, double t_min, int depth) { IntersectionDatum idat = testIntersection(ray, t_min, scenery); if (!idat.intersected) return bg_colour; RGBVec result_vec; ShadableObject *obj = static_cast<ShadableObject *>(idat.intersectedObj); double t = idat.coefficient; // compute lighting/shading for (std::vector<Light>::iterator lptr = lighting.begin(); lptr != lighting.end(); lptr++) { vec3 p = ray.intersectionPoint(t); vec3 n = obj->surfaceNormal(p); vec3 v = (ray.origin - lptr->pos).normalised(); vec3 l = (lptr->pos - p).normalised(); Ray shadowRay(p,l); // if we're not in shadow w.r.t this light if (!shadows_enabled || !traceShadowRay(shadowRay, scenery)) { result_vec += obj->material.shade(*lptr, n, v, l); } if (reflections_enabled && obj->material.reflective && depth < MAX_TRACE_DEPTH) { // recursively trace reflection rays: vec3 d = ray.direction; Ray reflected(p, d - n.scaled(2 * d.dot(n))); RGBVec reflectedColour = traceRay(reflected, REFLECTION_EPS, depth + 1); if (reflectedColour.r() == 0.0 && reflectedColour.g() == 0.0 && reflectedColour.b() == 0.0) continue; RGBVec k_m = obj->material.specular_colour; result_vec += reflectedColour.multiplyColour(k_m); } } return result_vec; }
SLfloat SLLightRect::shadowTestMC(SLRay* ray, // ray of hit point const SLVec3f& L, // vector from hit point to light const SLfloat lightDist) // distance to light { SLVec3f SP; // vector hit point to sample point in world coords SLfloat randX = rnd01(); SLfloat randY = rnd01(); // choose random point on rect as sample SP.set(updateAndGetWM().multVec(SLVec3f((randX*_width)-(_width*0.5f), (randY*_height)-(_height*0.5f), 0)) - ray->hitPoint); SLfloat SPDist = SP.length(); SP.normalize(); SLRay shadowRay(SPDist, SP, ray); SLScene::current->root3D()->hitRec(&shadowRay); if (shadowRay.length >= SPDist - FLT_EPSILON) return 1.0f; else return 0.0f; }
Vec3f Leafcutter::EvalutateLight(const OrientedLight& light, DifferentialGeometry& dp, const Vec3f &wo, Material *ms) { Vec3f wi = (light.position - dp.P).GetNormalized(); float maxDist = (light.position - dp.P).GetLength(); float cosAngle = max(0.0f, light.normal % (-wi)); float lenSqrEst = max(0.1f, (light.position - dp.P).GetLengthSqr()); //float lenSqrEst = (light.position - dp.P).GetLengthSqr(); BxdfUnion msu; ms->SampleReflectance(dp, msu); Vec3f brdf = msu.EvalSmoothCos(wo, wi, dp); Vec3f estimate = brdf * cosAngle / lenSqrEst; if(!estimate.IsZero()) { Ray shadowRay(dp.P, wi, maxDist, 0.0f); if(!_engine->IntersectAny(shadowRay)) { return light.le * estimate; } } return Vec3f::Zero(); }
glm::vec3 Matte::areaLightShade(const ShadeRec& sr)const { glm::vec3 incoming = -sr.ray.getDirection(); glm::vec3 ambientColor = ambientBrdf->rho(sr,incoming); glm::vec3 color = sr.world.getAmbient()->L(sr); color.x *= ambientColor.x; color.y *= ambientColor.y; color.z *= ambientColor.z; int numLights = sr.world.lights.size(); for(int i=0;i<numLights;i++) { glm::vec3 reflected = sr.world.lights[i]->getDirection(sr); float nDotI = glm::dot(sr.normal,reflected); if(nDotI > 0.0) { bool inShadow = false; if(hasShadows && sr.world.lights[i]->castsShadows()) { Ray shadowRay(sr.hitPoint,reflected); inShadow = sr.world.lights[i]->inShadow(shadowRay,sr); } if(!inShadow) { glm::vec3 diffuseColor = diffuseBrdf->f(sr,incoming,reflected); glm::vec3 lightColor = sr.world.lights[i]->L(sr) * sr.world.lights[i]->G(sr) * nDotI / sr.world.lights[i]->pdf(sr); lightColor.x *= diffuseColor.x; lightColor.y *= diffuseColor.y; lightColor.z *= diffuseColor.z; color += lightColor; } } } return color; }
Colour Raytracer::helpShade(Ray3D& ray, LightListNode* curLight, int n, float k) { Vector3D shadowDir; shadowDir = curLight->light->get_position() - ray.intersection.point; shadowDir[0] += k; shadowDir[1] += k; shadowDir[2] += k; shadowDir.normalize(); Point3D shadowOrigin = ray.intersection.point + 0.01*shadowDir; Ray3D shadowRay(shadowOrigin , shadowDir); traverseScene(_root, shadowRay); // Compute non-shadow colour curLight->light->shade(ray); // If ray intersects another object it falls in a shadow if (!shadowRay.intersection.none) return (1/n)*ray.col; else{ return Colour(0,0,0); } }
float3 directLight( Rand& rand, const RTScene& scene, const PathVertex& vertex, const ShadingCS& shadingCS) { //TODO: this does not work for multiple lights! auto & light = scene.accl->lights[0]; float3 lightPos; float lightPdf; //t is already incorporated into pdf float lightT; float3 wiDirectWorld; //sample the light light.sample(rand, vertex.position, &lightPos, &wiDirectWorld, &lightPdf, &lightT); //see if we're occluded Ray shadowRay(vertex.position, wiDirectWorld); //we cannot ignore the light here, as we need to differentiate b/t hit empty space //and hit light IntersectionQuery shadowQuery(shadowRay); Intersection sceneIsect; Intersection lightIsect; intersectScene(scene, shadowQuery, &sceneIsect, &lightIsect); bool hitLight = hitLightFirst(sceneIsect, lightIsect) && lightIsect.lightId == light.id; if(hitLight && facing(vertex.position, vertex.normal, lightIsect.position, lightIsect.normal)) { float3 wiDirect = shadingCS.local(wiDirectWorld); float3 brdfEval = vertex.material->eval(wiDirect, shadingCS.local(vertex.woWorld)); float3 cosTheta = float3(dot(vertex.normal, wiDirectWorld)); float3 le = light.material.emission; return le * brdfEval * cosTheta / lightPdf; } return float3(0); }
glm::vec3 Phong::shade(const ShadeRec& sr)const { glm::vec3 incoming(-sr.ray.getDirection()); glm::vec3 ambient = sr.world.getAmbient()->L(sr); glm::vec3 color = ambientBrdf->rho(sr,incoming); color.x *= ambient.x; color.y *= ambient.y; color.z *= ambient.z; int numLights = sr.world.lights.size(); for(int i=0;i<numLights;i++) { glm::vec3 reflected(sr.world.lights[i]->getDirection(sr)); float nDotI = glm::dot(sr.normal,reflected); if(nDotI > 0.0) { bool inShadow = false; if(hasShadows && sr.world.lights[i]->castsShadows()) { Ray shadowRay(sr.hitPoint,reflected); inShadow = sr.world.lights[i]->inShadow(shadowRay,sr); } if(!inShadow) { glm::vec3 gainedColor(diffuseBrdf->f(sr,incoming,reflected) + specularBrdf->f(sr,incoming,reflected)); glm::vec3 lightColor = sr.world.lights[i]->L(sr) * nDotI; lightColor.x *= gainedColor.x; lightColor.y *= gainedColor.y; lightColor.z *= gainedColor.z; color += lightColor; } } } return color; }
// ============================================================= // ============================================================= // ============================================================= int main() { printf("Small MCFTLE: Computing...\n"); // ============================================================= // initialize // ============================================================= Vec3d eye(1,0.5,2.3), lookAt(1,0.5,0.5), up(0,1,0); // camera parameters double fov = 45.0; Camera camera(eye, lookAt, up, fov, screenResolution); // pinhole camera Frame frame(screenResolution); // stores pixel statistics Flow flow; // vector field Timer timer; // timer to measure runtime // ============================================================= // progressive rendering // ============================================================= for (int it=0; it<maxIterationsPerPixel; ++it) { timer.tic(); // start runtime measurement #ifdef NDEBUG #pragma omp parallel for schedule(dynamic,16) #endif for (int px=0; px<screenResolution.x*screenResolution.y; ++px) { // select pixel and generate a ray Vec2i pxCoord(px%screenResolution.x, px/screenResolution.x); Ray viewRay = camera.GenerateRay(pxCoord); // intersect ray with domain (entry and exit) Vec2d t; if (!flow.IntersectRay(viewRay, t)) continue; // free path sampling to find a scattering event FreePathResult fprView = FreePathSampling(viewRay, flow); Vec3d radiance(1,1,1); // initialize with radiance of the background (white) // if we found a scattering event if (fprView.d <= fprView.dmax) { // compute the scattering albedo c Vec3d albedo = ScatteringAlbedo(fprView.ftle); // compute scattering location x_s and generate shadow ray Vec3d x_s = viewRay.Origin + fprView.d * viewRay.Direction; Ray shadowRay(x_s, -lightDirection); // intersect shadow ray with domain (determine exit) if (!flow.IntersectRay(shadowRay, t)) continue; // free path sampling to find a scattering event on the shadow ray FreePathResult fprLight = FreePathSampling(shadowRay, flow); // if we have scattered, we are in shadow. (actually visibility would be zero now, but for vis purposes we keep a little bit.) double visibility = 1; if (fprLight.d <= fprLight.dmax) visibility = visibility_minimum; // phase function double phase = 1.0 / (4.0 * M_PI); // isotropic phase function // compute in-scattered radiance radiance = albedo * phase * visibility * lightRadiance; } // add the result to the pixel statistics frame.AddPixel(pxCoord, radiance); } timer.toc(); // finish runtime measurement (prints result to console) // write intermediate result to file frame.ExportPfm("result.pfm"); frame.ExportBmp("result.bmp", contrast, brightness, gamma_val); printf("Iteration: %i / %i\n", it+1, maxIterationsPerPixel); } printf("\nComplete!\n"); return 0; }
Color pathTrace(const Ray& ray, ShapeSet& scene, std::list<Shape*>& lights, Rng& rng, size_t lightSamplesHint, size_t maxRayDepth, size_t pixelSampleIndex, Sampler **bounceSamplers) { // Accumulate total incoming radiance in 'result' Color result = Color(0.0f, 0.0f, 0.0f); // As we get through more and more bounces, we track how much the light is // diminished through each bounce Color throughput = Color(1.0f, 1.0f, 1.0f); // Start with the initial ray from the camera Ray currentRay = ray; // While we have bounces left we can still take... size_t numBounces = 0; while (numBounces < maxRayDepth) { // Trace the ray to see if we hit anything Intersection intersection(currentRay); if (!scene.intersect(intersection)) { // No hit, return black (background) break; } // Add in emission when directly visible or via a specular bounce if (numBounces == 0) { result += throughput * intersection.m_pMaterial->emittance(); } // Evaluate the material and intersection information at this bounce Point position = intersection.position(); Vector normal = intersection.m_normal; Vector outgoing = -currentRay.m_direction; Brdf* pBrdf = NULL; float brdfWeight = 1.0f; Color matColor = intersection.m_pMaterial->evaluate(position, normal, outgoing, pBrdf, brdfWeight); // No BRDF? We can't evaluate lighting, so bail. if (pBrdf == NULL) { return result; } // Evaluate direct lighting at this bounce. // For each light... for (std::list<Shape*>::iterator iter = lights.begin(); iter != lights.end(); ++iter) { // Set up samplers for evaluating this light StratifiedRandomSampler lightSampler(lightSamplesHint, lightSamplesHint, rng); StratifiedRandomSampler brdfSampler(lightSamplesHint, lightSamplesHint, rng); // Sample the light (with stratified random sampling to reduce noise) Color lightResult = Color(0.0f, 0.0f, 0.0f); size_t numLightSamples = lightSampler.total2DSamplesAvailable(); for (size_t lightSampleIndex = 0; lightSampleIndex < numLightSamples; ++lightSampleIndex) { // Sample the light using MIS between the light and the BRDF. // This means we ask the light for a direction, and the likelihood // of having sampled that direction (the PDF). Then we ask the // BRDF what it thinks of that direction (its PDF), and weight // the light sample with MIS. // // Then, we ask the BRDF for a direction, and the likelihood of // having sampled that direction (the PDF). Then we ask the // light what it thinks of that direction (its PDF, and whether // that direction even runs into the light at all), and weight // the BRDF sample with MIS. // // By doing both samples and asking both the BRDF and light for // their PDF for each one, we can combine the strengths of both // sampling methods and get the best of both worlds. It does // cost an extra shadow ray and evaluation, though, but it is // generally such an improvement in quality that it is very much // worth the overhead. // Ask the light for a random position/normal we can use for lighting Light *pLightShape = dynamic_cast<Light*>(*iter); float lsu, lsv; lightSampler.sample2D(lightSampleIndex, lsu, lsv); Point lightPoint; Vector lightNormal; float lightPdf = 0.0f; pLightShape->sampleSurface(position, normal, lsu, lsv, lightPoint, lightNormal, lightPdf); if (lightPdf > 0.0f) { // Ask the BRDF what it thinks of this light position (for MIS) Vector lightIncoming = position - lightPoint; float lightDistance = lightIncoming.normalize(); float brdfPdf = 0.0f; float brdfResult = pBrdf->evaluateSA(lightIncoming, outgoing, normal, brdfPdf); if (brdfResult > 0.0f && brdfPdf > 0.0f) { // Fire a shadow ray to make sure we can actually see the light position Ray shadowRay(position, -lightIncoming, lightDistance - kRayTMin); if (!scene.doesIntersect(shadowRay)) { // The light point is visible, so let's add that // contribution (mixed by MIS) float misWeightLight = powerHeuristic(1, lightPdf, 1, brdfPdf); lightResult += pLightShape->emitted() * intersection.m_colorModifier * matColor * brdfResult * std::fabs(dot(-lightIncoming, normal)) * misWeightLight / (lightPdf * brdfWeight); } } } // Ask the BRDF for a sample direction float bsu, bsv; brdfSampler.sample2D(lightSampleIndex, bsu, bsv); Vector brdfIncoming; float brdfPdf = 0.0f; float brdfResult = pBrdf->sampleSA(brdfIncoming, outgoing, normal, bsu, bsv, brdfPdf); if (brdfPdf > 0.0f && brdfResult > 0.0f) { Intersection shadowIntersection(Ray(position, -brdfIncoming)); bool intersected = scene.intersect(shadowIntersection); if (intersected && shadowIntersection.m_pShape == pLightShape) { // Ask the light what it thinks of this direction (for MIS) lightPdf = pLightShape->intersectPdf(shadowIntersection); if (lightPdf > 0.0f) { // BRDF chose the light, so let's add that // contribution (mixed by MIS) float misWeightBrdf = powerHeuristic(1, brdfPdf, 1, lightPdf); lightResult += pLightShape->emitted() * intersection.m_colorModifier * matColor * brdfResult * std::fabs(dot(-brdfIncoming, normal)) * misWeightBrdf / (brdfPdf * brdfWeight); } } } } // Average light samples if (numLightSamples) { lightResult /= numLightSamples; } // Add direct lighting at this bounce (modified by how much the // previous bounces have dimmed it) result += throughput * lightResult; } // Sample the BRDF to find the direction the next leg of the path goes in float brdfSampleU, brdfSampleV; bounceSamplers[numBounces]->sample2D(pixelSampleIndex, brdfSampleU, brdfSampleV); Vector incoming; float incomingBrdfPdf = 0.0f; float incomingBrdfResult = pBrdf->sampleSA(incoming, outgoing, normal, brdfSampleU, brdfSampleV, incomingBrdfPdf); if (incomingBrdfPdf > 0.0f) { currentRay.m_origin = position; currentRay.m_direction = -incoming; currentRay.m_tMax = kRayTMax; // Reduce lighting effect for the next bounce based on this bounce's BRDF throughput *= intersection.m_colorModifier * matColor * incomingBrdfResult * (std::fabs(dot(-incoming, normal)) / (incomingBrdfPdf * brdfWeight)); } else { break; // BRDF is zero, stop bouncing } numBounces++; } // This represents an estimate of the total light coming in along the path return result; }
Color3f Li(const Scene *scene, Sampler *sampler, const Ray3f &ray) const { /* Find the surface that is visible in the requested direction */ Intersection its; //check if the ray intersects the scene if (!scene->rayIntersect(ray, its)) { //check if a distant disk light is set const Emitter* distantsDisk = scene->getDistantEmitter(); if(distantsDisk == nullptr ) return Color3f(0.0f); //sample the distant disk light Vector3f d = ray.d; return distantsDisk->sampleL(d); } //get the Number of lights from the scene const std::vector<Emitter *> lights = scene->getEmitters(); uint32_t nLights = lights.size(); Color3f tp(1.0f, 1.0f, 1.0f); Color3f L(0.0f, 0.0f, 0.0f); Ray3f pathRay(ray.o, ray.d); bool deltaFlag = true; while(true) { if (its.mesh->isEmitter() && deltaFlag) { const Emitter* areaLightEM = its.mesh->getEmitter(); const areaLight* aEM = static_cast<const areaLight *> (areaLightEM); L += tp * aEM->sampleL(-pathRay.d, its.shFrame.n, its); } //Light sampling //randomly select a lightsource uint32_t var = uint32_t(std::min(sampler->next1D()*nLights, float(nLights) - 1.0f)); //init the light color Color3f Li(0.0f, 0.0f, 0.0f); Color3f Ld(1.0f, 1.0f, 1.0f); //create a sample for the light const BSDF* curBSDF = its.mesh->getBSDF(); const Point2f lightSample = sampler->next2D(); VisibilityTester vis; Vector3f wo; float lightpdf; float bsdfpdf; Normal3f n = its.shFrame.n; deltaFlag = curBSDF->isDeltaBSDF(); //sample the light { Li = lights[var]->sampleL(its.p, Epsilon, lightSample , &wo, &lightpdf, &vis); lightpdf /= float(nLights); //check if the pdf of the sample is greater than 0 and if the color is not black if(lightpdf > 0 && Li.maxCoeff() != 0.0f) { //calculate the cosine term wi in my case the vector to the light float cosTerm = std::abs(n.dot(wo)); const BSDFQueryRecord queryEM = BSDFQueryRecord(its.toLocal(- pathRay.d), its.toLocal(wo), EMeasure::ESolidAngle, sampler); Color3f f = curBSDF->eval(queryEM); if(f.maxCoeff() > 0.0f && f.minCoeff() >= 0.0f && vis.Unoccluded(scene)) { bsdfpdf = curBSDF->pdf(queryEM); float weight = BalanceHeuristic(float(1), lightpdf, float(1), bsdfpdf); if(curBSDF->isDeltaBSDF()) weight = 1.0f; if(bsdfpdf > 0.0f) { Ld = (weight * f * Li * cosTerm) / lightpdf; L += tp * Ld; } else { //cout << "bsdfpdf = " << bsdfpdf << endl; //cout << "f = " << f << endl; } } } } //Material part BSDFQueryRecord queryMats = BSDFQueryRecord(its.toLocal(-pathRay.d), Vector3f(0.0f), EMeasure::ESolidAngle, sampler); Color3f fi = curBSDF->sample(queryMats, sampler->next2D()); bsdfpdf = curBSDF->pdf(queryMats); lightpdf = 0.0f; if(fi.maxCoeff() > 0.0f && fi.minCoeff() >= 0.0f) { if(bsdfpdf > 0.0f) { Ray3f shadowRay(its.p, its.toWorld(queryMats.wo)); Intersection lightIsect; if (scene->rayIntersect(shadowRay, lightIsect)) { if(lightIsect.mesh->isEmitter()){ const Emitter* areaLightEMcur = lightIsect.mesh->getEmitter(); const areaLight* aEMcur = static_cast<const areaLight *> (areaLightEMcur); Li = aEMcur->sampleL(-shadowRay.d, lightIsect.shFrame.n, lightIsect); lightpdf = aEMcur->pdf(its.p, (lightIsect.p - its.p).normalized(), lightIsect.p, Normal3f(lightIsect.shFrame.n)); } } else { const Emitter* distantsDisk = scene->getDistantEmitter(); if(distantsDisk != nullptr ) { //check if THIS is right! Li = distantsDisk->sampleL(lightIsect.toWorld(queryMats.wo)); lightpdf = distantsDisk->pdf(Point3f(0.0f), wo, Point3f(0.0f), Normal3f(0.0f)); } } lightpdf /= float(nLights); //calculate the weights float weight = BalanceHeuristic(float(1), bsdfpdf, float(1), lightpdf); //check if the lightcolor is not black if(Li.maxCoeff() > 0.0f && lightpdf > 0.0f ) { //wo in my case the vector to the light Ld = weight * Li * fi; L += tp * Ld; } } tp *= fi; } else { break; } wo = its.toWorld(queryMats.wo); pathRay = Ray3f(its.p, wo); if (!scene->rayIntersect(pathRay, its)) { const Emitter* distantsDisk = scene->getDistantEmitter(); if(distantsDisk != nullptr ) { //sample the distant disk light Vector3f d = pathRay.d; L += tp * distantsDisk->sampleL(d); } break; } float maxCoeff = tp.maxCoeff(); float q = std::min(0.99f, maxCoeff); if(q < sampler->next1D()){ break; } tp /= q; } return L; }
void LightTracer::traceSample(PathSampleGenerator &sampler) { float lightPdf; const Primitive *light = chooseLightAdjoint(sampler, lightPdf); const Medium *medium = light->extMedium().get(); PositionSample point; if (!light->samplePosition(sampler, point)) return; DirectionSample direction; if (!light->sampleDirection(sampler, point, direction)) return; sampler.advancePath(); Vec3f throughput(point.weight/lightPdf); LensSample splat; if (_settings.minBounces == 0 && !light->isInfinite() && _scene->cam().sampleDirect(point.p, sampler, splat)) { Ray shadowRay(point.p, splat.d); shadowRay.setFarT(splat.dist); Vec3f transmission = generalizedShadowRay(shadowRay, medium, nullptr, 0); if (transmission != 0.0f) { Vec3f value = throughput*transmission*splat.weight *light->evalDirectionalEmission(point, DirectionSample(splat.d)); _splatBuffer->splatFiltered(splat.pixel, value); } } sampler.advancePath(); Ray ray(point.p, direction.d); throughput *= direction.weight; VolumeScatterEvent volumeEvent; SurfaceScatterEvent surfaceEvent; IntersectionTemporary data; IntersectionInfo info; Medium::MediumState state; state.reset(); Vec3f emission(0.0f); int bounce = 0; bool wasSpecular = true; bool didHit = _scene->intersect(ray, data, info); while ((didHit || medium) && bounce < _settings.maxBounces - 1) { bool hitSurface = true; if (medium) { volumeEvent = VolumeScatterEvent(&sampler, throughput, ray.pos(), ray.dir(), ray.farT()); if (!medium->sampleDistance(volumeEvent, state)) break; throughput *= volumeEvent.weight; volumeEvent.weight = Vec3f(1.0f); hitSurface = volumeEvent.t == volumeEvent.maxT; if (hitSurface && !didHit) break; } if (hitSurface) { surfaceEvent = makeLocalScatterEvent(data, info, ray, &sampler); Vec3f weight; Vec2f pixel; if (surfaceLensSample(_scene->cam(), surfaceEvent, medium, bounce + 1, ray, weight, pixel)) _splatBuffer->splatFiltered(pixel, weight*throughput); if (!handleSurface(surfaceEvent, data, info, medium, bounce, true, false, ray, throughput, emission, wasSpecular, state)) break; } else { volumeEvent.p += volumeEvent.t*volumeEvent.wi; Vec3f weight; Vec2f pixel; if (volumeLensSample(_scene->cam(), volumeEvent, medium, bounce + 1, ray, weight, pixel)) _splatBuffer->splatFiltered(pixel, weight*throughput); if (!handleVolume(volumeEvent, medium, bounce, true, false, ray, throughput, emission, wasSpecular, state)) break; } if (throughput.max() == 0.0f) break; if (std::isnan(ray.dir().sum() + ray.pos().sum())) break; if (std::isnan(throughput.sum())) break; sampler.advancePath(); bounce++; if (bounce < _settings.maxBounces) didHit = _scene->intersect(ray, data, info); } }
rgbColor whittedRayTracer::_L(ray& r, const int& depth) const{ if(depth > MAX_DEPTH){ return rgbColor(0.f); } const intersection isect = parent.intersect(r); //return rgbColor(0, isect.debugInfo / 1e8, 0); if(!isect.hit){ return rgbColor(0.f); }else if(isect.li != NULL){ return isect.li->L(r); } material& mat = isect.li ? *isect.li->getMaterial().get() : *isect.s->getMaterial().get(); const vec3& normal = isect.shadingNormal; const bsdf& bsdf = mat.getBsdf(isect.uv); const vec3 wo = worldToBsdf(-r.direction, isect); bxdfType sampledType; rgbColor colorSum(0.f); if(isect.s->getMaterial()->isEmissive()){ return mat.Le(); } // Diffuse calculations. float lightPdf = 0.f; for(int i=0; i<parent.numLights(); ++i){ const light& li = parent.getLight(i); if(li.isPointSource()){ vec3 lightDir; const rgbColor Li = li.sampleL(r.origin, lightDir, sampleUniform(), sampleUniform(), lightPdf); const float lightDist = norm(lightDir); lightDir = normalize(lightDir); // Test for shadowing early. ray shadowRay(r.origin, lightDir); shadowRay.tMax = lightDist; if(!parent.intersectB(shadowRay)){ const vec3 wi = worldToBsdf(lightDir, isect); const rgbColor f = bsdf.f(wo, wi, bxdfType(DIFFUSE | GLOSSY | REFLECTION)) + mat.Le(); colorSum += f * dot(normal, lightDir) * (Li / lightPdf); } }else{ rgbColor areaContrib(0.f); for(int j=0; j<areaSamples; ++j){ vec3 lightDir; const rgbColor Li = li.sampleL(r.origin, lightDir, sampleUniform(), sampleUniform(), lightPdf); ray shadowRay(r.origin, normalize(lightDir)); shadowRay.tMax = norm(lightDir) + EPSILON; if(!parent.intersectB(shadowRay) && li.intersect(shadowRay).hit){ lightDir = normalize(lightDir); const vec3 wi = worldToBsdf(lightDir, isect); const rgbColor f = bsdf.f(wo, wi, bxdfType(DIFFUSE | GLOSSY | REFLECTION)) + mat.Le(); areaContrib += f * dot(normal, lightDir) * (Li / lightPdf); } } colorSum += areaContrib / (float)areaSamples; } } // Trace specular rays. vec3 specDir; float pdf; const rgbColor fr = bsdf.sampleF(sampleUniform(), sampleUniform(), sampleUniform(), wo, specDir, bxdfType(GLOSSY | SPECULAR | REFLECTION), sampledType, pdf); if(!fr.isBlack()){ specDir = bsdfToWorld(specDir, isect); ray r2(r.origin, specDir); colorSum += (fr / pdf) * _L(r2, depth+1) * abs(dot(specDir, normal)); } const rgbColor ft = bsdf.sampleF(sampleUniform(), sampleUniform(), sampleUniform(), wo, specDir, bxdfType(GLOSSY | SPECULAR | TRANSMISSION), sampledType, pdf); if(!ft.isBlack()){ specDir = bsdfToWorld(specDir, isect); ray r2(r.origin, specDir); colorSum += (ft / pdf) * _L(r2, depth+1) * abs(dot(specDir, normal)); } if(!isFinite(colorSum.avg())) { return ERROR_COLOR; } else { return colorSum; } }