Source: MeshGouraudMaterial.js

/** @module */

/**
 * @file
 *
 * Summary.
 *
 * <p>MeshGouraudMaterial is an addon material in Three.js
 * used to implement per-vertex (Gouraud) shading.</p>
 * In early versions of Three.js, this per-vertex behavior belonged to MeshLambertMaterial.
 * However, starting from r144, MeshLambertMaterial was upgraded to use
 * per-fragment (Phong) shading to improve visual quality.
 * To preserve the low-cost, retro look of vertex-based lighting for specific use cases,
 * the original functionality was moved into the examples folder as MeshGouraudMaterial.
 *
 * @author Ricardo Cabello ({@link https://coopermrdoob.weebly.com/ Mr.doob})
 * @see <a href="/cwdc/13-webgl/examples/three/content/MeshGouraudMaterial.js">source</a>
 *
 */

import {
  UniformsUtils,
  UniformsLib,
  ShaderMaterial,
  Color,
  MultiplyOperation,
} from "three";

const GouraudShader = {
  name: "GouraudShader",

  uniforms: UniformsUtils.merge([
    UniformsLib.common,
    UniformsLib.specularmap,
    UniformsLib.envmap,
    UniformsLib.aomap,
    UniformsLib.lightmap,
    UniformsLib.emissivemap,
    UniformsLib.fog,
    UniformsLib.lights,
    {
      emissive: { value: new Color(0x000000) },
    },
  ]),

  vertexShader: /* glsl */ `

		#define GOURAUD

		varying vec3 vLightFront;
		varying vec3 vIndirectFront;

		#ifdef DOUBLE_SIDED
			varying vec3 vLightBack;
			varying vec3 vIndirectBack;
		#endif

		#include <common>
		#include <uv_pars_vertex>
		#include <envmap_pars_vertex>
		#include <bsdfs>
		#include <lights_pars_begin>
		#include <color_pars_vertex>
		#include <fog_pars_vertex>
		#include <morphtarget_pars_vertex>
		#include <skinning_pars_vertex>
		#include <shadowmap_pars_vertex>
		#include <logdepthbuf_pars_vertex>
		#include <clipping_planes_pars_vertex>

		void main() {

			#include <uv_vertex>
			#include <color_vertex>
			#include <morphcolor_vertex>

			#include <beginnormal_vertex>
			#include <morphnormal_vertex>
			#include <skinbase_vertex>
			#include <skinnormal_vertex>
			#include <defaultnormal_vertex>

			#include <begin_vertex>
			#include <morphtarget_vertex>
			#include <skinning_vertex>
			#include <project_vertex>
			#include <logdepthbuf_vertex>
			#include <clipping_planes_vertex>

			#include <worldpos_vertex>
			#include <envmap_vertex>

			// inlining legacy <lights_lambert_vertex>

			vec3 diffuse = vec3( 1.0 );

			vec3 geometryPosition = mvPosition.xyz;
			vec3 geometryNormal = normalize( transformedNormal );
			vec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );

			vec3 backGeometryNormal = - geometryNormal;

			vLightFront = vec3( 0.0 );
			vIndirectFront = vec3( 0.0 );
			#ifdef DOUBLE_SIDED
				vLightBack = vec3( 0.0 );
				vIndirectBack = vec3( 0.0 );
			#endif

			IncidentLight directLight;
			float dotNL;
			vec3 directLightColor_Diffuse;

			vIndirectFront += getAmbientLightIrradiance( ambientLightColor );

			#if defined( USE_LIGHT_PROBES )

				vIndirectFront += getLightProbeIrradiance( lightProbe, geometryNormal );

			#endif

			#ifdef DOUBLE_SIDED

				vIndirectBack += getAmbientLightIrradiance( ambientLightColor );

				#if defined( USE_LIGHT_PROBES )

					vIndirectBack += getLightProbeIrradiance( lightProbe, backGeometryNormal );

				#endif

			#endif

			#if NUM_POINT_LIGHTS > 0

				#pragma unroll_loop_start
				for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {

					getPointLightInfo( pointLights[ i ], geometryPosition, directLight );

					dotNL = dot( geometryNormal, directLight.direction );
					directLightColor_Diffuse = directLight.color;

					vLightFront += saturate( dotNL ) * directLightColor_Diffuse;

					#ifdef DOUBLE_SIDED

						vLightBack += saturate( - dotNL ) * directLightColor_Diffuse;

					#endif

				}
				#pragma unroll_loop_end

			#endif

			#if NUM_SPOT_LIGHTS > 0

				#pragma unroll_loop_start
				for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {

					getSpotLightInfo( spotLights[ i ], geometryPosition, directLight );

					dotNL = dot( geometryNormal, directLight.direction );
					directLightColor_Diffuse = directLight.color;

					vLightFront += saturate( dotNL ) * directLightColor_Diffuse;

					#ifdef DOUBLE_SIDED

						vLightBack += saturate( - dotNL ) * directLightColor_Diffuse;

					#endif
				}
				#pragma unroll_loop_end

			#endif

			#if NUM_DIR_LIGHTS > 0

				#pragma unroll_loop_start
				for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {

					getDirectionalLightInfo( directionalLights[ i ], directLight );

					dotNL = dot( geometryNormal, directLight.direction );
					directLightColor_Diffuse = directLight.color;

					vLightFront += saturate( dotNL ) * directLightColor_Diffuse;

					#ifdef DOUBLE_SIDED

						vLightBack += saturate( - dotNL ) * directLightColor_Diffuse;

					#endif

				}
				#pragma unroll_loop_end

			#endif

			#if NUM_HEMI_LIGHTS > 0

				#pragma unroll_loop_start
				for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {

					vIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal );

					#ifdef DOUBLE_SIDED

						vIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometryNormal );

					#endif

				}
				#pragma unroll_loop_end

			#endif

			#include <shadowmap_vertex>
			#include <fog_vertex>

		}`,

  fragmentShader: /* glsl */ `

		#define GOURAUD

		uniform vec3 diffuse;
		uniform vec3 emissive;
		uniform float opacity;

		varying vec3 vLightFront;
		varying vec3 vIndirectFront;

		#ifdef DOUBLE_SIDED
			varying vec3 vLightBack;
			varying vec3 vIndirectBack;
		#endif

		#include <common>
		#include <dithering_pars_fragment>
		#include <color_pars_fragment>
		#include <uv_pars_fragment>
		#include <map_pars_fragment>
		#include <alphamap_pars_fragment>
		#include <alphatest_pars_fragment>
		#include <aomap_pars_fragment>
		#include <lightmap_pars_fragment>
		#include <emissivemap_pars_fragment>
		#include <envmap_common_pars_fragment>
		#include <envmap_pars_fragment>
		#include <bsdfs>
		#include <lights_pars_begin>
		#include <fog_pars_fragment>
		#include <shadowmap_pars_fragment>
		#include <shadowmask_pars_fragment>
		#include <specularmap_pars_fragment>
		#include <logdepthbuf_pars_fragment>
		#include <clipping_planes_pars_fragment>

		void main() {

			#include <clipping_planes_fragment>

			vec4 diffuseColor = vec4( diffuse, opacity );
			ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
			vec3 totalEmissiveRadiance = emissive;

			#include <logdepthbuf_fragment>
			#include <map_fragment>
			#include <color_fragment>
			#include <alphamap_fragment>
			#include <alphatest_fragment>
			#include <specularmap_fragment>
			#include <emissivemap_fragment>

			// accumulation

			#ifdef DOUBLE_SIDED

				reflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;

			#else

				reflectedLight.indirectDiffuse += vIndirectFront;

			#endif

			#ifdef USE_LIGHTMAP

				vec4 lightMapTexel = texture2D( lightMap, vLightMapUv );
				vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;
				reflectedLight.indirectDiffuse += lightMapIrradiance;

			#endif

			reflectedLight.indirectDiffuse *= BRDF_Lambert( diffuseColor.rgb );

			#ifdef DOUBLE_SIDED

				reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;

			#else

				reflectedLight.directDiffuse = vLightFront;

			#endif

			reflectedLight.directDiffuse *= BRDF_Lambert( diffuseColor.rgb ) * getShadowMask();

			// modulation

			#include <aomap_fragment>

			vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;

			#include <envmap_fragment>

			#include <opaque_fragment>
			#include <tonemapping_fragment>
			#include <colorspace_fragment>
			#include <fog_fragment>
			#include <premultiplied_alpha_fragment>
			#include <dithering_fragment>

		}`,
};

