body {
	background-color: #000;
	margin: 0px;
	overflow: hidden;
}

var mesh, renderer, scene, camera, controls;



    renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );

    scene = new THREE.Scene();
    
    camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
    camera.position.set( 150, 100, 150 );
    camera.lookAt(new THREE.Vector3(0, 0, 0));

    controls = new THREE.OrbitControls( camera, document.body );
    
    scene.add( new THREE.AmbientLight( 0x222222 ) );
    
    var light = new THREE.DirectionalLight( 0xffffff, 1 );
    light.position.set( 80, 80, 80 );
    scene.add( light );
  
    scene.add( new THREE.AxesHelper( 20 ) );
    
  
  
  const loader = new THREE.TextureLoader;

    var geometry = new THREE.PlaneBufferGeometry( 100, 100 );
  geometry.rotateX( -90 * THREE.Math.DEG2RAD );
  geometry.translate(50, 0, 50)
  

    // Patches

    function DecalPatch(shader) {
        
        // Use string.replace or this helper https://github.com/Fyrestar/ShaderMaterialExtend
        
        THREE.patchShader(shader, {
          
          header: 'uniform sampler2D tDecal; uniform vec4 uDecal;',
          
          fragment: {
              '#include <fog_fragment>': `

              vec2 offset = uDecal.xy + vUv / uDecal.zw;
              gl_FragColor += texture2D(tDecal, offset);

`  
          },
          
          uniforms: {
            uDecal: new THREE.Vector4(-0.4, -0.6, .3, .3),
            tDecal: loader.load('https://threejs.org/examples/textures/sprite0.jpg')
          }
          
        });
        
      }

    function TintBluePatch(shader) {
      
      
        THREE.patchShader(shader, {
          
          
          fragment: {
              '#include <fog_fragment>': 'gl_FragColor.rgb *= vec3(0.0, 0.0, 1.0);'
          }
          
        });
      
    }


    /*
    Even though TintBluePatch would turn all blue by the assigned order, DecalPatch wil be bascially called after TintBluePatch in shader code, higher priority = closer to the end of the shader code assuming all patches insert their code like:
    
    shader.fragmentShader = shader.fragmentShader.replace(
      '#include <fog_fragment>',
      '#include <fog_fragment>' + newCode
    );
    
    */

    DecalPatch.priority = 1;

    var material = new THREE.MeshPhongMaterial({
      map: loader.load('https://threejs.org/examples/textures/brick_diffuse.jpg')
    });


    material.onBeforeCompile = [DecalPatch, TintBluePatch];

    mesh = new THREE.Mesh( geometry, material );
  
    scene.add( mesh );

  function animate() {

      requestAnimationFrame( animate );

      renderer.render( scene, camera );

  }



animate();
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://threejs.org/build/three.js
  2. https://threejs.org/examples/js/controls/OrbitControls.js
  3. https://threejs.org/examples/js/controls/TransformControls.js
  4. https://mevedia.com/share/ChainableOnBeforeCompile.js?x
  5. https://mevedia.com/share/ShaderMaterialExtend.js