@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
68 lines (53 loc) • 2.09 kB
JavaScript
import { v3_length } from "../../../../../core/geom/vec3/v3_length.js";
import { PI2 } from "../../../../../core/math/PI2.js";
/**
* @see 2003 "Global Illumination Compendium" by Philip Dutré (equation 36)
* @see http://blog.hvidtfeldts.net/index.php/2015/01/path-tracing-3d-fractals/
* @param {number[]} out
* @param {number} out_offset
* @param {number[]} normal
* @param {number} normal_offset
* @param {number} power
* @param {function():number} random
*/
export function getBiasedNormalSample(
out,
out_offset,
normal,
normal_offset,
power,
random
) {
const dir_x = normal[normal_offset];
const dir_y = normal[normal_offset + 1];
const dir_z = normal[normal_offset + 2];
// we build orthonormal vectors with respect to the direction vector
let o1_x, o1_y, o1_z;
if (Math.abs(dir_x) > Math.abs(dir_z)) {
o1_x = -dir_y;
o1_y = dir_x;
o1_z = 0;
} else {
o1_x = 0;
o1_y = -dir_z;
o1_z = dir_y;
}
// normalize orthonormal vector
const o1_norm = 1 / v3_length(o1_x, o1_y, o1_z);
o1_x *= o1_norm;
o1_y *= o1_norm;
o1_z *= o1_norm;
const o2_x = dir_y * o1_z - dir_z * o1_y;
const o2_y = dir_z * o1_x - dir_x * o1_z;
const o2_z = dir_x * o1_y - dir_y * o1_x;
// we can skip normalizing second orthonormal vector, as it will be guaranteed to be unit length already
// for explanation, see https://math.stackexchange.com/questions/23259/is-the-cross-product-of-two-unit-vectors-itself-a-unit-vector#:~:text=If%20you%20know%20that%20the,(a%20length%20of%20one).
const r_x = random() * PI2;
const r_y = Math.pow(random(), 1 / (power + 1));
const oneminus = Math.sqrt(1.0 - r_y * r_y);
const k0 = Math.cos(r_x) * oneminus;
const k1 = Math.sin(r_x) * oneminus;
out[out_offset] = k0 * o1_x + k1 * o2_x + r_y * dir_x;
out[out_offset + 1] = k0 * o1_y + k1 * o2_y + r_y * dir_y;
out[out_offset + 2] = k0 * o1_z + k1 * o2_z + r_y * dir_z;
}