Extending Uniforms for customized Instanced Lambert Material

Hi all, I created an customized lambert material for instancing (based on: https://threejs.org/examples/?q=instanc#webgl_buffergeometry_instancing_lambert), and I was trying to give extends its uniforms with some parameters relating to clipping, but somehow the material doesn’t seem to recognize it. Maybe the way I add uniform was wrong? could you guys help me here?

export function getMoleculeMaterialInstanced(options) {
    var uniforms = THREE.UniformsUtils.merge([
        THREE.ShaderLib.lambert.uniforms,
        {
            xClippingPlaneMax: { type: 'f', value: options.x_high },
            xClippingPlaneMin: { type: 'f', value: options.x_low  },
            yClippingPlaneMax: { type: 'f', value: options.y_high },
            yClippingPlaneMin: { type: 'f', value: options.y_low  },
            zClippingPlaneMax: { type: 'f', value: options.z_high },
            zClippingPlaneMin: { type: 'f', value: options.z_low  },
        }
      ]);
    var material = new THREE.MeshLambertMaterial( {
        uniforms: uniforms,
        vertexColors: THREE.VertexColors,
    } );
    material.onBeforeCompile = function( shader ) {
        shader.vertexShader = `
            #define LAMBERT
            // instanced
            attribute vec3 offset;
            // attribute vec3 instanceColor;
            // attribute float instanceScale;
            varying vec3 vLightFront;
            varying vec3 vIndirectFront;
            varying vec3 slicePosition;
        
            #ifdef DOUBLE_SIDED
                varying vec3 vLightBack;
                varying vec3 vIndirectBack;
            #endif
        
            #include <common>
            #include <uv_pars_vertex>
            #include <uv2_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 <uv2_vertex>
                #include <color_vertex>
        
                // vertex colors instanced
                // #ifdef USE_COLOR
                //  vColor.xyz = instanceColor.xyz;
                // #endif
        
                #include <beginnormal_vertex>
                #include <morphnormal_vertex>
                #include <skinbase_vertex>
                #include <skinnormal_vertex>
                #include <defaultnormal_vertex>
        
                #include <begin_vertex>
        
                // position instanced
                // transformed *= instanceScale;
                // transformed = transformed + instanceOffset;
                transformed = transformed + offset;
                slicePosition = transformed;
        
                #include <morphtarget_vertex>
                #include <skinning_vertex>
                #include <project_vertex>
                #include <logdepthbuf_vertex>
                #include <clipping_planes_vertex>
        
                #include <worldpos_vertex>
                #include <envmap_vertex>
                #include <lights_lambert_vertex>
                #include <shadowmap_vertex>
                #include <fog_vertex>
        
            }
            `;

        shader.fragmentShader = `
        uniform vec3 diffuse;
        uniform vec3 emissive;
        uniform float opacity;
        
        varying vec3 vLightFront;
        varying vec3 vIndirectFront;
        varying vec3 slicePosition;
        uniform float xClippingPlaneMax;
        uniform float xClippingPlaneMin;
        uniform float yClippingPlaneMax;
        uniform float yClippingPlaneMin;
        uniform float zClippingPlaneMax;
        uniform float zClippingPlaneMin;
        
        #ifdef DOUBLE_SIDED
            varying vec3 vLightBack;
            varying vec3 vIndirectBack;
        #endif
                
        #include <common>
        #include <packing>
        #include <dithering_pars_fragment>
        #include <color_pars_fragment>
        #include <uv_pars_fragment>
        #include <uv2_pars_fragment>
        #include <map_pars_fragment>
        #include <alphamap_pars_fragment>
        #include <aomap_pars_fragment>
        #include <lightmap_pars_fragment>
        #include <emissivemap_pars_fragment>
        #include <envmap_common_pars_fragment>
        #include <envmap_pars_fragment>
        #include <cube_uv_reflection_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() {
            if(slicePosition.x<xClippingPlaneMin) discard;
            if(slicePosition.x>xClippingPlaneMax) discard;
            if(slicePosition.y<yClippingPlaneMin) discard;
            if(slicePosition.y>yClippingPlaneMax) discard;
            if(slicePosition.z<zClippingPlaneMin) discard;
            if(slicePosition.z>zClippingPlaneMax) discard;
        
            #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
            reflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );
        
            #ifdef DOUBLE_SIDED
        
                reflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;
        
            #else
        
                reflectedLight.indirectDiffuse += vIndirectFront;
        
            #endif
        
            #include <lightmap_fragment>
        
            reflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );
        
            #ifdef DOUBLE_SIDED
        
                reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;
        
            #else
        
                reflectedLight.directDiffuse = vLightFront;
        
            #endif
        
            reflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();
        
            // modulation
            #include <aomap_fragment>
        
            vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;
        
            #include <envmap_fragment>
        
            gl_FragColor = vec4( outgoingLight, diffuseColor.a );
        
            #include <tonemapping_fragment>
            #include <encodings_fragment>
            #include <fog_fragment>
            #include <premultiplied_alpha_fragment>
            #include <dithering_fragment>
        }
        `;
    };
    console.log(material, material.uniforms)
    return material;
}

It’s not allowed to pass uniforms as a parameter to MeshLambertMaterial. This only works for ShaderMaterial and RawShaderMaterial. When extending a material with onBeforeCompile(), there is no need to use UniformsUtils. Do it like in this example that shows how to extend MeshNormalMaterial:

https://threejs.org/examples/webgl_materials_modified

Here is an in depth tutorial for using onBeforeCompile

In short, the way onBeforeCompile works is somewhat limited. Three forces you to modify the shader source at runtime. As @Mugen87 pointed out, the built-in Material doesn’t know what to do with the uniforms.

The MeshLambertMaterial eventually creates a ShaderMaterial with uniforms, but this is obscured from you and you’re not allowed to access this instance.
Despite not being able to see it, you do have access to properties in new ShaderMaterial(properties).

Essentially:

myLambertMaterial.onBeforeCompile = PROPERTIES => {....}

The object you get in this callback looks exactly the same as the object you would pass to the ShaderMaterial. Thus, if you do shader.uniforms = UniformsUtils.merge( shader.uniforms, myUniforms) it should work.

The syntax in onBeforeCompile is very limited. At most you can do things like

shader.foo = merge(foo,bar)
1 Like