2020
2121#define BVH_DEBUG_VISUALIZATION false
2222
23+ int ToneMapping::method = ToneMapping::ACES;
24+
2325// Kernel that writes the image to the OpenGL PBO directly.
2426__global__ void sendImageToPBO (uchar4 * pbo, glm::ivec2 resolution,
25- int iter, glm::vec3* Image) {
27+ int iter, glm::vec3* Image, int toneMapping ) {
2628 int x = (blockIdx .x * blockDim .x ) + threadIdx .x ;
2729 int y = (blockIdx .y * blockDim .y ) + threadIdx .y ;
2830
2931 if (x < resolution.x && y < resolution.y ) {
3032 int index = x + (y * resolution.x );
3133
32- // ACES tonemapping and gamma correction
34+ // Tonemapping and gamma correction
3335 glm::vec3 color = Image[index] / float (iter);
34- glm::vec3 mapped = Math::ACES (color);
35- mapped = color;
36- mapped = Math::correctGamma (mapped);
37- glm::ivec3 iColor = glm::clamp (glm::ivec3 (mapped * 255 .f ), glm::ivec3 (0 ), glm::ivec3 (255 ));
36+
37+ switch (toneMapping) {
38+ case ToneMapping::Filmic:
39+ color = Math::filmic (color);
40+ break ;
41+ case ToneMapping::ACES:
42+ color = Math::ACES (color);
43+ break ;
44+ case ToneMapping::None:
45+ break ;
46+ }
47+ color = Math::correctGamma (color);
48+ glm::ivec3 iColor = glm::clamp (glm::ivec3 (color * 255 .f ), glm::ivec3 (0 ), glm::ivec3 (255 ));
3849
3950 // Each thread writes one pixel location in the texture (textel)
4051 pbo[index].w = 0 ;
@@ -149,20 +160,50 @@ __global__ void computeIntersections(
149160#if BVH_DEBUG_VISUALIZATION
150161 scene->visualizedIntersect (pathSegments[pathIdx].ray , intersections[pathIdx]);
151162#else
152- scene->intersect (pathSegments[pathIdx].ray , intersections[pathIdx]);
153- /* AABB box;
154- box.pMin = glm::vec3(-3.f);
155- box.pMax = glm::vec3(3.f);
156- float dist;
157- bool hit = box.intersect(pathSegments[pathIdx].ray, dist);
158- intersections[pathIdx].primitive = hit ? 1 : NullPrimitive;
159- intersections[pathIdx].position = pathSegments[pathIdx].ray.getPoint(dist);*/
163+ Intersection intersec;
164+ PathSegment segment = pathSegments[pathIdx];
165+ scene->intersect (segment.ray , intersec);
166+
167+ if (intersec.primId != NullPrimitive) {
168+ if (scene->devMaterials [intersec.matId ].type == Material::Type::Light) {
169+ #if SCENE_LIGHT_SINGLE_SIDED
170+ if (glm::dot (intersec.norm , segment.ray .direction ) < 0 .f ) {
171+ intersec.primId = NullPrimitive;
172+ }
173+ else
174+ #endif
175+ if (depth != 0 ) {
176+ // If not first ray, preserve previous sampling information for
177+ // MIS calculation
178+ intersec.prevPos = segment.ray .origin ;
179+ // intersec.prevBSDFPdf = segment.BSDFPdf;
180+ }
181+ }
182+ else {
183+ intersec.wo = -segment.ray .direction ;
184+ }
185+ }
186+ intersections[pathIdx] = intersec;
160187#endif
161188 }
162189}
163190
191+ __global__ void computeTerminatedRays (
192+ int depth,
193+ PathSegment* segments,
194+ Intersection* intersections,
195+ DevScene* scene,
196+ int numPaths
197+ ) {
198+ int idx = blockDim .x * blockIdx .x + threadIdx .x ;
199+ if (idx >= numPaths) {
200+ return ;
201+ }
202+ }
203+
164204__global__ void pathIntegSampleSurface (
165205 int iter,
206+ int depth,
166207 PathSegment* segments,
167208 Intersection* intersections,
168209 DevScene* scene,
@@ -175,93 +216,114 @@ __global__ void pathIntegSampleSurface(
175216 return ;
176217 }
177218 Intersection intersec = intersections[idx];
178- if (intersec.primitive == NullPrimitive) {
219+
220+ if (intersec.primId == NullPrimitive) {
179221 // TODO
180222 // Environment map
181- segments[idx].pixelIndex = PixelIdxForTerminated;
223+ if (Math::luminance (segments[idx].radiance ) < 1e-4f ) {
224+ segments[idx].pixelIndex = PixelIdxForTerminated;
225+ }
226+ else {
227+ segments[idx].remainingBounces = 0 ;
228+ }
182229 return ;
183230 }
184231
185- PathSegment& segment = segments[idx];
186- thrust::default_random_engine rng = makeSeededRandomEngine (iter, idx, 4 + iter * SamplesConsumedOneIter);
187- Material material = scene->devMaterials [intersec.materialId ];
188- bool deltaBSDF = material.type == Material::Type::Dielectric;
189-
190- if (!deltaBSDF) {
191- glm::vec3 radiance;
192- glm::vec3 wi;
193- float lightPdf = scene->sampleDirectLight (intersec.position , sample4D (rng), radiance, wi);
194- float BSDFPdf = material.pdf (intersec.normal , intersec.incomingDir , wi);
195- segment.radiance += segment.throughput * material.BSDF (intersec.normal , intersec.incomingDir , wi) *
196- radiance * glm::dot (intersec.normal , wi) * Math::powerHeuristic (lightPdf, BSDFPdf) / lightPdf;
197- }
198-
199232#if BVH_DEBUG_VISUALIZATION
200233 float logDepth = 0 .f ;
201234 int size = scene->BVHSize ;
202235 while (size) {
203236 logDepth += 1 .f ;
204237 size >>= 1 ;
205238 }
206- segment.radiance = glm::vec3 (float (intersec.primitive ) / logDepth * .1f );
239+ segment.radiance = glm::vec3 (float (intersec.primId ) / logDepth * .1f );
207240 // segment.radiance = intersec.primitive > 16 ? glm::vec3(1.f) : glm::vec3(0.f);
208241 segment.remainingBounces = 0 ;
209242 return ;
210243#endif
211- /* segment.radiance = intersec.position;
212- segment.remainingBounces = 0;
213- return;*/
214244
215- if (material.type == Material::Type::Light) {
216- // TODO
217- // MIS
245+ PathSegment& segment = segments[idx];
246+ thrust::default_random_engine rng = makeSeededRandomEngine (iter, idx, 4 + depth * SamplesConsumedOneIter);
247+
248+ Material material = scene->devMaterials [intersec.matId ];
249+ glm::vec3 accRadiance (0 .f );
218250
219- segment.radiance += segment.throughput * material.baseColor * material.emittance ;
251+ if (material.type == Material::Type::Light) {
252+ glm::vec3 radiance = material.baseColor * material.emittance ;
253+ if (depth == 0 ) {
254+ accRadiance += radiance;
255+ }
256+ else if (segment.deltaSample ) {
257+ accRadiance += radiance * segment.throughput ;
258+ }
259+ else {
260+ float lightPdf = Math::pdfAreaToSolidAngle (Math::luminance (radiance) * scene->sumLightPowerInv ,
261+ intersec.prevPos , intersec.pos , intersec.norm );
262+ float BSDFPdf = segment.BSDFPdf ;
263+ accRadiance += radiance * segment.throughput * Math::powerHeuristic (BSDFPdf, lightPdf);
264+ }
220265 segment.remainingBounces = 0 ;
221266 }
222267 else {
223- if (material.type != Material::Type::Dielectric && glm::dot (intersec.normal , intersec.incomingDir ) < 0 .f ) {
224- intersec.normal = -intersec.normal ;
268+ bool deltaBSDF = (material.type == Material::Type::Dielectric);
269+ if (material.type != Material::Type::Dielectric && glm::dot (intersec.norm , intersec.wo ) < 0 .f ) {
270+ intersec.norm = -intersec.norm ;
271+ }
272+
273+ if (!deltaBSDF) {
274+ glm::vec3 radiance;
275+ glm::vec3 wi;
276+ float lightPdf = scene->sampleDirectLight (intersec.pos , sample4D (rng), radiance, wi);
277+
278+ if (lightPdf > 0 .f ) {
279+ float BSDFPdf = material.pdf (intersec.norm , intersec.wo , wi);
280+ accRadiance += segment.throughput * material.BSDF (intersec.norm , intersec.wo , wi) *
281+ radiance * Math::satDot (intersec.norm , wi) / lightPdf * Math::powerHeuristic (lightPdf, BSDFPdf);
282+ }
225283 }
226284
227285 BSDFSample sample;
228- material.sample (intersec.normal , intersec.incomingDir , sample3D (rng), sample);
286+ material.sample (intersec.norm , intersec.wo , sample3D (rng), sample);
229287
230288 if (sample.type == BSDFSampleType::Invalid) {
231289 // Terminate path if sampling fails
232290 segment.remainingBounces = 0 ;
233291 }
234292 else {
235- bool isSampleDelta = (sample.type & BSDFSampleType::Specular);
293+ bool deltaSample = (sample.type & BSDFSampleType::Specular);
236294 segment.throughput *= sample.bsdf / sample.pdf *
237- (isSampleDelta ? 1 .f : Math::absDot (intersec.normal , sample.dir ));
238- segment.ray = makeOffsetedRay (intersec.position , sample.dir );
295+ (deltaSample ? 1 .f : Math::absDot (intersec.norm , sample.dir ));
296+ segment.ray = makeOffsetedRay (intersec.pos , sample.dir );
297+ segment.BSDFPdf = sample.pdf ;
298+ segment.deltaSample = deltaSample;
239299 segment.remainingBounces --;
240300 }
241301 }
302+ // if (depth == 1)
303+ segment.radiance += accRadiance;
242304}
243305
244306// Add the current iteration's output to the overall image
245- __global__ void finalGather (int nPaths, glm::vec3* Image , PathSegment* iterationPaths) {
307+ __global__ void finalGather (int nPaths, glm::vec3* image , PathSegment* iterationPaths) {
246308 int index = (blockIdx .x * blockDim .x ) + threadIdx .x ;
247309
248310 if (index < nPaths) {
249311 PathSegment iterationPath = iterationPaths[index];
250- if (iterationPath.pixelIndex >= 0 && iterationPath.remainingBounces = = 0 ) {
251- Image [iterationPath.pixelIndex ] += iterationPath.radiance ;
312+ if (iterationPath.pixelIndex >= 0 && iterationPath.remainingBounces < = 0 ) {
313+ image [iterationPath.pixelIndex ] += iterationPath.radiance ;
252314 }
253315 }
254316}
255317
256318struct CompactTerminatedPaths {
257319 __host__ __device__ bool operator () (const PathSegment& segment) {
258- return !(segment.pixelIndex >= 0 && segment.remainingBounces = = 0 );
320+ return !(segment.pixelIndex >= 0 && segment.remainingBounces < = 0 );
259321 }
260322};
261323
262324struct RemoveInvalidPaths {
263325 __host__ __device__ bool operator () (const PathSegment& segment) {
264- return segment.pixelIndex < 0 || segment.remainingBounces = = 0 ;
326+ return segment.pixelIndex < 0 || segment.remainingBounces < = 0 ;
265327 }
266328};
267329
@@ -335,21 +397,20 @@ void pathTrace(uchar4* pbo, int frame, int iter) {
335397 // tracing
336398 dim3 numBlocksPathSegmentTracing = (numPaths + blockSize1D - 1 ) / blockSize1D;
337399 computeIntersections<<<numBlocksPathSegmentTracing, blockSize1D>>> (
338- depth,
400+ depth,
339401 numPaths,
340402 devPaths,
341403 hstScene->devScene ,
342404 devIntersections
343405 );
344406 checkCUDAError (" PT::computeInteractions" );
345407 cudaDeviceSynchronize ();
346- depth++;
347408
348409 // TODO: compare between directly shading the path segments and shading
349410 // path segments that have been reshuffled to be contiguous in memory.
350411
351412 pathIntegSampleSurface<<<numBlocksPathSegmentTracing, blockSize1D>>> (
352- iter, devPaths, devIntersections, hstScene->devScene , numPaths
413+ iter, depth, devPaths, devIntersections, hstScene->devScene , numPaths
353414 );
354415 checkCUDAError (" PT::sampleSurface" );
355416 cudaDeviceSynchronize ();
@@ -361,7 +422,8 @@ void pathTrace(uchar4* pbo, int frame, int iter) {
361422 numPaths = end - devPathsThr;
362423 // std::cout << "Remaining paths: " << numPaths << "\n";
363424
364- iterationComplete = numPaths == 0 ;
425+ iterationComplete = (numPaths == 0 );
426+ depth++;
365427
366428 if (guiData != nullptr ) {
367429 guiData->TracedDepth = depth;
@@ -376,7 +438,7 @@ void pathTrace(uchar4* pbo, int frame, int iter) {
376438 // /////////////////////////////////////////////////////////////////////////
377439
378440 // Send results to OpenGL buffer for rendering
379- sendImageToPBO<<<blocksPerGrid2D, blockSize2D>>> (pbo, cam.resolution , iter, devImage);
441+ sendImageToPBO<<<blocksPerGrid2D, blockSize2D>>> (pbo, cam.resolution , iter, devImage, ToneMapping::method );
380442
381443 // Retrieve image from GPU
382444 cudaMemcpy (hstScene->state .image .data (), devImage,
0 commit comments