void Path::AdvancePath(PathRenderEngine *renderEngine, Sampler *sampler, const RayBuffer *rayBuffer, SampleBuffer *sampleBuffer) { SLGScene *scene = renderEngine->scene; //-------------------------------------------------------------------------- // Select the path ray hit //-------------------------------------------------------------------------- const RayHit *rayHit = NULL; switch (state) { case EYE_VERTEX: if (scene->volumeIntegrator) { rayHit = rayBuffer->GetRayHit(currentPathRayIndex); // Use Russian Roulette to check if I have to do participating media computation or not if (sample.GetLazyValue() <= scene->volumeIntegrator->GetRRProbability()) { Ray volumeRay(pathRay.o, pathRay.d, 0.f, rayHit->Miss() ? std::numeric_limits<float>::infinity() : rayHit->t); scene->volumeIntegrator->GenerateLiRays(scene, &sample, volumeRay, volumeComp); radiance += volumeComp->GetEmittedLight(); if (volumeComp->GetRayCount() > 0) { // Do the EYE_VERTEX_VOLUME_STEP state = EYE_VERTEX_VOLUME_STEP; eyeHit = *(rayBuffer->GetRayHit(currentPathRayIndex)); return; } } } else rayHit = rayBuffer->GetRayHit(currentPathRayIndex); break; case NEXT_VERTEX: rayHit = rayBuffer->GetRayHit(currentPathRayIndex); break; case EYE_VERTEX_VOLUME_STEP: // Add scattered light radiance += throughput * volumeComp->CollectResults(rayBuffer) / scene->volumeIntegrator->GetRRProbability(); rayHit = &eyeHit; break; case TRANSPARENT_SHADOW_RAYS_STEP: rayHit = &eyeHit; break; case TRANSPARENT_ONLY_SHADOW_RAYS_STEP: case ONLY_SHADOW_RAYS: // Nothing break; default: assert(false); break; } //-------------------------------------------------------------------------- // Finish direct light sampling //-------------------------------------------------------------------------- if (((state == NEXT_VERTEX) || (state == ONLY_SHADOW_RAYS) || (state == TRANSPARENT_SHADOW_RAYS_STEP) || (state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP)) && (tracedShadowRayCount > 0)) { unsigned int leftShadowRaysToTrace = 0; for (unsigned int i = 0; i < tracedShadowRayCount; ++i) { const RayHit *shadowRayHit = rayBuffer->GetRayHit(currentShadowRayIndex[i]); if (shadowRayHit->Miss()) { // Nothing was hit, light is visible radiance += lightColor[i] / lightPdf[i]; } else { // Something was hit check if it is transparent const unsigned int currentShadowTriangleIndex = shadowRayHit->index; const unsigned int currentShadowMeshIndex = scene->dataSet->GetMeshID(currentShadowTriangleIndex); Material *triMat = scene->objectMaterials[currentShadowMeshIndex]; if (triMat->IsShadowTransparent()) { // It is shadow transparent, I need to continue to trace the ray shadowRay[leftShadowRaysToTrace] = Ray( shadowRay[i](shadowRayHit->t), shadowRay[i].d, shadowRay[i].mint, shadowRay[i].maxt - shadowRayHit->t); lightColor[leftShadowRaysToTrace] = lightColor[i] * triMat->GetSahdowTransparency(); lightPdf[leftShadowRaysToTrace] = lightPdf[i]; leftShadowRaysToTrace++; } else { // Check if there is a texture with alpha TexMapInstance *tm = scene->objectTexMaps[currentShadowMeshIndex]; if (tm) { const TextureMap *map = tm->GetTexMap(); if (map->HasAlpha()) { const ExtMesh *mesh = scene->objects[currentShadowMeshIndex]; const UV triUV = mesh->InterpolateTriUV(scene->dataSet->GetMeshTriangleID(currentShadowTriangleIndex), shadowRayHit->b1, shadowRayHit->b2); const float alpha = map->GetAlpha(triUV); if (alpha < 1.f) { // It is shadow transparent, I need to continue to trace the ray shadowRay[leftShadowRaysToTrace] = Ray( shadowRay[i](shadowRayHit->t), shadowRay[i].d, shadowRay[i].mint, shadowRay[i].maxt - shadowRayHit->t); lightColor[leftShadowRaysToTrace] = lightColor[i] * (1.f - alpha); lightPdf[leftShadowRaysToTrace] = lightPdf[i]; leftShadowRaysToTrace++; } } } } } } if (leftShadowRaysToTrace > 0) { tracedShadowRayCount = leftShadowRaysToTrace; if ((state == ONLY_SHADOW_RAYS) || (state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP)) state = TRANSPARENT_ONLY_SHADOW_RAYS_STEP; else { eyeHit = *rayHit; state = TRANSPARENT_SHADOW_RAYS_STEP; } return; } } //-------------------------------------------------------------------------- // Calculate next step //-------------------------------------------------------------------------- depth++; const bool missed = rayHit ? rayHit->Miss() : false; if (missed || (state == ONLY_SHADOW_RAYS) || (state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP) || (depth >= renderEngine->maxPathDepth)) { if (missed && scene->infiniteLight && (scene->useInfiniteLightBruteForce || specularBounce)) { // Add the light emitted by the infinite light radiance += scene->infiniteLight->Le(pathRay.d) * throughput; } // Hit nothing/only shadow rays/maxdepth, terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); return; } // Something was hit const unsigned int currentTriangleIndex = rayHit->index; const unsigned int currentMeshIndex = scene->dataSet->GetMeshID(currentTriangleIndex); // Get the triangle const ExtMesh *mesh = scene->objects[currentMeshIndex]; const unsigned int triIndex = scene->dataSet->GetMeshTriangleID(currentTriangleIndex); // Get the material const Material *triMat = scene->objectMaterials[currentMeshIndex]; // Check if it is a light source if (triMat->IsLightSource()) { if (specularBounce) { // Only TriangleLight can be directly hit const LightMaterial *mLight = (LightMaterial *) triMat; Spectrum Le = mLight->Le(mesh, triIndex, -pathRay.d); radiance += Le * throughput; } // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); return; } //-------------------------------------------------------------------------- // Build the shadow rays (if required) //-------------------------------------------------------------------------- // Interpolate face normal Normal N = mesh->InterpolateTriNormal(triIndex, rayHit->b1, rayHit->b2); const SurfaceMaterial *triSurfMat = (SurfaceMaterial *) triMat; const Point hitPoint = pathRay(rayHit->t); const Vector wo = -pathRay.d; Spectrum surfaceColor; if (mesh->HasColors()) surfaceColor = mesh->InterpolateTriColor(triIndex, rayHit->b1, rayHit->b2); else surfaceColor = Spectrum(1.f, 1.f, 1.f); // Check if I have to apply texture mapping or normal mapping TexMapInstance *tm = scene->objectTexMaps[currentMeshIndex]; BumpMapInstance *bm = scene->objectBumpMaps[currentMeshIndex]; NormalMapInstance *nm = scene->objectNormalMaps[currentMeshIndex]; if (tm || bm || nm) { // Interpolate UV coordinates if required const UV triUV = mesh->InterpolateTriUV(triIndex, rayHit->b1, rayHit->b2); // Check if there is an assigned texture map if (tm) { const TextureMap *map = tm->GetTexMap(); // Apply texture mapping surfaceColor *= map->GetColor(triUV); // Check if the texture map has an alpha channel if (map->HasAlpha()) { const float alpha = map->GetAlpha(triUV); if ((alpha == 0.0f) || ((alpha < 1.f) && (sample.GetLazyValue() > alpha))) { pathRay = Ray(pathRay(rayHit->t), pathRay.d, RAY_EPSILON, pathRay.maxt - rayHit->t); state = NEXT_VERTEX; tracedShadowRayCount = 0; return; } } } // Check if there is an assigned bump/normal map if (bm || nm) { if (nm) { // Apply normal mapping const Spectrum color = nm->GetTexMap()->GetColor(triUV); const float x = 2.f * (color.r - 0.5f); const float y = 2.f * (color.g - 0.5f); const float z = 2.f * (color.b - 0.5f); Vector v1, v2; CoordinateSystem(Vector(N), &v1, &v2); N = Normalize(Normal( v1.x * x + v2.x * y + N.x * z, v1.y * x + v2.y * y + N.y * z, v1.z * x + v2.z * y + N.z * z)); } if (bm) { // Apply bump mapping const TextureMap *map = bm->GetTexMap(); const UV &dudv = map->GetDuDv(); const float b0 = map->GetColor(triUV).Filter(); const UV uvdu(triUV.u + dudv.u, triUV.v); const float bu = map->GetColor(uvdu).Filter(); const UV uvdv(triUV.u, triUV.v + dudv.v); const float bv = map->GetColor(uvdv).Filter(); const float scale = bm->GetScale(); const Vector bump(scale * (bu - b0), scale * (bv - b0), 1.f); Vector v1, v2; CoordinateSystem(Vector(N), &v1, &v2); N = Normalize(Normal( v1.x * bump.x + v2.x * bump.y + N.x * bump.z, v1.y * bump.x + v2.y * bump.y + N.y * bump.z, v1.z * bump.x + v2.z * bump.y + N.z * bump.z)); } } } // Flip the normal if required Normal shadeN = (Dot(pathRay.d, N) > 0.f) ? -N : N; tracedShadowRayCount = 0; const bool skipInfiniteLight = !renderEngine->onlySampleSpecular; const unsigned int lightCount = scene->GetLightCount(skipInfiniteLight); if (triSurfMat->IsDiffuse() && (scene->GetLightCount(skipInfiniteLight) > 0)) { // Direct light sampling: trace shadow rays switch (renderEngine->lightStrategy) { case ALL_UNIFORM: { // ALL UNIFORM direct sampling light strategy const Spectrum lightThroughtput = throughput * surfaceColor; for (unsigned int j = 0; j < lightCount; ++j) { // Select the light to sample const LightSource *light = scene->GetLight(j, skipInfiniteLight); for (unsigned int i = 0; i < renderEngine->shadowRayCount; ++i) { // Select a point on the light surface lightColor[tracedShadowRayCount] = light->Sample_L( scene, hitPoint, &shadeN, sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(), &lightPdf[tracedShadowRayCount], &shadowRay[tracedShadowRayCount]); // Scale light pdf for ALL_UNIFORM strategy lightPdf[tracedShadowRayCount] *= renderEngine->shadowRayCount; if (lightPdf[tracedShadowRayCount] <= 0.0f) continue; const Vector lwi = shadowRay[tracedShadowRayCount].d; lightColor[tracedShadowRayCount] *= lightThroughtput * Dot(shadeN, lwi) * triSurfMat->f(wo, lwi, shadeN); if (!lightColor[tracedShadowRayCount].Black()) tracedShadowRayCount++; } } break; } case ONE_UNIFORM: { // ONE UNIFORM direct sampling light strategy const Spectrum lightThroughtput = throughput * surfaceColor; for (unsigned int i = 0; i < renderEngine->shadowRayCount; ++i) { // Select the light to sample float lightStrategyPdf; const LightSource *light = scene->SampleAllLights(sample.GetLazyValue(), &lightStrategyPdf, skipInfiniteLight); // Select a point on the light surface lightColor[tracedShadowRayCount] = light->Sample_L( scene, hitPoint, &shadeN, sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(), &lightPdf[tracedShadowRayCount], &shadowRay[tracedShadowRayCount]); if (lightPdf[tracedShadowRayCount] <= 0.f) continue; const Vector lwi = shadowRay[tracedShadowRayCount].d; lightColor[tracedShadowRayCount] *= lightThroughtput * Dot(shadeN, lwi) * triSurfMat->f(wo, lwi, shadeN); if (!lightColor[tracedShadowRayCount].Black()) { lightPdf[tracedShadowRayCount] *= lightStrategyPdf * renderEngine->shadowRayCount; tracedShadowRayCount++; } } break; } default: assert(false); } } //-------------------------------------------------------------------------- // Build the next vertex path ray //-------------------------------------------------------------------------- float fPdf; Vector wi; const Spectrum f = triSurfMat->Sample_f(wo, &wi, N, shadeN, sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(), renderEngine->onlySampleSpecular, &fPdf, specularBounce) * surfaceColor; if ((fPdf <= 0.f) || f.Black()) { if (tracedShadowRayCount > 0) state = ONLY_SHADOW_RAYS; else { // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); } return; } throughput *= f / fPdf; if (depth > renderEngine->rrDepth) { // Russian Roulette switch (renderEngine->rrStrategy) { case PROBABILITY: { if (renderEngine->rrProb >= sample.GetLazyValue()) throughput /= renderEngine->rrProb; else { // Check if terminate the path or I have still to trace shadow rays if (tracedShadowRayCount > 0) state = ONLY_SHADOW_RAYS; else { // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); } return; } break; } case IMPORTANCE: { const float prob = Max(throughput.Filter(), renderEngine->rrImportanceCap); if (prob >= sample.GetLazyValue()) throughput /= prob; else { // Check if terminate the path or I have still to trace shadow rays if (tracedShadowRayCount > 0) state = ONLY_SHADOW_RAYS; else { // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); } return; } break; } default: assert(false); } } pathRay.o = hitPoint; pathRay.d = wi; state = NEXT_VERTEX; }
void BSDF::Init(const bool fromL, const Scene &scene, const Ray &ray, const RayHit &rayHit, const float u0) { fromLight = fromL; isPassThrough = false; isLightSource = false; const unsigned int currentTriangleIndex = rayHit.index; const unsigned int currentMeshIndex = scene.dataSet->GetMeshID(currentTriangleIndex); // Get the triangle const ExtMesh *mesh = scene.objects[currentMeshIndex]; const unsigned int triIndex = scene.dataSet->GetMeshTriangleID(currentTriangleIndex); // Get the material material = scene.objectMaterials[currentMeshIndex]; // Check if it is a light source if (material->IsLightSource()) { isLightSource = true; // SLG light sources are like black bodies return; } surfMat = (const SurfaceMaterial *)material; hitPoint = ray(rayHit.t); if (mesh->HasColors()) surfaceColor = mesh->InterpolateTriColor(triIndex, rayHit.b1, rayHit.b2); else surfaceColor = Spectrum(1.f, 1.f, 1.f); // Interpolate face normal geometryN = mesh->GetGeometryNormal(triIndex); shadeN = mesh->InterpolateTriNormal(triIndex, rayHit.b1, rayHit.b2); // Check if I have to apply texture mapping or normal mapping TexMapInstance *tm = scene.objectTexMaps[currentMeshIndex]; BumpMapInstance *bm = scene.objectBumpMaps[currentMeshIndex]; NormalMapInstance *nm = scene.objectNormalMaps[currentMeshIndex]; if (tm || bm || nm) { // Interpolate UV coordinates if required const UV triUV = mesh->InterpolateTriUV(triIndex, rayHit.b1, rayHit.b2); // Check if there is an assigned texture map if (tm) { const TextureMap *map = tm->GetTexMap(); // Apply texture mapping surfaceColor *= map->GetColor(triUV); // Check if the texture map has an alpha channel if (map->HasAlpha()) { const float alpha = map->GetAlpha(triUV); if ((alpha == 0.0f) || ((alpha < 1.f) && (u0 > alpha))) { // It is a pass-through material isPassThrough = true; return; } } } // Check if there is an assigned bump/normal map if (bm || nm) { if (nm) { // Apply normal mapping const Spectrum color = nm->GetTexMap()->GetColor(triUV); const float x = 2.f * (color.r - 0.5f); const float y = 2.f * (color.g - 0.5f); const float z = 2.f * (color.b - 0.5f); Vector v1, v2; CoordinateSystem(Vector(shadeN), &v1, &v2); shadeN = Normalize(Normal( v1.x * x + v2.x * y + shadeN.x * z, v1.y * x + v2.y * y + shadeN.y * z, v1.z * x + v2.z * y + shadeN.z * z)); } if (bm) { // Apply bump mapping const TextureMap *map = bm->GetTexMap(); const UV &dudv = map->GetDuDv(); const float b0 = map->GetColor(triUV).Filter(); const UV uvdu(triUV.u + dudv.u, triUV.v); const float bu = map->GetColor(uvdu).Filter(); const UV uvdv(triUV.u, triUV.v + dudv.v); const float bv = map->GetColor(uvdv).Filter(); const float scale = bm->GetScale(); const Vector bump(scale * (bu - b0), scale * (bv - b0), 1.f); Vector v1, v2; CoordinateSystem(Vector(shadeN), &v1, &v2); shadeN = Normalize(Normal( v1.x * bump.x + v2.x * bump.y + shadeN.x * bump.z, v1.y * bump.x + v2.y * bump.y + shadeN.y * bump.z, v1.z * bump.x + v2.z * bump.y + shadeN.z * bump.z)); } } } frame.SetFromZ(shadeN); }