UNPKG

@xeokit/xeokit-sdk

Version:

3D BIM IFC Viewer SDK for AEC engineering applications. Open Source JavaScript Toolkit based on pure WebGL for top performance, real-world coordinates and full double precision

181 lines (153 loc) 11.3 kB
import {LinearEncoding, sRGBEncoding} from "../../../constants/constants.js"; import {setupTexture} from "../../../webgl/WebGLRenderer.js"; export const PBRProgram = function(programVariables, geometry, scene, lightSetup, sao) { const setup2dTexture = (name, isSrgb, getTexture) => { return setupTexture(programVariables, "sampler2D", name, isSrgb ? sRGBEncoding : LinearEncoding, (set, state) => { const texture = getTexture(state.layerTextureSet); texture && set(texture.texture); }); }; const attributes = geometry.attributes; const getIrradiance = lightSetup.getIrradiance; const colorMap = setup2dTexture("uColorMap", true, textureSet => textureSet.colorTexture); const metallicRoughMap = setup2dTexture("uMetallicRoughMap", false, textureSet => textureSet.metallicRoughnessTexture); const emissiveMap = setup2dTexture("uEmissiveMap", true, textureSet => textureSet.emissiveTexture); const normalMap = setup2dTexture("uNormalMap", false, textureSet => textureSet.normalsTexture); const aOMap = setup2dTexture("uAOMap", false, textureSet => textureSet.occlusionTexture); const vViewPosition = programVariables.createVarying("vec3", "vViewPosition", () => `${attributes.position.view}.xyz`); const vViewNormal = programVariables.createVarying("vec3", "vViewNormal", () => attributes.normal.view); const vColor = programVariables.createVarying("vec4", "vColor", () => attributes.color); const vUV = programVariables.createVarying("vec2", "vUV", () => attributes.uv); const vMetallicRoughness = programVariables.createVarying("vec2", "vMetallicRoughness", () => attributes.metallicRoughness); const vWorldNormal = programVariables.createVarying("vec3", "vWorldNormal", () => `${attributes.normal.world}.xyz`); const outColor = programVariables.createOutput("vec4", "outColor"); const saturate = programVariables.createFragmentDefinition( "saturate", (name, src) => src.push(`float ${name}(const in float a) { return clamp(a, 0.0, 1.0); }`)); const BRDF_Specular_GGX = programVariables.createFragmentDefinition( "BRDF_Specular_GGX", (name, src) => { src.push("vec3 F_Schlick(const in vec3 specularColor, const in float dotLH) {"); src.push(" float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );"); src.push(" return ( 1.0 - specularColor ) * fresnel + specularColor;"); src.push("}"); src.push("float G_GGX_Smith(const in float alpha, const in float dotNL, const in float dotNV) {"); src.push(" float a2 = ( alpha * alpha );"); src.push(" float gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * ( dotNL * dotNL ) );"); src.push(" float gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * ( dotNV * dotNV ) );"); src.push(" return 1.0 / ( gl * gv );"); src.push("}"); src.push("float G_GGX_SmithCorrelated(const in float alpha, const in float dotNL, const in float dotNV) {"); src.push(" float a2 = ( alpha * alpha );"); src.push(" float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * ( dotNV * dotNV ) );"); src.push(" float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * ( dotNL * dotNL ) );"); src.push(" return 0.5 / max( gv + gl, 1e-6 );"); src.push("}"); src.push("float D_GGX(const in float alpha, const in float dotNH) {"); src.push(" float a2 = ( alpha * alpha );"); src.push(" float denom = ( dotNH * dotNH) * ( a2 - 1.0 ) + 1.0;"); src.push(" return 0.31830988618 * a2 / ( denom * denom);"); // 1/PI src.push("}"); src.push(`vec3 ${name}(const in vec3 incidentLightDirection, const in vec3 viewNormal, const in vec3 viewEyeDir, const in vec3 specularColor, const in float roughness) {`); src.push(" float alpha = ( roughness * roughness );"); src.push(" vec3 halfDir = normalize( incidentLightDirection + viewEyeDir );"); src.push(` float dotNL = ${saturate}( dot( viewNormal, incidentLightDirection ) );`); src.push(` float dotNV = ${saturate}( dot( viewNormal, viewEyeDir ) );`); src.push(` float dotNH = ${saturate}( dot( viewNormal, halfDir ) );`); src.push(` float dotLH = ${saturate}( dot( incidentLightDirection, halfDir ) );`); src.push(" vec3 F = F_Schlick( specularColor, dotLH );"); src.push(" float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );"); src.push(" float D = D_GGX( alpha, dotNH );"); src.push(" return F * (G * D);"); src.push("}"); }); const BRDF_Specular_GGX_Environment = programVariables.createFragmentDefinition( "BRDF_Specular_GGX_Environment", (name, src) => { src.push(`vec3 ${name}(const in vec3 viewNormal, const in vec3 viewEyeDir, const in vec3 specularColor, const in float roughness) {`); src.push(` float dotNV = ${saturate}(dot(viewNormal, viewEyeDir));`); src.push(" const vec4 c0 = vec4( -1, -0.0275, -0.572, 0.022);"); src.push(" const vec4 c1 = vec4( 1, 0.0425, 1.04, -0.04);"); src.push(" vec4 r = roughness * c0 + c1;"); src.push(" float a004 = min(r.x * r.x, exp2(-9.28 * dotNV)) * r.x + r.y;"); src.push(" vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;"); src.push(" return specularColor * AB.x + AB.y;"); src.push("}"); }); const perturbNormal2Arb = programVariables.createFragmentDefinition( "perturbNormal2Arb", (name, src) => { src.push(`vec3 ${name}( vec3 eye_pos, vec3 surf_norm, vec2 uv, vec4 texel ) {`); src.push(" if (texel.r == 0.0 && texel.g == 0.0 && texel.b == 0.0) {"); src.push(" return surf_norm;"); src.push(" }"); src.push(" vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );"); src.push(" vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );"); src.push(" vec2 st0 = dFdx( uv.st );"); src.push(" vec2 st1 = dFdy( uv.st );"); src.push(" vec3 S = normalize( q0 * st1.t - q1 * st0.t );"); src.push(" vec3 T = normalize( -q0 * st1.s + q1 * st0.s );"); src.push(" vec3 N = normalize( surf_norm );"); src.push(" vec3 mapN = texel.xyz * 2.0 - 1.0;"); src.push(" mat3 tsn = mat3( S, T, N );"); // src.push(" mapN *= 3.0;"); src.push(" return normalize( tsn * mapN );"); src.push("}"); }); return { programName: "PBR", getHash: () => [lightSetup.getHash(), sao ? "sao" : "nosao", !!scene.gammaOutput], getLogDepth: scene.logarithmicDepthBufferEnabled && (vFragDepth => vFragDepth), renderPassFlag: 0, // COLOR_OPAQUE | COLOR_TRANSPARENT clippingCaps: scene._sectionPlanesState.clippingCaps && outColor, incrementDrawState: true, appendFragmentOutputs: (src, getGammaOutputExpression, gl_FragCoord) => { src.push("const float PI = 3.14159265359;"); src.push("vec3 reflDiff = vec3(0.0);"); src.push("vec3 reflSpec = vec3(0.0);"); src.push(`vec3 rgb = ${vColor}.rgb;`); src.push(`float opacity = ${vColor}.a;`); src.push("vec3 baseColor = rgb;"); src.push("float specularF0 = 1.0;"); src.push(`float metallic = float(${vMetallicRoughness}.r) / 255.0;`); src.push(`float roughness = float(${vMetallicRoughness}.g) / 255.0;`); src.push("float dielectricSpecular = 0.16 * specularF0 * specularF0;"); src.push(`vec4 colorTexel = ${colorMap(vUV)};`); src.push("baseColor *= colorTexel.rgb;"); // src.push("opacity *=/= colorTexel.a;"); // batching had "*=", instancing had "=" src.push(`vec3 metalRoughTexel = ${metallicRoughMap(vUV)}.rgb;`); src.push("metallic *= metalRoughTexel.b;"); src.push("roughness *= metalRoughTexel.g;"); src.push("vec3 diffuseColor = baseColor * (1.0 - dielectricSpecular) * (1.0 - metallic);"); src.push("float specularRoughness = clamp(roughness, 0.04, 1.0);"); src.push("vec3 specularColor = mix(vec3(dielectricSpecular), baseColor, metallic);"); src.push(`vec3 viewNormal = -${perturbNormal2Arb}(${vViewPosition}, normalize(${vViewNormal}), ${vUV}, ${normalMap(vUV)});`); src.push(`vec3 viewEyeDir = normalize(${vViewPosition});`); getIrradiance && src.push(`reflDiff += ${getIrradiance(`normalize(${vWorldNormal})`)};`); if (lightSetup.getReflection) { const reflectVec = `reflect(viewEyeDir, viewNormal)`; const viewReflectVec = `normalize((vec4(${reflectVec}, 0.0) * ${geometry.viewMatrix}).xyz)`; const maxMIPLevel = "8.0"; const blinnExpFromRoughness = `(2.0 / pow(specularRoughness + 0.0001, 2.0) - 2.0)`; const desiredMIPLevel = `${maxMIPLevel} - 0.79248 - 0.5 * log2(pow(${blinnExpFromRoughness}, 2.0) + 1.0)`; const specularMIPLevel = `0.5 * clamp(${desiredMIPLevel}, 0.0, ${maxMIPLevel})`; // TODO: a random factor - fix this const radiance = lightSetup.getReflection(viewReflectVec, specularMIPLevel); const specularBRDFContrib = `${BRDF_Specular_GGX_Environment}(viewNormal, viewEyeDir, specularColor, specularRoughness)`; src.push(`reflSpec += ${radiance} * ${specularBRDFContrib};`); } lightSetup.directionalLights.forEach((light, i) => { src.push(`vec3 lightDirection${i} = -${light.getDirection(geometry.viewMatrix, vViewPosition)};`); // This "-" might be wrong, but it used to be like that const dotNL = `${saturate}(dot(viewNormal, lightDirection${i}))`; src.push(`vec3 irradiance${i} = ${dotNL} * ${light.getColor()};`); src.push(`reflDiff += irradiance${i};`); src.push(`reflSpec += irradiance${i} * PI * ${BRDF_Specular_GGX}(lightDirection${i}, viewNormal, viewEyeDir, specularColor, specularRoughness);`); }); src.push(`vec3 emissiveColor = ${emissiveMap(vUV)}.rgb;`); // TODO: correct gamma function src.push(`float aoFactor = ${aOMap(vUV)}.r;`); const ambient = `${lightSetup.getAmbientColor()} * baseColor * opacity * rgb`; src.push(`vec3 outgoingLight = emissiveColor + reflDiff * diffuseColor + reflSpec + ${ambient};`); src.push(`${outColor} = vec4(outgoingLight * aoFactor${sao ? ` * ${sao.getAmbient(gl_FragCoord)}` : ""}, opacity);`); getGammaOutputExpression && src.push(`${outColor} = ${getGammaOutputExpression(outColor)};`); } }; };