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.
178 lines (149 loc) • 6.7 kB
JavaScript
//#region src/core/shaders/chunks/fragment/head/get-IBL-transmission.ts
/** WGSL functions to calculate the transmission effect of transmissive meshes using the renderer scene background texture. */
const getIBLTransmission = `
fn getVolumeTransmissionRay(normal: vec3f, viewDirection: vec3f, thickness: f32, ior: f32, modelScale: vec3f) -> vec3f {
let refractionVector = refract(-viewDirection, normal, 1.0 / ior);
return normalize(refractionVector) * thickness * modelScale;
}
fn applyIorToRoughness(roughness: f32, ior: f32) -> f32 {
return roughness * saturate(ior * 2.0 - 2.0);
}
// since opaque objects are rendered only once before transmissive objects
// the transmission sample might be tone mapped and in sRGB space
// so we try to invert this here
fn patchTransmissionSample(transmissionSample: vec4f, isTransmissiveLinear: bool, transmissiveToneMapping: u32) -> vec4f {
var color = transmissionSample;
color = select(sRGBToLinear_4(color), color, isTransmissiveLinear);
if(transmissiveToneMapping == 1u) {
color = vec4(inverseKhronosToneMapping(color.rgb), color.a);
} else if(transmissiveToneMapping == 2u) {
color = vec4(inverseReinhardToneMapping(color.rgb), color.a);
} else if(transmissiveToneMapping == 3u) {
color = vec4(inverseCineonToneMapping(color.rgb), color.a);
}
return color;
}
fn getTransmissionSample( fragCoord: vec2f, roughness: f32, ior: f32, transmissionSceneTexture: texture_2d<f32>, transmissionSampler: sampler ) -> vec4f {
let transmissionSamplerSize: vec2f = vec2f(textureDimensions(transmissionSceneTexture));
let lod: f32 = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior );
return textureSampleLevel( transmissionSceneTexture, transmissionSampler, fragCoord.xy, lod );
}
fn volumeAttenuation(transmissionDistance: f32, attenuationColor: vec3f, attenuationDistance: f32) -> vec3f {
if (isinf(attenuationDistance)) {
return vec3(1.0);
} else {
let attenuationCoefficient = -log(attenuationColor) / attenuationDistance;
let transmittance = exp(-attenuationCoefficient * transmissionDistance);
return transmittance;
}
}
fn getIBLVolumeRefraction(
normal: vec3f,
viewDirection: vec3f,
roughness: f32,
diffuseContribution: vec3f,
fab: vec2f,
specularColorBlended: vec3f,
specularF90: f32,
position: vec3f,
modelScale: vec3f,
viewMatrix: mat4x4f,
projMatrix: mat4x4f,
dispersion: f32,
ior: f32,
thickness: f32,
attenuationColor: vec3f,
attenuationDistance: f32,
isTransmissiveLinear: bool,
transmissiveToneMapping: u32,
transmissionBackgroundTexture: texture_2d<f32>,
defaultSampler: sampler,
) -> vec4f {
var transmittedLight: vec4f;
var transmissionRayLength: f32;
var transmittance: vec3f;
// Calculate the transmission ray
let transmissionRay: vec3f = getVolumeTransmissionRay(normal, viewDirection, thickness, ior, modelScale);
let refractedRayExit = position + transmissionRay;
// Transform to NDC space
let ndcPos = projMatrix * viewMatrix * vec4(refractedRayExit, 1.0);
var refractionCoords = ndcPos.xy / ndcPos.w;
refractionCoords = (refractionCoords + 1.0) / 2.0;
refractionCoords = vec2(refractionCoords.x, 1.0 - refractionCoords.y); // webgpu Y flip
// Sample the transmission texture
transmittedLight = getTransmissionSample(refractionCoords, roughness, ior, transmissionBackgroundTexture, defaultSampler);
transmittedLight = patchTransmissionSample(transmittedLight, isTransmissiveLinear, transmissiveToneMapping);
// Compute transmittance
transmittance = diffuseContribution * volumeAttenuation(length(transmissionRay), attenuationColor, attenuationDistance);
// Apply attenuation to transmitted light
let attenuatedColor = transmittance * transmittedLight.rgb;
// Compute Fresnel term using an environment BRDF
let F = specularColorBlended * fab.x + specularF90 * fab.y;
// Average the transmittance for a single factor
let transmittanceFactor = (transmittance.r + transmittance.g + transmittance.b) / 3.0;
// Combine results into the final color
return vec4(
(1.0 - F) * attenuatedColor,
1.0 - (1.0 - transmittedLight.a) * transmittanceFactor
);
}
fn getIBLVolumeRefractionWithDispersion(
normal: vec3f,
viewDirection: vec3f,
roughness: f32,
diffuseContribution: vec3f,
fab: vec2f,
specularColorBlended: vec3f,
specularF90: f32,
position: vec3f,
modelScale: vec3f,
viewMatrix: mat4x4f,
projMatrix: mat4x4f,
dispersion: f32,
ior: f32,
thickness: f32,
attenuationColor: vec3f,
attenuationDistance: f32,
isTransmissiveLinear: bool,
transmissiveToneMapping: u32,
transmissionBackgroundTexture: texture_2d<f32>,
defaultSampler: sampler,
) -> vec4f {
var transmittedLight: vec4f;
var transmissionRayLength: f32;
var transmittance: vec3f;
let halfSpread: f32 = (ior - 1.0) * 0.025 * dispersion;
let iors: vec3f = vec3(ior - halfSpread, ior, ior + halfSpread);
for(var i: i32 = 0; i < 3; i++) {
let transmissionRay: vec3f = getVolumeTransmissionRay(normal, viewDirection, thickness, iors[i], modelScale);
transmissionRayLength = length(transmissionRay);
let refractedRayExit = position + transmissionRay;
// Transform to NDC space
let ndcPos = projMatrix * viewMatrix * vec4(refractedRayExit, 1.0);
var refractionCoords = ndcPos.xy / ndcPos.w;
refractionCoords = (refractionCoords + 1.0) / 2.0;
refractionCoords = vec2(refractionCoords.x, 1.0 - refractionCoords.y); // webgpu Y flip
var transmissionSample: vec4f = getTransmissionSample(refractionCoords, roughness, iors[i], transmissionBackgroundTexture, defaultSampler);
transmissionSample = patchTransmissionSample(transmissionSample, isTransmissiveLinear, transmissiveToneMapping);
transmittedLight[i] = transmissionSample[i];
transmittedLight.a += transmissionSample.a;
// Compute transmittance
let diffuse: vec3f = diffuseContribution;
transmittance[i] = diffuse[i] * volumeAttenuation(length(transmissionRay), attenuationColor, attenuationDistance)[i];
}
transmittedLight.a /= 3.0;
// Apply attenuation to transmitted light
let attenuatedColor = transmittance * transmittedLight.rgb;
// Compute Fresnel term using an environment BRDF
let F = specularColorBlended * fab.x + specularF90 * fab.y;
// Average the transmittance for a single factor
let transmittanceFactor = (transmittance.r + transmittance.g + transmittance.b) / 3.0;
// Combine results into the final color
return vec4(
(1.0 - F) * attenuatedColor,
1.0 - (1.0 - transmittedLight.a) * transmittanceFactor
);
}
`;
//#endregion
export { getIBLTransmission };