/**
 * Class for {@link https://en.wikipedia.org/wiki/Gouraud_shading Gouraud shading}.
 * @class
 * @extends THREE.Material
 */
class MeshGouraudMaterial extends ShaderMaterial {
  constructor(parameters) {
    super();

    console.warn(
      "THREE.MeshGouraudMaterial: MeshGouraudMaterial has been deprecated and will be removed with r183. Use THREE.MeshLambertMaterial instead.",
    ); // @deprecated r173

    this.isMeshGouraudMaterial = true;

    this.type = "MeshGouraudMaterial";

    //this.color = new THREE.Color( 0xffffff ); // diffuse

    //this.map = null;

    //this.lightMap = null;
    //this.lightMapIntensity = 1.0;

    //this.aoMap = null;
    //this.aoMapIntensity = 1.0;

    //this.emissive = new THREE.Color( 0x000000 );
    //this.emissiveIntensity = 1.0;
    //this.emissiveMap = null;

    //this.specularMap = null;

    //this.alphaMap = null;

    //this.envMap = null;
    this.combine = MultiplyOperation; // combine has no uniform
    //this.reflectivity = 1;
    //this.refractionRatio = 0.98;

    this.fog = false; // set to use scene fog
    this.lights = true; // set to use scene lights
    this.clipping = false; // set to use user-defined clipping planes

    const shader = GouraudShader;

    this.defines = Object.assign({}, shader.defines);
    this.uniforms = UniformsUtils.clone(shader.uniforms);
    this.vertexShader = shader.vertexShader;
    this.fragmentShader = shader.fragmentShader;

    const exposePropertyNames = [
      "map",
      "lightMap",
      "lightMapIntensity",
      "aoMap",
      "aoMapIntensity",
      "emissive",
      "emissiveIntensity",
      "emissiveMap",
      "specularMap",
      "alphaMap",
      "envMap",
      "reflectivity",
      "refractionRatio",
      "opacity",
      "diffuse",
    ];

    for (const propertyName of exposePropertyNames) {
      Object.defineProperty(this, propertyName, {
        get: function () {
          return this.uniforms[propertyName].value;
        },

        set: function (value) {
          this.uniforms[propertyName].value = value;
        },
      });
    }

    Object.defineProperty(
      this,
      "color",
      Object.getOwnPropertyDescriptor(this, "diffuse"),
    );

    this.setValues(parameters);
  }

  copy(source) {
    super.copy(source);

    this.color.copy(source.color);

    this.map = source.map;

    this.lightMap = source.lightMap;
    this.lightMapIntensity = source.lightMapIntensity;

    this.aoMap = source.aoMap;
    this.aoMapIntensity = source.aoMapIntensity;

    this.emissive.copy(source.emissive);
    this.emissiveMap = source.emissiveMap;
    this.emissiveIntensity = source.emissiveIntensity;

    this.specularMap = source.specularMap;

    this.alphaMap = source.alphaMap;

    this.envMap = source.envMap;
    this.combine = source.combine;
    this.reflectivity = source.reflectivity;
    this.refractionRatio = source.refractionRatio;

    this.wireframe = source.wireframe;
    this.wireframeLinewidth = source.wireframeLinewidth;
    this.wireframeLinecap = source.wireframeLinecap;
    this.wireframeLinejoin = source.wireframeLinejoin;

    this.fog = source.fog;

    return this;
  }
}

export { MeshGouraudMaterial };