@itwin/core-frontend
Version:
iTwin.js frontend components
128 lines (111 loc) • 5.75 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module WebGL
*/
import { addFrustum } from "./Common";
const computeDirectionalLighting = `
void computeDirectionalLight (inout float diffuse, inout float specular, vec3 normal, vec3 toEye, vec3 lightDir, float lightIntensity, float specularExponent) {
diffuse += lightIntensity * max(dot(normal, lightDir), 0.0);
vec3 toReflectedLight = normalize(reflect(lightDir, normal));
float specularDot = max(dot(toReflectedLight, toEye), 0.0001);
// NB: If specularDot and specularExponent are both zero, 0^0 done below can return NaN. Must make sure specularDot is larger than zero (hence 0.0001 or greater, as ensured above).
specular += lightIntensity * pow(specularDot, specularExponent);
}
`;
// mat_weights: x=diffuse y=specular
const applyLighting = `
if (baseColor.a <= 0.0 || !u_surfaceFlags[kSurfaceBitIndex_ApplyLighting])
return baseColor;
// Extract surface properties
vec3 rgb = baseColor.rgb;
vec3 toEye = kFrustumType_Perspective == u_frustum.z ? normalize(v_eyeSpace.xyz) : vec3(0.0, 0.0, -1.0);
// Extract material properties
float diffuseWeight = mat_weights.x;
float specularWeight = mat_weights.y * u_lightSettings[13];
float specularExponent = mat_specular.a;
vec3 specularColor = mat_specular.rgb;
const float ambientWeight = 1.0; // Ignore MicroStation's ambient weights - usually bogus.
// Compute directional lights
const vec3 portraitDir = vec3(-0.7071, 0.0, 0.7071);
float portraitIntensity = u_lightSettings[12];
float sunIntensity = u_lightSettings[0];
float directionalDiffuseIntensity = 0.0;
float directionalSpecularIntensity = 0.0;
computeDirectionalLight(directionalDiffuseIntensity, directionalSpecularIntensity, g_normal, toEye, u_sunDir, sunIntensity, specularExponent);
computeDirectionalLight(directionalDiffuseIntensity, directionalSpecularIntensity, g_normal ,toEye, portraitDir, portraitIntensity, specularExponent);
const float directionalFudge = 0.92; // leftover from old lighting implementation
vec3 diffuseAccum = directionalDiffuseIntensity * diffuseWeight * rgb * directionalFudge; // directional light is white.
vec3 specularAccum = directionalSpecularIntensity * specularWeight * specularColor;
// Compute ambient light
float ambientIntensity = u_lightSettings[4];
vec3 ambientColor = vec3(u_lightSettings[1], u_lightSettings[2], u_lightSettings[3]);
if (ambientColor.r + ambientColor.g + ambientColor.b == 0.0)
ambientColor = rgb;
diffuseAccum += ambientIntensity * ambientWeight * ambientColor;
// Compute hemisphere lights
vec3 ground = vec3(u_lightSettings[5], u_lightSettings[6], u_lightSettings[7]);
vec3 sky = vec3(u_lightSettings[8], u_lightSettings[9], u_lightSettings[10]);
float hemiIntensity = u_lightSettings[11];
// diffuse
float hemiDot = dot(g_normal, u_upVector);
float hemiDiffuseWeight = 0.5 * hemiDot + 0.5;
vec3 hemiColor = mix(ground, sky, hemiDiffuseWeight);
diffuseAccum += hemiIntensity * hemiColor * rgb;
// sky specular
vec3 reflectSky = normalize(reflect(u_upVector, g_normal));
float skyDot = max(dot(reflectSky, toEye), 0.0001);
float hemiSpecWeight = hemiIntensity * pow(skyDot, specularExponent);
// ground specular
vec3 reflectGround = normalize(reflect(-u_upVector, g_normal));
float groundDot = max(dot(reflectGround, toEye), 0.0001);
hemiSpecWeight += hemiIntensity * pow(groundDot, specularExponent);
specularAccum += hemiSpecWeight * specularColor * hemiColor;
vec3 litColor = diffuseAccum + specularAccum;
// Apply fresnel reflection.
float fresnelIntensity = u_lightSettings[15];
if (0.0 != fresnelIntensity) {
float fresnel = -dot(toEye, g_normal);
if (fresnelIntensity < 0.0) {
fresnelIntensity = abs(fresnelIntensity);
fresnel = 1.0 - fresnel;
}
fresnel = clamp(1.0 - fresnel, 0.0, 1.0);
litColor = litColor * (1.0 + fresnelIntensity * fresnel);
}
// Clamp while preserving hue.
float maxIntensity = max(litColor.r, max(litColor.g, litColor.b));
float numCel = u_lightSettings[14];
if (numCel > 0.0)
baseColor.rgb = baseColor.rgb * ceil(maxIntensity * numCel) / numCel;
else
baseColor.rgb = litColor / max(1.0, maxIntensity);
return baseColor;
`;
/** NB: addMaterial() sets up the mat_* variables used by applyLighting.
* @internal
*/
export function addLighting(builder) {
addFrustum(builder);
const frag = builder.frag;
frag.addFunction(computeDirectionalLighting);
frag.set(8 /* FragmentShaderComponent.ApplyLighting */, applyLighting);
frag.addUniform("u_sunDir", 4 /* VariableType.Vec3 */, (prog) => {
prog.addProgramUniform("u_sunDir", (uniform, params) => {
params.target.uniforms.bindSunDirection(uniform);
});
}, 3 /* VariablePrecision.High */);
frag.addUniform("u_lightSettings[16]", 2 /* VariableType.Float */, (prog) => {
prog.addProgramUniform("u_lightSettings[0]", (uniform, params) => {
params.target.uniforms.lights.bind(uniform);
});
});
frag.addUniform("u_upVector", 4 /* VariableType.Vec3 */, (prog) => {
prog.addProgramUniform("u_upVector", (uniform, params) => {
params.target.uniforms.frustum.bindUpVector(uniform);
});
});
}
//# sourceMappingURL=Lighting.js.map