Skip to content
This repository has been archived by the owner on Nov 22, 2022. It is now read-only.

Commit

Permalink
Next event estimation (#71)
Browse files Browse the repository at this point in the history
* test next event estimation

* start implementing shadow catcher

* add shadow catcher support (kindof)

* big cleanup

* more tweaks

* revert scene changes

* set strataCount back to default
  • Loading branch information
Lucas Crane authored Feb 20, 2020
1 parent dc4c249 commit 7c875ca
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 207 deletions.
2 changes: 2 additions & 0 deletions scenes/renderer-test/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ function init() {
const geo = new THREE.PlaneBufferGeometry(1000, 1000);
const mat = new THREE.MeshStandardMaterial();
mat.shadowCatcher = true;
mat.roughness = 0.5;
mat.metalness = 0.0;
const mesh = new THREE.Mesh(geo, mat);
mesh.rotateX(Math.PI / 2);
model.add(mesh);
Expand Down
2 changes: 1 addition & 1 deletion scenes/sample-models/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function createGroundMesh() {
const geo = new THREE.PlaneBufferGeometry(100, 100);
const mat = new THREE.MeshStandardMaterial();
mat.color.set(0xffffff);
mat.roughness = 1.0;
mat.roughness = 0.5;
mat.metalness = 0.0;
mat.shadowCatcher = true;
const mesh = new THREE.Mesh(geo, mat);
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/RayTracePass.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export function makeRayTracePass(gl, {
const samplingDimensions = [];

for (let i = 1; i <= bounces; i++) {
// specular or diffuse reflection, light importance sampling, material sampling, next path direction
samplingDimensions.push(2, 2, 2, 2);
// specular or diffuse reflection, light importance sampling, next path direction
samplingDimensions.push(2, 2, 2);
if (i >= 2) {
// russian roulette sampling
// this step is skipped on the first bounce
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/RenderingPipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export function makeRenderingPipeline({
// higher number results in faster convergence over time, but with lower quality initial samples
const strataCount = 6;

const desiredTimeForPreview = 14;

const decomposedScene = decomposeScene(scene);

const mergedMesh = mergeMeshesToGeometry(decomposedScene.meshes);
Expand Down Expand Up @@ -159,7 +161,6 @@ export function makeRenderingPipeline({
}

function setPreviewBufferDimensions() {
const desiredTimeForPreview = 10;
const numPixelsForPreview = desiredTimeForPreview / tileRender.getTimePerPixel();

const aspectRatio = screenWidth / screenHeight;
Expand Down
16 changes: 16 additions & 0 deletions src/renderer/glsl/chunks/random.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,20 @@ float randomSample() {
vec2 randomSampleVec2() {
return vec2(randomSample(), randomSample());
}

struct MaterialSamples {
vec2 s1;
vec2 s2;
vec2 s3;
};

MaterialSamples getRandomMaterialSamples() {
MaterialSamples samples;

samples.s1 = randomSampleVec2();
samples.s2 = randomSampleVec2();
samples.s3 = randomSampleVec2();

return samples;
}
`;
4 changes: 1 addition & 3 deletions src/renderer/glsl/chunks/rayTraceCore.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ export default `
#define THICK_GLASS 2
#define SHADOW_CATCHER 3

#define SAMPLES_PER_MATERIAL 8

const float IOR = 1.5;
const float INV_IOR = 1.0 / IOR;

Expand Down Expand Up @@ -71,11 +69,11 @@ export default `
struct Path {
Ray ray;
vec3 li;
vec3 albedo;
float alpha;
vec3 beta;
bool specularBounce;
bool abort;
float misWeight;
};

uniform Camera camera;
Expand Down
15 changes: 9 additions & 6 deletions src/renderer/glsl/chunks/sampleGlassSpecular.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ export default `
#ifdef USE_GLASS

void sampleGlassSpecular(SurfaceInteraction si, int bounce, inout Path path) {
bool lastBounce = bounce == BOUNCES;
vec3 viewDir = -path.ray.d;
float cosTheta = dot(si.normal, viewDir);

MaterialSamples samples = getRandomMaterialSamples();

float reflectionOrRefraction = samples.s1.x;

float F = si.materialType == THIN_GLASS ?
fresnelSchlick(abs(cosTheta), R0) : // thin glass
fresnelSchlickTIR(cosTheta, R0, IOR); // thick glass

vec3 lightDir;

float reflectionOrRefraction = randomSample();

if (reflectionOrRefraction < F) {
lightDir = reflect(-viewDir, si.normal);
} else {
Expand All @@ -23,13 +26,13 @@ void sampleGlassSpecular(SurfaceInteraction si, int bounce, inout Path path) {
path.beta *= si.color;
}

path.misWeight = 1.0;

initRay(path.ray, si.position + EPS * lightDir, lightDir);

// advance sample index by unused stratified samples
const int usedSamples = 1;
sampleIndex += SAMPLES_PER_MATERIAL - usedSamples;
path.li += lastBounce ? path.beta * sampleBackgroundFromDirection(lightDir) : vec3(0.0);

path.li += bounce == BOUNCES ? path.beta * sampleBackgroundFromDirection(lightDir) : vec3(0.0);
path.specularBounce = true;
}

#endif
Expand Down
139 changes: 61 additions & 78 deletions src/renderer/glsl/chunks/sampleMaterial.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -3,123 +3,106 @@

export default `

vec3 importanceSampleLight(SurfaceInteraction si, vec3 viewDir, bool lastBounce, vec2 random) {
vec3 li;
void sampleMaterial(SurfaceInteraction si, int bounce, inout Path path) {
bool lastBounce = bounce == BOUNCES;
mat3 basis = orthonormalBasis(si.normal);
vec3 viewDir = -path.ray.d;

float lightPdf;
vec2 uv;
vec3 lightDir = sampleEnvmap(random, uv, lightPdf);
MaterialSamples samples = getRandomMaterialSamples();

float cosThetaL = dot(si.normal, lightDir);
vec2 diffuseOrSpecular = samples.s1;
vec2 lightDirSample = samples.s2;
vec2 bounceDirSample = samples.s3;

float orientation = dot(si.faceNormal, viewDir) * cosThetaL;
if (orientation < 0.0) {
return li;
}
// Step 1: Add direct illumination of the light source (the hdr map)
// On every bounce but the last, importance sample the light source
// On the last bounce, multiple importance sample the brdf AND the light source, determined by random var

float diffuseWeight = 1.0;
Ray ray;
initRay(ray, si.position + EPS * lightDir, lightDir);
if (intersectSceneShadow(ray)) {
if (lastBounce) {
diffuseWeight = 0.0;
} else {
return li;
}
vec3 lightDir;
vec2 uv;
float lightPdf;
bool brdfSample = false;

if (lastBounce && diffuseOrSpecular.x < 0.5) {
// reuse this sample by multiplying by 2 to bring sample from [0, 0.5), to [0, 1)
lightDir = 2.0 * diffuseOrSpecular.x < mix(0.5, 0.0, si.metalness) ?
lightDirDiffuse(si.faceNormal, viewDir, basis, lightDirSample) :
lightDirSpecular(si.faceNormal, viewDir, basis, si.roughness, lightDirSample);

uv = cartesianToEquirect(lightDir);
lightPdf = envmapPdf(uv);
brdfSample = true;
} else {
lightDir = sampleEnvmap(lightDirSample, uv, lightPdf);
}

vec3 irr = textureLinear(envmap, uv).xyz;

float scatteringPdf;
vec3 brdf = materialBrdf(si, viewDir, lightDir, cosThetaL, diffuseWeight, scatteringPdf);

float weight = powerHeuristic(lightPdf, scatteringPdf);

li = brdf * irr * abs(cosThetaL) * weight / lightPdf;

return li;
}

vec3 importanceSampleMaterial(SurfaceInteraction si, vec3 viewDir, bool lastBounce, vec3 lightDir) {
vec3 li;

float cosThetaL = dot(si.normal, lightDir);

float occluded = 1.0;

float orientation = dot(si.faceNormal, viewDir) * cosThetaL;
if (orientation < 0.0) {
return li;
// light dir points towards surface. invalid dir.
occluded = 0.0;
}

float diffuseWeight = 1.0;
Ray ray;
initRay(ray, si.position + EPS * lightDir, lightDir);
if (intersectSceneShadow(ray)) {

initRay(path.ray, si.position + EPS * lightDir, lightDir);
if (intersectSceneShadow(path.ray)) {
if (lastBounce) {
diffuseWeight = 0.0;
} else {
return li;
occluded = 0.0;
}
}

vec2 uv = cartesianToEquirect(lightDir);

float lightPdf = envmapPdf(uv);

vec3 irr = textureLinear(envmap, uv).rgb;

float scatteringPdf;
vec3 brdf = materialBrdf(si, viewDir, lightDir, cosThetaL, diffuseWeight, scatteringPdf);

float weight = powerHeuristic(scatteringPdf, lightPdf);

li += brdf * irr * abs(cosThetaL) * weight / scatteringPdf;

return li;
}
float weight;
if (lastBounce) {
weight = brdfSample ?
2.0 * powerHeuristic(scatteringPdf, lightPdf) / scatteringPdf :
2.0 * powerHeuristic(lightPdf, scatteringPdf) / lightPdf;
} else {
weight = powerHeuristic(lightPdf, scatteringPdf) / lightPdf;
}

void sampleMaterial(SurfaceInteraction si, int bounce, inout Path path) {
mat3 basis = orthonormalBasis(si.normal);
vec3 viewDir = -path.ray.d;
path.li += path.beta * occluded * brdf * irr * abs(cosThetaL) * weight;;

vec2 diffuseOrSpecular = randomSampleVec2();
// Step 2: Setup ray direction for next bounce by importance sampling the BRDF

vec3 lightDir = diffuseOrSpecular.x < mix(0.5, 0.0, si.metalness) ?
lightDirDiffuse(si.faceNormal, viewDir, basis, randomSampleVec2()) :
lightDirSpecular(si.faceNormal, viewDir, basis, si.roughness, randomSampleVec2());
if (lastBounce) {
return;
}

bool lastBounce = bounce == BOUNCES;
lightDir = diffuseOrSpecular.y < mix(0.5, 0.0, si.metalness) ?
lightDirDiffuse(si.faceNormal, viewDir, basis, bounceDirSample) :
lightDirSpecular(si.faceNormal, viewDir, basis, si.roughness, bounceDirSample);

// Add path contribution
path.li += path.beta * (
importanceSampleLight(si, viewDir, lastBounce, randomSampleVec2()) +
importanceSampleMaterial(si, viewDir, lastBounce, lightDir)
);
cosThetaL = dot(si.normal, lightDir);

// Get new path direction
orientation = dot(si.faceNormal, viewDir) * cosThetaL;
path.abort = orientation < 0.0;

if (lastBounce) {
if (path.abort) {
return;
}

lightDir = diffuseOrSpecular.y < mix(0.5, 0.0, si.metalness) ?
lightDirDiffuse(si.faceNormal, viewDir, basis, randomSampleVec2()) :
lightDirSpecular(si.faceNormal, viewDir, basis, si.roughness, randomSampleVec2());
brdf = materialBrdf(si, viewDir, lightDir, cosThetaL, 1.0, scatteringPdf);

float cosThetaL = dot(si.normal, lightDir);
uv = cartesianToEquirect(lightDir);
lightPdf = envmapPdf(uv);

float scatteringPdf;
vec3 brdf = materialBrdf(si, viewDir, lightDir, cosThetaL, 1.0, scatteringPdf);
path.misWeight = powerHeuristic(scatteringPdf, lightPdf);

path.beta *= abs(cosThetaL) * brdf / scatteringPdf;

initRay(path.ray, si.position + EPS * lightDir, lightDir);

// If new ray direction is pointing into the surface,
// the light path is physically impossible and we terminate the path.
float orientation = dot(si.faceNormal, viewDir) * cosThetaL;
path.abort = orientation < 0.0;

path.specularBounce = false;
}

initRay(path.ray, si.position + EPS * lightDir, lightDir);
}
`;
Loading

0 comments on commit 7c875ca

Please sign in to comment.