float Noise(float x, float y, float z) { // Compute noise cell coordinates and offsets int ix = Floor2Int(x), iy = Floor2Int(y), iz = Floor2Int(z); float dx = x - ix, dy = y - iy, dz = z - iz; // Compute gradient weights ix &= (NOISE_PERM_SIZE-1); iy &= (NOISE_PERM_SIZE-1); iz &= (NOISE_PERM_SIZE-1); float w000 = Grad(ix, iy, iz, dx, dy, dz); float w100 = Grad(ix+1, iy, iz, dx-1, dy, dz); float w010 = Grad(ix, iy+1, iz, dx, dy-1, dz); float w110 = Grad(ix+1, iy+1, iz, dx-1, dy-1, dz); float w001 = Grad(ix, iy, iz+1, dx, dy, dz-1); float w101 = Grad(ix+1, iy, iz+1, dx-1, dy, dz-1); float w011 = Grad(ix, iy+1, iz+1, dx, dy-1, dz-1); float w111 = Grad(ix+1, iy+1, iz+1, dx-1, dy-1, dz-1); // Compute trilinear interpolation of weights float wx = NoiseWeight(dx), wy = NoiseWeight(dy), wz = NoiseWeight(dz); float x00 = Lerp(wx, w000, w100); float x10 = Lerp(wx, w010, w110); float x01 = Lerp(wx, w001, w101); float x11 = Lerp(wx, w011, w111); float y0 = Lerp(wy, x00, x10); float y1 = Lerp(wy, x01, x11); return Lerp(wz, y0, y1); }
static real64 PerlinNoise3DFunction(real64 x, real64 y, real64 z) { // Compute noise cell coordinates and offsets int32_t ix = Floor2Int(x); int32_t iy = Floor2Int(y); int32_t iz = Floor2Int(z); real64 dx = x - ix, dy = y - iy, dz = z - iz; // Compute gradient weights ix &= (NOISE_PERM_SIZE-1); iy &= (NOISE_PERM_SIZE-1); iz &= (NOISE_PERM_SIZE-1); real64 w000 = Grad3d(ix, iy, iz, dx, dy, dz); real64 w100 = Grad3d(ix+1, iy, iz, dx-1, dy, dz); real64 w010 = Grad3d(ix, iy+1, iz, dx, dy-1, dz); real64 w110 = Grad3d(ix+1, iy+1, iz, dx-1, dy-1, dz); real64 w001 = Grad3d(ix, iy, iz+1, dx, dy, dz-1); real64 w101 = Grad3d(ix+1, iy, iz+1, dx-1, dy, dz-1); real64 w011 = Grad3d(ix, iy+1, iz+1, dx, dy-1, dz-1); real64 w111 = Grad3d(ix+1, iy+1, iz+1, dx-1, dy-1, dz-1); // Compute trilinear interpolation of weights real64 wx = PerlinFade(dx); real64 wy = PerlinFade(dy); real64 wz = PerlinFade(dz); real64 x00 = Lerp(wx, w000, w100); real64 x10 = Lerp(wx, w010, w110); real64 x01 = Lerp(wx, w001, w101); real64 x11 = Lerp(wx, w011, w111); real64 y0 = Lerp(wy, x00, x10); real64 y1 = Lerp(wy, x01, x11); return Lerp(wz, y0, y1); }
void Sampler::ComputeSubWindow(int num, int count, int *newXStart, int *newXEnd, int *newYStart, int *newYEnd) const { // Determine how many tiles to use in each dimension, _nx_ and _ny_ int dx = xPixelEnd - xPixelStart, dy = yPixelEnd - yPixelStart; int nx = count, ny = 1; while ((nx & 0x1) == 0 && 2 * dx * ny < dy * nx) { nx >>= 1; ny <<= 1; } Assert(nx * ny == count); // Compute $x$ and $y$ pixel sample range for sub-window int xo = num % nx, yo = num / nx; float tx0 = float(xo) / float(nx), tx1 = float(xo+1) / float(nx); float ty0 = float(yo) / float(ny), ty1 = float(yo+1) / float(ny); *newXStart = Floor2Int(Lerp(tx0, xPixelStart, xPixelEnd)); *newXEnd = Floor2Int(Lerp(tx1, xPixelStart, xPixelEnd)); *newYStart = Floor2Int(Lerp(ty0, yPixelStart, yPixelEnd)); *newYEnd = Floor2Int(Lerp(ty1, yPixelStart, yPixelEnd)); /*std::cout << "Subtask " << num << "/" << count << " handles following values:" << std::endl; std::cout << "(" << *newXStart << " x " << *newYStart << ") -> (" << *newXEnd << " x " << *newYEnd << ")" << std::endl; std::cout << "---" << std::endl;*/ }
Spectrum UniformSampleOneLight(const Scene *scene, const Renderer *renderer, MemoryArena &arena, const Point &p, const Normal &n, const Vector &wo, float rayEpsilon, float time, BSDF *bsdf, const Sample *sample, RNG &rng, int lightNumOffset, const LightSampleOffsets *lightSampleOffset, const BSDFSampleOffsets *bsdfSampleOffset) { // Randomly choose a single light to sample, _light_ int nLights = int(scene->lights.size()); if (nLights == 0) return Spectrum(0.); int lightNum; if (lightNumOffset != -1) lightNum = Floor2Int(sample->oneD[lightNumOffset][0] * nLights); else lightNum = Floor2Int(rng.RandomFloat() * nLights); lightNum = min(lightNum, nLights-1); Light *light = scene->lights[lightNum]; // Initialize light and bsdf samples for single light sample LightSample lightSample; BSDFSample bsdfSample; if (lightSampleOffset != NULL && bsdfSampleOffset != NULL) { lightSample = LightSample(sample, *lightSampleOffset, 0); bsdfSample = BSDFSample(sample, *bsdfSampleOffset, 0); } else { lightSample = LightSample(rng); bsdfSample = BSDFSample(rng); } return (float)nLights * EstimateDirect(scene, renderer, arena, light, p, n, wo, rayEpsilon, time, bsdf, rng, lightSample, bsdfSample, BxDFType(BSDF_ALL & ~BSDF_SPECULAR)); }
Spectrum Evaluate(const DifferentialGeometry &dg) const { float s, t, dsdx, dtdx, dsdy, dtdy; mapping->Map(dg, &s, &t, &dsdx, &dtdx, &dsdy, &dtdy); float cs[COLOR_SAMPLES]; memset(cs, 0, COLOR_SAMPLES * sizeof(float)); cs[0] = s - Floor2Int(s); cs[1] = t - Floor2Int(t); return Spectrum(cs); }
void ImageFilm::GetSampleExtent(int *xstart, int *xend, int *ystart, int *yend) const { *xstart = Floor2Int(xPixelStart + .5f - filter->xWidth); *xend = Floor2Int(xPixelStart + .5f + xPixelCount + filter->xWidth); *ystart = Floor2Int(yPixelStart + .5f - filter->yWidth); *yend = Floor2Int(yPixelStart + .5f + yPixelCount + filter->yWidth); }
Spectrum Evaluate(const DifferentialGeometry &dg) const { float s, t, dsdx, dtdx, dsdy, dtdy; mapping->Map(dg, &s, &t, &dsdx, &dtdx, &dsdy, &dtdy); //(dpl)=changed this to allow for non constant COLOR_SAMPLES float *cs = new float[COLOR_SAMPLES]; memset(cs, 0, COLOR_SAMPLES * sizeof(float)); cs[0] = s - Floor2Int(s); cs[1] = t - Floor2Int(t); return Spectrum(cs); }
void ImageFilm::AddSample(const Sample &sample, const Ray &ray, const Spectrum &L, float alpha) { // Compute sample's raster extent float dImageX = sample.imageX - 0.5f; float dImageY = sample.imageY - 0.5f; int x0 = Ceil2Int (dImageX - filter->xWidth); int x1 = Floor2Int(dImageX + filter->xWidth); int y0 = Ceil2Int (dImageY - filter->yWidth); int y1 = Floor2Int(dImageY + filter->yWidth); x0 = max(x0, xPixelStart); x1 = min(x1, xPixelStart + xPixelCount - 1); y0 = max(y0, yPixelStart); y1 = min(y1, yPixelStart + yPixelCount - 1); if ((x1-x0) < 0 || (y1-y0) < 0) return; // Loop over filter support and add sample to pixel arrays // Precompute $x$ and $y$ filter table offsets int *ifx = (int *)alloca((x1-x0+1) * sizeof(int)); for (int x = x0; x <= x1; ++x) { float fx = fabsf((x - dImageX) * filter->invXWidth * FILTER_TABLE_SIZE); ifx[x-x0] = min(Floor2Int(fx), FILTER_TABLE_SIZE-1); } int *ify = (int *)alloca((y1-y0+1) * sizeof(int)); for (int y = y0; y <= y1; ++y) { float fy = fabsf((y - dImageY) * filter->invYWidth * FILTER_TABLE_SIZE); ify[y-y0] = min(Floor2Int(fy), FILTER_TABLE_SIZE-1); } for (int y = y0; y <= y1; ++y) for (int x = x0; x <= x1; ++x) { // Evaluate filter value at $(x,y)$ pixel int offset = ify[y-y0]*FILTER_TABLE_SIZE + ifx[x-x0]; float filterWt = filterTable[offset]; //printf("BEFORE add weight : "); //L.printSelf(); // Update pixel values with filtered sample contribution Pixel &pixel = (*pixels)(x - xPixelStart, y - yPixelStart); pixel.L.AddWeighted(filterWt, L); //printf("AFTER add weight : "); //pixel.L.printSelf(); pixel.alpha += alpha * filterWt; pixel.weightSum += filterWt; } // Possibly write out in-progress image if (--sampleCount == 0) { WriteImage(); sampleCount = writeFrequency; } }
static Spectrum L(const Scene *scene, const Renderer *renderer, const Camera *camera, MemoryArena &arena, RNG &rng, int maxDepth, bool ignoreDirect, const MLTSample &sample) { // Generate camera ray from Metropolis sample RayDifferential ray; float cameraWeight = camera->GenerateRayDifferential(sample.cameraSample, &ray); Spectrum pathThroughput = cameraWeight, L = 0.; bool specularBounce = false, allSpecular = true; for (int pathLength = 0; pathLength < maxDepth; ++pathLength) { // Find next intersection in Metropolis light path Intersection isect; if (!scene->Intersect(ray, &isect)) { bool includeLe = ignoreDirect ? (specularBounce && !allSpecular) : (pathLength == 0 || specularBounce); if (includeLe) for (uint32_t i = 0; i < scene->lights.size(); ++i) L += pathThroughput * scene->lights[i]->Le(ray); break; } if (ignoreDirect ? (specularBounce && !allSpecular) : (specularBounce || pathLength == 0)) L += pathThroughput * isect.Le(-ray.d); BSDF *bsdf = isect.GetBSDF(ray, arena); const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; const PathSample &ps = sample.pathSamples[pathLength]; // Sample direct illumination for Metropolis path vertex if (!ignoreDirect || pathLength > 0) { LightSample lightSample(ps.lightDir0, ps.lightDir1, ps.lightNum0); BSDFSample bsdfSample(ps.bsdfLightDir0, ps.bsdfLightDir1, ps.bsdfLightComponent); uint32_t lightNum = Floor2Int(ps.lightNum1 * scene->lights.size()); lightNum = min(lightNum, (uint32_t)(scene->lights.size()-1)); const Light *light = scene->lights[lightNum]; L += pathThroughput * EstimateDirect(scene, renderer, arena, light, p, n, wo, isect.rayEpsilon, sample.cameraSample.time, bsdf, rng, lightSample, bsdfSample); } // Sample direction for outgoing Metropolis path direction BSDFSample outgoingBSDFSample(ps.bsdfDir0, ps.bsdfDir1, ps.bsdfComponent); Vector wi; float pdf; BxDFType flags; Spectrum f = bsdf->Sample_f(wo, &wi, outgoingBSDFSample, &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.) break; specularBounce = (flags & BSDF_SPECULAR) != 0; allSpecular &= specularBounce; pathThroughput *= f * AbsDot(wi, n) / pdf; ray = RayDifferential(p, wi, ray, isect.rayEpsilon); //pathThroughput *= renderer->Transmittance(scene, ray, NULL, rng, arena); } return L; }
Spectrum Evaluate(const DifferentialGeometry &dg) const { Vector dpdx, dpdy; Point P = mapping->Map(dg, &dpdx, &dpdy); P *= scale; float marble = P.y + variation * FBm(P, scale * dpdx, scale * dpdy, omega, octaves); float t = .5f + .5f * sinf(marble); // Evaluate marble spline at _t_ static float c[][3] = { { .58f, .58f, .6f }, { .58f, .58f, .6f }, { .58f, .58f, .6f }, { .5f, .5f, .5f }, { .6f, .59f, .58f }, { .58f, .58f, .6f }, { .58f, .58f, .6f }, {.2f, .2f, .33f }, { .58f, .58f, .6f }, }; #define NC sizeof(c) / sizeof(c[0]) #define NSEG (NC-3) int first = Floor2Int(t * NSEG); t = (t * NSEG - first); Spectrum c0(c[first]), c1(c[first+1]), c2(c[first+2]), c3(c[first+3]); // Bezier spline evaluated with de Castilejau's algorithm Spectrum s0 = (1.f - t) * c0 + t * c1; Spectrum s1 = (1.f - t) * c1 + t * c2; Spectrum s2 = (1.f - t) * c2 + t * c3; s0 = (1.f - t) * s0 + t * s1; s1 = (1.f - t) * s1 + t * s2; // Extra scale of 1.5 to increase variation among colors return 1.5f * ((1.f - t) * s0 + t * s1); }
Vector3D PotentialField::gradiance(const Point3D& m_pos) const { Vector3D ret; Vector3D diag = m_pos - bound.pMin; if (diag.x < 0 || diag.y < 0 || diag.z < 0) { return ret; } // ceil grid index double divIdx[3] = { diag.x / division, diag.y / division, diag.z / division }; int ceilIdx = Ceil2Int(divIdx[0]) + Ceil2Int(divIdx[1]) * grids[1] + Ceil2Int(divIdx[2]) * grids[0] * grids[1]; int floorIdx = Floor2Int(divIdx[0]) + Floor2Int(divIdx[1]) * grids[1] + Floor2Int(divIdx[2]) * grids[0] * grids[1]; ret = lerp(m_field[floorIdx], m_field[ceilIdx], diag.getLength() / divDiag); return ret; }
COREDLL Spectrum UniformSampleOneLight(const Scene *scene, const Point &p, const Normal &n, const Vector &wo, BSDF *bsdf, const Sample *sample, int lightSampleOffset, int lightNumOffset, int bsdfSampleOffset, int bsdfComponentOffset) { // Randomly choose a single light to sample, _light_ int nLights = int(scene->lights.size()); int lightNum; if (lightNumOffset != -1) lightNum = Floor2Int(sample->oneD[lightNumOffset][0] * nLights); else lightNum = Floor2Int(RandomFloat() * nLights); lightNum = min(lightNum, nLights-1); Light *light = scene->lights[lightNum]; return (float)nLights * EstimateDirect(scene, light, p, n, wo, bsdf, sample, lightSampleOffset, bsdfSampleOffset, bsdfComponentOffset, 0); }
static real64 PerlinNoise2DFunction(real64 x, real64 y) { // Compute noise cell coordinates and offsets int32_t ix = Floor2Int(x); int32_t iy = Floor2Int(y); real64 dx = x - ix, dy = y - iy; // Compute gradient weights ix &= (NOISE_PERM_SIZE-1); iy &= (NOISE_PERM_SIZE-1); real64 w00 = Grad2d(ix, iy, dx, dy); real64 w10 = Grad2d(ix+1, iy, dx-1.0f, dy); real64 w01 = Grad2d(ix, iy+1, dx, dy-1.0f); real64 w11 = Grad2d(ix+1, iy+1, dx-1.0f, dy-1.0f); // Compute bilinear interpolation of weights real64 wx = PerlinFade(dx); real64 wy = PerlinFade(dy); real64 x0 = Lerp(wx, w00, w10); real64 x1 = Lerp(wx, w01, w11); return Lerp(wy, x0, x1); }
Spectrum BSDF::Sample_f(const Vector &woW, Vector *wiW, float u1, float u2, float u3, float *pdf, BxDFType flags, BxDFType *sampledType) const { // Choose which _BxDF_ to sample int matchingComps = NumComponents(flags); if (matchingComps == 0) { *pdf = 0.f; return Spectrum(0.f); } int which = min(Floor2Int(u3 * matchingComps), matchingComps-1); BxDF *bxdf = NULL; int count = which; for (int i = 0; i < nBxDFs; ++i) if (bxdfs[i]->MatchesFlags(flags)) if (count-- == 0) { bxdf = bxdfs[i]; break; } Assert(bxdf); // NOBOOK // Sample chosen _BxDF_ Vector wi; Vector wo = WorldToLocal(woW); *pdf = 0.f; Spectrum f = bxdf->Sample_f(wo, &wi, u1, u2, pdf); if (*pdf == 0.f) return 0.f; if (sampledType) *sampledType = bxdf->type; *wiW = LocalToWorld(wi); // Compute overall PDF with all matching _BxDF_s if (!(bxdf->type & BSDF_SPECULAR) && matchingComps > 1) { for (int i = 0; i < nBxDFs; ++i) { if (bxdfs[i] != bxdf && bxdfs[i]->MatchesFlags(flags)) *pdf += bxdfs[i]->Pdf(wo, wi); } } if (matchingComps > 1) *pdf /= matchingComps; // Compute value of BSDF for sampled direction if (!(bxdf->type & BSDF_SPECULAR)) { f = 0.; if (Dot(*wiW, ng) * Dot(woW, ng) > 0) // ignore BTDFs flags = BxDFType(flags & ~BSDF_TRANSMISSION); else // ignore BRDFs flags = BxDFType(flags & ~BSDF_REFLECTION); for (int i = 0; i < nBxDFs; ++i) if (bxdfs[i]->MatchesFlags(flags)) f += bxdfs[i]->f(wo, wi); } return f; }
Spectrum BidirIntegrator::Li(const Scene *scene, const RayDifferential &ray, const Sample *sample, float *alpha) const { Spectrum L(0.); // Generate eye and light sub-paths BidirVertex eyePath[MAX_VERTS], lightPath[MAX_VERTS]; int nEye = generatePath(scene, ray, sample, eyeBSDFOffset, eyeBSDFCompOffset, eyePath, MAX_VERTS); if (nEye == 0) { *alpha = 0.; return L; } *alpha = 1; // Choose light for bidirectional path int lightNum = Floor2Int(sample->oneD[lightNumOffset][0] * scene->lights.size()); lightNum = min(lightNum, (int)scene->lights.size() - 1); Light *light = scene->lights[lightNum]; float lightWeight = float(scene->lights.size()); // Sample ray from light source to start light path Ray lightRay; float lightPdf; float u[4]; u[0] = sample->twoD[lightPosOffset][0]; u[1] = sample->twoD[lightPosOffset][1]; u[2] = sample->twoD[lightDirOffset][0]; u[3] = sample->twoD[lightDirOffset][1]; Spectrum Le = light->Sample_L(scene, u[0], u[1], u[2], u[3], &lightRay, &lightPdf); if (lightPdf == 0.) return 0.f; Le = lightWeight / lightPdf; int nLight = generatePath(scene, lightRay, sample, lightBSDFOffset, lightBSDFCompOffset, lightPath, MAX_VERTS); // Connect bidirectional path prefixes and evaluate throughput Spectrum directWt(1.0); for (int i = 1; i <= nEye; ++i) { // Handle direct lighting for bidirectional integrator directWt /= eyePath[i-1].rrWeight; L += directWt * UniformSampleOneLight(scene, eyePath[i-1].p, eyePath[i-1].ng, eyePath[i-1].wi, eyePath[i-1].bsdf, sample, directLightOffset[i-1], directLightNumOffset[i-1], directBSDFOffset[i-1], directBSDFCompOffset[i-1]) / weightPath(eyePath, i, lightPath, 0); directWt *= eyePath[i-1].bsdf->f(eyePath[i-1].wi, eyePath[i-1].wo) * AbsDot(eyePath[i-1].wo, eyePath[i-1].ng) / eyePath[i-1].bsdfWeight; for (int j = 1; j <= nLight; ++j) L += Le * evalPath(scene, eyePath, i, lightPath, j) / weightPath(eyePath, i, lightPath, j); } return L; }
float VolumeGrid::Density(const Point &Pobj) const { if (!extent.Inside(Pobj)) return 0; // Compute voxel coordinates and offsets for _Pobj_ float voxx = (Pobj.x - extent.pMin.x) / (extent.pMax.x - extent.pMin.x) * nx - .5f; float voxy = (Pobj.y - extent.pMin.y) / (extent.pMax.y - extent.pMin.y) * ny - .5f; float voxz = (Pobj.z - extent.pMin.z) / (extent.pMax.z - extent.pMin.z) * nz - .5f; int vx = Floor2Int(voxx); int vy = Floor2Int(voxy); int vz = Floor2Int(voxz); float dx = voxx - vx, dy = voxy - vy, dz = voxz - vz; // Trilinearly interpolate density values to compute local density float d00 = Lerp(dx, D(vx, vy, vz), D(vx+1, vy, vz)); float d10 = Lerp(dx, D(vx, vy+1, vz), D(vx+1, vy+1, vz)); float d01 = Lerp(dx, D(vx, vy, vz+1), D(vx+1, vy, vz+1)); float d11 = Lerp(dx, D(vx, vy+1, vz+1),D(vx+1, vy+1, vz+1)); float d0 = Lerp(dy, d00, d10); float d1 = Lerp(dy, d01, d11); return Lerp(dz, d0, d1); }
static real64 PerlinNoise1DFunction(real64 x) { // Compute noise cell coordinates and offsets int32_t ix = Floor2Int(x); real64 dx = x - ix; // Compute gradient weights ix &= (NOISE_PERM_SIZE-1); real64 w00 = Grad1d(ix, dx); real64 w10 = Grad1d(ix+1, dx-1.0f); // Compute bilinear interpolation of weights real64 wx = PerlinFade(dx); real64 x0 = Lerp(wx, w00, w10); return x0; }
static real64 PerlinNoise3DFunctionPeriodic(real64 x, real64 y, real64 z, int px, int py, int pz) { // Compute noise cell coordinates and offsets int32_t ix = Floor2Int(x); int32_t iy = Floor2Int(y); int32_t iz = Floor2Int(z); real64 dx = x - ix, dy = y - iy, dz = z - iz; int32_t ix0 = (ix%px) & (NOISE_PERM_SIZE-1); int32_t iy0 = (iy%py) & (NOISE_PERM_SIZE-1); int32_t iz0 = (iz%pz) & (NOISE_PERM_SIZE-1); int32_t ix1 = ((ix+1)%px) & (NOISE_PERM_SIZE-1); int32_t iy1 = ((iy+1)%py) & (NOISE_PERM_SIZE-1); int32_t iz1 = ((iz+1)%pz) & (NOISE_PERM_SIZE-1); real64 w000 = Grad3d(ix0, iy0, iz0, dx, dy, dz); real64 w100 = Grad3d(ix1, iy0, iz0, dx-1, dy, dz); real64 w010 = Grad3d(ix0, iy1, iz0, dx, dy-1, dz); real64 w110 = Grad3d(ix1, iy1, iz0, dx-1, dy-1, dz); real64 w001 = Grad3d(ix0, iy0, iz1, dx, dy, dz-1); real64 w101 = Grad3d(ix1, iy0, iz1, dx-1, dy, dz-1); real64 w011 = Grad3d(ix0, iy1, iz1, dx, dy-1, dz-1); real64 w111 = Grad3d(ix1, iy1, iz1, dx-1, dy-1, dz-1); // Compute trilinear interpolation of weights real64 wx = PerlinFade(dx); real64 wy = PerlinFade(dy); real64 wz = PerlinFade(dz); real64 x00 = Lerp(wx, w000, w100); real64 x10 = Lerp(wx, w010, w110); real64 x01 = Lerp(wx, w001, w101); real64 x11 = Lerp(wx, w011, w111); real64 y0 = Lerp(wy, x00, x10); real64 y1 = Lerp(wy, x01, x11); return Lerp(wz, y0, y1); }
Spectrum VolumePatIntegrator::UniformSampleLight(const Scene *scene, const Renderer *renderer, MemoryArena &arena, const Point &p, const Normal &n, const Vector &wo, float rayEpsilon, float time, RNG &rng) const { // Randomly choose a single light to sample, _light_ int nLights = int(scene->lights.size()); if (nLights == 0) return Spectrum(0.); int lightNum; lightNum = Floor2Int(rng.RandomFloat() * nLights); lightNum = min(lightNum, nLights-1); Light *light = scene->lights[lightNum]; // Initialize light sample for single light sampling LightSample lightSample(rng); return (float) nLights * EstimateDirectLight(scene, renderer, arena, light, p, n, wo, rayEpsilon, time, rng, lightSample); }
float FBm(const Point &P, const Vector &dpdx, const Vector &dpdy, float omega, int maxOctaves) { // Compute number of octaves for antialiased FBm float s2 = max(dpdx.LengthSquared(), dpdy.LengthSquared()); float foctaves = min((float)maxOctaves, 1.f - .5f * Log2(s2)); int octaves = Floor2Int(foctaves); // Compute sum of octaves of noise for FBm float sum = 0., lambda = 1., o = 1.; for (int i = 0; i < octaves; ++i) { sum += o * Noise(lambda * P); lambda *= 1.99f; o *= omega; } float partialOctave = foctaves - octaves; sum += o * SmoothStep(.3f, .7f, partialOctave) * Noise(lambda * P); return sum; }
float Turbulence(const Point &P, const Vector &dpdx, const Vector &dpdy, float omega, int maxOctaves) { // Compute number of octaves for antialiased FBm float s2 = max(dpdx.LengthSquared(), dpdy.LengthSquared()); float foctaves = min((float)maxOctaves, 1.f - .5f * Log2(s2)); int octaves = Floor2Int(foctaves); // Compute sum of octaves of noise for turbulence float sum = 0., lambda = 1., o = 1.; for (int i = 0; i < octaves; ++i) { sum += o * fabsf(Noise(lambda * P)); lambda *= 1.99f; o *= omega; } float partialOctave = foctaves - octaves; sum += o * SmoothStep(.3f, .7f, partialOctave) * fabsf(Noise(lambda * P)); // finally, add in value to account for average value of fabsf(Noise()) // (~0.2) for the remaining octaves... sum += (maxOctaves - foctaves) * 0.2f; return sum; }
Spectrum ExPhotonIntegrator::Li(const Scene *scene, const RayDifferential &ray, const Sample *sample, float *alpha) const { // Compute reflected radiance with photon map Spectrum L(0.); Intersection isect; if (scene->Intersect(ray, &isect)) { if (alpha) *alpha = 1.; Vector wo = -ray.d; // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray); const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; L += UniformSampleAllLights(scene, p, n, wo, bsdf, sample, lightSampleOffset, bsdfSampleOffset, bsdfComponentOffset); // Compute indirect lighting for photon map integrator L += LPhoton(causticMap, nCausticPaths, nLookup, bsdf, isect, wo, maxDistSquared); if (finalGather) { #if 1 // Do one-bounce final gather for photon map BxDFType nonSpecular = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_DIFFUSE | BSDF_GLOSSY); if (bsdf->NumComponents(nonSpecular) > 0) { // Find indirect photons around point for importance sampling u_int nIndirSamplePhotons = 50; PhotonProcess proc(nIndirSamplePhotons, p); proc.photons = (ClosePhoton *)alloca(nIndirSamplePhotons * sizeof(ClosePhoton)); float searchDist2 = maxDistSquared; while (proc.foundPhotons < nIndirSamplePhotons) { float md2 = searchDist2; proc.foundPhotons = 0; indirectMap->Lookup(p, proc, md2); searchDist2 *= 2.f; } // Copy photon directions to local array Vector *photonDirs = (Vector *)alloca(nIndirSamplePhotons * sizeof(Vector)); for (u_int i = 0; i < nIndirSamplePhotons; ++i) photonDirs[i] = proc.photons[i].photon->wi; // Use BSDF to do final gathering Spectrum Li = 0.; static StatsCounter gatherRays("Photon Map", // NOBOOK "Final gather rays traced"); // NOBOOK for (int i = 0; i < gatherSamples; ++i) { // Sample random direction from BSDF for final gather ray Vector wi; float u1 = sample->twoD[gatherSampleOffset[0]][2*i]; float u2 = sample->twoD[gatherSampleOffset[0]][2*i+1]; float u3 = sample->oneD[gatherComponentOffset[0]][i]; float pdf; Spectrum fr = bsdf->Sample_f(wo, &wi, u1, u2, u3, &pdf, BxDFType(BSDF_ALL & (~BSDF_SPECULAR))); if (fr.Black() || pdf == 0.f) continue; // Trace BSDF final gather ray and accumulate radiance RayDifferential bounceRay(p, wi); ++gatherRays; // NOBOOK Intersection gatherIsect; if (scene->Intersect(bounceRay, &gatherIsect)) { // Compute exitant radiance using precomputed irradiance Spectrum Lindir = 0.f; Normal n = gatherIsect.dg.nn; if (Dot(n, bounceRay.d) > 0) n = -n; RadiancePhotonProcess proc(gatherIsect.dg.p, n); float md2 = INFINITY; radianceMap->Lookup(gatherIsect.dg.p, proc, md2); if (proc.photon) Lindir = proc.photon->Lo; Lindir *= scene->Transmittance(bounceRay); // Compute MIS weight for BSDF-sampled gather ray // Compute PDF for photon-sampling of direction _wi_ float photonPdf = 0.f; float conePdf = UniformConePdf(cosGatherAngle); for (u_int j = 0; j < nIndirSamplePhotons; ++j) if (Dot(photonDirs[j], wi) > .999f * cosGatherAngle) photonPdf += conePdf; photonPdf /= nIndirSamplePhotons; float wt = PowerHeuristic(gatherSamples, pdf, gatherSamples, photonPdf); Li += fr * Lindir * AbsDot(wi, n) * wt / pdf; } } L += Li / gatherSamples; // Use nearby photons to do final gathering Li = 0.; for (int i = 0; i < gatherSamples; ++i) { // Sample random direction using photons for final gather ray float u1 = sample->oneD[gatherComponentOffset[1]][i]; float u2 = sample->twoD[gatherSampleOffset[1]][2*i]; float u3 = sample->twoD[gatherSampleOffset[1]][2*i+1]; int photonNum = min((int)nIndirSamplePhotons - 1, Floor2Int(u1 * nIndirSamplePhotons)); // Sample gather ray direction from _photonNum_ Vector vx, vy; CoordinateSystem(photonDirs[photonNum], &vx, &vy); Vector wi = UniformSampleCone(u2, u3, cosGatherAngle, vx, vy, photonDirs[photonNum]); // Trace photon-sampled final gather ray and accumulate radiance Spectrum fr = bsdf->f(wo, wi); if (fr.Black()) continue; // Compute PDF for photon-sampling of direction _wi_ float photonPdf = 0.f; float conePdf = UniformConePdf(cosGatherAngle); for (u_int j = 0; j < nIndirSamplePhotons; ++j) if (Dot(photonDirs[j], wi) > .999f * cosGatherAngle) photonPdf += conePdf; photonPdf /= nIndirSamplePhotons; RayDifferential bounceRay(p, wi); ++gatherRays; // NOBOOK Intersection gatherIsect; if (scene->Intersect(bounceRay, &gatherIsect)) { // Compute exitant radiance using precomputed irradiance Spectrum Lindir = 0.f; Normal n = gatherIsect.dg.nn; if (Dot(n, bounceRay.d) > 0) n = -n; RadiancePhotonProcess proc(gatherIsect.dg.p, n); float md2 = INFINITY; radianceMap->Lookup(gatherIsect.dg.p, proc, md2); if (proc.photon) Lindir = proc.photon->Lo; Lindir *= scene->Transmittance(bounceRay); // Compute MIS weight for photon-sampled gather ray float bsdfPdf = bsdf->Pdf(wo, wi); float wt = PowerHeuristic(gatherSamples, photonPdf, gatherSamples, bsdfPdf); Li += fr * Lindir * AbsDot(wi, n) * wt / photonPdf; } } L += Li / gatherSamples; } #else // look at radiance map directly.. Normal nn = n; if (Dot(nn, ray.d) > 0.) nn = -n; RadiancePhotonProcess proc(p, nn); float md2 = INFINITY; radianceMap->Lookup(p, proc, md2); if (proc.photon) L += proc.photon->Lo; #endif } else { L += LPhoton(indirectMap, nIndirectPaths, nLookup, bsdf, isect, wo, maxDistSquared); } if (specularDepth++ < maxSpecularDepth) { Vector wi; // Trace rays for specular reflection and refraction Spectrum f = bsdf->Sample_f(wo, &wi, BxDFType(BSDF_REFLECTION | BSDF_SPECULAR)); if (!f.Black()) { // Compute ray differential _rd_ for specular reflection RayDifferential rd(p, wi); rd.hasDifferentials = true; rd.rx.o = p + isect.dg.dpdx; rd.ry.o = p + isect.dg.dpdy; // Compute differential reflected directions Normal dndx = bsdf->dgShading.dndu * bsdf->dgShading.dudx + bsdf->dgShading.dndv * bsdf->dgShading.dvdx; Normal dndy = bsdf->dgShading.dndu * bsdf->dgShading.dudy + bsdf->dgShading.dndv * bsdf->dgShading.dvdy; Vector dwodx = -ray.rx.d - wo, dwody = -ray.ry.d - wo; float dDNdx = Dot(dwodx, n) + Dot(wo, dndx); float dDNdy = Dot(dwody, n) + Dot(wo, dndy); rd.rx.d = wi - dwodx + 2 * Vector(Dot(wo, n) * dndx + dDNdx * n); rd.ry.d = wi - dwody + 2 * Vector(Dot(wo, n) * dndy + dDNdy * n); L += scene->Li(rd, sample) * f * AbsDot(wi, n); } f = bsdf->Sample_f(wo, &wi, BxDFType(BSDF_TRANSMISSION | BSDF_SPECULAR)); if (!f.Black()) { // Compute ray differential _rd_ for specular transmission RayDifferential rd(p, wi); rd.hasDifferentials = true; rd.rx.o = p + isect.dg.dpdx; rd.ry.o = p + isect.dg.dpdy; float eta = bsdf->eta; Vector w = -wo; if (Dot(wo, n) < 0) eta = 1.f / eta; Normal dndx = bsdf->dgShading.dndu * bsdf->dgShading.dudx + bsdf->dgShading.dndv * bsdf->dgShading.dvdx; Normal dndy = bsdf->dgShading.dndu * bsdf->dgShading.dudy + bsdf->dgShading.dndv * bsdf->dgShading.dvdy; Vector dwodx = -ray.rx.d - wo, dwody = -ray.ry.d - wo; float dDNdx = Dot(dwodx, n) + Dot(wo, dndx); float dDNdy = Dot(dwody, n) + Dot(wo, dndy); float mu = eta * Dot(w, n) - Dot(wi, n); float dmudx = (eta - (eta*eta*Dot(w,n))/Dot(wi, n)) * dDNdx; float dmudy = (eta - (eta*eta*Dot(w,n))/Dot(wi, n)) * dDNdy; rd.rx.d = wi + eta * dwodx - Vector(mu * dndx + dmudx * n); rd.ry.d = wi + eta * dwody - Vector(mu * dndy + dmudy * n); L += scene->Li(rd, sample) * f * AbsDot(wi, n); } } --specularDepth; } else { // Handle ray with no intersection if (alpha) *alpha = 0.; for (u_int i = 0; i < scene->lights.size(); ++i) L += scene->lights[i]->Le(ray); if (alpha && !L.Black()) *alpha = 1.; return L; } return L; }
RGB BSDF::Sample_f(const Vector &woWorld, Vector *wiWorld, const BSDFSample &bsdfSample, float *pdf, BxDFType flags, BxDFType *sampledType) const { int numComp = NumComponents(flags); if (numComp == 0) { *pdf = 0; if (sampledType) *sampledType = BxDFType(0); return 0; } int whichComp = min(Floor2Int(bsdfSample.uComponent * numComp), numComp - 1); int count = whichComp; BxDF *bxdf = nullptr; //被选中的Bxdf for (int i = 0; i < mNumBxdf; ++i) { if (mBxdfs[i]->MatchesFlag(flags) && count-- == 0) { //这技巧 GOOD bxdf = mBxdfs[i]; break; } } Vector wo = WorldToLocal(woWorld); //获得局部坐标下的wo Vector wi; *pdf = 0.f; RGB f = bxdf->Sample_f(wo, &wi, bsdfSample.uDir[0], bsdfSample.uDir[1], pdf); //这里的主要作用是采样出射方向 if (*pdf == 0) { if (sampledType) *sampledType = BxDFType(0); return 0; } if (sampledType) *sampledType = bxdf->type; *wiWorld = LocalToWorld(wi); //获得世界坐标系下的wi //计算pdf if (!(bxdf->type & BSDF_SPECULAR) && numComp > 1) { for (int i = 0; i < mNumBxdf; ++i) { if (bxdf != mBxdfs[i] && mBxdfs[i]->MatchesFlag(flags)) { *pdf += mBxdfs[i]->Pdf(wo, wi); } } } if (numComp > 1) { *pdf /= numComp; } //开始计算BRDF系数 if (!(bxdf->type & BSDF_SPECULAR)) { f=0;//因为不是镜面反射 所以重新计算BRDF if (Dot(*wiWorld, mNG) * Dot(woWorld, mNG) > 0) //反射 flags = BxDFType(flags & ~BSDF_TRANSMISSION); else//折射 flags = BxDFType(flags & ~BSDF_REFLECTION); for (int i = 0; i < mNumBxdf; ++i) if (mBxdfs[i]->MatchesFlag(flags)) f += mBxdfs[i]->f(wo, wi); } return f; }
Spectrum SingleScatteringFluorescenceRWLIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Sample *sample, RNG &rng, Spectrum *T, MemoryArena &arena) const { VolumeRegion *vr = scene->volumeRegion; float t0, t1; if (!vr || !vr->IntersectP(ray, &t0, &t1) || (t1-t0) == 0.f) { *T = 1.f; return 0.f; } // Do single scattering volume integration in _vr_ Spectrum Lv(0.); // Prepare for volume integration stepping int nSamples = Ceil2Int((t1-t0) / stepSize); float step = (t1 - t0) / nSamples; Spectrum Tr(1.f); Point p = ray(t0), pPrev; Vector w = -ray.d; t0 += sample->oneD[scatterSampleOffset][0] * step; // Compute sample patterns for single scattering samples float *lightNum = arena.Alloc<float>(nSamples); LDShuffleScrambled1D(1, nSamples, lightNum, rng); float *lightComp = arena.Alloc<float>(nSamples); LDShuffleScrambled1D(1, nSamples, lightComp, rng); float *lightPos = arena.Alloc<float>(2*nSamples); LDShuffleScrambled2D(1, nSamples, lightPos, rng); uint32_t sampOffset = 0; for (int i = 0; i < nSamples; ++i, t0 += step) { // Advance to sample at _t0_ and update _T_ pPrev = p; p = ray(t0); Ray tauRay(pPrev, p - pPrev, 0.f, 1.f, ray.time, ray.depth); Spectrum stepTau = vr->tau(tauRay, 0.5f * stepSize, rng.RandomFloat()); Tr *= Exp(-stepTau); // Possibly terminate ray marching if transmittance is small if (Tr.y() < 1e-3) { const float continueProb = .5f; if (rng.RandomFloat() > continueProb) { Tr = 0.f; break; } Tr /= continueProb; } // Compute fluorescence emission Spectrum sigma = vr->Mu(p, w, ray.time); if (!sigma.IsBlack() && scene->lights.size() > 0) { int nLights = scene->lights.size(); int ln = min(Floor2Int(lightNum[sampOffset] * nLights), nLights-1); Light *light = scene->lights[ln]; // Add contribution of _light_ due to the in-scattering at _p_ float pdf; VisibilityTester vis; Vector wo; LightSample ls(lightComp[sampOffset], lightPos[2*sampOffset], lightPos[2*sampOffset+1]); Spectrum L = light->Sample_L(p, 0.f, ls, ray.time, &wo, &pdf, &vis); if (!L.IsBlack() && pdf > 0.f && vis.Unoccluded(scene)) { Spectrum Ld = L * vis.Transmittance(scene, renderer, NULL, rng, arena); int lambdaExcIndex = light->GetLaserWavelengthIndex(); float Lpower = Ld.GetLaserEmissionPower(lambdaExcIndex); float yield = vr->Yeild(Point()); Spectrum fEx = vr->fEx(Point()); Spectrum fEm = vr->fEm(Point()); float scale = fEx.GetSampleValueAtWavelengthIndex(lambdaExcIndex); Lv += Lpower * Tr * sigma * vr->p(p, w, -wo, ray.time) * scale * fEm * yield * float(nLights) / pdf; } } ++sampOffset; } *T = Tr; return Lv * step; }
void ExPhotonIntegrator::Preprocess(const Scene *scene) { if (scene->lights.size() == 0) return; ProgressReporter progress(nCausticPhotons+ // NOBOOK nIndirectPhotons, "Shooting photons"); // NOBOOK vector<Photon> causticPhotons; vector<Photon> indirectPhotons; vector<Photon> directPhotons; vector<RadiancePhoton> radiancePhotons; causticPhotons.reserve(nCausticPhotons); // NOBOOK indirectPhotons.reserve(nIndirectPhotons); // NOBOOK // Initialize photon shooting statistics static StatsCounter nshot("Photon Map", "Number of photons shot from lights"); bool causticDone = (nCausticPhotons == 0); bool indirectDone = (nIndirectPhotons == 0); // Compute light power CDF for photon shooting int nLights = int(scene->lights.size()); float *lightPower = (float *)alloca(nLights * sizeof(float)); float *lightCDF = (float *)alloca((nLights+1) * sizeof(float)); for (int i = 0; i < nLights; ++i) lightPower[i] = scene->lights[i]->Power(scene).y(); float totalPower; ComputeStep1dCDF(lightPower, nLights, &totalPower, lightCDF); // Declare radiance photon reflectance arrays vector<Spectrum> rpReflectances, rpTransmittances; while (!causticDone || !indirectDone) { ++nshot; // Give up if we're not storing enough photons if (nshot > 500000 && (unsuccessful(nCausticPhotons, causticPhotons.size(), nshot) || unsuccessful(nIndirectPhotons, indirectPhotons.size(), nshot))) { Error("Unable to store enough photons. Giving up.\n"); return; } // Trace a photon path and store contribution // Choose 4D sample values for photon float u[4]; u[0] = RadicalInverse((int)nshot+1, 2); u[1] = RadicalInverse((int)nshot+1, 3); u[2] = RadicalInverse((int)nshot+1, 5); u[3] = RadicalInverse((int)nshot+1, 7); // Choose light to shoot photon from float lightPdf; float uln = RadicalInverse((int)nshot+1, 11); int lightNum = Floor2Int(SampleStep1d(lightPower, lightCDF, totalPower, nLights, uln, &lightPdf) * nLights); lightNum = min(lightNum, nLights-1); const Light *light = scene->lights[lightNum]; // Generate _photonRay_ from light source and initialize _alpha_ RayDifferential photonRay; float pdf; Spectrum alpha = light->Sample_L(scene, u[0], u[1], u[2], u[3], &photonRay, &pdf); if (pdf == 0.f || alpha.Black()) continue; alpha /= pdf * lightPdf; if (!alpha.Black()) { // Follow photon path through scene and record intersections bool specularPath = false; Intersection photonIsect; int nIntersections = 0; while (scene->Intersect(photonRay, &photonIsect)) { ++nIntersections; // Handle photon/surface intersection alpha *= scene->Transmittance(photonRay); Vector wo = -photonRay.d; BSDF *photonBSDF = photonIsect.GetBSDF(photonRay); BxDFType specularType = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_SPECULAR); bool hasNonSpecular = (photonBSDF->NumComponents() > photonBSDF->NumComponents(specularType)); if (hasNonSpecular) { // Deposit photon at surface Photon photon(photonIsect.dg.p, alpha, wo); if (nIntersections == 1) { // Deposit direct photon directPhotons.push_back(photon); } else { // Deposit either caustic or indirect photon if (specularPath) { // Process caustic photon intersection if (!causticDone) { causticPhotons.push_back(photon); if (causticPhotons.size() == nCausticPhotons) { causticDone = true; nCausticPaths = (int)nshot; causticMap = new KdTree<Photon, PhotonProcess>(causticPhotons); } progress.Update(); } } else { // Process indirect lighting photon intersection if (!indirectDone) { indirectPhotons.push_back(photon); if (indirectPhotons.size() == nIndirectPhotons) { indirectDone = true; nIndirectPaths = (int)nshot; indirectMap = new KdTree<Photon, PhotonProcess>(indirectPhotons); } progress.Update(); } } } if (finalGather && RandomFloat() < .125f) { // Store data for radiance photon static StatsCounter rp("Photon Map", "Radiance photons created"); // NOBOOK ++rp; // NOBOOK Normal n = photonIsect.dg.nn; if (Dot(n, photonRay.d) > 0.f) n = -n; radiancePhotons.push_back(RadiancePhoton(photonIsect.dg.p, n)); Spectrum rho_r = photonBSDF->rho(BSDF_ALL_REFLECTION); rpReflectances.push_back(rho_r); Spectrum rho_t = photonBSDF->rho(BSDF_ALL_TRANSMISSION); rpTransmittances.push_back(rho_t); } } // Sample new photon ray direction Vector wi; float pdf; BxDFType flags; // Get random numbers for sampling outgoing photon direction float u1, u2, u3; if (nIntersections == 1) { u1 = RadicalInverse((int)nshot+1, 13); u2 = RadicalInverse((int)nshot+1, 17); u3 = RadicalInverse((int)nshot+1, 19); } else { u1 = RandomFloat(); u2 = RandomFloat(); u3 = RandomFloat(); } // Compute new photon weight and possibly terminate with RR Spectrum fr = photonBSDF->Sample_f(wo, &wi, u1, u2, u3, &pdf, BSDF_ALL, &flags); if (fr.Black() || pdf == 0.f) break; Spectrum anew = alpha * fr * AbsDot(wi, photonBSDF->dgShading.nn) / pdf; float continueProb = min(1.f, anew.y() / alpha.y()); if (RandomFloat() > continueProb || nIntersections > 10) break; alpha = anew / continueProb; specularPath = (nIntersections == 1 || specularPath) && ((flags & BSDF_SPECULAR) != 0); photonRay = RayDifferential(photonIsect.dg.p, wi); } } BSDF::FreeAll(); } progress.Done(); // NOBOOK // Precompute radiance at a subset of the photons KdTree<Photon, PhotonProcess> directMap(directPhotons); int nDirectPaths = nshot; if (finalGather) { ProgressReporter p2(radiancePhotons.size(), "Computing photon radiances"); // NOBOOK for (u_int i = 0; i < radiancePhotons.size(); ++i) { // Compute radiance for radiance photon _i_ RadiancePhoton &rp = radiancePhotons[i]; const Spectrum &rho_r = rpReflectances[i]; const Spectrum &rho_t = rpTransmittances[i]; Spectrum E; Point p = rp.p; Normal n = rp.n; if (!rho_r.Black()) { E = estimateE(&directMap, nDirectPaths, p, n) + estimateE(indirectMap, nIndirectPaths, p, n) + estimateE(causticMap, nCausticPaths, p, n); rp.Lo += E * INV_PI * rho_r; } if (!rho_t.Black()) { E = estimateE(&directMap, nDirectPaths, p, -n) + estimateE(indirectMap, nIndirectPaths, p, -n) + estimateE(causticMap, nCausticPaths, p, -n); rp.Lo += E * INV_PI * rho_t; } p2.Update(); // NOBOOK } radianceMap = new KdTree<RadiancePhoton, RadiancePhotonProcess>(radiancePhotons); p2.Done(); // NOBOOK } }
Spectrum UseRadianceProbes::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Intersection &isect, const Sample *sample, RNG &rng, MemoryArena &arena, int wavelength) const { Spectrum L(0.); Vector wo = -ray.d; // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray, arena, wavelength); const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; // Compute reflection for radiance probes integrator if (!includeDirectInProbes) L += UniformSampleAllLights(scene, renderer, arena, p, n, wo, isect.rayEpsilon, ray.time, bsdf, sample, rng, lightSampleOffsets, bsdfSampleOffsets); // Compute reflected lighting using radiance probes // Compute probe coordinates and offsets for lookup point Vector offset = bbox.Offset(p); float voxx = (offset.x * nProbes[0]) - 0.5f; float voxy = (offset.y * nProbes[1]) - 0.5f; float voxz = (offset.z * nProbes[2]) - 0.5f; int vx = Floor2Int(voxx), vy = Floor2Int(voxy), vz = Floor2Int(voxz); float dx = voxx - vx, dy = voxy - vy, dz = voxz - vz; // Get radiance probe coefficients around lookup point const Spectrum *b000 = c_inXYZ(lmax, vx, vy, vz); const Spectrum *b100 = c_inXYZ(lmax, vx+1, vy, vz); const Spectrum *b010 = c_inXYZ(lmax, vx, vy+1, vz); const Spectrum *b110 = c_inXYZ(lmax, vx+1, vy+1, vz); const Spectrum *b001 = c_inXYZ(lmax, vx, vy, vz+1); const Spectrum *b101 = c_inXYZ(lmax, vx+1, vy, vz+1); const Spectrum *b011 = c_inXYZ(lmax, vx, vy+1, vz+1); const Spectrum *b111 = c_inXYZ(lmax, vx+1, vy+1, vz+1); // Compute incident radiance from radiance probe coefficients Spectrum *c_inp = arena.Alloc<Spectrum>(SHTerms(lmax)); for (int i = 0; i < SHTerms(lmax); ++i) { // Do trilinear interpolation to compute SH coefficients at point Spectrum c00 = Lerp(dx, b000[i], b100[i]); Spectrum c10 = Lerp(dx, b010[i], b110[i]); Spectrum c01 = Lerp(dx, b001[i], b101[i]); Spectrum c11 = Lerp(dx, b011[i], b111[i]); Spectrum c0 = Lerp(dy, c00, c10); Spectrum c1 = Lerp(dy, c01, c11); c_inp[i] = Lerp(dz, c0, c1); } // Convolve incident radiance to compute irradiance function Spectrum *c_E = arena.Alloc<Spectrum>(SHTerms(lmax)); SHConvolveCosTheta(lmax, c_inp, c_E); // Evaluate irradiance function and accumulate reflection Spectrum rho = bsdf->rho(wo, rng, BSDF_ALL_REFLECTION); float *Ylm = ALLOCA(float, SHTerms(lmax)); SHEvaluate(Vector(Faceforward(n, wo)), lmax, Ylm); Spectrum E = 0.f; for (int i = 0; i < SHTerms(lmax); ++i) E += c_E[i] * Ylm[i]; L += rho * INV_PI * E.Clamp(); return L; }
Spectrum PhotonVolumeIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Sample *sample, RNG &rng, Spectrum *T, MemoryArena &arena) const { VolumeRegion *vr = scene->volumeRegion; RainbowVolume* rv = dynamic_cast<RainbowVolume*>(vr); KdTree<Photon>* volumeMap = photonShooter->volumeMap; float t0, t1; if (!vr || !vr->IntersectP(ray, &t0, &t1) || (t1-t0) == 0.f){ *T = 1.f; return 0.f; } // Do single scattering & photon multiple scattering volume integration in _vr_ Spectrum Lv(0.); // Prepare for volume integration stepping int nSamples = Ceil2Int((t1-t0) / stepSize); float step = (t1 - t0) / nSamples; Spectrum Tr(1.f); Point p = ray(t0), pPrev; Vector w = -ray.d; t0 += sample->oneD[scatterSampleOffset][0] * step; float *lightNum = arena.Alloc<float>(nSamples); LDShuffleScrambled1D(1, nSamples, lightNum, rng); float *lightComp = arena.Alloc<float>(nSamples); LDShuffleScrambled1D(1, nSamples, lightComp, rng); float *lightPos = arena.Alloc<float>(2*nSamples); LDShuffleScrambled2D(1, nSamples, lightPos, rng); int sampOffset = 0; ClosePhoton *lookupBuf = new ClosePhoton[nSamples]; for (int i = 0; i < nSamples; ++i, t0 += step) { // Advance to sample at _t0_ and update _T_ pPrev = p; p = ray(t0); Ray tauRay(pPrev, p - pPrev, 0.f, 1.f, ray.time, ray.depth); Spectrum stepTau = vr->tau(tauRay,.5f * stepSize, rng.RandomFloat()); Tr = Exp(-stepTau); // Possibly terminate raymarching if transmittance is small. if (Tr.y() < 1e-3) { const float continueProb = .5f; if (rng.RandomFloat() > continueProb){ Tr = 0.f; break; } Tr /= continueProb; } // Compute single-scattering source term at _p_ & photon mapped MS Spectrum L_i(0.); Spectrum L_d(0.); Spectrum L_ii(0.); // Lv += Tr*vr->Lve(p, w, ray.time); Spectrum ss = vr->sigma_s(p, w, ray.time); Spectrum sa = vr->sigma_a(p, w, ray.time); if (!ss.IsBlack() && scene->lights.size() > 0) { int nLights = scene->lights.size(); int ln = min(Floor2Int(lightNum[sampOffset] * nLights), nLights-1); Light *light = scene->lights[ln]; // Add contribution of _light_ due to scattering at _p_ float pdf; VisibilityTester vis; Vector wo; LightSample ls(lightComp[sampOffset], lightPos[2*sampOffset], lightPos[2*sampOffset+1]); Spectrum L = light->Sample_L(p, 0.f, ls, ray.time, &wo, &pdf, &vis); if (!L.IsBlack() && pdf > 0.f && vis.Unoccluded(scene)) { Spectrum Ld = L * vis.Transmittance(scene,renderer, NULL, rng, arena); if(rv){ L_d = rv->rainbowReflection(Ld, ray.d, wo); } else { L_d = vr->p(p, w, -wo, ray.time) * Ld * float(nLights)/pdf; } } } // Compute 'indirect' in-scattered radiance from photon map if(!rv){ L_ii += LPhoton(volumeMap, nUsed, lookupBuf, w, p, vr, maxDistSquared, ray.time); } // Compute total in-scattered radiance if (sa.y()!=0.0 || ss.y()!=0.0) L_i = L_d + (ss/(sa+ss))*L_ii; else L_i = L_d; Spectrum nLv = (sa*vr->Lve(p,w,ray.time)*step) + (ss*L_i*step) + (Tr * Lv) ; Lv = nLv; sampOffset++; } *T = Tr; return Lv; }
Spectrum SingleScatteringIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Sample *sample, Spectrum *T, MemoryArena &arena) const { VolumeRegion *vr = scene->volumeRegion; float t0, t1; if (!vr || !vr->IntersectP(ray, &t0, &t1)) { *T = 1.f; return 0.f; } // Do single scattering volume integration in _vr_ Spectrum Lv(0.); // Prepare for volume integration stepping int nSamples = Ceil2Int((t1-t0) / stepSize); float step = (t1 - t0) / nSamples; Spectrum Tr(1.f); Point p = ray(t0), pPrev; Vector w = -ray.d; t0 += sample->oneD[scatterSampleOffset][0] * step; // Compute sample patterns for single scattering samples float *lightNum = arena.Alloc<float>(nSamples); LDShuffleScrambled1D(1, nSamples, lightNum, *sample->rng); float *lightComp = arena.Alloc<float>(nSamples); LDShuffleScrambled1D(1, nSamples, lightComp, *sample->rng); float *lightPos = arena.Alloc<float>(2*nSamples); LDShuffleScrambled2D(1, nSamples, lightPos, *sample->rng); u_int sampOffset = 0; for (int i = 0; i < nSamples; ++i, t0 += step) { // Advance to sample at _t0_ and update _T_ pPrev = p; p = ray(t0); Ray tauRay(pPrev, p - pPrev, 0.f, 1.f, ray.time, ray.depth); Spectrum stepTau = vr->tau(tauRay, .5f * stepSize, sample->rng->RandomFloat()); Tr *= Exp(-stepTau); // Possibly terminate ray marching if transmittance is small if (Tr.y() < 1e-3) { const float continueProb = .5f; if (sample->rng->RandomFloat() > continueProb) break; Tr /= continueProb; } // Compute single-scattering source term at _p_ Lv += Tr * vr->Lve(p, w, ray.time); Spectrum ss = vr->sigma_s(p, w, ray.time); if (!ss.IsBlack() && scene->lights.size() > 0) { int nLights = scene->lights.size(); int ln = min(Floor2Int(lightNum[sampOffset] * nLights), nLights-1); Light *light = scene->lights[ln]; // Add contribution of _light_ due to scattering at _p_ float pdf; VisibilityTester vis; Vector wo; LightSample ls(lightComp[sampOffset], lightPos[2*sampOffset], lightPos[2*sampOffset+1]); Spectrum L = light->Sample_L(p, 0.f, ls, ray.time, &wo, &pdf, &vis); if (!L.IsBlack() && pdf > 0.f && vis.Unoccluded(scene)) { Spectrum Ld = L * vis.Transmittance(scene, renderer, NULL, sample->rng, arena); Lv += Tr * ss * vr->p(p, w, -wo, ray.time) * Ld * float(nLights) / pdf; } } ++sampOffset; } *T = Tr; return Lv * step; }
//----------------------------------------------------------------------------- // Draws shield decals //----------------------------------------------------------------------------- void C_Shield::DrawShieldDecals( Vector* pt, bool hitDecals ) { if (m_Decals.Size() == 0) return; // Compute ripples: for ( int r = m_Decals.Size(); --r >= 0; ) { // At the moment, nothing passes! bool passDecal = false; if ((!hitDecals) && (passDecal == hitDecals)) continue; SetCurrentDecal( r ); // We have to force a flush here because we're changing the proxy state if (!hitDecals) materials->Bind( m_pPassDecal, (IClientRenderable*)this ); else materials->Bind( passDecal ? m_pPassDecal2 : m_pHitDecal, (IClientRenderable*)this ); float dtime = gpGlobals->curtime - m_Decals[r].m_StartTime; float decay = exp( -( 2 * dtime) ); // Retire the animation if it wraps // This gets set by TextureAnimatedWrapped above if ((m_Decals[r].m_StartTime < 0.0f) || (decay < 1e-3)) { m_Decals.Remove(r); continue; } IMesh* pMesh = materials->GetDynamicMesh(); // Figure out the quads we must mod2x.... float u0 = m_Decals[r].m_RippleU - m_Decals[r].m_Radius; float u1 = m_Decals[r].m_RippleU + m_Decals[r].m_Radius; float v0 = m_Decals[r].m_RippleV - m_Decals[r].m_Radius; float v1 = m_Decals[r].m_RippleV + m_Decals[r].m_Radius; float du = u1 - u0; float dv = v1 - v0; int i0 = Floor2Int( v0 * (m_SubdivisionCount - 1) ); int i1 = Ceil2Int( v1 * (m_SubdivisionCount - 1) ); int j0 = Floor2Int( u0 * (m_SubdivisionCount - 1) ); int j1 = Ceil2Int( u1 * (m_SubdivisionCount - 1) ); if (i0 < 0) i0 = 0; if (i1 >= m_SubdivisionCount) i1 = m_SubdivisionCount - 1; if (j0 < 0) j0 = 0; if (j1 >= m_SubdivisionCount) j1 = m_SubdivisionCount - 1; int numTriangles = (i1 - i0) * (j1 - j0) * 2; CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, numTriangles ); float decalDu = m_InvSubdivisionCount / du; float decalDv = m_InvSubdivisionCount / dv; unsigned char color[3]; color[0] = s_ImpactDecalColor[0] * decay; color[1] = s_ImpactDecalColor[1] * decay; color[2] = s_ImpactDecalColor[2] * decay; for ( int i = i0; i < i1; ++i) { float t = (float)i * m_InvSubdivisionCount; for (int j = j0; j < j1; ++j) { float s = (float)j * m_InvSubdivisionCount; int idx = i * m_SubdivisionCount + j; // Compute (u,v) into the decal float decalU = (s - u0) / du; float decalV = (t - v0) / dv; meshBuilder.Position3fv( pt[idx].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU, decalV ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + m_SubdivisionCount].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU, decalV + decalDv ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + 1].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU + decalDu, decalV ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + 1].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU + decalDu, decalV ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + m_SubdivisionCount].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU, decalV + decalDv ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + m_SubdivisionCount + 1].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU + decalDu, decalV + decalDv ); meshBuilder.AdvanceVertex(); } } meshBuilder.End(); pMesh->Draw(); } }
Spectrum PhotonIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Intersection &isect, const Sample *sample, RNG &rng, MemoryArena &arena) const { Spectrum L(0.); Vector wo = -ray.d; // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Evaluate BSDF at hit pbrt::Point BSDF *bsdf = isect.GetBSDF(ray, arena); const pbrt::Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; L += UniformSampleAllLights(scene, renderer, arena, p, n, wo, isect.rayEpsilon, ray.time, bsdf, sample, rng, lightSampleOffsets, bsdfSampleOffsets); // Compute caustic lighting for photon map integrator ClosePhoton *lookupBuf = arena.Alloc<ClosePhoton>(nLookup); L += LPhoton(causticMap, nCausticPaths, nLookup, lookupBuf, bsdf, rng, isect, wo, maxDistSquared); // Compute indirect lighting for photon map integrator if (finalGather && indirectMap != NULL) { #if 1 // Do one-bounce final gather for photon map BxDFType nonSpecular = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_DIFFUSE | BSDF_GLOSSY); if (bsdf->NumComponents(nonSpecular) > 0) { // Find indirect photons around point for importance sampling const uint32_t nIndirSamplePhotons = 50; PhotonProcess proc(nIndirSamplePhotons, arena.Alloc<ClosePhoton>(nIndirSamplePhotons)); float searchDist2 = maxDistSquared; while (proc.nFound < nIndirSamplePhotons) { float md2 = searchDist2; proc.nFound = 0; indirectMap->Lookup(p, proc, md2); searchDist2 *= 2.f; } // Copy photon directions to local array Vector *photonDirs = arena.Alloc<Vector>(nIndirSamplePhotons); for (uint32_t i = 0; i < nIndirSamplePhotons; ++i) photonDirs[i] = proc.photons[i].photon->wi; // Use BSDF to do final gathering Spectrum Li = 0.; for (int i = 0; i < gatherSamples; ++i) { // Sample random direction from BSDF for final gather ray Vector wi; float pdf; BSDFSample bsdfSample(sample, bsdfGatherSampleOffsets, i); Spectrum fr = bsdf->Sample_f(wo, &wi, bsdfSample, &pdf, BxDFType(BSDF_ALL & ~BSDF_SPECULAR)); if (fr.IsBlack() || pdf == 0.f) continue; Assert(pdf >= 0.f); // Trace BSDF final gather ray and accumulate radiance RayDifferential bounceRay(p, wi, ray, isect.rayEpsilon); Intersection gatherIsect; if (scene->Intersect(bounceRay, &gatherIsect)) { // Compute exitant radiance _Lindir_ using radiance photons Spectrum Lindir = 0.f; Normal nGather = gatherIsect.dg.nn; nGather = Faceforward(nGather, -bounceRay.d); RadiancePhotonProcess proc(nGather); float md2 = INFINITY; radianceMap->Lookup(gatherIsect.dg.p, proc, md2); if (proc.photon != NULL) Lindir = proc.photon->Lo; Lindir *= renderer->Transmittance(scene, bounceRay, NULL, rng, arena); // Compute MIS weight for BSDF-sampled gather ray // Compute PDF for photon-sampling of direction _wi_ float photonPdf = 0.f; float conePdf = UniformConePdf(cosGatherAngle); for (uint32_t j = 0; j < nIndirSamplePhotons; ++j) if (Dot(photonDirs[j], wi) > .999f * cosGatherAngle) photonPdf += conePdf; photonPdf /= nIndirSamplePhotons; float wt = PowerHeuristic(gatherSamples, pdf, gatherSamples, photonPdf); Li += fr * Lindir * (AbsDot(wi, n) * wt / pdf); } } L += Li / gatherSamples; // Use nearby photons to do final gathering Li = 0.; for (int i = 0; i < gatherSamples; ++i) { // Sample random direction using photons for final gather ray BSDFSample gatherSample(sample, indirGatherSampleOffsets, i); int photonNum = min((int)nIndirSamplePhotons - 1, Floor2Int(gatherSample.uComponent * nIndirSamplePhotons)); // Sample gather ray direction from _photonNum_ Vector vx, vy; CoordinateSystem(photonDirs[photonNum], &vx, &vy); Vector wi = UniformSampleCone(gatherSample.uDir[0], gatherSample.uDir[1], cosGatherAngle, vx, vy, photonDirs[photonNum]); // Trace photon-sampled final gather ray and accumulate radiance Spectrum fr = bsdf->f(wo, wi); if (fr.IsBlack()) continue; RayDifferential bounceRay(p, wi, ray, isect.rayEpsilon); Intersection gatherIsect; PBRT_PHOTON_MAP_STARTED_GATHER_RAY(&bounceRay); if (scene->Intersect(bounceRay, &gatherIsect)) { // Compute exitant radiance _Lindir_ using radiance photons Spectrum Lindir = 0.f; Normal nGather = gatherIsect.dg.nn; nGather = Faceforward(nGather, -bounceRay.d); RadiancePhotonProcess proc(nGather); float md2 = INFINITY; radianceMap->Lookup(gatherIsect.dg.p, proc, md2); if (proc.photon != NULL) Lindir = proc.photon->Lo; Lindir *= renderer->Transmittance(scene, bounceRay, NULL, rng, arena); // Compute PDF for photon-sampling of direction _wi_ float photonPdf = 0.f; float conePdf = UniformConePdf(cosGatherAngle); for (uint32_t j = 0; j < nIndirSamplePhotons; ++j) if (Dot(photonDirs[j], wi) > .999f * cosGatherAngle) photonPdf += conePdf; photonPdf /= nIndirSamplePhotons; // Compute MIS weight for photon-sampled gather ray float bsdfPdf = bsdf->Pdf(wo, wi); float wt = PowerHeuristic(gatherSamples, photonPdf, gatherSamples, bsdfPdf); Li += fr * Lindir * AbsDot(wi, n) * wt / photonPdf; } PBRT_PHOTON_MAP_FINISHED_GATHER_RAY(&bounceRay); } L += Li / gatherSamples; } #else // for debugging / examples: use the photon map directly Normal nn = Faceforward(n, -ray.d); RadiancePhotonProcess proc(nn); float md2 = INFINITY; radianceMap->Lookup(p, proc, md2); if (proc.photon) L += proc.photon->Lo; #endif } else L += LPhoton(indirectMap, nIndirectPaths, nLookup, lookupBuf, bsdf, rng, isect, wo, maxDistSquared); if (ray.depth+1 < maxSpecularDepth) { Vector wi; // Trace rays for specular reflection and refraction L += SpecularReflect(ray, bsdf, rng, isect, renderer, scene, sample, arena); L += SpecularTransmit(ray, bsdf, rng, isect, renderer, scene, sample, arena); } return L; }