Source: material/PBRMaterial.js

import Class from '../core/Class';
import Color from '../math/Color';
import Material from './Material';
import capabilities from '../renderer/capabilities';

/**
 * PBR材质
 * @class
 * @extends Material
 * @example
 * const material = new Hilo3d.PBRMaterial();
 */
const PBRMaterial = Class.create(/** @lends PBRMaterial.prototype */ {
    Extends: Material,

    /**
     * @default true
     * @type {boolean}
     */
    isPBRMaterial: true,

    /**
     * @default PBRMaterial
     * @type {string}
     */
    className: 'PBRMaterial',

    /**
     * 光照类型,只能为 PBR 或 NONE
     * @default PBR
     * @type {'PBR'|'NONE'}
     */
    lightType: 'PBR',

    /**
     * gammaCorrection
     * @type {Boolean}
     * @default true
     */
    gammaCorrection: true,

    /**
     * 是否使用物理灯光
     * @type {Boolean}
     * @default true
     */
    usePhysicsLight: true,

    /**
     * 基础颜色
     * @default null
     * @type {Color}
     */
    baseColor: null,

    /**
     * 基础颜色贴图(sRGB空间)
     * @default null
     * @type {Texture}
     */
    baseColorMap: null,

    /**
     * 金属度
     * @default 1
     * @type {Number}
     */
    metallic: 1,

    /**
     * 金属度贴图
     * @default null
     * @type {Texture}
     */
    metallicMap: null,

    /**
     * 粗糙度
     * @default 1
     * @type {Number}
     */
    roughness: 1,

    /**
     * 粗糙度贴图
     * @default null
     * @type {Texture}
     */
    roughnessMap: null,

    /**
     * 金属度及粗糙度贴图,金属度为B通道,粗糙度为G通道,可以指定R通道作为环境光遮蔽
     * @default null
     * @type {Texture}
     */
    metallicRoughnessMap: null,

    /**
     * 环境光遮蔽贴图
     * @default null
     * @type {Texture}
     */
    occlusionMap: null,
    /**
     * 环境光遮蔽强度
     * @default 1
     * @type {Number}
     */
    occlusionStrength: 1,

    /**
     * 环境光遮蔽贴图(occlusionMap)包含在 metallicRoughnessMap 的R通道中
     * @default false
     * @type {boolean}
     */
    isOcclusionInMetallicRoughnessMap: false,

    /**
     * 漫反射辐照(Diffuse IBL)贴图
     * @default null
     * @type {CubeTexture|Texture}
     */
    diffuseEnvMap: null,
    /**
     * 漫反射 SphericalHarmonics3
     * @type {SphericalHarmonics3}
     */
    diffuseEnvSphereHarmonics3: null,

    /**
     * 漫反射环境强度
     * @default 1
     * @type {Number}
     */
    diffuseEnvIntensity: 1,

    /**
     * BRDF贴图,跟环境反射贴图一起使用 [示例]{@link https://gw.alicdn.com/tfs/TB1EvwBRFXXXXbNXpXXXXXXXXXX-256-256.png}
     * @default null
     * @type {Texture}
     */
    brdfLUT: null,

    /**
     * 环境反射(Specular IBL)贴图强度
     * @default 1
     * @type {Number}
     */
    specularEnvIntensity: 1,

    /**
     * 环境反射(Specular IBL)贴图
     * @default null
     * @type {CubeTexture|Texture}
     */
    specularEnvMap: null,

    /**
     * 环境反射是否包含 mipmaps
     * @default false
     * @type {boolean}
     */
    isSpecularEnvMapIncludeMipmaps: false,

    /**
     * 放射光贴图(sRGB 空间)
     * @default null
     * @type {Texture}
     */
    emission: null,

    /**
     * The emissive color of the material.
     * @default new Color(0, 0, 0)
     * @type {Color}
     */
    emissionFactor: null,

    /**
     * 是否基于反射光泽度的 PBR,具体见 [KHR_materials_pbrSpecularGlossiness]{@link https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_pbrSpecularGlossiness}
     * @default false
     * @type {boolean}
     */
    isSpecularGlossiness: false,

    /**
     * 镜面反射率,针对 isSpecularGlossiness 渲染
     * @default Color(1, 1, 1)
     * @type {Color}
     */
    specular: null,

    /**
     * 光泽度,针对 isSpecularGlossiness 渲染,默认PBR无效
     * @default 1
     * @type {number}
     */
    glossiness: 1,

    /**
     * 镜面反射即光泽度贴图,RGB 通道为镜面反射率,A 通道为光泽度
     * @default null
     * @type {Texture}
     */
    specularGlossinessMap: null,

    /**
     * The clearcoat layer intensity.
     * @default 0
     * @type {number}
     */
    clearcoatFactor: 0,

    /**
     * The clearcoat layer intensity texture.
     * @default null
     * @type {Texture}
     */
    clearcoatMap: null,

    /**
     * The clearcoat layer roughness.
     * @default 0
     * @type {number}
     */
    clearcoatRoughnessFactor: 0,

    /**
     * The clearcoat layer roughness texture.
     * @default null
     * @type {Texture}
     */
    clearcoatRoughnessMap: null,

    /**
     * The clearcoat normal map texture.
     * @default null
     * @type {Texture}
     */
    clearcoatNormalMap: null,

    usedUniformVectors: 11,

    /**
     * @constructs
     * @param {Object} [params] 初始化参数,所有params都会复制到实例上
     * @param {string} [params.lightType=PBR] 光照类型,只能为 PBR 或 NONE
     * @param {Color} [params.baseColor=new Color(1, 1, 1)] 基础颜色
     * @param {Texture} [params.baseColorMap] 基础颜色贴图(sRGB空间)
     * @param {number} [params.metallic=1] 金属度
     * @param {Texture} [params.metallicMap] 金属度贴图
     * @param {number} [params.roughness=1] 粗糙度
     * @param {Texture} [params.roughnessMap] 粗糙度贴图
     * @param {Texture} [params.occlusionMap] 环境光遮蔽贴图
     * @param {number} [params.occlusionStrength=1] 环境光遮蔽强度
     * @param {Texture|Color} [params.emission] 放射光贴图(sRGB 空间),或颜色
     * @param {Texture} [params.diffuseEnvMap] 漫反射辐照(Diffuse IBL)贴图
     * @param {SphericalHarmonics3} [params.diffuseEnvSphereHarmonics3] 漫反射 SphericalHarmonics3
     * @param {number} [params.diffuseEnvIntensity=1] 漫反射强度
     * @param {Texture} [params.specularEnvMap] 环境反射(Specular IBL)贴图
     * @param {Texture} [params.brdfLUT] BRDF贴图,跟环境反射贴图一起使用
     * @param {number} [params.specularEnvIntensity=1] 环境反射(Specular IBL)贴图强度
     * @param {any} [params.[value:string]] 其它属性
     */
    constructor(params) {
        this.baseColor = new Color(1, 1, 1);
        this.specular = new Color(1, 1, 1);
        this.emissionFactor = new Color(0, 0, 0);

        PBRMaterial.superclass.constructor.call(this, params);

        Object.assign(this.uniforms, {
            u_baseColor: 'BASECOLOR',
            u_metallic: 'METALLIC',
            u_roughness: 'ROUGHNESS',
            u_specular: 'SPECULAR',
            u_emissionFactor: 'EMISSIONFACTOR',
            u_glossiness: 'GLOSSINESS',
            u_brdfLUT: 'BRDFLUT',
            u_diffuseEnvMap: 'DIFFUSEENVMAP',
            u_diffuseEnvIntensity: 'DIFFUSEENVINTENSITY',
            u_occlusionStrength: 'OCCLUSIONSTRENGTH',
            u_specularEnvMap: 'SPECULARENVMAP',
            u_specularEnvIntensity: 'SPECULARENVINTENSITY',
            u_specularEnvMapMipCount: 'SPECULARENVMAPMIPCOUNT',
            u_diffuseEnvSphereHarmonics3: 'DIFFUSEENVSPHEREHARMONICS3',
            u_clearcoatFactor: 'CLEARCOATFACTOR',
            u_clearcoatRoughnessFactor: 'CLEARCOATROUGHNESSFACTOR',
        });

        this.addTextureUniforms({
            u_baseColorMap: 'BASECOLORMAP',
            u_metallicMap: 'METALLICMAP',
            u_roughnessMap: 'ROUGHNESSMAP',
            u_metallicRoughnessMap: 'METALLICROUGHNESSMAP',
            u_occlusionMap: 'OCCLUSIONMAP',
            u_specularGlossinessMap: 'SPECULARGLOSSINESSMAP',
            u_lightMap: 'LIGHTMAP',
            u_clearcoatMap: 'CLEARCOATMAP',
            u_clearcoatRoughnessMap: 'CLEARCOATROUGHNESSMAP',
            u_clearcoatNormalMap: 'CLEARCOATNORMALMAP',
        });
    },
    getRenderOption(option = {}) {
        PBRMaterial.superclass.getRenderOption.call(this, option);
        const textureOption = this._textureOption.reset(option);

        textureOption.add(this.baseColorMap, 'BASE_COLOR_MAP');
        textureOption.add(this.metallicMap, 'METALLIC_MAP');
        textureOption.add(this.roughnessMap, 'ROUGHNESS_MAP');
        textureOption.add(this.metallicRoughnessMap, 'METALLIC_ROUGHNESS_MAP');
        textureOption.add(this.diffuseEnvMap, 'DIFFUSE_ENV_MAP');
        textureOption.add(this.occlusionMap, 'OCCLUSION_MAP');
        textureOption.add(this.lightMap, 'LIGHT_MAP');

        if (this.brdfLUT) {
            textureOption.add(this.specularEnvMap, 'SPECULAR_ENV_MAP');

            if (capabilities.SHADER_TEXTURE_LOD && this.specularEnvMap) {
                option.USE_SHADER_TEXTURE_LOD = 1;
            }
        }

        if (this.isSpecularGlossiness) {
            option.PBR_SPECULAR_GLOSSINESS = 1;
            textureOption.add(this.specularGlossinessMap, 'SPECULAR_GLOSSINESS_MAP');
        }

        if (this.isOcclusionInMetallicRoughnessMap) {
            option.IS_OCCLUSION_MAP_IN_METALLIC_ROUGHNESS_MAP = 1;
        }

        if (this.occlusionStrength !== 1) {
            option.OCCLUSION_STRENGTH = 1;
        }

        if (this.diffuseEnvSphereHarmonics3) {
            option.HAS_NORMAL = 1;
            option.DIFFUSE_ENV_SPHERE_HARMONICS3 = 1;
        }

        if (this.specularEnvMap || this.diffuseEnvSphereHarmonics3 || this.specularEnvMap) {
            option.NEED_WORLD_NORMAL = 1;
        }

        if (this.specularEnvMap && this.isSpecularEnvMapIncludeMipmaps) {
            option.IS_SPECULAR_ENV_MAP_INCLUDE_MIPMAPS = 1;
        }

        if (this.clearcoatFactor > 0) {
            option.HAS_CLEARCOAT = 1;
            option.HAS_NORMAL = 1;

            if (this.clearcoatMap) {
                textureOption.add(this.clearcoatMap, 'CLEARCOAT_MAP');
            }

            if (this.clearcoatNormalMap) {
                textureOption.add(this.clearcoatNormalMap, 'CLEARCOAT_NORMAL_MAP');
            }

            if (this.clearcoatRoughnessMap) {
                textureOption.add(this.clearcoatRoughnessMap, 'CLEARCOAT_ROUGHNESS_MAP');
            }
        }

        textureOption.update();

        return option;
    }
});

export default PBRMaterial;