Im trying to understand an issue i am facing with ThreeJS. I have a simple cube on a page and i have a PNG of a white question mark, with the rest of the image transparent. When i add the texture to the cube;
cubeGeometry = new THREE.BoxGeometry( 5, 5, 5 );
cubeMaterial = new THREE.MeshStandardMaterial( { color: this.cubeColor, flatShading: true, map:new THREE.TextureLoader().load( require("./question-mark.png") ) } );
cubeMesh = new THREE.Mesh( cubeGeometry, cubeMaterial );
The transparent pixels in the PNG appear black no matter what i do. Whats even stranger is the white pixels of the PNG appear the color of this.cubeColor - which is set to orange.
I would expect the cube color to be orange with the transparent pixels of the PNG taking that color and the white pixels in the PNG to remain white.
Anyone shed some light ?
what I see
the texture
Im trying to understand an issue i am facing with ThreeJS. I have a simple cube on a page and i have a PNG of a white question mark, with the rest of the image transparent. When i add the texture to the cube;
cubeGeometry = new THREE.BoxGeometry( 5, 5, 5 );
cubeMaterial = new THREE.MeshStandardMaterial( { color: this.cubeColor, flatShading: true, map:new THREE.TextureLoader().load( require("./question-mark.png") ) } );
cubeMesh = new THREE.Mesh( cubeGeometry, cubeMaterial );
The transparent pixels in the PNG appear black no matter what i do. Whats even stranger is the white pixels of the PNG appear the color of this.cubeColor - which is set to orange.
I would expect the cube color to be orange with the transparent pixels of the PNG taking that color and the white pixels in the PNG to remain white.
Anyone shed some light ?
what I see
the texture
Share Improve this question edited Dec 24, 2019 at 1:48 user128511 asked Dec 22, 2019 at 22:11 rosey3rosey3 231 silver badge4 bronze badges 2- This SO answer describes how to do what you appear to want -- render a decal texture. – WestLangley Commented Dec 23, 2019 at 3:08
- Why not use the alphaMap property? – 2pha Commented Dec 23, 2019 at 22:37
1 Answer
Reset to default 5You can apply 2 or more materials like this
const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth);
// prepare geometry to use 2 materials
geometry.clearGroups();
geometry.addGroup( 0, Infinity, 0 );
geometry.addGroup( 0, Infinity, 1 );
const loader = new THREE.TextureLoader();
// create 2 materials
const material0 = new THREE.MeshBasicMaterial({
color: 'orange',
});
const material1 = new THREE.MeshBasicMaterial({
map: loader.load('https://i.imgur./iFom4eT.png'),
transparent: true,
});
// apply 2 materials to geometry
const materials = [material0, material1];
const cube = new THREE.Mesh(geometry, materials);
Example, note it uses BoxBufferGeometry
not BoxGeometry
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const scene = new THREE.Scene();
scene.background = new THREE.Color('purple');
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth);
// prepare geometry to use 2 materials
geometry.clearGroups();
geometry.addGroup( 0, Infinity, 0 );
geometry.addGroup( 0, Infinity, 1 );
const loader = new THREE.TextureLoader();
// create 2 materials
const material0 = new THREE.MeshBasicMaterial({
color: 'orange',
});
const material1 = new THREE.MeshBasicMaterial({
map: loader.load('https://i.imgur./hrzoDMj.png'),
transparent: true,
});
// apply 2 materials to geometry
const materials = [material0, material1];
const cube = new THREE.Mesh(geometry, materials);
scene.add(cube);
function render(time) {
time *= 0.001; // convert time to seconds
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cube.rotation.x = time;
cube.rotation.y = time;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<script src="https://threejsfundamentals/threejs/resources/threejs/r110/build/three.min.js"></script>
<canvas id="c"></canvas>
note that if you put a color and a texture (map) in a single material the 2 colors, (1) the color you specified and (2) the color from the texture, are multiplied with each other. So, wherever the texture has zeros, RGBA = 0,0,0,0 then the result is 0,0,0,0 as anything multiplied by zero is zero. This is why you got the results you got originally where outside the question mark is transparent and inside is orange.
Knowing this you can use your white question mark and change its color by setting the color on the material that is using the texture.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const scene = new THREE.Scene();
scene.background = new THREE.Color('purple');
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth);
// prepare geometry to use 2 materials
geometry.clearGroups();
geometry.addGroup( 0, Infinity, 0 );
geometry.addGroup( 0, Infinity, 1 );
const loader = new THREE.TextureLoader();
// create 2 materials
const material0 = new THREE.MeshBasicMaterial({
color: 'orange',
});
const material1 = new THREE.MeshBasicMaterial({
map: loader.load('https://i.imgur./hrzoDMj.png'),
transparent: true,
});
// apply 2 materials to geometry
const materials = [material0, material1];
const cube = new THREE.Mesh(geometry, materials);
scene.add(cube);
function render(time) {
time *= 0.001; // convert time to seconds
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
material1.color.setHSL(time, 1, 0.5);
cube.rotation.x = time;
cube.rotation.y = time;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<script src="https://threejsfundamentals/threejs/resources/threejs/r110/build/three.min.js"></script>
<canvas id="c"></canvas>