import * as THREE from 'three'; import { MeshStandardMaterial, MeshStandardMaterialParameters } from 'three'; export interface SpecularGlossinessMaterialParameters extends MeshStandardMaterialParameters { // #region Properties (4) glossiness?: number | undefined; glossinessMap?: THREE.Texture | null | undefined; specular?: THREE.ColorRepresentation | undefined; specularMap2?: THREE.Texture | null | undefined; // #endregion Properties (4) } export class SpecularGlossinessMaterial extends MeshStandardMaterial { // #region Properties (1) public isGLTFSpecularGlossinessMaterial: boolean; // #endregion Properties (1) // #region Constructors (1) constructor(params: SpecularGlossinessMaterialParameters) { super(); this.isGLTFSpecularGlossinessMaterial = true; //various chunks that need replacing const specularMap2ParsFragmentChunk = [ '#ifdef USE_SPECULARMAP2', ' uniform sampler2D specularMap2;', '#endif' ].join('\n'); const glossinessMapParsFragmentChunk = [ '#ifdef USE_GLOSSINESSMAP', ' uniform sampler2D glossinessMap;', '#endif' ].join('\n'); const specularMap2FragmentChunk = [ 'vec3 specularFactor = specular;', '#ifdef USE_SPECULARMAP2', ' vec4 texelSpecular = texture2D( specularMap2, vUv );', ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', ' specularFactor *= texelSpecular.rgb;', '#endif' ].join('\n'); const glossinessMapFragmentChunk = [ 'float glossinessFactor = glossiness;', '#ifdef USE_GLOSSINESSMAP', ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );', ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', ' glossinessFactor *= texelGlossiness.a;', '#endif' ].join('\n'); const lightPhysicalFragmentChunk = [ 'PhysicalMaterial material;', 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );', 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', 'material.roughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.', 'material.roughness += geometryRoughness;', 'material.roughness = min( material.roughness, 1.0 );', 'material.specularColor = specularFactor;', ].join('\n'); const uniforms: { [key: string]: { value: any } } = { specular: { value: new THREE.Color().setHex(0xffffff) }, glossiness: { value: 1 }, specularMap2: { value: null }, glossinessMap: { value: null } }; (this)._extraUniforms = uniforms; this.onBeforeCompile = function (shader) { for (const uniformName in uniforms) { shader.uniforms[uniformName] = uniforms[uniformName]; } shader.fragmentShader = shader.fragmentShader .replace('uniform float roughness;', 'uniform vec3 specular;') .replace('uniform float metalness;', 'uniform float glossiness;') .replace('#include ', specularMap2ParsFragmentChunk) .replace('#include ', glossinessMapParsFragmentChunk) .replace('#include ', specularMap2FragmentChunk) .replace('#include ', glossinessMapFragmentChunk) .replace('#include ', lightPhysicalFragmentChunk); }; Object.defineProperties(this, { specular: { get: function () { return uniforms.specular.value; }, set: function (v) { uniforms.specular.value = v; } }, specularMap2: { get: function () { return uniforms.specularMap2.value; }, set: function (v) { uniforms.specularMap2.value = v; if (v) { this.defines.USE_SPECULARMAP2 = ''; // USE_UV is set by the renderer for specular maps } else { delete this.defines.USE_SPECULARMAP2; } } }, glossiness: { get: function () { return uniforms.glossiness.value; }, set: function (v) { uniforms.glossiness.value = v; } }, glossinessMap: { get: function () { return uniforms.glossinessMap.value; }, set: function (v) { uniforms.glossinessMap.value = v; if (v) { this.defines.USE_GLOSSINESSMAP = ''; this.defines.USE_UV = ''; } else { delete this.defines.USE_GLOSSINESSMAP; delete this.defines.USE_UV; } } } }); delete (this).metalness; delete (this).roughness; delete (this).metalnessMap; delete (this).roughnessMap; this.setValues(params); } // #endregion Constructors (1) // #region Public Methods (1) public copy(source: SpecularGlossinessMaterial) { super.copy(source); (this).specularMap2 = (source).specularMap2; (this).specular.copy((source).specular); (this).glossinessMap = (source).glossinessMap; (this).glossiness = (source).glossiness; delete (this).metalness; delete (this).roughness; delete (this).metalnessMap; delete (this).roughnessMap; return this; } // #endregion Public Methods (1) }