最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - ThreeJS transparent PNG textures black - Stack Overflow

programmeradmin2浏览0评论

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
Add a ment  | 

1 Answer 1

Reset to default 5

You 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>

发布评论

评论列表(0)

  1. 暂无评论