@babylonjs/viewer
Version:
The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.
794 lines (786 loc) • 30.8 kB
JavaScript
import { F as F32, B as BU, S as SS, k as getOrCreateSampler, b1 as createRenderTarget, i as TU, b2 as drawList, as as getBilinearSampler, b3 as _vis, b4 as biasedMipLevelCount, aa as PBR2_HAS_REFRACTION, T as ThrowLiteError, aD as PBR_HAS_THICKNESS_MAP } from './index-By0tcgYN.esm.js';
import { recordMipmaps } from './generate-mipmaps-BL7R_dXN.esm.js';
function createImageProcessingTask(config, engine, scene) {
let state = null;
const task = {
name: config.name ?? "image-processing",
engine,
scene,
_passes: [],
record() {
disposeImageProcessingState(state);
state = createImageProcessingState(engine, config.source);
},
execute() {
if (!state) {
return 0;
}
const img = scene.imageProcessing;
const data = new F32([img.exposure, img.contrast, img.toneMappingEnabled === true ? 1 : 0, 0]);
engine._device.queue.writeBuffer(state.params, 0, data);
const pass = engine._currentEncoder.beginRenderPass({
colorAttachments: [
{
view: engine.scRT._colorView,
loadOp: "clear",
storeOp: "store",
clearValue: scene.clearColor
}
]
});
pass.setPipeline(state.pipeline);
pass.setBindGroup(0, state.bindGroup);
pass.draw(3);
pass.end();
return 1;
},
dispose() {
disposeImageProcessingState(state);
state = null;
this._passes.length = 0;
}
};
return task;
}
function createImageProcessingState(engine, source) {
const texture = resolveImageProcessingTexture(source);
if (!texture) {
throw new Error("Image processing source has no color texture");
}
const device = engine._device;
const sampleCount = texture.sampleCount ?? 1;
const multisampled = sampleCount > 1;
const params = device.createBuffer({ size: 16, usage: BU.UNIFORM | BU.COPY_DST });
const bgl = device.createBindGroupLayout({
entries: [
{ binding: 0, visibility: SS.FRAGMENT, buffer: { type: "uniform" } },
{ binding: 1, visibility: SS.FRAGMENT, texture: { sampleType: multisampled ? "unfilterable-float" : "float", multisampled } }
]
});
const common = `struct P{e:f32,c:f32,t:f32,p:f32}
@group(0)@binding(0)var<uniform> p:P;
@vertex fn vs(@builtin(vertex_index)i:u32)->@builtin(position) vec4f{var a=array<vec2f,3>(vec2f(-1,-3),vec2f(3,1),vec2f(-1,1));return vec4f(a[i],0,1);}
fn ip(r:vec4f)->vec4f{var c=r.rgb*p.e;
if(p.t>0.5){c=1.0-exp2(-1.590579*c);}
c=clamp(pow(max(c,vec3f(0)),vec3f(1/2.2)),vec3f(0),vec3f(1));
let h=c*c*(3.0-2.0*c);
if(p.c<1.0){c=mix(vec3f(0.5),c,p.c);}else{c=mix(c,h,p.c-1.0);}
return vec4f(max(c,vec3f(0)),r.a);}`;
const textureDecl = multisampled ? `@group(0)@binding(1)var s:texture_multisampled_2d<f32>;` : `@group(0)@binding(1)var s:texture_2d<f32>;`;
const fragment = multisampled ? `@fragment fn fs(@builtin(position) q:vec4f)->@location(0) vec4f{let d=textureDimensions(s);let px=clamp(vec2i(q.xy),vec2i(0),vec2i(d)-1);let n=textureNumSamples(s);var c=vec4f(0);for(var i=0u;i<n;i++){c+=ip(textureLoad(s,px,i));}return c/f32(n);}` : `@fragment fn fs(@builtin(position) q:vec4f)->@location(0) vec4f{let d=textureDimensions(s);return ip(textureLoad(s,clamp(vec2i(q.xy),vec2i(0),vec2i(d)-1),0));}`;
const shader = device.createShaderModule({ code: `${common}${textureDecl}${fragment}` });
const pipeline = device.createRenderPipeline({
layout: device.createPipelineLayout({ bindGroupLayouts: [bgl] }),
vertex: { module: shader, entryPoint: "vs" },
fragment: { module: shader, entryPoint: "fs", targets: [{ format: engine.format }] },
primitive: { topology: "triangle-list" }
});
const bindGroup = device.createBindGroup({
layout: bgl,
entries: [
{ binding: 0, resource: { buffer: params } },
{ binding: 1, resource: texture.createView() }
]
});
return { pipeline, bindGroup, params };
}
function resolveImageProcessingTexture(source) {
const resolved = typeof source === "function" ? source() : source;
if (!resolved) {
return null;
}
if ("_colorTexture" in resolved) {
return resolved._colorTexture;
}
return resolved.texture;
}
function disposeImageProcessingState(state) {
state?.params.destroy();
}
const _trilinearAnisotropicDesc = {
magFilter: "linear",
minFilter: "linear",
mipmapFilter: "linear",
addressModeU: "repeat",
addressModeV: "repeat",
addressModeW: "repeat",
maxAnisotropy: 4
};
function getTrilinearAnisotropicSampler(engine) {
return getOrCreateSampler(engine, _trilinearAnisotropicDesc);
}
let _depthGrab = null;
function _installDepthGrab(impl) {
_depthGrab = impl;
}
const BLIT_SHADER = `@group(0)@binding(0)var t:texture_2d<f32>;@group(0)@binding(1)var s:sampler;struct V{@builtin(position)p:vec4f,@location(0)u:vec2f};@vertex fn vs(@builtin(vertex_index)i:u32)->V{var p=array<vec2f,3>(vec2f(-1,-1),vec2f(3,-1),vec2f(-1,3));var u=array<vec2f,3>(vec2f(0,1),vec2f(2,1),vec2f(0,-1));return V(vec4f(p[i],0,1),u[i]);}@fragment fn fs(v:V)->@location(0)vec4f{return textureSample(t,s,v.u);}`;
const BLIT_MSAA_SHADER = `@group(0)@binding(0)var t:texture_multisampled_2d<f32>;struct V{@builtin(position)p:vec4f,@location(0)u:vec2f};@vertex fn vs(@builtin(vertex_index)i:u32)->V{var p=array<vec2f,3>(vec2f(-1,-1),vec2f(3,-1),vec2f(-1,3));var u=array<vec2f,3>(vec2f(0,1),vec2f(2,1),vec2f(0,-1));return V(vec4f(p[i],0,1),u[i]);}fn l(p:vec2i)->vec4f{let n=textureNumSamples(t);var c=vec4f(0);for(var i=0u;i<n;i++){c+=textureLoad(t,p,i);}return c/f32(n);}@fragment fn fs(v:V)->@location(0)vec4f{let d=vec2i(textureDimensions(t));let q=clamp(v.u*vec2f(d)-.5,vec2f(0),vec2f(d-vec2i(1)));let p=vec2i(floor(q));let f=fract(q);let p1=min(p+vec2i(1),d-vec2i(1));return mix(mix(l(p),l(vec2i(p1.x,p.y)),f.x),mix(l(vec2i(p.x,p1.y)),l(p1),f.x),f.y);}`;
const REFRACTION_LOD_BIAS = 4;
let blitPipelines = null;
let blitShader = null;
let blitMsaaShader = null;
let blitBgl = null;
let blitMsaaBgl = null;
let blitDevice = null;
function enableSceneTransmission(scene, engine) {
markPbrMaterialsLinear(scene);
enableSceneTransmissionTasks(scene, engine);
}
function _t(scene, engine) {
const states = scene.meshes.flatMap((mesh) => {
const mat = mesh.material;
return mat ? [[mat, mat._linearImageProcessing, mat._renderFeatures]] : [];
});
markPbrMaterialsLinear(scene);
return [
() => enableSceneTransmissionTasks(scene, engine),
() => {
for (const [mat, linear, features] of states) {
mat._linearImageProcessing = linear;
mat._renderFeatures = features;
}
}
];
}
function enableSceneTransmissionTasks(scene, engine) {
let lastRenderTask = null;
for (const task of scene._frameGraph._tasks) {
if ("_renderables" in task) {
const renderTask = task;
enableRenderTaskTransmission(renderTask, engine);
lastRenderTask = renderTask;
}
}
if (lastRenderTask && !scene._frameGraph._tasks.some((task) => task.name === "transmission-image-processing")) {
scene._frameGraph._tasks.push(createImageProcessingTask({ name: "transmission-image-processing", source: lastRenderTask._config.rt }, engine, scene));
}
}
function enableRenderTaskTransmission(task, engine, options) {
if (task._config.transmission?.grabDepth) {
const priorPreload = task._preload?.bind(task);
task._preload = async () => {
if (priorPreload) {
await priorPreload();
}
await import('./transmission-depth-grab-D7CMQ5g6.esm.js');
};
}
const grab = {
get texture() {
return task._targetSignature._transmissionTexture ?? null;
},
get depthTexture() {
return task._targetSignature._transmissionDepthTexture ?? null;
}
};
if (task._executeWithTransmission) {
return grab;
}
{
retargetRenderTaskToLinearOffscreen(task);
}
let state = null;
const record = task.record.bind(task);
const execute = task.execute?.bind(task);
const dispose = task.dispose?.bind(task);
task.record = () => {
disposeRenderTaskTransmission(state);
state = createRenderTaskTransmission(task, engine);
task._targetSignature._transmissionTexture = state.texture;
record();
configureTransmissionSource(state, task, engine);
};
if (execute) {
task.execute = () => executeRenderTaskLinear(task.scene, execute);
}
task.dispose = () => {
disposeRenderTaskTransmission(state);
state = null;
dispose?.();
};
task._executeWithTransmission = (sampleCount) => executePassWithTransmission(task, engine, state, sampleCount);
return grab;
}
function retargetRenderTaskToLinearOffscreen(task) {
const cfg = task._config;
const oldDesc = cfg.rt._descriptor;
const surface = task.scene.surface;
const sampleCount = surface.msaaSamples;
const ownsDepth = !cfg.depth;
const newRt = createRenderTarget({
lbl: "transmission-linear",
format: "rgba16float",
dFormat: ownsDepth ? oldDesc.dFormat ?? "depth24plus-stencil8" : void 0,
_depthClearValue: oldDesc._depthClearValue,
_depthCompare: oldDesc._depthCompare,
samples: sampleCount,
size: surface
});
cfg.rt = newRt;
cfg.rst = void 0;
const sig = task._targetSignature;
sig._colorFormat = "rgba16float";
sig._depthStencilFormat = cfg.depth?._descriptor.dFormat ?? newRt._descriptor.dFormat;
sig._depthCompare = newRt._descriptor._depthCompare;
sig._sampleCount = sampleCount;
task._opaqueBundles.length = 0;
task._lastVersion = -1;
}
function executeRenderTaskLinear(scene, execute) {
const imageProcessing = scene.imageProcessing;
const toneMappingEnabled = imageProcessing.toneMappingEnabled;
const clearColor = scene.clearColor;
const linearClearColor = inverseImageProcessedColor(clearColor, imageProcessing.exposure, imageProcessing.contrast, toneMappingEnabled === true);
imageProcessing.toneMappingEnabled = -1;
scene.clearColor = linearClearColor;
try {
return execute();
} finally {
scene.clearColor = clearColor;
imageProcessing.toneMappingEnabled = toneMappingEnabled;
}
}
function inverseImageProcessedColor(color, exposure, contrast, toneMapping) {
return {
r: inverseImageProcessedChannel(color.r, exposure, contrast, toneMapping),
g: inverseImageProcessedChannel(color.g, exposure, contrast, toneMapping),
b: inverseImageProcessedChannel(color.b, exposure, contrast, toneMapping),
a: color.a
};
}
function inverseImageProcessedChannel(value, exposure, contrast, toneMapping) {
let c = clamp01(value);
if (contrast < 1) {
c = contrast > 0 ? clamp01((c - 0.5 * (1 - contrast)) / contrast) : 0.5;
} else if (contrast > 1) {
const mixAmount = contrast - 1;
let lo = 0;
let hi = 1;
for (let i = 0; i < 16; i++) {
const mid = (lo + hi) * 0.5;
const high = mid * mid * (3 - 2 * mid);
const out = mid + (high - mid) * mixAmount;
if (out < c) {
lo = mid;
} else {
hi = mid;
}
}
c = (lo + hi) * 0.5;
}
c = c ** 2.2;
if (toneMapping) {
c = -Math.log2(Math.max(1 - c, 1e-6)) / 1.5905790328979492;
}
return exposure > 0 ? c / exposure : c;
}
function clamp01(v) {
return Math.min(Math.max(v, 0), 1);
}
function markPbrMaterialsLinear(scene) {
for (const mesh of scene.meshes) {
const mat = mesh.material;
if (mat) {
mat._linearImageProcessing = true;
mat._renderFeatures = void 0;
}
}
}
function createRenderTaskTransmission(task, engine) {
const rt = task._config.rt;
const width = 1024;
const height = 1024;
const format = "rgba16float";
const mipLevelCount = transmissionMipLevelCount(task._config.transmission, width, height);
const generateMipmaps = mipLevelCount > 1;
const texture = engine._device.createTexture({
label: task.name,
size: { width, height },
format,
mipLevelCount,
usage: TU.RENDER_ATTACHMENT | TU.TEXTURE_BINDING | TU.COPY_DST
});
const tex = {
texture,
view: texture.createView(),
sampler: getTrilinearAnisotropicSampler(engine),
width,
height,
invertY: false
};
return {
texture: tex,
_baseView: texture.createView({ baseMipLevel: 0, mipLevelCount: 1 }),
_sourceWidth: rt._width,
_sourceHeight: rt._height,
_sourceTexture: null,
_blit: null,
_depth: null,
_copyCount: normalizeCopyCount(task._config.transmission),
_generateMipmaps: generateMipmaps,
_copies: 0
};
}
function configureTransmissionSource(state, task, engine) {
const rt = task._config.rt;
state._sourceWidth = rt._width;
state._sourceHeight = rt._height;
state._sourceTexture = rt._colorTexture;
const sampleCount = task._targetSignature._sampleCount;
const sig = task._targetSignature;
sig._transmissionDepthTexture = null;
const depthSource = rt._depthTexture;
if (task._config.transmission?.grabDepth && _depthGrab && depthSource && rt._width > 0 && rt._height > 0) {
state._depth = _depthGrab.create(engine, depthSource, rt._width, rt._height, sampleCount > 1);
sig._transmissionDepthTexture = state._depth.texture;
}
if (!state._sourceTexture) {
return;
}
state._blit = shouldBlitTransmission(state, sampleCount) ? createTransmissionBlit(state, engine, state._sourceTexture, sampleCount > 1) : null;
}
function disposeRenderTaskTransmission(state) {
state?.texture.texture.destroy();
state?._depth?.texture.texture.destroy();
}
function executePassWithTransmission(task, engine, state, sampleCount) {
state._copies = 0;
const transparent = task._transparentBindings;
const resolveView = sampleCount > 1 ? task._config.rst?._colorView ?? null : null;
let pass = beginTaskPass(task, resolveView, sampleCount, false);
let draws = drawBaseTask(task, pass);
let lastPipeline = null;
let overlay = null;
for (let i = 0; i < transparent.length; i++) {
const binding = transparent[i];
if (binding.renderable.mesh?.renderOnTop === true) {
(overlay ??= []).push(binding);
continue;
}
const transmissive = binding.renderable._transmissive === true;
if (transmissive && canUpdateTransmission(state)) {
pass.end();
updateTransmissionTexture(state, engine);
pass = beginTaskPass(task, resolveView, sampleCount, true);
setPassState(task, pass);
lastPipeline = null;
}
const mesh = binding.renderable.mesh;
if (mesh && mesh.visible === false) {
continue;
}
if (binding.pipeline !== lastPipeline) {
pass.setPipeline(binding.pipeline);
lastPipeline = binding.pipeline;
}
draws += binding.draw(pass, engine);
}
if (overlay) {
draws += drawList(pass, overlay, engine);
}
pass.end();
return draws;
}
function updateTransmissionTexture(state, engine) {
if (!state._sourceTexture) {
throw new Error("No transmission source");
}
if (state._blit) {
blitToTransmission(state, engine);
} else {
engine._currentEncoder.copyTextureToTexture(
{ texture: state._sourceTexture },
{ texture: state.texture.texture },
{ width: state.texture.width, height: state.texture.height }
);
}
if (state._generateMipmaps) {
recordMipmaps(engine, state.texture.texture, engine._currentEncoder);
}
if (state._depth) {
_depthGrab?.record(engine, state._depth);
}
state._copies++;
}
function getBlitPipeline(engine, format, multisampled) {
const device = engine._device;
if (device !== blitDevice) {
blitPipelines?.clear();
blitPipelines = null;
blitShader = null;
blitMsaaShader = null;
blitBgl = null;
blitMsaaBgl = null;
blitDevice = device;
}
if (multisampled) {
blitMsaaShader ??= device.createShaderModule({ code: BLIT_MSAA_SHADER });
blitMsaaBgl ??= device.createBindGroupLayout({
entries: [{ binding: 0, visibility: SS.FRAGMENT, texture: { sampleType: "unfilterable-float", multisampled: true } }]
});
} else {
blitShader ??= device.createShaderModule({ code: BLIT_SHADER });
blitBgl ??= device.createBindGroupLayout({
entries: [
{ binding: 0, visibility: SS.FRAGMENT, texture: { sampleType: "float" } },
{ binding: 1, visibility: SS.FRAGMENT, sampler: {} }
]
});
}
blitPipelines ??= /* @__PURE__ */ new Map();
const key = `${format}:${multisampled ? "msaa" : ""}`;
let pipeline = blitPipelines.get(key);
if (!pipeline) {
const bgl = multisampled ? blitMsaaBgl : blitBgl;
pipeline = device.createRenderPipeline({
label: "transmission-copy",
layout: device.createPipelineLayout({ bindGroupLayouts: [bgl] }),
vertex: { module: multisampled ? blitMsaaShader : blitShader, entryPoint: "vs" },
fragment: { module: multisampled ? blitMsaaShader : blitShader, entryPoint: "fs", targets: [{ format }] },
primitive: { topology: "triangle-list" }
});
blitPipelines.set(key, pipeline);
}
return pipeline;
}
function shouldBlitTransmission(state, sampleCount) {
return sampleCount > 1 || state._sourceWidth !== state.texture.width || state._sourceHeight !== state.texture.height;
}
function createTransmissionBlit(state, engine, source, multisampled) {
const device = engine._device;
const pipeline = getBlitPipeline(engine, state.texture.texture.format, multisampled);
const bindGroup = device.createBindGroup({
layout: multisampled ? blitMsaaBgl : blitBgl,
entries: multisampled ? [{ binding: 0, resource: source.createView() }] : [
{ binding: 0, resource: source.createView() },
{ binding: 1, resource: getBilinearSampler(engine) }
]
});
return { _pipeline: pipeline, _bindGroup: bindGroup };
}
function blitToTransmission(state, engine) {
const blit = state._blit;
const pass = engine._currentEncoder.beginRenderPass({
colorAttachments: [{ view: state._baseView, loadOp: "clear", storeOp: "store", clearValue: { r: 0, g: 0, b: 0, a: 0 } }]
});
pass.setPipeline(blit._pipeline);
pass.setBindGroup(0, blit._bindGroup);
pass.draw(3);
pass.end();
}
function canUpdateTransmission(state) {
return state._copyCount === 0 || state._copies < state._copyCount;
}
function beginTaskPass(task, resolveTarget, sampleCount, load) {
const att = task._colorAttachment;
const depthLoadOp = load || !task._config.clr ? "load" : "clear";
if (load) {
att.loadOp = "load";
}
const depthAttachment = task._renderPassDescriptor.depthStencilAttachment;
if (depthAttachment) {
depthAttachment.depthLoadOp = depthLoadOp;
if (depthAttachment.stencilLoadOp) {
depthAttachment.stencilLoadOp = depthLoadOp;
}
}
if (sampleCount > 1) {
att.resolveTarget = resolveTarget ?? void 0;
} else {
att.resolveTarget = void 0;
}
return task.engine._currentEncoder.beginRenderPass(task._renderPassDescriptor);
}
function setPassState(task, pass) {
const cfg = task._config;
const rt = cfg.rt;
const scene = task.scene;
const camera = cfg.cam ?? scene.camera;
const v = camera?.viewport;
if (v) {
const rw = rt._width;
const rh = rt._height;
const x = Math.floor(v.x * rw);
const y = Math.floor((1 - v.y - v.height) * rh);
const w = Math.ceil((v.x + v.width) * rw) - x;
const h = Math.ceil((1 - v.y) * rh) - y;
pass.setViewport(x, y, w, h, 0, 1);
pass.setScissorRect(x, y, w, h);
}
pass.setBindGroup(0, task._sceneBG);
}
function drawBaseTask(task, pass) {
const eng = task.engine;
const rt = task._config.rt;
const scene = task.scene;
const opaqueBindings = task._opaqueBindings;
const opaqueBundles = task._opaqueBundles;
setPassState(task, pass);
if (task._lastVersion !== scene._renderableVersion || task._lastVis !== _vis || opaqueBundles.length === 0) {
const desc = rt._descriptor;
const be = eng._device.createRenderBundleEncoder({
colorFormats: desc.format ? [desc.format] : [],
depthStencilFormat: desc.dFormat,
sampleCount: desc.samples ?? 1
});
be.setBindGroup(0, task._sceneBG);
drawList(be, opaqueBindings, eng);
opaqueBundles[0] = be.finish();
task._lastVersion = scene._renderableVersion;
task._lastVis = _vis;
}
let draws = opaqueBindings.length;
pass.executeBundles(opaqueBundles);
pass.setBindGroup(0, task._sceneBG);
draws += drawList(pass, task._directBindings, eng);
return draws;
}
function normalizeCopyCount(cfg) {
const count = cfg?.copyCount ?? 1;
return count === Infinity ? 0 : Math.max(0, count | 0);
}
function transmissionMipLevelCount(cfg, width, height) {
if (cfg?.generateMipmaps === false) {
return 1;
}
const full = Math.floor(Math.log2(Math.max(width, height))) + 1;
const defaultCount = biasedMipLevelCount(width, height, REFRACTION_LOD_BIAS);
const requested = cfg?.mipLevelCount;
if (requested === void 0) {
return Math.min(full, defaultCount);
}
return Math.min(full, Math.max(1, requested | 0));
}
const PBR2_HAS_VOLUME = 1 << 5;
const PBR2_HAS_REFRACTION_MAP = 1 << 6;
const PBR2_HAS_THICKNESS_GLTF_CHANNEL = 1 << 7;
const PBR2_LINEAR_IMAGE_PROCESSING = 1 << 14;
const PBR2_HAS_DISPERSION = 1 << 20;
const LINEAR_IMAGE_PROCESSING_SLOTS = { NI: `if(scene.vImageInfos.w>=0.0){`, BC: `}` };
function makeRefractionMod(hasVolume, hasMap, hasThicknessMap, useGltfThicknessChannel, hasDispersion, dispersionSampleWgsl) {
const thicknessScaleLine = hasVolume || hasThicknessMap ? `let ts=max(length(mesh.world[0].xyz),max(length(mesh.world[1].xyz),length(mesh.world[2].xyz)));` : ``;
const mapUvDecl = hasMap ? `let refractionMapUV=vec2<f32>(dot(material.refractionMapUVm.xy,input.uv),dot(material.refractionMapUVm.zw,input.uv))+material.refractionMapUVt.xy;
` : ``;
const thickUvDecl = hasThicknessMap ? `let thicknessUV=vec2<f32>(dot(material.thicknessUVm.xy,input.uv),dot(material.thicknessUVm.zw,input.uv))+material.thicknessUVt.xy;
` : ``;
const thicknessLine = hasThicknessMap ? `let ths=textureSample(thicknessTexture_,thicknessSampler_,thicknessUV).${useGltfThicknessChannel ? "g" : "r"};
let th=(material.thicknessParams.x+ths*material.thicknessParams.y)*ts;` : hasVolume ? `let th=material.refractionParams.z*ts;` : `let th=material.refractionParams.z;`;
const textureLine = hasMap ? `let ri=material.refractionParams.x*textureSample(refractionMapTexture,refractionMapSampler,refractionMapUV).r;` : `let ri=material.refractionParams.x;`;
const absorptionLine = hasVolume ? `let ab=exp(material.volumeParams.rgb*th);` : ``;
const refractionLine = hasVolume ? `let fr=er*surfaceAlbedo*(ri*ab)*(vec3<f32>(1.0)-colorSpecularEnvReflectance.rgb);` : `let fr=er*surfaceAlbedo*ri*(vec3<f32>(1.0)-colorSpecularEnvReflectance.rgb);`;
const sampleLines = hasDispersion && dispersionSampleWgsl ? dispersionSampleWgsl : `let rd=refract(-V,N,material.refractionParams.y);
let cp=scene.viewProjection*vec4<f32>(input.worldPos+rd*th,1.0);
let ruv=(cp.xy/cp.w)*vec2<f32>(0.5,-0.5)+vec2<f32>(0.5,0.5);
let er=textureSampleLevel(refractionTexture,refractionSampler_,ruv,lv).rgb*material.environmentIntensity;`;
return `{
${thicknessScaleLine}
${mapUvDecl}${thickUvDecl}${textureLine}
${thicknessLine}
let ro=1.0-ri;
let ra=mix(alphaG,0.0,clamp(material.refractionParams.w*3.0-2.0,0.0,1.0));
let lv=clamp(log2(f32(textureDimensions(refractionTexture).x)*ra)-4.0,0.0,f32(textureNumLevels(refractionTexture)-1));
${sampleLines}
${absorptionLine}
${refractionLine}
color=finalIrradiance*ro*ro+finalRadianceScaled+finalSpecularScaled+directDiffuse*ro*ro+fr+emissive;
}`;
}
function createRefractionRttFragment(hasVolume, hasMap, hasThicknessMap, useGltfThicknessChannel, linearImageProcessing, hasDispersion, dispersionSampleWgsl) {
const uboFields = [{ _name: "refractionParams", _type: "vec4<f32>" }];
if (hasVolume) {
uboFields.push({ _name: "volumeParams", _type: "vec4<f32>" });
}
if (hasThicknessMap) {
uboFields.push({ _name: "thicknessParams", _type: "vec4<f32>" });
}
if (hasMap) {
uboFields.push({ _name: "refractionMapUVm", _type: "vec4<f32>" }, { _name: "refractionMapUVt", _type: "vec4<f32>" });
}
if (hasThicknessMap) {
uboFields.push({ _name: "thicknessUVm", _type: "vec4<f32>" }, { _name: "thicknessUVt", _type: "vec4<f32>" });
}
const bindings = [
{ _name: "refractionTexture", _type: { _kind: "texture", _textureType: "texture_2d<f32>" }, _visibility: 2 },
{ _name: "refractionSampler_", _type: { _kind: "sampler", _samplerType: "sampler" }, _visibility: 2 }
];
if (hasMap) {
bindings.push(
{ _name: "refractionMapTexture", _type: { _kind: "texture", _textureType: "texture_2d<f32>" }, _visibility: 2 },
{ _name: "refractionMapSampler", _type: { _kind: "sampler", _samplerType: "sampler" }, _visibility: 2 }
);
}
if (hasThicknessMap) {
bindings.push(
{ _name: "thicknessTexture_", _type: { _kind: "texture", _textureType: "texture_2d<f32>" }, _visibility: 2 },
{ _name: "thicknessSampler_", _type: { _kind: "sampler", _samplerType: "sampler" }, _visibility: 2 }
);
}
return {
_id: "refraction",
_dependencies: ["ibl"],
_uboFields: uboFields,
_bindings: bindings,
_fragmentSlots: linearImageProcessing ? { AI: makeRefractionMod(hasVolume, hasMap, hasThicknessMap, useGltfThicknessChannel, hasDispersion, dispersionSampleWgsl), ...LINEAR_IMAGE_PROCESSING_SLOTS } : { AI: makeRefractionMod(hasVolume, hasMap, hasThicknessMap, useGltfThicknessChannel, hasDispersion, dispersionSampleWgsl) }
};
}
function writeRefractionUvTransform(data, offsets, name, tex) {
const mOff = offsets.get(`${name}m`);
const tOff = offsets.get(`${name}t`);
if (mOff === void 0 || tOff === void 0) {
return;
}
const mi = mOff / 4;
const ti = tOff / 4;
const sx = tex?.uScale ?? 1;
const sy = tex?.vScale ?? 1;
const ang = tex?.uAng ?? 0;
if (ang === 0) {
data[mi] = sx;
data[mi + 1] = 0;
data[mi + 2] = 0;
data[mi + 3] = sy;
} else {
const c = Math.cos(ang);
const s = Math.sin(ang);
data[mi] = c * sx;
data[mi + 1] = s * sy;
data[mi + 2] = -s * sx;
data[mi + 3] = c * sy;
}
data[ti] = tex?.uOffset ?? 0;
data[ti + 1] = tex?.vOffset ?? 0;
data[ti + 2] = 0;
data[ti + 3] = 0;
}
function writeRefractionUBO(data, mat, offsets) {
const ss = mat.subsurface;
const refr = ss?.refraction;
if (!refr) {
return;
}
const off = offsets.get("refractionParams");
if (off === void 0) {
return;
}
const o = off / 4;
data[o] = refr.intensity ?? 0;
const ior = refr.indexOfRefraction ?? 1.5;
const thick = ss.thickness;
data[o + 1] = 1 / (refr.useThicknessAsDepth && thick?.max ? ior : 1);
data[o + 2] = refr.useThicknessAsDepth ? thick?.max ?? 0 : 0;
data[o + 3] = 1 / ior;
const vOff = offsets.get("volumeParams");
if (vOff !== void 0) {
const vo = vOff / 4;
const tint = ss.tint?.color ?? [1, 1, 1];
const dist = Math.max(ss.tint?.atDistance ?? 1, 1e-4);
data[vo] = Math.log(Math.max(tint[0], 1e-6)) / dist;
data[vo + 1] = Math.log(Math.max(tint[1], 1e-6)) / dist;
data[vo + 2] = Math.log(Math.max(tint[2], 1e-6)) / dist;
data[vo + 3] = refr.dispersion ?? 0;
}
const tOff = offsets.get("thicknessParams");
if (tOff !== void 0) {
const to = tOff / 4;
const min = thick?.min ?? 0;
const max = thick?.max ?? 1;
data[to] = min;
data[to + 1] = max - min;
}
writeRefractionUvTransform(data, offsets, "refractionMapUV", refr.texture);
writeRefractionUvTransform(data, offsets, "thicknessUV", thick?.texture);
}
function makeRefractionRttExt(dispersionSampleWgsl) {
return {
id: "refraction",
phase: "fragment",
detect(mat) {
const m = mat;
const ss = m.subsurface;
const refr = ss?.refraction;
const linearImageProcessing = m._linearImageProcessing ? PBR2_LINEAR_IMAGE_PROCESSING : 0;
const intensity = m.transmissive ? refr?.intensity ?? 0 : 0;
if (intensity <= 0) {
return { f: 0, f2: linearImageProcessing };
}
let f = 0;
let f2 = linearImageProcessing | PBR2_HAS_REFRACTION;
if (refr?.texture) {
f2 |= PBR2_HAS_REFRACTION_MAP;
}
if (ss?.thickness?.texture) {
f |= PBR_HAS_THICKNESS_MAP;
}
if (ss?.thickness?.useGlTFChannel) {
f2 |= PBR2_HAS_THICKNESS_GLTF_CHANNEL;
}
if (ss?.tint?.atDistance !== void 0) {
f2 |= PBR2_HAS_VOLUME;
if (refr?.dispersion) {
f2 |= PBR2_HAS_DISPERSION;
}
}
return { f, f2 };
},
frag(ctx) {
const linearImageProcessing = (ctx._features2 & PBR2_LINEAR_IMAGE_PROCESSING) !== 0;
if (!(ctx._features2 & PBR2_HAS_REFRACTION)) {
return linearImageProcessing ? { _id: "linear", _fragmentSlots: LINEAR_IMAGE_PROCESSING_SLOTS } : null;
}
return createRefractionRttFragment(
(ctx._features2 & PBR2_HAS_VOLUME) !== 0,
(ctx._features2 & PBR2_HAS_REFRACTION_MAP) !== 0,
(ctx._features & PBR_HAS_THICKNESS_MAP) !== 0,
(ctx._features2 & PBR2_HAS_THICKNESS_GLTF_CHANNEL) !== 0,
linearImageProcessing,
(ctx._features2 & PBR2_HAS_DISPERSION) !== 0,
dispersionSampleWgsl
);
},
writeUbo(data, mat, offsets) {
writeRefractionUBO(data, mat, offsets);
},
bind(ctx, entries, b) {
if (!(ctx._features2 & PBR2_HAS_REFRACTION)) {
return b;
}
const texture = ctx._refractionTexture;
if (!texture) {
ThrowLiteError(201);
}
entries.push({ binding: b++, resource: texture.view });
entries.push({ binding: b++, resource: texture.sampler });
if ((ctx._features2 & PBR2_HAS_REFRACTION_MAP) !== 0) {
const map = ctx._material.subsurface?.refraction?.texture;
entries.push({ binding: b++, resource: map.view });
entries.push({ binding: b++, resource: getTrilinearAnisotropicSampler(ctx._engine) });
}
if ((ctx._features & PBR_HAS_THICKNESS_MAP) !== 0) {
const thickness = ctx._material.subsurface?.thickness?.texture;
entries.push({ binding: b++, resource: thickness.view });
entries.push({ binding: b++, resource: thickness.sampler });
}
return b;
},
textures(mat, out) {
const tex = mat.subsurface?.refraction?.texture;
if (tex) {
out.push(tex);
}
const thickness = mat.subsurface?.thickness?.texture;
if (thickness) {
out.push(thickness);
}
}
};
}
function registerPbrTransmission(scene, engine, register, dispersionSampleWgsl) {
scene._p?.(_t(scene, engine)) || enableSceneTransmission(scene, engine);
register(makeRefractionRttExt(dispersionSampleWgsl));
}
var pbrTransmissionExt = /*#__PURE__*/Object.freeze({
__proto__: null,
registerPbrTransmission: registerPbrTransmission
});
export { _installDepthGrab as _, pbrTransmissionExt as p };
//# sourceMappingURL=pbr-transmission-ext-BTqVnS2g.esm.js.map