UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

125 lines (90 loc) 3.18 kB
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] } }