@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.
311 lines (308 loc) • 11.9 kB
JavaScript
import { az as PBR_HAS_CLEARCOAT, Z as PBR_HAS_METALLIC_REFLECTANCE_MAP, $ as PBR_HAS_REFLECTANCE_MAP } from './index-By0tcgYN.esm.js';
const PBR2_CC_INT_MAP = 1 << 0;
const PBR2_CC_ROUGH_MAP = 1 << 1;
const PBR2_CC_NORMAL_MAP = 1 << 2;
const PBR2_CC_F0_REMAP_OFF = 1 << 3;
const PBR2_CC_UV_TX = 1 << 25;
const STAGE_FRAGMENT = 2;
const CC_HELPERS = `
fn visibility_Kelemen(VdotH_kl: f32) -> f32 {
return 0.25 / (VdotH_kl * VdotH_kl + 0.0000001);
}
fn getR0RemappedForClearCoat(f0_rc: vec3<f32>, ccA: f32, ccB: f32) -> vec3<f32> {
let sf0 = sqrt(f0_rc);
let num = ccA + ccB * sf0;
let den = ccB + ccA * sf0;
return saturate((num / den) * (num / den));
}
fn ccSchlick(f0: f32, cosTheta: f32) -> f32 {
let t = 1.0 - cosTheta;
let t2 = t * t;
return f0 + (1.0 - f0) * (t2 * t2 * t);
}
`;
const ccUvExpr = (name) => `(vec2<f32>(dot(material.${name}m.xy, input.uv), dot(material.${name}m.zw, input.uv)) + material.${name}t.xy)`;
const CC_INT_TEX = (uv) => `material.ccParams.x * textureSample(ccIntensityTexture, ccIntensitySampler_, ${uv}).r`;
const CC_INT_PLAIN = `material.ccParams.x`;
const CC_ROUGH_TEX = (uv) => `clamp(material.ccParams.y * textureSample(ccRoughnessTexture, ccRoughnessSampler_, ${uv}).g, 0.0, 1.0)`;
const CC_ROUGH_PLAIN = `material.ccParams.y`;
const CC_NORMAL_COMPUTE = (uv) => `
let cc_dp1 = dpdx(input.worldPos);
let cc_dp2 = dpdy(input.worldPos);
let cc_duv1 = dpdx(input.uv);
let cc_duv2 = dpdy(input.uv);
let cc_dp2perp = cross(cc_dp2, N_geom);
let cc_dp1perp = cross(N_geom, cc_dp1);
let cc_tFrame = cc_dp2perp * cc_duv1.x + cc_dp1perp * cc_duv2.x;
let cc_bFrame = -(cc_dp2perp * cc_duv1.y + cc_dp1perp * cc_duv2.y);
let cc_det = max(dot(cc_tFrame, cc_tFrame), dot(cc_bFrame, cc_bFrame));
let cc_invmax = select(inverseSqrt(cc_det), 0.0, cc_det == 0.0);
let cc_frame = mat3x3<f32>(cc_tFrame * cc_invmax, cc_bFrame * cc_invmax, N_geom);
let ccNormSampleRaw = textureSample(ccNormalTexture, ccNormalSampler_, ${uv}).rgb * 2.0 - 1.0;
let ccNormScale = material.ccParams.z;
var ccN = normalize(cc_frame * normalize(ccNormSampleRaw * vec3<f32>(ccNormScale, ccNormScale, 1.0)));
`;
function makeF0Remap(intensityExpr) {
return `
{
let ccInt_r = ${intensityExpr};
let remappedF0 = getR0RemappedForClearCoat(colorF0, material.ccRefractionParams.z, material.ccRefractionParams.w);
colorF0 = mix(colorF0, remappedF0, ccInt_r);
}
`;
}
function makeDirectMod(intensityExpr, roughnessExpr, hasNormalMap) {
const N = hasNormalMap ? "ccN" : "N_geom";
return `
var ccDirectAttenuation = 1.0;
var ccDirectSpecularTerm = vec3<f32>(0.0);
{
let ccInt_dl = ${intensityExpr};
let ccRough_dl = ${roughnessExpr};
let ccF0_dl = material.ccRefractionParams.x;
let ccAlphaG_dl = ccRough_dl * ccRough_dl + 0.0005;
let ccNdotL_dl = saturate(dot(${N}, L));
let ccH_dl = normalize(V + L);
let ccNdotH_dl = clamp(dot(${N}, ccH_dl), 0.0000001, 1.0);
let ccVdotH_dl = saturate(dot(V, ccH_dl));
let ccD_dl = distributionGGX(ccNdotH_dl, ccAlphaG_dl);
let ccVis_dl = visibility_Kelemen(ccVdotH_dl);
let ccFresnel_dl = ccSchlick(ccF0_dl, ccVdotH_dl);
let ccTerm = ccFresnel_dl * ccD_dl * ccVis_dl * ccNdotL_dl;
ccDirectSpecularTerm = vec3<f32>(ccTerm) * lightColor * lightAtten * material.directIntensity * ccInt_dl;
ccDirectAttenuation = 1.0 - ccFresnel_dl * ccInt_dl;
}
`;
}
function makeIblMod(intensityExpr, roughnessExpr, hasNormalMap, hasSpecularAA, hasBaseNormalMap) {
const N = hasNormalMap ? "ccN" : "N_geom";
const alphaG = hasSpecularAA ? `let ccAlphaG_ibl_base = ccRough_ibl * ccRough_ibl + 0.0005;
let cc_nDfdx_AA = dpdx(${N});
let cc_nDfdy_AA = dpdy(${N});
let cc_slopeSquare_AA = max(dot(cc_nDfdx_AA, cc_nDfdx_AA), dot(cc_nDfdy_AA, cc_nDfdy_AA));
let ccAlphaG_ibl = ccAlphaG_ibl_base + sqrt(cc_slopeSquare_AA) * 0.75;` : `let ccAlphaG_ibl = ccRough_ibl * ccRough_ibl + 0.0005;`;
const ehoLine = hasBaseNormalMap ? `let ccEho_ibl = environmentHorizonOcclusion(-V, ${N}, N_geom);` : `let ccEho_ibl = 1.0;`;
return `
{
let ccInt_ibl = ${intensityExpr};
let ccRough_ibl = ${roughnessExpr};
let ccF0_ibl = material.ccRefractionParams.x;
let ccR_raw = reflect(-V, ${N});
let ccR_ibl = rotateY(ccR_raw, scene.envRotationY);
let ccNdotV_ibl = abs(dot(${N}, V)) + 0.0000001;
${alphaG}
var ccSpecLod_ibl = log2(cubemapDim * ccAlphaG_ibl) * scene.vImageInfos.z;
let ccEnvRadiance_ibl = textureSampleLevel(iblTexture, iblSampler, ccR_ibl, clamp(ccSpecLod_ibl, 0.0, maxLod)).rgb * material.environmentIntensity;
let ccBrdf_ibl = textureSample(brdfLUT, brdfSampler_, vec2<f32>(ccNdotV_ibl, ccRough_ibl)).rgb;
${ehoLine}
let ccSpecEnvRefl = (vec3<f32>(ccF0_ibl) * ccBrdf_ibl.y + (vec3<f32>(1.0) - vec3<f32>(ccF0_ibl)) * ccBrdf_ibl.x) * ccInt_ibl * ccEho_ibl;
let ccFresnelIBL = ccSchlick(ccF0_ibl, ccNdotV_ibl);
let ccConservation_ibl = 1.0 - ccFresnelIBL * ccInt_ibl;
let ccFinalRadiance_ibl = ccEnvRadiance_ibl * ccSpecEnvRefl;
color = finalIrradiance * ccConservation_ibl
+ finalRadianceScaled * ccConservation_ibl
+ finalSpecularScaled * ccDirectAttenuation
+ directDiffuse * ccDirectAttenuation
+ ccDirectSpecularTerm
+ ccFinalRadiance_ibl
+ emissive;
}
`;
}
function makeNonIblMod(intensityExpr) {
return `
{
let ccF0_noIbl = material.ccRefractionParams.x;
let ccInt_noIbl = ${intensityExpr};
let ccFresnelNoIbl = ccSchlick(ccF0_noIbl, NdotV);
let ccCons_noIbl = 1.0 - ccFresnelNoIbl * ccInt_noIbl;
color = (color - emissive) * ccCons_noIbl + emissive + ccDirectSpecularTerm;
}
`;
}
function createClearcoatFragment(features, features2, hasIbl, hasBaseNormalMap, hasSpecularAA) {
if ((features & PBR_HAS_CLEARCOAT) === 0) {
return null;
}
const hasReflectance = (features & (PBR_HAS_METALLIC_REFLECTANCE_MAP | PBR_HAS_REFLECTANCE_MAP)) !== 0;
const hasIntensityMap = (features2 & PBR2_CC_INT_MAP) !== 0;
const hasRoughnessMap = (features2 & PBR2_CC_ROUGH_MAP) !== 0;
const hasNormalMap = (features2 & PBR2_CC_NORMAL_MAP) !== 0;
const disableF0Remap = (features2 & PBR2_CC_F0_REMAP_OFF) !== 0;
const hasUvTx = (features2 & PBR2_CC_UV_TX) !== 0;
const intUv = hasUvTx ? ccUvExpr("ccIntUV") : "input.uv";
const roughUv = hasUvTx ? ccUvExpr("ccRoughUV") : "input.uv";
const normUv = hasUvTx ? ccUvExpr("ccNormUV") : "input.uv";
const intensityExpr = hasIntensityMap ? CC_INT_TEX(intUv) : CC_INT_PLAIN;
const roughnessExpr = hasRoughnessMap ? CC_ROUGH_TEX(roughUv) : CC_ROUGH_PLAIN;
const slots = {
MF: disableF0Remap ? "" : makeF0Remap(intensityExpr),
AD: makeDirectMod(intensityExpr, roughnessExpr, hasNormalMap),
BL: `var ccDirectAttenuation = 1.0;
var ccDirectSpecularTerm = vec3<f32>(0.0);`
};
if (hasNormalMap) {
slots.AC = CC_NORMAL_COMPUTE(normUv);
}
if (hasIbl) {
slots.AI = makeIblMod(intensityExpr, roughnessExpr, hasNormalMap, hasSpecularAA, hasBaseNormalMap);
} else {
slots.NI = makeNonIblMod(intensityExpr);
}
const deps = [];
if (hasIbl) {
deps.push("ibl");
}
if (hasReflectance) {
deps.push("reflectance");
}
const suffix = (hasIntensityMap ? "I" : "") + (hasRoughnessMap ? "R" : "") + (hasNormalMap ? "N" : "") + (disableF0Remap ? "X" : "") + (hasSpecularAA ? "A" : "") + (hasBaseNormalMap ? "B" : "") + (hasUvTx ? "U" : "");
const bindings = [];
if (hasIntensityMap) {
bindings.push(
{ _name: "ccIntensityTexture", _type: { _kind: "texture", _textureType: "texture_2d<f32>" }, _visibility: STAGE_FRAGMENT },
{ _name: "ccIntensitySampler_", _type: { _kind: "sampler", _samplerType: "sampler" }, _visibility: STAGE_FRAGMENT }
);
}
if (hasRoughnessMap) {
bindings.push(
{ _name: "ccRoughnessTexture", _type: { _kind: "texture", _textureType: "texture_2d<f32>" }, _visibility: STAGE_FRAGMENT },
{ _name: "ccRoughnessSampler_", _type: { _kind: "sampler", _samplerType: "sampler" }, _visibility: STAGE_FRAGMENT }
);
}
if (hasNormalMap) {
bindings.push(
{ _name: "ccNormalTexture", _type: { _kind: "texture", _textureType: "texture_2d<f32>" }, _visibility: STAGE_FRAGMENT },
{ _name: "ccNormalSampler_", _type: { _kind: "sampler", _samplerType: "sampler" }, _visibility: STAGE_FRAGMENT }
);
}
const uboFields = [
{ _name: "ccParams", _type: "vec4<f32>" },
{ _name: "ccRefractionParams", _type: "vec4<f32>" }
];
if (hasUvTx) {
if (hasIntensityMap) {
uboFields.push({ _name: "ccIntUVm", _type: "vec4<f32>" }, { _name: "ccIntUVt", _type: "vec4<f32>" });
}
if (hasRoughnessMap) {
uboFields.push({ _name: "ccRoughUVm", _type: "vec4<f32>" }, { _name: "ccRoughUVt", _type: "vec4<f32>" });
}
if (hasNormalMap) {
uboFields.push({ _name: "ccNormUVm", _type: "vec4<f32>" }, { _name: "ccNormUVt", _type: "vec4<f32>" });
}
}
return {
_id: suffix ? `clearcoat-${suffix}` : "clearcoat",
_dependencies: deps.length > 0 ? deps : void 0,
_uboFields: uboFields,
_bindings: bindings,
_helperFunctions: CC_HELPERS,
_fragmentSlots: slots
};
}
function writeClearcoatUBO(data, material, offsets) {
const cc = material.clearCoat;
if (!cc?.isEnabled || !offsets.has("ccParams")) {
return;
}
const off = offsets.get("ccParams") / 4;
const ior = cc.indexOfRefraction ?? 1.5;
const a = 1 - ior;
const b = 1 + ior;
data[off] = cc.intensity ?? 1;
data[off + 1] = cc.roughness ?? 0;
data[off + 2] = cc.bumpTextureScale ?? 1;
data[off + 4] = Math.pow(-a / b, 2);
data[off + 5] = 1 / ior;
data[off + 6] = a;
data[off + 7] = b;
writeCcUvTransform(data, offsets, "ccIntUV", cc.texture);
writeCcUvTransform(data, offsets, "ccRoughUV", cc.roughnessTexture);
writeCcUvTransform(data, offsets, "ccNormUV", cc.bumpTexture);
}
function writeCcUvTransform(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 sx = tex?.uScale ?? 1;
const sy = tex?.vScale ?? 1;
const ang = tex?.uAng ?? 0;
const mi = mOff / 4;
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;
}
const ti = tOff / 4;
data[ti] = tex?.uOffset ?? 0;
data[ti + 1] = tex?.vOffset ?? 0;
}
const CC_TEX = [
[PBR2_CC_INT_MAP, "texture"],
[PBR2_CC_ROUGH_MAP, "roughnessTexture"],
[PBR2_CC_NORMAL_MAP, "bumpTexture"]
];
const pbrExt = {
id: "clearcoat",
phase: "base-tex",
detect(mat) {
const cc = mat.clearCoat;
if (!cc?.isEnabled) {
return { f: 0, f2: 0 };
}
let f2 = 0;
for (const [flag, key] of CC_TEX) {
if (cc[key]) {
f2 |= flag;
}
}
const ccHasTx = (t) => !!t?._hasTx;
if (ccHasTx(cc.texture) || ccHasTx(cc.roughnessTexture) || ccHasTx(cc.bumpTexture)) {
f2 |= PBR2_CC_UV_TX;
}
if (cc.useF0Remap === false) {
f2 |= PBR2_CC_F0_REMAP_OFF;
}
return { f: PBR_HAS_CLEARCOAT, f2 };
},
frag: (ctx) => createClearcoatFragment(ctx._features, ctx._features2, ctx._hasIbl, ctx._hasAnyNormal, ctx._hasSpecularAA),
writeUbo: writeClearcoatUBO,
bind(ctx, entries, b) {
const cc = ctx._material.clearCoat;
if (!cc) {
return b;
}
for (const [flag, key] of CC_TEX) {
const tex = cc[key];
if ((ctx._features2 & flag) !== 0 && tex) {
entries.push({ binding: b++, resource: tex.view });
entries.push({ binding: b++, resource: tex.sampler });
}
}
return b;
},
textures(mat, t) {
const cc = mat.clearCoat;
if (!cc) {
return;
}
for (const [, key] of CC_TEX) {
const tex = cc[key];
if (tex) {
t.push(tex);
}
}
}
};
export { createClearcoatFragment, pbrExt, writeClearcoatUBO };
//# sourceMappingURL=clearcoat-fragment-EB6BX2a_.esm.js.map