UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

150 lines (111 loc) 5.19 kB
import { assert } from "../../../../../../core/assert.js"; import { UINT32_MAX } from "../../../../../../core/binary/UINT32_MAX.js"; import { array_copy } from "../../../../../../core/collection/array/array_copy.js"; import { decode_octahedron_to_unit } from "../../../../../../core/geom/3d/normal/octahedron/decode_octahedron_to_unit.js"; import { Ray3 } from "../../../../../../core/geom/3d/ray/Ray3.js"; import { v3_distance } from "../../../../../../core/geom/vec3/v3_distance.js"; import { v3_dot_array_array } from "../../../../../../core/geom/vec3/v3_dot_array_array.js"; import { max2 } from "../../../../../../core/math/max2.js"; import { generate_hammersley_jitter } from "../../../../../../core/math/statistics/generate_hammersley_jitter.js"; /** * Weights of rays that are aligned with the probe direction get exponentially weighted by this exponent * @type {number} */ const DEPTH_SHARPNESS = 3; const scratch_ray = new Ray3(); /** * * @type {number[]} */ const ray_hit = []; /** * * @param {number[]} result * @param {number} result_offset * @param {PathTracedScene} scene * @param {number[]|Vector3} position * @param {number} position_offset * @param {number} resolution * @param {number} max_depth * @param {number} [sub_sample_count] */ export function bake_octahedral_depth_map( result, result_offset, scene, position, position_offset, resolution, max_depth, sub_sample_count=4 ) { assert.lessThan(result_offset + resolution * resolution, UINT32_MAX, 'Result overflow'); scratch_ray.tMax = max_depth; array_copy(position, position_offset, scratch_ray, 0, 3); const NORMAL_BIAS = 1e-6; const ray_direction = scratch_ray.direction; const probe_direction = new Float32Array(3); const texel_uv_scale = 0.5 / (resolution); const jittered_sub_samples = generate_hammersley_jitter(sub_sample_count); for (let oct_x = 0; oct_x < resolution; oct_x++) { for (let oct_y = 0; oct_y < resolution; oct_y++) { let distance_sum = 0; let distance2_sum = 0; let weight_sum = 0; // offset position by half a pixel, as we are storing values for pixel centers const u = (oct_x + 0.5) / (resolution); const v = (oct_y + 0.5) / (resolution); decode_octahedron_to_unit( probe_direction, 0, u * 2 - 1, v * 2 - 1 ); for (let sub_sample_index = 0; sub_sample_index < sub_sample_count; sub_sample_index++) { const sample_index2 = sub_sample_index * 2; const sample_u = jittered_sub_samples[sample_index2]; const sample_v = jittered_sub_samples[sample_index2 + 1]; const ray_u = u + (sample_u * 2 - 1) * texel_uv_scale; const ray_v = v + (sample_v * 2 - 1) * texel_uv_scale; decode_octahedron_to_unit( ray_direction, 0, ray_u * 2 - 1, ray_v * 2 - 1 ); let distance = scene.trace(ray_hit, scratch_ray); if (distance < 0) { // no hit distance = max_depth; } else { const surface_normal_x = ray_hit[3]; const surface_normal_y = ray_hit[4]; const surface_normal_z = ray_hit[5]; const surface_position_x = ray_hit[0]; const surface_position_y = ray_hit[1]; const surface_position_z = ray_hit[2]; const hit_normal_bias = NORMAL_BIAS; // sink the contact into the surface along the hit normal const biased_hit_x = surface_position_x + surface_normal_x * hit_normal_bias; const biased_hit_y = surface_position_y + surface_normal_y * hit_normal_bias; const biased_hit_z = surface_position_z + surface_normal_z * hit_normal_bias; distance = v3_distance( scratch_ray[0], scratch_ray[1], scratch_ray[2], biased_hit_x, biased_hit_y, biased_hit_z ); } const direction_dot_product = v3_dot_array_array(ray_direction, 0, probe_direction, 0); const weight = Math.pow( max2(0, direction_dot_product ), DEPTH_SHARPNESS ); distance_sum += distance * weight; distance2_sum += distance * distance * weight; weight_sum += weight; } const pixel_index = oct_y * resolution + oct_x; const address = result_offset + pixel_index * 2; const mean = distance_sum / weight_sum; const mean2 = distance2_sum / weight_sum; result[address] = mean; result[address + 1] = mean2; } } }