gpu-curtains
Version:
gpu-curtains is a 3D WebGPU rendering engine. It can be used as a standalone 3D engine, but also includes extra classes focused on mapping 3d objects to DOM elements; It allows users to synchronize values such as position, sizing, or scale between them.
125 lines (103 loc) • 3.45 kB
JavaScript
import { constants } from "../../chunks/utils/constants.mjs";
import { generateTBN } from "../../chunks/utils/generate-TBN.mjs";
import { hammersley2D } from "../../chunks/utils/hammersley-2D.mjs";
//#region src/core/shaders/full/compute/compute-diffuse-from-cubemap.ts
/**
* Compute a diffuse cube map texture from a specular cube map {@link Texture}.
* @param cubemapTexture - Cube map {@link Texture} to use.
*/
const computeDiffuseFromCubemap = (cubemapTexture) => `
${constants}
${hammersley2D}
${generateTBN}
// Mipmap Filtered Samples (GPU Gems 3, 20.4)
// https://developer.nvidia.com/gpugems/gpugems3/part-iii-rendering/chapter-20-gpu-based-importance-sampling
// https://cgg.mff.cuni.cz/~jaroslav/papers/2007-sketch-fis/Final_sap_0073.pdf
fn computeLod(pdf: f32) -> f32 {
// https://cgg.mff.cuni.cz/~jaroslav/papers/2007-sketch-fis/Final_sap_0073.pdf
return 0.5 * log2( 6.0 * f32(params.faceSize) * f32(params.faceSize) / (f32(params.sampleCount) * pdf));
}
fn transformDirection(face: u32, uv: vec2f) -> vec3f {
// Transform the direction based on the cubemap face
switch (face) {
case 0u {
// +X
return vec3f( 1.0, uv.y, -uv.x);
}
case 1u {
// -X
return vec3f(-1.0, uv.y, uv.x);
}
case 2u {
// +Y
return vec3f( uv.x, -1.0, uv.y);
}
case 3u {
// -Y
return vec3f( uv.x, 1.0, -uv.y);
}
case 4u {
// +Z
return vec3f( uv.x, uv.y, 1.0);
}
case 5u {
// -Z
return vec3f(-uv.x, uv.y, -1.0);
}
default {
return vec3f(0.0, 0.0, 0.0);
}
}
}
@compute @workgroup_size(8, 8, 1) fn main(
@builtin(global_invocation_id) GlobalInvocationID: vec3u,
) {
let faceSize: u32 = params.faceSize;
let sampleCount: u32 = params.sampleCount;
let face: u32 = GlobalInvocationID.z;
let x: u32 = GlobalInvocationID.x;
let y: u32 = GlobalInvocationID.y;
if (x >= faceSize || y >= faceSize) {
return;
}
let texelSize: f32 = 1.0 / f32(faceSize);
let halfTexel: f32 = texelSize * 0.5;
var uv: vec2f = vec2(
(f32(x) + halfTexel) * texelSize,
(f32(y) + halfTexel) * texelSize
);
uv = uv * 2.0 - 1.0;
let normal: vec3f = transformDirection(face, uv);
var irradiance: vec3f = vec3f(0.0, 0.0, 0.0);
for (var i: u32 = 0; i < sampleCount; i++) {
// generate a quasi monte carlo point in the unit square [0.1)^2
let xi: vec2f = hammersley2d(i, sampleCount);
let cosTheta: f32 = sqrt(1.0 - xi.y);
let sinTheta: f32 = sqrt(1.0 - cosTheta * cosTheta);
let phi: f32 = 2.0 * PI * xi.x;
let pdf: f32 = cosTheta / PI; // evaluation for solid angle, therefore drop the sinTheta
let sampleVec: vec3f = vec3f(
sinTheta * cos(phi),
sinTheta * sin(phi),
cosTheta
);
let TBN: mat3x3f = generateTBN(normalize(normal));
var direction: vec3f = TBN * sampleVec;
// invert along Y axis
direction.y *= -1.0;
let lod: f32 = computeLod(pdf);
let sampleLevel = min(lod, f32(params.maxMipLevel));
// Convert sampleVec to texture coordinates of the specular env map
irradiance += textureSampleLevel(
${cubemapTexture.options.name},
clampSampler,
direction,
sampleLevel
).rgb;
}
irradiance /= f32(sampleCount);
textureStore(diffuseEnvMap, vec2(x, y), face, vec4f(irradiance, 1.0));
}
`;
//#endregion
export { computeDiffuseFromCubemap };