Hello, I created 3 Sphere geometries with a MeshMatCapMaterial and all is working fine. I added to add to each geometry the same MatCap but I would like to add for each geometry a different base color. I can’t get this to work, I already looked into the documentation extensively and tried to find similar examples but unfortunately without result. Hopefully someone here can point me in the right direction how to do this. Thanks in advance.
I forgot to mention one thing. The only way that I get this to work is when I load 3 times separately the MeshMatCapMaterial and give a color to the MeshCapMaterial, but since I’m using the same MatCap for all the 3 spheres I figured that by doing it in this way I might unnecessarily make my scene more resource heavy. When this doesn’t impact the size or when this is the only way of achieving this I will go for this option. So any advise on this is welcome. Thanks.
The normal workflow for this use case would be to load the matcap only once but create three different materials. Each material uses the same texture but has different color
values.
Having more than one material will not make your app more resource intensive. The renderer is able to share shader programs between materials if possible (which will be true in your case).
This is what I tried to do by adding the Matcap material and the MeshPhongMaterial to the mesh but I can’t get this to work it will only load one of the 2 materials but not both. What am I doing wrong here or do you maybe have a example so I can see how this is done?
I suggest you show what you do with a live example: https://jsfiddle.net/f2Lommf5/
Thanks @Mugen87 I hope it is ok when I paste my code here? For now I only tried to add the second material to “sphere” and so what it does, it will only apply the first material that is added to the mesh the second material is just being ignored without any errors. When I turn the order of the materials around only the other material is being applied again without errors. It’s probably something simple that I’m overlooking here but I just don’t see what.
// Model
let myModel;
let modelPath_MyModel = 'https://mywebsite/wp-content/uploads/2021/05/new_brain.glb';
let isLoaded = false;
// Material
let texturePath_Enviroment = 'https://mywebsite/wp-content/uploads/2021/05/Matcap-test21-min.png';
let mat_Enviroment;
let mat_GlassMatCapBack;
let mat_GlassMatCapFront;
let mat_BrainCoreBlue;
let mat_BrainInnerOutterBlue;
let mat_BrainCoreRed;
let mat_BrainInnerOutterRed;
let mat_BrainInnerOutterRed2;
let brainRight;
const statsEnabled = false;
let container, stats;
// three
let scene;
let camera;
let renderer;
let controls;
let delta;
let mouseX = 0;
let mouseY = 0;
let targetX = 0;
let targetY = 0;
const windowHalfX = window.innerWidth / 2;
const windowHalfY = window.innerHeight / 2;
function initMainApp () {
buildScene();
buildLightEnviroment();
buildMaterial();
buildModel();
watchLoadingManager();
buildSphereGeometry();
}
function buildScene () {
const container = document.querySelector('#mainCanvas');
scene = new THREE.Scene();
//scene.background = new THREE.Color(0x000000);
camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.z = 14;
camera.position.y = 1;
const cameraHolder = new THREE.Group();
cameraHolder.add(camera);
scene.add(cameraHolder);
renderer = new THREE.WebGLRenderer({ container, antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(1.5);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.shadowMap.needsUpdate = true;
stats = new Stats();
container.appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement );
controls.enableDamping = true;
controls.dampingFactor = 0.08;
controls.enableZoom = false;
controls.enablePan = false;
controls.enabled = true;
controls.maxAzimuthAngle = Math.PI * 0.2;
controls.minAzimuthAngle = Math.PI * 1.7;
controls.maxPolarAngle = 1.7;
controls.minPolarAngle = 1.2;
controls.autoRotate = false;
controls.autoRotateSpeed = 2;
}
function buildLightEnviroment () {
const light_Directional = new THREE.DirectionalLight(0xFFFFFF);
light_Directional.position.set(3, 3, 3);
light_Directional.intensity = 0.5;
light_Directional.castShadow = true;
light_Directional.shadow.bias = -0.00001;
light_Directional.shadow.mapSize.width = 2048;
light_Directional.shadow.mapSize.height = 2048;
scene.add(light_Directional);
const light_Ambient = new THREE.AmbientLight(0xFFFFFF);
light_Ambient.intensity = 0.15;
scene.add(light_Ambient);
const lightpoint3 = new THREE.PointLight(0xFFFFFF,0.4);
lightpoint3.position.set(100,200,500);
//lightpoint3.castShadow = true;
scene.add(lightpoint3);
mat_Enviroment = new THREE.TextureLoader().load(texturePath_Enviroment);
mat_Enviroment.mapping = THREE.SphericalReflectionMapping;
}
function buildSphereGeometry () {
const geometry = new THREE.SphereGeometry( 0.50, 32, 32 );
const geometry1 = new THREE.SphereGeometry( 0.50, 32, 32 );
const geometry2 = new THREE.SphereGeometry( 0.50, 32, 32 );
const material = new THREE.MeshMatcapMaterial({transparent: true, opacity: 0.95})
const matcapTexture = new THREE.TextureLoader().load('https://mywebsite/wp-content/uploads/2021/05/Matcap-test-12-min.png');
material.matcap = matcapTexture
const material2 = new THREE.MeshPhongMaterial({color: 0xffdd66, transparent: true})
const sphere = new THREE.Mesh( geometry, material2, material);
//sphere.color = 0xff4514;
sphere.position.y = 1;
sphere.position.x = -0.6;
sphere.position.z = 1.1;
scene.add( sphere );
const sphere2 = new THREE.Mesh( geometry1, material );
sphere2.position.y = 2.8;
sphere2.position.x = 0;
scene.add( sphere2 );
const sphere3 = new THREE.Mesh( geometry2, material );
sphere3.position.y = 1.5;
sphere3.position.x = 0.6;
sphere3.position.z = -0.9;
scene.add( sphere3 );
gsap.fromTo(sphere3.position, {y: 1},{duration: 2, y: 1.1, ease: 'none', yoyo: true, repeat: -1 });
gsap.fromTo(sphere.position, {y: 1},{duration: 2, y: 1.2, z:1.2, ease: 'none', yoyo: true, repeat: -1 });
gsap.fromTo(sphere2.position, {y: 2.7},{duration: 2, y: 2.8, ease: 'none', yoyo: true, repeat: -1 });
};
function buildModel () {
const loader_GLTF = new THREE.GLTFLoader();
loader_GLTF.load(modelPath_MyModel, function (gltf) {
myModel = gltf.scene;
scene.add(myModel);
myModel.position.y = -2.3;
myModel.traverse(function (child) {
if (child.isMesh) {
child.frustumCulled = false;
if ( child.name == 'brain_right_core') {
//gsap.to(child.rotation, {duration: 2, delay: 2, x: 360, ease: 'bounce' });
child.material = mat_BrainCoreBlue;
child.renderOrder = 1;
child.castShadow = true;
child.receiveShadow = true;
} else if ( child.name == 'brain_right_inner' ) {
child.material = mat_BrainInnerOutterBlue;
child.renderOrder = 2;
} else if ( child.name == 'brain_right_outer' ) {
child.material = mat_BrainInnerOutterBlue;
child.renderOrder = 4;
} else if ( child.name == 'brain_left_core') {
child.material = mat_BrainCoreRed;
child.renderOrder = 2;
} else if ( child.name == 'brain_left_inner') {
child.material = mat_BrainInnerOutterRed;
child.renderOrder = 3;
child.castShadow = true;
child.receiveShadow = true;
} else if ( child.name == 'brain_left_outer') {
child.material = mat_BrainInnerOutterRed2;
child.renderOrder = 5;
} else if ( child.name == 'bottle') {
child.material = mat_GlassMatCapBack;
child.renderOrder = 6;
let bottleClone = child.clone();
bottleClone.material = mat_GlassMatCapFront;
scene.getObjectByName( "BrainBottle" ).add( bottleClone );
}
}
});});
}
function buildMaterial () {
mat_GlassMatCapBack = new THREE.MeshMatcapMaterial({
color: 0xFFFFFF,
matcap: mat_Enviroment,
side: THREE.BackSide,
transparent: true,
opacity: 0.35,
blending: THREE.AdditiveBlending, // THREE.CustomBlending for normal look
});
mat_GlassMatCapFront = new THREE.MeshMatcapMaterial({
color: 0xFFFFFF,
matcap: mat_Enviroment,
side: THREE.FrontSide,
transparent: true,
opacity: 1,
blending: THREE.AdditiveBlending, // THREE.CustomBlending for normal look
});
mat_BrainCoreBlue = new THREE.MeshPhongMaterial({
color: 0x25cdda,
emissive: 0x000000,
blending: THREE.CustomBlending,
side: THREE.FrontSide,
depthWrite: false,
depthTest: true,
transparent: true,
opacity: 0.60,
});
mat_BrainInnerOutterBlue = new THREE.MeshPhongMaterial({
color: 0x25cdda,
emissive: 0x00FFFF,
blending: THREE.CustomBlending,
side: THREE.FrontSide,
depthWrite: false,
depthTest: true,
transparent: true,
opacity: 0.20,
});
mat_BrainCoreRed = new THREE.MeshPhongMaterial({
color: 0xcb5332,
emissive: 0x000000,
blending: THREE.CustomBlending,
side: THREE.FrontSide,
depthWrite: false,
depthTest: true,
transparent: true,
opacity: 0.80,
});
mat_BrainInnerOutterRed = new THREE.MeshPhongMaterial({
color: 0xcb5332,
emissive: 0xFFFF00,
blending: THREE.CustomBlending,
side: THREE.FrontSide,
depthWrite: false,
depthTest: true,
transparent: true,
opacity: 0.20,
});
mat_BrainInnerOutterRed2 = new THREE.MeshPhongMaterial({
color: 0x000000,
emissive: 0xFFFF00,
blending: THREE.CustomBlending,
side: THREE.FrontSide,
depthWrite: false,
depthTest: true,
transparent: true,
opacity: 0.20,
});
}
// EVENTS
document.addEventListener( 'mousemove', onDocumentMouseMove );
window.addEventListener( 'resize', onWindowResize );
function onWindowResize() {
renderer.setSize( window.innerWidth, window.innerHeight );
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX );
mouseY = ( event.clientY - windowHalfY );
}
function animate () {
requestAnimationFrame(animate);
render();
if ( statsEnabled ) stats.update();
}
function render() {
targetX = mouseX * .001;
targetY = mouseY * .001;
if ( myModel );{
myModel.rotation.y += 0.05 * ( targetX - myModel.rotation.y );
myModel.rotation.x += 0.05 * ( targetY - myModel.rotation.x );
}
renderer.render(scene, camera);
controls.update();
}
function startApplication () {
animate();
}
function watchLoadingManager () {
THREE.DefaultLoadingManager.onStart = function ( url, itemsLoaded, itemsTotal ) {
// console.log('Started loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.');
};
THREE.DefaultLoadingManager.onLoad = function () {
console.log('Loading Complete!');
isLoaded = true;
startApplication();
};
THREE.DefaultLoadingManager.onProgress = function ( url, itemsLoaded, itemsTotal ) {
// console.log('Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.');
};
THREE.DefaultLoadingManager.onError = function ( url ) {
// console.log('There was an error loading ' + url);
};
}
initMainApp();
Finally got it working. I still have a lot to learn that is obvious. Thank you anyway for taking your time to answer. I could just create a new material without having to load the texture loader again