@luma.gl/shadertools
Version:
Shader module system for luma.gl
1,103 lines (981 loc) • 36.3 kB
JavaScript
// luma.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
// Attribution:
// MIT license, Copyright (c) 2016-2017 Mohamad Moneimne and Contributors
// This fragment shader defines a reference implementation for Physically Based Shading of
// a microfacet surface material defined by a glTF model.
// TODO - better do the checks outside of shader
export const vs = /* glsl */ `\
out vec3 pbr_vPosition;
out vec2 pbr_vUV0;
out vec2 pbr_vUV1;
out mat3 pbr_vTBN;
out vec3 pbr_vNormal;
void pbr_setPositionNormalTangentUV(
vec4 position,
vec4 normal,
vec4 tangent,
vec2 uv0,
vec2 uv1
)
{
vec4 pos = pbrProjection.modelMatrix * position;
pbr_vPosition = vec3(pos.xyz) / pos.w;
vec3 normalW = normalize(vec3(pbrProjection.normalMatrix * vec4(normal.xyz, 0.0)));
vec3 tangentW = normalize(vec3(pbrProjection.modelMatrix * vec4(tangent.xyz, 0.0)));
vec3 bitangentW = cross(normalW, tangentW) * tangent.w;
pbr_vTBN = mat3(tangentW, bitangentW, normalW);
pbr_vNormal = normalize(vec3(pbrProjection.modelMatrix * vec4(normal.xyz, 0.0)));
pbr_vUV0 = uv0;
pbr_vUV0 = vec2(0.,0.);
pbr_vUV1 = uv1;
}
`;
export const fs = /* glsl */ `\
precision highp float;
layout(std140) uniform pbrMaterialUniforms {
// Material is unlit
bool unlit;
// Base color map
bool baseColorMapEnabled;
vec4 baseColorFactor;
bool normalMapEnabled;
float normalScale; // #ifdef HAS_NORMALMAP
bool emissiveMapEnabled;
vec3 emissiveFactor; // #ifdef HAS_EMISSIVEMAP
vec2 metallicRoughnessValues;
bool metallicRoughnessMapEnabled;
bool occlusionMapEnabled;
float occlusionStrength; // #ifdef HAS_OCCLUSIONMAP
bool alphaCutoffEnabled;
float alphaCutoff; // #ifdef ALPHA_CUTOFF
vec3 specularColorFactor;
float specularIntensityFactor;
bool specularColorMapEnabled;
bool specularIntensityMapEnabled;
float ior;
float transmissionFactor;
bool transmissionMapEnabled;
float thicknessFactor;
float attenuationDistance;
vec3 attenuationColor;
float clearcoatFactor;
float clearcoatRoughnessFactor;
bool clearcoatMapEnabled;
bool clearcoatRoughnessMapEnabled;
vec3 sheenColorFactor;
float sheenRoughnessFactor;
bool sheenColorMapEnabled;
bool sheenRoughnessMapEnabled;
float iridescenceFactor;
float iridescenceIor;
vec2 iridescenceThicknessRange;
bool iridescenceMapEnabled;
float anisotropyStrength;
float anisotropyRotation;
vec2 anisotropyDirection;
bool anisotropyMapEnabled;
float emissiveStrength;
// IBL
bool IBLenabled;
vec2 scaleIBLAmbient; // #ifdef USE_IBL
// debugging flags used for shader output of intermediate PBR variables
// #ifdef PBR_DEBUG
vec4 scaleDiffBaseMR;
vec4 scaleFGDSpec;
// #endif
int baseColorUVSet;
mat3 baseColorUVTransform;
int metallicRoughnessUVSet;
mat3 metallicRoughnessUVTransform;
int normalUVSet;
mat3 normalUVTransform;
int occlusionUVSet;
mat3 occlusionUVTransform;
int emissiveUVSet;
mat3 emissiveUVTransform;
int specularColorUVSet;
mat3 specularColorUVTransform;
int specularIntensityUVSet;
mat3 specularIntensityUVTransform;
int transmissionUVSet;
mat3 transmissionUVTransform;
int thicknessUVSet;
mat3 thicknessUVTransform;
int clearcoatUVSet;
mat3 clearcoatUVTransform;
int clearcoatRoughnessUVSet;
mat3 clearcoatRoughnessUVTransform;
int clearcoatNormalUVSet;
mat3 clearcoatNormalUVTransform;
int sheenColorUVSet;
mat3 sheenColorUVTransform;
int sheenRoughnessUVSet;
mat3 sheenRoughnessUVTransform;
int iridescenceUVSet;
mat3 iridescenceUVTransform;
int iridescenceThicknessUVSet;
mat3 iridescenceThicknessUVTransform;
int anisotropyUVSet;
mat3 anisotropyUVTransform;
} pbrMaterial;
// Samplers
uniform sampler2D pbr_baseColorSampler;
uniform sampler2D pbr_normalSampler;
uniform sampler2D pbr_emissiveSampler;
uniform sampler2D pbr_metallicRoughnessSampler;
uniform sampler2D pbr_occlusionSampler;
uniform sampler2D pbr_specularColorSampler;
uniform sampler2D pbr_specularIntensitySampler;
uniform sampler2D pbr_transmissionSampler;
uniform sampler2D pbr_thicknessSampler;
uniform sampler2D pbr_clearcoatSampler;
uniform sampler2D pbr_clearcoatRoughnessSampler;
uniform sampler2D pbr_clearcoatNormalSampler;
uniform sampler2D pbr_sheenColorSampler;
uniform sampler2D pbr_sheenRoughnessSampler;
uniform sampler2D pbr_iridescenceSampler;
uniform sampler2D pbr_iridescenceThicknessSampler;
uniform sampler2D pbr_anisotropySampler;
// Inputs from vertex shader
in vec3 pbr_vPosition;
in vec2 pbr_vUV0;
in vec2 pbr_vUV1;
in mat3 pbr_vTBN;
in vec3 pbr_vNormal;
// Encapsulate the various inputs used by the various functions in the shading equation
// We store values in this struct to simplify the integration of alternative implementations
// of the shading terms, outlined in the Readme.MD Appendix.
struct PBRInfo {
float NdotL; // cos angle between normal and light direction
float NdotV; // cos angle between normal and view direction
float NdotH; // cos angle between normal and half vector
float LdotH; // cos angle between light direction and half vector
float VdotH; // cos angle between view direction and half vector
float perceptualRoughness; // roughness value, as authored by the model creator (input to shader)
float metalness; // metallic value at the surface
vec3 reflectance0; // full reflectance color (normal incidence angle)
vec3 reflectance90; // reflectance color at grazing angle
float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2])
vec3 diffuseColor; // color contribution from diffuse lighting
vec3 specularColor; // color contribution from specular lighting
vec3 n; // normal at surface point
vec3 v; // vector from surface point to camera
};
const float M_PI = 3.141592653589793;
const float c_MinRoughness = 0.04;
vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor);
vec4 SRGBtoLINEAR(vec4 srgbIn)
{
vec3 linOut = pow(srgbIn.xyz,vec3(2.2));
vec3 bLess = step(vec3(0.04045),srgbIn.xyz);
vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
return vec4(linOut,srgbIn.w);;
return srgbIn;
}
vec2 getMaterialUV(int uvSet, mat3 uvTransform)
{
vec2 baseUV = uvSet == 1 ? pbr_vUV1 : pbr_vUV0;
return (uvTransform * vec3(baseUV, 1.0)).xy;
}
// Build the tangent basis from interpolated attributes or screen-space derivatives.
mat3 getTBN(vec2 uv)
{
vec3 pos_dx = dFdx(pbr_vPosition);
vec3 pos_dy = dFdy(pbr_vPosition);
vec3 tex_dx = dFdx(vec3(uv, 0.0));
vec3 tex_dy = dFdy(vec3(uv, 0.0));
vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t);
vec3 ng = normalize(pbr_vNormal);
vec3 ng = cross(pos_dx, pos_dy);
t = normalize(t - ng * dot(ng, t));
vec3 b = normalize(cross(ng, t));
mat3 tbn = mat3(t, b, ng);
mat3 tbn = pbr_vTBN;
return tbn;
}
// Find the normal for this fragment, pulling either from a predefined normal map
// or from the interpolated mesh normal and tangent attributes.
vec3 getMappedNormal(sampler2D normalSampler, mat3 tbn, float normalScale, vec2 uv)
{
vec3 n = texture(normalSampler, uv).rgb;
return normalize(tbn * ((2.0 * n - 1.0) * vec3(normalScale, normalScale, 1.0)));
}
vec3 getNormal(mat3 tbn, vec2 uv)
{
vec3 n = getMappedNormal(pbr_normalSampler, tbn, pbrMaterial.normalScale, uv);
// The tbn matrix is linearly interpolated, so we need to re-normalize
vec3 n = normalize(tbn[2].xyz);
return n;
}
vec3 getClearcoatNormal(mat3 tbn, vec3 baseNormal, vec2 uv)
{
return getMappedNormal(pbr_clearcoatNormalSampler, tbn, 1.0, uv);
return baseNormal;
}
// Calculation of the lighting contribution from an optional Image Based Light source.
// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
// See our README.md on Environment Maps [3] for additional discussion.
vec3 getIBLContribution(PBRInfo pbrInfo, vec3 n, vec3 reflection)
{
float mipCount = 9.0; // resolution of 512x512
float lod = (pbrInfo.perceptualRoughness * mipCount);
// retrieve a scale and bias to F0. See [1], Figure 3
vec3 brdf = SRGBtoLINEAR(texture(pbr_brdfLUT,
vec2(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness))).rgb;
vec3 diffuseLight = SRGBtoLINEAR(texture(pbr_diffuseEnvSampler, n)).rgb;
vec3 specularLight = SRGBtoLINEAR(texture(pbr_specularEnvSampler, reflection, lod)).rgb;
vec3 specularLight = SRGBtoLINEAR(texture(pbr_specularEnvSampler, reflection)).rgb;
vec3 diffuse = diffuseLight * pbrInfo.diffuseColor;
vec3 specular = specularLight * (pbrInfo.specularColor * brdf.x + brdf.y);
// For presentation, this allows us to disable IBL terms
diffuse *= pbrMaterial.scaleIBLAmbient.x;
specular *= pbrMaterial.scaleIBLAmbient.y;
return diffuse + specular;
}
// Basic Lambertian diffuse
// Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog
// See also [1], Equation 1
vec3 diffuse(PBRInfo pbrInfo)
{
return pbrInfo.diffuseColor / M_PI;
}
// The following equation models the Fresnel reflectance term of the spec equation (aka F())
// Implementation of fresnel from [4], Equation 15
vec3 specularReflection(PBRInfo pbrInfo)
{
return pbrInfo.reflectance0 +
(pbrInfo.reflectance90 - pbrInfo.reflectance0) *
pow(clamp(1.0 - pbrInfo.VdotH, 0.0, 1.0), 5.0);
}
// This calculates the specular geometric attenuation (aka G()),
// where rougher material will reflect less light back to the viewer.
// This implementation is based on [1] Equation 4, and we adopt their modifications to
// alphaRoughness as input as originally proposed in [2].
float geometricOcclusion(PBRInfo pbrInfo)
{
float NdotL = pbrInfo.NdotL;
float NdotV = pbrInfo.NdotV;
float r = pbrInfo.alphaRoughness;
float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));
return attenuationL * attenuationV;
}
// The following equation(s) model the distribution of microfacet normals across
// the area being drawn (aka D())
// Implementation from "Average Irregularity Representation of a Roughened Surface
// for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz
// Follows the distribution function recommended in the SIGGRAPH 2013 course notes
// from EPIC Games [1], Equation 3.
float microfacetDistribution(PBRInfo pbrInfo)
{
float roughnessSq = pbrInfo.alphaRoughness * pbrInfo.alphaRoughness;
float f = (pbrInfo.NdotH * roughnessSq - pbrInfo.NdotH) * pbrInfo.NdotH + 1.0;
return roughnessSq / (M_PI * f * f);
}
float maxComponent(vec3 value)
{
return max(max(value.r, value.g), value.b);
}
float getDielectricF0(float ior)
{
float clampedIor = max(ior, 1.0);
float ratio = (clampedIor - 1.0) / (clampedIor + 1.0);
return ratio * ratio;
}
vec2 normalizeDirection(vec2 direction)
{
float directionLength = length(direction);
return directionLength > 0.0001 ? direction / directionLength : vec2(1.0, 0.0);
}
vec2 rotateDirection(vec2 direction, float rotation)
{
float s = sin(rotation);
float c = cos(rotation);
return vec2(direction.x * c - direction.y * s, direction.x * s + direction.y * c);
}
vec3 getIridescenceTint(float iridescence, float thickness, float NdotV)
{
if (iridescence <= 0.0) {
return vec3(1.0);
}
float phase = 0.015 * thickness * pbrMaterial.iridescenceIor + (1.0 - NdotV) * 6.0;
vec3 thinFilmTint =
0.5 + 0.5 * cos(vec3(phase, phase + 2.0943951, phase + 4.1887902));
return mix(vec3(1.0), thinFilmTint, iridescence);
}
vec3 getVolumeAttenuation(float thickness)
{
if (thickness <= 0.0) {
return vec3(1.0);
}
vec3 attenuationCoefficient =
-log(max(pbrMaterial.attenuationColor, vec3(0.0001))) /
max(pbrMaterial.attenuationDistance, 0.0001);
return exp(-attenuationCoefficient * thickness);
}
PBRInfo createClearcoatPBRInfo(PBRInfo basePBRInfo, vec3 clearcoatNormal, float clearcoatRoughness)
{
float perceptualRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
float alphaRoughness = perceptualRoughness * perceptualRoughness;
float NdotV = clamp(abs(dot(clearcoatNormal, basePBRInfo.v)), 0.001, 1.0);
return PBRInfo(
basePBRInfo.NdotL,
NdotV,
basePBRInfo.NdotH,
basePBRInfo.LdotH,
basePBRInfo.VdotH,
perceptualRoughness,
0.0,
vec3(0.04),
vec3(1.0),
alphaRoughness,
vec3(0.0),
vec3(0.04),
clearcoatNormal,
basePBRInfo.v
);
}
vec3 calculateClearcoatContribution(
PBRInfo pbrInfo,
vec3 lightColor,
vec3 clearcoatNormal,
float clearcoatFactor,
float clearcoatRoughness
) {
if (clearcoatFactor <= 0.0) {
return vec3(0.0);
}
PBRInfo clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
return calculateFinalColor(clearcoatPBRInfo, lightColor) * clearcoatFactor;
}
vec3 calculateClearcoatIBLContribution(
PBRInfo pbrInfo,
vec3 clearcoatNormal,
vec3 reflection,
float clearcoatFactor,
float clearcoatRoughness
) {
if (clearcoatFactor <= 0.0) {
return vec3(0.0);
}
PBRInfo clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
return getIBLContribution(clearcoatPBRInfo, clearcoatNormal, reflection) * clearcoatFactor;
}
vec3 calculateSheenContribution(
PBRInfo pbrInfo,
vec3 lightColor,
vec3 sheenColor,
float sheenRoughness
) {
if (maxComponent(sheenColor) <= 0.0) {
return vec3(0.0);
}
float sheenFresnel = pow(clamp(1.0 - pbrInfo.VdotH, 0.0, 1.0), 5.0);
float sheenVisibility = mix(1.0, pbrInfo.NdotL * pbrInfo.NdotV, sheenRoughness);
return pbrInfo.NdotL *
lightColor *
sheenColor *
(0.25 + 0.75 * sheenFresnel) *
sheenVisibility *
(1.0 - pbrInfo.metalness);
}
float calculateAnisotropyBoost(
PBRInfo pbrInfo,
vec3 anisotropyTangent,
float anisotropyStrength
) {
if (anisotropyStrength <= 0.0) {
return 1.0;
}
vec3 anisotropyBitangent = normalize(cross(pbrInfo.n, anisotropyTangent));
float bitangentViewAlignment = abs(dot(pbrInfo.v, anisotropyBitangent));
return mix(1.0, 0.65 + 0.7 * bitangentViewAlignment, anisotropyStrength);
}
vec3 calculateMaterialLightColor(
PBRInfo pbrInfo,
vec3 lightColor,
vec3 clearcoatNormal,
float clearcoatFactor,
float clearcoatRoughness,
vec3 sheenColor,
float sheenRoughness,
vec3 anisotropyTangent,
float anisotropyStrength
) {
float anisotropyBoost = calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
vec3 color = calculateFinalColor(pbrInfo, lightColor) * anisotropyBoost;
color += calculateClearcoatContribution(
pbrInfo,
lightColor,
clearcoatNormal,
clearcoatFactor,
clearcoatRoughness
);
color += calculateSheenContribution(pbrInfo, lightColor, sheenColor, sheenRoughness);
return color;
}
void PBRInfo_setAmbientLight(inout PBRInfo pbrInfo) {
pbrInfo.NdotL = 1.0;
pbrInfo.NdotH = 0.0;
pbrInfo.LdotH = 0.0;
pbrInfo.VdotH = 1.0;
}
void PBRInfo_setDirectionalLight(inout PBRInfo pbrInfo, vec3 lightDirection) {
vec3 n = pbrInfo.n;
vec3 v = pbrInfo.v;
vec3 l = normalize(lightDirection); // Vector from surface point to light
vec3 h = normalize(l+v); // Half vector between both l and v
pbrInfo.NdotL = clamp(dot(n, l), 0.001, 1.0);
pbrInfo.NdotH = clamp(dot(n, h), 0.0, 1.0);
pbrInfo.LdotH = clamp(dot(l, h), 0.0, 1.0);
pbrInfo.VdotH = clamp(dot(v, h), 0.0, 1.0);
}
void PBRInfo_setPointLight(inout PBRInfo pbrInfo, PointLight pointLight) {
vec3 light_direction = normalize(pointLight.position - pbr_vPosition);
PBRInfo_setDirectionalLight(pbrInfo, light_direction);
}
void PBRInfo_setSpotLight(inout PBRInfo pbrInfo, SpotLight spotLight) {
vec3 light_direction = normalize(spotLight.position - pbr_vPosition);
PBRInfo_setDirectionalLight(pbrInfo, light_direction);
}
vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) {
// Calculate the shading terms for the microfacet specular shading model
vec3 F = specularReflection(pbrInfo);
float G = geometricOcclusion(pbrInfo);
float D = microfacetDistribution(pbrInfo);
// Calculation of analytical lighting contribution
vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInfo);
vec3 specContrib = F * G * D / (4.0 * pbrInfo.NdotL * pbrInfo.NdotV);
// Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
return pbrInfo.NdotL * lightColor * (diffuseContrib + specContrib);
}
vec4 pbr_filterColor(vec4 colorUnused)
{
vec2 baseColorUV = getMaterialUV(pbrMaterial.baseColorUVSet, pbrMaterial.baseColorUVTransform);
vec2 metallicRoughnessUV = getMaterialUV(
pbrMaterial.metallicRoughnessUVSet,
pbrMaterial.metallicRoughnessUVTransform
);
vec2 normalUV = getMaterialUV(pbrMaterial.normalUVSet, pbrMaterial.normalUVTransform);
vec2 occlusionUV = getMaterialUV(pbrMaterial.occlusionUVSet, pbrMaterial.occlusionUVTransform);
vec2 emissiveUV = getMaterialUV(pbrMaterial.emissiveUVSet, pbrMaterial.emissiveUVTransform);
vec2 specularColorUV = getMaterialUV(
pbrMaterial.specularColorUVSet,
pbrMaterial.specularColorUVTransform
);
vec2 specularIntensityUV = getMaterialUV(
pbrMaterial.specularIntensityUVSet,
pbrMaterial.specularIntensityUVTransform
);
vec2 transmissionUV = getMaterialUV(
pbrMaterial.transmissionUVSet,
pbrMaterial.transmissionUVTransform
);
vec2 thicknessUV = getMaterialUV(pbrMaterial.thicknessUVSet, pbrMaterial.thicknessUVTransform);
vec2 clearcoatUV = getMaterialUV(pbrMaterial.clearcoatUVSet, pbrMaterial.clearcoatUVTransform);
vec2 clearcoatRoughnessUV = getMaterialUV(
pbrMaterial.clearcoatRoughnessUVSet,
pbrMaterial.clearcoatRoughnessUVTransform
);
vec2 clearcoatNormalUV = getMaterialUV(
pbrMaterial.clearcoatNormalUVSet,
pbrMaterial.clearcoatNormalUVTransform
);
vec2 sheenColorUV = getMaterialUV(
pbrMaterial.sheenColorUVSet,
pbrMaterial.sheenColorUVTransform
);
vec2 sheenRoughnessUV = getMaterialUV(
pbrMaterial.sheenRoughnessUVSet,
pbrMaterial.sheenRoughnessUVTransform
);
vec2 iridescenceUV = getMaterialUV(
pbrMaterial.iridescenceUVSet,
pbrMaterial.iridescenceUVTransform
);
vec2 iridescenceThicknessUV = getMaterialUV(
pbrMaterial.iridescenceThicknessUVSet,
pbrMaterial.iridescenceThicknessUVTransform
);
vec2 anisotropyUV = getMaterialUV(
pbrMaterial.anisotropyUVSet,
pbrMaterial.anisotropyUVTransform
);
// The albedo may be defined from a base texture or a flat color
vec4 baseColor =
SRGBtoLINEAR(texture(pbr_baseColorSampler, baseColorUV)) * pbrMaterial.baseColorFactor;
vec4 baseColor = pbrMaterial.baseColorFactor;
if (baseColor.a < pbrMaterial.alphaCutoff) {
discard;
}
vec3 color = vec3(0, 0, 0);
float transmission = 0.0;
if(pbrMaterial.unlit){
color.rgb = baseColor.rgb;
}
else{
// Metallic and Roughness material properties are packed together
// In glTF, these factors can be specified by fixed scalar values
// or from a metallic-roughness map
float perceptualRoughness = pbrMaterial.metallicRoughnessValues.y;
float metallic = pbrMaterial.metallicRoughnessValues.x;
// Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
// This layout intentionally reserves the 'r' channel for (optional) occlusion map data
vec4 mrSample = texture(pbr_metallicRoughnessSampler, metallicRoughnessUV);
perceptualRoughness = mrSample.g * perceptualRoughness;
metallic = mrSample.b * metallic;
perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
metallic = clamp(metallic, 0.0, 1.0);
mat3 tbn = getTBN(normalUV);
vec3 n = getNormal(tbn, normalUV); // normal at surface point
vec3 v = normalize(pbrProjection.camera - pbr_vPosition); // Vector from surface point to camera
float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
bool useExtendedPBR =
pbrMaterial.specularColorMapEnabled ||
pbrMaterial.specularIntensityMapEnabled ||
abs(pbrMaterial.specularIntensityFactor - 1.0) > 0.0001 ||
maxComponent(abs(pbrMaterial.specularColorFactor - vec3(1.0))) > 0.0001 ||
abs(pbrMaterial.ior - 1.5) > 0.0001 ||
pbrMaterial.transmissionMapEnabled ||
pbrMaterial.transmissionFactor > 0.0001 ||
pbrMaterial.clearcoatMapEnabled ||
pbrMaterial.clearcoatRoughnessMapEnabled ||
pbrMaterial.clearcoatFactor > 0.0001 ||
pbrMaterial.clearcoatRoughnessFactor > 0.0001 ||
pbrMaterial.sheenColorMapEnabled ||
pbrMaterial.sheenRoughnessMapEnabled ||
maxComponent(pbrMaterial.sheenColorFactor) > 0.0001 ||
pbrMaterial.sheenRoughnessFactor > 0.0001 ||
pbrMaterial.iridescenceMapEnabled ||
pbrMaterial.iridescenceFactor > 0.0001 ||
abs(pbrMaterial.iridescenceIor - 1.3) > 0.0001 ||
abs(pbrMaterial.iridescenceThicknessRange.x - 100.0) > 0.0001 ||
abs(pbrMaterial.iridescenceThicknessRange.y - 400.0) > 0.0001 ||
pbrMaterial.anisotropyMapEnabled ||
pbrMaterial.anisotropyStrength > 0.0001 ||
abs(pbrMaterial.anisotropyRotation) > 0.0001 ||
length(pbrMaterial.anisotropyDirection - vec2(1.0, 0.0)) > 0.0001;
bool useExtendedPBR = false;
if (!useExtendedPBR) {
// Keep the baseline metallic-roughness implementation byte-for-byte equivalent in behavior.
float alphaRoughness = perceptualRoughness * perceptualRoughness;
vec3 f0 = vec3(0.04);
vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
diffuseColor *= 1.0 - metallic;
vec3 specularColor = mix(f0, baseColor.rgb, metallic);
float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
vec3 specularEnvironmentR0 = specularColor.rgb;
vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
vec3 reflection = -normalize(reflect(v, n));
PBRInfo pbrInfo = PBRInfo(
0.0, // NdotL
NdotV,
0.0, // NdotH
0.0, // LdotH
0.0, // VdotH
perceptualRoughness,
metallic,
specularEnvironmentR0,
specularEnvironmentR90,
alphaRoughness,
diffuseColor,
specularColor,
n,
v
);
PBRInfo_setAmbientLight(pbrInfo);
color += calculateFinalColor(pbrInfo, lighting.ambientColor);
for(int i = 0; i < lighting.directionalLightCount; i++) {
if (i < lighting.directionalLightCount) {
PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
}
}
for(int i = 0; i < lighting.pointLightCount; i++) {
if (i < lighting.pointLightCount) {
PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
}
}
for(int i = 0; i < lighting.spotLightCount; i++) {
if (i < lighting.spotLightCount) {
PBRInfo_setSpotLight(pbrInfo, lighting_getSpotLight(i));
float attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), pbr_vPosition);
color += calculateFinalColor(pbrInfo, lighting_getSpotLight(i).color / attenuation);
}
}
if (pbrMaterial.IBLenabled) {
color += getIBLContribution(pbrInfo, n, reflection);
}
if (pbrMaterial.occlusionMapEnabled) {
float ao = texture(pbr_occlusionSampler, occlusionUV).r;
color = mix(color, color * ao, pbrMaterial.occlusionStrength);
}
vec3 emissive = pbrMaterial.emissiveFactor;
if (pbrMaterial.emissiveMapEnabled) {
emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, emissiveUV)).rgb;
}
color += emissive * pbrMaterial.emissiveStrength;
color = mix(color, baseColor.rgb, pbrMaterial.scaleDiffBaseMR.y);
color = mix(color, vec3(metallic), pbrMaterial.scaleDiffBaseMR.z);
color = mix(color, vec3(perceptualRoughness), pbrMaterial.scaleDiffBaseMR.w);
return vec4(pow(color, vec3(1.0 / 2.2)), baseColor.a);
}
float specularIntensity = pbrMaterial.specularIntensityFactor;
if (pbrMaterial.specularIntensityMapEnabled) {
specularIntensity *= texture(pbr_specularIntensitySampler, specularIntensityUV).a;
}
vec3 specularFactor = pbrMaterial.specularColorFactor;
if (pbrMaterial.specularColorMapEnabled) {
specularFactor *= SRGBtoLINEAR(texture(pbr_specularColorSampler, specularColorUV)).rgb;
}
transmission = pbrMaterial.transmissionFactor;
if (pbrMaterial.transmissionMapEnabled) {
transmission *= texture(pbr_transmissionSampler, transmissionUV).r;
}
transmission = clamp(transmission * (1.0 - metallic), 0.0, 1.0);
float thickness = max(pbrMaterial.thicknessFactor, 0.0);
thickness *= texture(pbr_thicknessSampler, thicknessUV).g;
float clearcoatFactor = pbrMaterial.clearcoatFactor;
float clearcoatRoughness = pbrMaterial.clearcoatRoughnessFactor;
if (pbrMaterial.clearcoatMapEnabled) {
clearcoatFactor *= texture(pbr_clearcoatSampler, clearcoatUV).r;
}
if (pbrMaterial.clearcoatRoughnessMapEnabled) {
clearcoatRoughness *= texture(pbr_clearcoatRoughnessSampler, clearcoatRoughnessUV).g;
}
clearcoatFactor = clamp(clearcoatFactor, 0.0, 1.0);
clearcoatRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
vec3 clearcoatNormal = getClearcoatNormal(getTBN(clearcoatNormalUV), n, clearcoatNormalUV);
vec3 sheenColor = pbrMaterial.sheenColorFactor;
float sheenRoughness = pbrMaterial.sheenRoughnessFactor;
if (pbrMaterial.sheenColorMapEnabled) {
sheenColor *= SRGBtoLINEAR(texture(pbr_sheenColorSampler, sheenColorUV)).rgb;
}
if (pbrMaterial.sheenRoughnessMapEnabled) {
sheenRoughness *= texture(pbr_sheenRoughnessSampler, sheenRoughnessUV).a;
}
sheenRoughness = clamp(sheenRoughness, c_MinRoughness, 1.0);
float iridescence = pbrMaterial.iridescenceFactor;
if (pbrMaterial.iridescenceMapEnabled) {
iridescence *= texture(pbr_iridescenceSampler, iridescenceUV).r;
}
iridescence = clamp(iridescence, 0.0, 1.0);
float iridescenceThickness = mix(
pbrMaterial.iridescenceThicknessRange.x,
pbrMaterial.iridescenceThicknessRange.y,
0.5
);
iridescenceThickness = mix(
pbrMaterial.iridescenceThicknessRange.x,
pbrMaterial.iridescenceThicknessRange.y,
texture(pbr_iridescenceThicknessSampler, iridescenceThicknessUV).g
);
float anisotropyStrength = clamp(pbrMaterial.anisotropyStrength, 0.0, 1.0);
vec2 anisotropyDirection = normalizeDirection(pbrMaterial.anisotropyDirection);
if (pbrMaterial.anisotropyMapEnabled) {
vec3 anisotropySample = texture(pbr_anisotropySampler, anisotropyUV).rgb;
anisotropyStrength *= anisotropySample.b;
vec2 mappedDirection = anisotropySample.rg * 2.0 - 1.0;
if (length(mappedDirection) > 0.0001) {
anisotropyDirection = normalize(mappedDirection);
}
}
anisotropyDirection = rotateDirection(anisotropyDirection, pbrMaterial.anisotropyRotation);
vec3 anisotropyTangent = normalize(tbn[0] * anisotropyDirection.x + tbn[1] * anisotropyDirection.y);
if (length(anisotropyTangent) < 0.0001) {
anisotropyTangent = normalize(tbn[0]);
}
float anisotropyViewAlignment = abs(dot(v, anisotropyTangent));
perceptualRoughness = mix(
perceptualRoughness,
clamp(perceptualRoughness * (1.0 - 0.6 * anisotropyViewAlignment), c_MinRoughness, 1.0),
anisotropyStrength
);
// Roughness is authored as perceptual roughness; as is convention,
// convert to material roughness by squaring the perceptual roughness [2].
float alphaRoughness = perceptualRoughness * perceptualRoughness;
float dielectricF0 = getDielectricF0(pbrMaterial.ior);
vec3 dielectricSpecularF0 = min(
vec3(dielectricF0) * specularFactor * specularIntensity,
vec3(1.0)
);
vec3 iridescenceTint = getIridescenceTint(iridescence, iridescenceThickness, NdotV);
dielectricSpecularF0 = mix(
dielectricSpecularF0,
dielectricSpecularF0 * iridescenceTint,
iridescence
);
vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - dielectricSpecularF0);
diffuseColor *= (1.0 - metallic) * (1.0 - transmission);
vec3 specularColor = mix(dielectricSpecularF0, baseColor.rgb, metallic);
float baseLayerEnergy = 1.0 - clearcoatFactor * 0.25;
diffuseColor *= baseLayerEnergy;
specularColor *= baseLayerEnergy;
// Compute reflectance.
float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
// For typical incident reflectance range (between 4% to 100%) set the grazing
// reflectance to 100% for typical fresnel effect.
// For very low reflectance range on highly diffuse objects (below 4%),
// incrementally reduce grazing reflecance to 0%.
float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
vec3 specularEnvironmentR0 = specularColor.rgb;
vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
vec3 reflection = -normalize(reflect(v, n));
PBRInfo pbrInfo = PBRInfo(
0.0, // NdotL
NdotV,
0.0, // NdotH
0.0, // LdotH
0.0, // VdotH
perceptualRoughness,
metallic,
specularEnvironmentR0,
specularEnvironmentR90,
alphaRoughness,
diffuseColor,
specularColor,
n,
v
);
// Apply ambient light
PBRInfo_setAmbientLight(pbrInfo);
color += calculateMaterialLightColor(
pbrInfo,
lighting.ambientColor,
clearcoatNormal,
clearcoatFactor,
clearcoatRoughness,
sheenColor,
sheenRoughness,
anisotropyTangent,
anisotropyStrength
);
// Apply directional light
for(int i = 0; i < lighting.directionalLightCount; i++) {
if (i < lighting.directionalLightCount) {
PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
color += calculateMaterialLightColor(
pbrInfo,
lighting_getDirectionalLight(i).color,
clearcoatNormal,
clearcoatFactor,
clearcoatRoughness,
sheenColor,
sheenRoughness,
anisotropyTangent,
anisotropyStrength
);
}
}
// Apply point light
for(int i = 0; i < lighting.pointLightCount; i++) {
if (i < lighting.pointLightCount) {
PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
color += calculateMaterialLightColor(
pbrInfo,
lighting_getPointLight(i).color / attenuation,
clearcoatNormal,
clearcoatFactor,
clearcoatRoughness,
sheenColor,
sheenRoughness,
anisotropyTangent,
anisotropyStrength
);
}
}
for(int i = 0; i < lighting.spotLightCount; i++) {
if (i < lighting.spotLightCount) {
PBRInfo_setSpotLight(pbrInfo, lighting_getSpotLight(i));
float attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), pbr_vPosition);
color += calculateMaterialLightColor(
pbrInfo,
lighting_getSpotLight(i).color / attenuation,
clearcoatNormal,
clearcoatFactor,
clearcoatRoughness,
sheenColor,
sheenRoughness,
anisotropyTangent,
anisotropyStrength
);
}
}
// Calculate lighting contribution from image based lighting source (IBL)
if (pbrMaterial.IBLenabled) {
color += getIBLContribution(pbrInfo, n, reflection) *
calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
color += calculateClearcoatIBLContribution(
pbrInfo,
clearcoatNormal,
-normalize(reflect(v, clearcoatNormal)),
clearcoatFactor,
clearcoatRoughness
);
color += sheenColor * pbrMaterial.scaleIBLAmbient.x * (1.0 - sheenRoughness) * 0.25;
}
// Apply optional PBR terms for additional (optional) shading
if (pbrMaterial.occlusionMapEnabled) {
float ao = texture(pbr_occlusionSampler, occlusionUV).r;
color = mix(color, color * ao, pbrMaterial.occlusionStrength);
}
vec3 emissive = pbrMaterial.emissiveFactor;
if (pbrMaterial.emissiveMapEnabled) {
emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, emissiveUV)).rgb;
}
color += emissive * pbrMaterial.emissiveStrength;
if (transmission > 0.0) {
color = mix(color, color * getVolumeAttenuation(thickness), transmission);
}
// This section uses mix to override final color for reference app visualization
// of various parameters in the lighting equation.
// TODO: Figure out how to debug multiple lights
// color = mix(color, F, pbr_scaleFGDSpec.x);
// color = mix(color, vec3(G), pbr_scaleFGDSpec.y);
// color = mix(color, vec3(D), pbr_scaleFGDSpec.z);
// color = mix(color, specContrib, pbr_scaleFGDSpec.w);
// color = mix(color, diffuseContrib, pbr_scaleDiffBaseMR.x);
color = mix(color, baseColor.rgb, pbrMaterial.scaleDiffBaseMR.y);
color = mix(color, vec3(metallic), pbrMaterial.scaleDiffBaseMR.z);
color = mix(color, vec3(perceptualRoughness), pbrMaterial.scaleDiffBaseMR.w);
}
float alpha = clamp(baseColor.a * (1.0 - transmission), 0.0, 1.0);
return vec4(pow(color,vec3(1.0/2.2)), alpha);
}
`;
//# sourceMappingURL=pbr-material-glsl.js.map