@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
125 lines (90 loc) • 3.18 kB
JavaScript
import { vec3 } from "gl-matrix";
import { array_copy } from "../../../../core/collection/array/array_copy.js";
import { Ray3 } from "../../../../core/geom/3d/ray/Ray3.js";
import { getBiasedNormalSample } from "./sampling/getBiasedNormalSample.js";
/*
Ray hit data layout:
position: v3
normal: v3
t: float
u: float
v: float
primitive_index: uint
instance_id: uint
*/
const irradiance = [0, 0, 0];
const trace_result = [];
const _ray_0 = new Ray3();
const tmp_0 = new Float32Array(6);
/**
* Normalized probability of terminating the ray
* @type {number}
*/
const RUSSIAN_ROULETTE_PROBABILITY = 0.5;
const color_mask = new Float32Array(3);
export class PathTracer {
/**
*
* @param {number[]|Float32Array} out
* @param {number[]|Float32Array|Ray3} ray
* @param {number} min_bounce
* @param {number} max_bounce
* @param {function} random
* @param {PathTracedScene} scene
*/
path_trace(
out,
ray,
min_bounce,
max_bounce,
random = Math.random,
scene
) {
_ray_0.copy(ray);
irradiance[0] = 0;
irradiance[1] = 0;
irradiance[2] = 0;
color_mask[0] = 1;
color_mask[1] = 1;
color_mask[2] = 1;
let i;
for (i = 0; i < max_bounce; ++i) {
if (i >= min_bounce) {
const russian_roulette_roll = random();
if (russian_roulette_roll < RUSSIAN_ROULETTE_PROBABILITY) {
// terminate ray
break;
}
}
const hit_distance = scene.trace(trace_result, _ray_0);
if (hit_distance < 0) {
// ray didn't hit anything
break;
}
scene.sample_material(tmp_0, trace_result, _ray_0);
vec3.multiply(color_mask, color_mask, tmp_0);
// construct new ray, aligned on the normal of the contact
array_copy(tmp_0, 3, _ray_0, 3, 3); // copy new direction
array_copy(trace_result, 0, _ray_0, 0, 3); // copy new origin
// move ray forward by a small amount to avoid self-occlusion
_ray_0.shiftForward(0.00001);
// light sampling
scene.sample_lights(tmp_0, 0, _ray_0);
// sample on cosine lobe ray
getBiasedNormalSample(_ray_0, 3, _ray_0, 3, 1, random);
// // accumulate irradiance
irradiance[0] += color_mask[0] * tmp_0[0];
irradiance[1] += color_mask[1] * tmp_0[1];
irradiance[2] += color_mask[2] * tmp_0[2];
}
if (i === 0) {
// ray didn't hit anything on the very first bounce
// sample "environment" and terminate path as there is nothing to reflect off of
scene.sample_background(tmp_0, 0, _ray_0, 3);
vec3.multiply(irradiance, irradiance, tmp_0);
}
out[0] = irradiance[0]
out[1] = irradiance[1]
out[2] = irradiance[2]
}
}