One mesh have two ShaderMaterial?

I have learn threejs for one year, I can use THREE.ShaderMaterial to produce shader when three is one vertexShader and one fragmentShader, but one vertexShader and one fragmentShader not satisfying the effect, if anyone know how to add two THREE.ShaderMaterial to one .obj/.glb model

it would be better you you describe what you’re after, add reference images or links if necessary. “two shadermaterials” can mean anything.

1 Like

Your question is unclear. My answer is just a shot in the dark.

One THREE.ShaderMaterial corresponds to a WebGL program and it has exactly one vertex shader and exactly one fragment shader.

  1. If your model has several meshes, then each mesh may have its own material, including THREE.ShaderMaterial.
  2. Each THREE.Mesh (in a model or not) may have an array of materials, instead of one material and each material goes to individual face of the mesh. I expect that each element in this array of materials could be THREE.ShaderMaterial, but I have never tried it.

More complex approaches are:

  1. Combining several shaders into one shader and control what part to execute with GLSL if statement.
  2. Modifying the shader material in onBeforeRender callback.
  3. Using EffectComposer to apply multiple shader effects in a row.

Thank for your reply, my model has one mesh loaded from .glb file, it is a person model. Just as you said, One THREE.ShaderMaterial corresponds to a WebGL program and it has exactly one vertex shader and exactly one fragment shader, I tried one THREE.ShaderMaterial
and succed in the very beginning, but its VFX (Visual Effects Supervisor) is complicated, one THREE.ShaderMaterial can’t finish all effects, so I want my person mesh go through two THREE.ShaderMaterial, that’s my question. I know EffectComposer, it most render the scene background, not my person model

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - materials - shaders [custom]</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link type="text/css" rel="stylesheet" href="main.css">
	</head>
	<body>

		<div id="container"></div>
		<div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - shader material demo. featuring <a href="http://www.pouet.net/prod.php?which=52761" target="_blank" rel="noopener">Monjori by Mic</a></div>
		<script id="vertexShader" type="x-shader/x-vertex">
			uniform float time;
			varying vec2 vUv;
			varying vec3 worldNormal;
			varying vec3 worldViewDir;
			varying vec4 screenPos;
			void main()
			{
				vUv = uv;

				vec4 mvPosition = modelViewMatrix * vec4( position, 1.0);
				gl_Position = projectionMatrix * mvPosition;

				float _ProjectionParams = 1.0;
				
				screenPos = gl_Position*0.5;
				screenPos.y = 0.5 - screenPos.y;
				screenPos.xy = vec2(screenPos.x, screenPos.y * _ProjectionParams) + screenPos.w;
				screenPos.zw = gl_Position.zw;

				vec4 _worldNormal = modelMatrix * vec4(normal, 1.0);
				worldNormal = _worldNormal.xyz;
				worldNormal = normalize(worldNormal);

				vec4 _worldPos = modelMatrix* vec4(position, 1.0);
				worldViewDir = cameraPosition - _worldPos.xyz;

			}

		</script>

		<script id="fragment_shader_pass2" type="x-shader/x-fragment">
			varying vec2 vUv;
			uniform sampler2D colorTexture2;  
			void main(void) {
				vec2 _vUv = vec2(vUv.x, 1.0-vUv.y);
				gl_FragColor = texture2D(colorTexture2, _vUv);
				//gl_FragColor = vec4(0,0,0,1);
			}
		</script>

		<script id="vertexShaderPass2" type="x-shader/x-vertex">
			varying vec2 vUv;
			void main()	{
				vUv = uv;
				gl_Position = vec4( position, 1.0 );
			}
		</script>

		<!-- Import maps polyfill -->
		<!-- Remove this when import maps will be widely supported -->
		<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
		<script async src="./dist/three.multipass.post.processing.min.js"></script>

		<script type="importmap">
			{
				"imports": {
					"three": "../build/three.module.js",
					"three/addons/": "./jsm/"
				}
			}
		</script>

		<script type="module">

			import * as THREE from 'three';

			import Stats from 'three/addons/libs/stats.module.js';

			import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';


			let stats;

			let camera, scene, renderer, clock;

			let uniforms1, uniforms2;

			init();
			animate();

			function init() {

				const container = document.getElementById('container');

				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 3000 );
				camera.position.z = 4;

				scene = new THREE.Scene();
				scene.background = new THREE.Color(0xffffff)
				clock = new THREE.Clock();

				const geometry = new THREE.BoxGeometry( 0.75, 0.75, 0.75 );

				let glb_loader = new GLTFLoader();

				uniforms1 = {
					'time': { value: 1.0 },
					'colorTexture': { value: new THREE.TextureLoader().load( 'data/000000.jpg' ) },
					'colorTexture1': { value: new THREE.TextureLoader().load( 'data/AlphaTexture.png' ) },
					'transparent':{value:true},
				};
				uniforms1[ 'colorTexture1' ].value.wrapS = uniforms1[ 'colorTexture1' ].value.wrapT = THREE.RepeatWrapping;

				const params = [
					['fragment_shader1', uniforms1],
				];

				for (let i = 0; i < params.length; i++) {

					const material = new THREE.ShaderMaterial({

						uniforms: params[i][1],
						vertexShader: document.getElementById( 'vertexShader' ).textContent,
						fragmentShader: document.getElementById( params[ i ][ 0 ] ).textContent,
						'transparent': true,
						'depthTest': true,
						'depthWrite': true
					});
					
					glb_loader.load(
						'data/test1.glb',
						(object)=>{
							object.scene.traverse(function(child){
								if(child.isMesh){
									child.frustumCulled = false;
                            		child.material = material;
								}
							})

							object.scene.scale.set(2, 2, 2)
                    		object.scene.position.set(0, -1, 0);
							scene.add(object.scene);
						}
					)
				}

				renderer = new THREE.WebGLRenderer();
				renderer.setPixelRatio( window.devicePixelRatio );
				container.appendChild( renderer.domElement );

				stats = new Stats();
				container.appendChild( stats.dom );

				onWindowResize();

				window.addEventListener( 'resize', onWindowResize );

			}

			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

			}

			//

			function animate() {

				requestAnimationFrame( animate );

				render();
				stats.update();

			}

			function render() {

				const delta = clock.getDelta();

				uniforms1['time'].value += delta;

				renderer.render( scene, camera );

			}

		</script>

	</body>
</html>

I load a .glb file, its person model with one mesh, I use THREE.ShaderMaterial to add VFX(Visual Effects) on mesh, but VFX is complicated that one THREE.ShaderMaterial can’t finish, so I want add another THREE.ShaderMaterial after previous, that’s my question, looking forward to your reply

Why can’t one shader finish the VFX?

Try:

const myGLBThing = object.scene.children[0]
const mesh_a = new Mesh(myGLBThing.geometry, new ShaderMaterial()
mesh_a.renderOrder = 1

const mesh_b = new Mesh(myGLBThing.geometry, new ShaderMaterial())
mesh_b.renderOrder = 2 

For starters.

1 Like

My suggestion is to merge the code of both vertex shaders into a single vertex shader.

1 Like

like everyone is saying, you would usually merge effects. btw there is a very good library for threejs that treats effects/shaders as layers that can be mixed matched and blended: GitHub - pmndrs/lamina: 🍰 An extensible, layer based shader material for ThreeJS

2 Likes

thank you, i tried merge two shaderMaterial to one and succeed

yes,i tried and succeed,thank you!!!

1 Like

thank you.