javascriptthree.jswebglaframewebgl-extensions

How to create custom shaders using THREE.ShaderLib


I have been trying to learn THREEJS shader materials. So far I understand how uniforms, vertexShader, and fragmentShader play a role in projecting and coloring the vertices and fragments in the world of glsl and webgl. I have been trying to find some good examples where ShaderMaterial of THREEJS is extended using the THREE.ShaderLib.

Suppose I want to extend a standard threejs material (THREE.ShaderLib['standard']) to write the envmap texture, is that possible? Or is it absolutely necessary that I write everything from scratch?


Solution

  • Shaders are just strings, it's up to you what you do with them and how you obtain them. With that being said, three.js has numerous tools to help you build them better.

    At the highest level, there is an abstraction in form of THREE.Material. Where you describe abstract properties and three configures the shader under the hood.

    //no shaders involved
    var redPlastic = new THREE.MeshStandardMaterial({
       color: 'red',
       roughness: notVeryRough
    }) 
    

    ShaderMaterial expects you to write raw GLSL, but still includes some things that you would otherwise have to do manually. So "writing from scratch" is a not entirely correct. With RawShaderMaterial you would write everything from scratch. THREE.ShaderMaterial:

    varying vec2 vUv;
    void main(){
      vUv = uv; // <- magic! where does uv come from?
      vec4 worldPosition = modelMatrix * vec4( position, 1.); // <- more magic! where do modelMatrix and position come from?
      gl_Position = projectionMatrix * viewMatrix * worldPosition; // more!
    }
    

    At runtime, when three is compiled and included on a page, the THREE namespace has THREE.ShaderChunk dictionary. These are various named snippets of GLSL that all the materials are built out of.

    You can copy these snippets from their source files, and paste them in your own shader file, or string.

    You can write it with a string template:

    `${THREE.ShaderChunk.some_chunk}
     void main(){
     ...
     ${anotherChunk}
     gl_Position = ...
    `
    

    But if you want to extend the built in materials, there is a (IMHO buggy and wonky :)) feature of the material called onBeforeCompile. With this, you pass a callback to any of the built in materials, and get the shader object before compilation. Here you can inject your own glsl, swap out chunks or anything else you can think of doing to a string.

    In order to use this one needs to be familiar with the structure of the shaders built from: https://github.com/mrdoob/three.js/tree/dev/src/renderers/shaders/ShaderChunk