Skip to content

Commit aaa4123

Browse files
committed
light importance sampling 100% + MIS
1 parent 31da541 commit aaa4123

File tree

8 files changed

+210
-98
lines changed

8 files changed

+210
-98
lines changed

src/main.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,19 @@ void saveImage() {
158158
for (int x = 0; x < width; x++) {
159159
for (int y = 0; y < height; y++) {
160160
int index = x + (y * width);
161-
glm::vec3 pix = renderState->image[index] / samples;
162-
pix = Math::correctGamma(Math::ACES(pix));
163-
img.setPixel(width - 1 - x, y, pix);
161+
glm::vec3 color = renderState->image[index] / samples;
162+
switch (ToneMapping::method) {
163+
case ToneMapping::Filmic:
164+
color = Math::filmic(color);
165+
break;
166+
case ToneMapping::ACES:
167+
color = Math::ACES(color);
168+
break;
169+
case ToneMapping::None:
170+
break;
171+
}
172+
color = Math::correctGamma(color);
173+
img.setPixel(width - 1 - x, y, color);
164174
}
165175
}
166176

@@ -232,6 +242,9 @@ void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods
232242
case GLFW_KEY_S:
233243
saveImage();
234244
break;
245+
case GLFW_KEY_T:
246+
ToneMapping::method = (ToneMapping::method + 1) % 3;
247+
break;
235248
case GLFW_KEY_SPACE:
236249
camChanged = true;
237250
renderState = &scene->state;

src/material.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,17 +122,17 @@ struct Material {
122122
}
123123

124124
__device__ glm::vec3 lambertianBSDF(glm::vec3 n, glm::vec3 wo, glm::vec3 wi) {
125-
return baseColor * Math::satDot(n, wi) * PiInv;
125+
return baseColor * PiInv;
126126
}
127127

128128
__device__ float lambertianPdf(glm::vec3 n, glm::vec3 wo, glm::vec3 wi) {
129-
return glm::dot(n, wi) * PiInv;
129+
return Math::satDot(n, wi) * PiInv;
130130
}
131131

132132
__device__ void lambertianSample(glm::vec3 n, glm::vec3 wo, glm::vec3 r, BSDFSample& sample) {
133133
sample.dir = Math::sampleHemisphereCosine(n, r.x, r.y);
134134
sample.bsdf = baseColor * PiInv;
135-
sample.pdf = glm::dot(n, sample.dir) * PiInv;
135+
sample.pdf = Math::satDot(n, sample.dir) * PiInv;
136136
sample.type = Diffuse | Reflection;
137137
}
138138

src/mathUtil.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ namespace Math {
137137

138138
__device__ inline float pdfAreaToSolidAngle(float pdf, glm::vec3 x, glm::vec3 y, glm::vec3 ny) {
139139
glm::vec3 yx = x - y;
140-
return pdf * absDot(ny, glm::normalize(yx)) / glm::dot(yx, yx);
140+
return pdf * glm::dot(yx, yx) / absDot(ny, glm::normalize(yx));
141141
}
142142

143143
/**

src/pathtrace.cu

Lines changed: 116 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,32 @@
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

256318
struct 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

262324
struct 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,

src/pathtrace.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@
44
#include <vector>
55
#include "scene.h"
66

7+
class ToneMapping {
8+
public:
9+
enum {
10+
None = 0, Filmic = 1, ACES = 2
11+
};
12+
static int method;
13+
};
14+
715
void InitDataContainer(GuiDataContainer* guiData);
816
void pathTraceInit(Scene *scene);
917
void pathTraceFree();
10-
void pathTrace(uchar4 *pbo, int frame, int iteration);
18+
void pathTrace(uchar4 *pbo, int frame, int iteration);

0 commit comments

Comments
 (0)