three.js:纹理变全黑

3

我有一个简单的盒子几何体,想用纹理来装饰它。然而,我的盒子根本不显示(或者可能是100%黑色)。这个问题来源于这个问题。我已经更新了代码以反映原始问题上gaitat给出的答案。我写了另一个简单的测试网站来演示这个新问题。以下是该网站的内容:

"use strict";

// make DOM elements:
var container = document.createElement( 'div' );
document.body.appendChild( container );
var info = document.createElement( 'div' );
container.appendChild( info );

// a 'Box2' geometry instance:  (see geometry implementation below)
var myBox2geom = new THREE.BoxGeometry( 100, 100, 100, 10, 10, 10 );  // args: x,y,z-dimensions and width of their segments

// create scene:
var scene = new THREE.Scene();

// make a corresponding 'Box2' mesh:
new THREE.TextureLoader().load(
 "http://mrdoob.github.io/three.js/examples/textures/crate.gif",
 function ( texture ) {
  texture.minFilter = THREE.NearestFilter;
  var material = new THREE.MeshLambertMaterial( { map: texture, side: THREE.DoubleSide } );
  var myBox2mesh = new THREE.Mesh(myBox2geom, material);
  // add mesh to scene:
  scene.add( myBox2mesh );
 },
 function () {},  // onProgress function
 function ( error ) { console.log( error ) }  // no error gets logged
);

// make light:
var light = new THREE.PointLight( 0xffffff );
light.position.set(100, 200, 300);
light.lookAt( new THREE.Vector3( 0, 0, 0 ) );
scene.add( light );

// make camera:
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.x = 100;
camera.position.y = 200;
camera.position.z = 300;
camera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
scene.add( camera );

// make renderer:
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );

// aaaand render!
renderer.render( scene, camera );



THREE.Box2Geometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {

 THREE.Geometry.call( this );

 this.parameters = {
  width: width,
  height: height,
  depth: depth,
  widthSegments: widthSegments,
  heightSegments: heightSegments,
  depthSegments: depthSegments
 };

 this.widthSegments = widthSegments || 1;
 this.heightSegments = heightSegments || 1;
 this.depthSegments = depthSegments || 1;

 var constructee = this;  // constructee = the instance currently being constructed by the Box2Geometry constructor

 var width_half = width / 2;    // width  = the distance along x in the absolute 3D space
 var height_half = height / 2;  // height = the distance along y in the absolute 3D space
 var depth_half = depth / 2;    // depth  = the distance along z in the absolute 3D space

 buildPlane( 'z', 'y', -1, -1, depth, height, width_half, 0 ); // px
 buildPlane( 'z', 'y',  1, -1, depth, height, -width_half, 1 ); // nx
 buildPlane( 'x', 'z',  1,  1, width, depth, height_half, 2 ); // py
 buildPlane( 'x', 'z',  1, -1, width, depth, -height_half, 3 ); // ny
 buildPlane( 'x', 'y',  1, -1, width, height, depth_half, 4 ); // pz
 buildPlane( 'x', 'y', -1, -1, width, height, -depth_half, 5 ); // nz

 function buildPlane( u, v, uDir, vDir, uDist, vDist, wDist_half, materialIndex ) {

  var w, iu, iv,
   segU = constructee.widthSegments,  // number of segments along u   // width  = x
   segV = constructee.heightSegments, // number of segments along v   // height = y
   uDist_half = uDist / 2,  // the extent of the plane along u, divided by two
   vDist_half = vDist / 2,  // the extent of the plane along v, divided by two
   offset = constructee.vertices.length;

  if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {

   w = 'z';

  } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {

   w = 'y';
   segV = constructee.depthSegments;

  } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {

   w = 'x';
   segU = constructee.depthSegments;

  }

  var segUi = segU + 1,  // i = inc = incremented (by one)
   segVi = segV + 1,  // i = inc = incremented (by one)
   segmentDist_u = uDist / segU,
   segmentDist_v = vDist / segV,
   normal = new THREE.Vector3();

  normal[ w ] = wDist_half > 0 ? 1 : -1;

  for ( iv = 0; iv < segVi; iv++ ) {

   for ( iu = 0; iu < segUi; iu++ ) {

    var vertex = new THREE.Vector3();
    vertex[ u ] = ( iu * segmentDist_u - uDist_half ) * uDir;
    vertex[ v ] = ( iv * segmentDist_v - vDist_half ) * vDir;
    vertex[ w ] = wDist_half;

    constructee.vertices.push( vertex );

   }

  }

  for ( iv = 0; iv < segV; iv++ ) {

   for ( iu = 0; iu < segU; iu++ ) {

    var a = iu         + segUi *   iv;
    var b = iu         + segUi * ( iv + 1 );
    var c = ( iu + 1 ) + segUi * ( iv + 1 );
    var d = ( iu + 1 ) + segUi *   iv;

    var uva = new THREE.Vector2(   iu       / segU, 1 -   iv       / segV );
    var uvb = new THREE.Vector2(   iu       / segU, 1 - ( iv + 1 ) / segV );
    var uvc = new THREE.Vector2( ( iu + 1 ) / segU, 1 - ( iv + 1 ) / segV );
    var uvd = new THREE.Vector2( ( iu + 1 ) / segU, 1 -   iv       / segV );

    var face1 = new THREE.Face3( a + offset, b + offset, d + offset );
    face1.normal.copy( normal );
    face1.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
    face1.materialIndex = materialIndex;

    constructee.faces.push( face1 );
    constructee.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );

    var face2 = new THREE.Face3( b + offset, c + offset, d + offset );
    face2.normal.copy( normal );
    face2.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
    face2.materialIndex = materialIndex;

    constructee.faces.push( face2 );
    constructee.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );

   }

  }

 }

 this.mergeVertices();
};
THREE.Box2Geometry.prototype = Object.create( THREE.Geometry.prototype );
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
  </head>
  <body>
    <script src="https://raw.githubusercontent.com/mrdoob/three.js/master/build/three.js"></script>
    <script src="main2.js"></script>
  </body>
</html>


1
你只调用了一次render,而且由于TextureLoader是异步的,你的render调用在mesh被添加到场景之前就已经完成了。另外,除非有理由需要这样做,最好在纹理加载回调之外创建你的材质和myBoxMesh,然后在textureLoader回调中仅设置材质的map参数。 - 2pha
1
另外,你的测试在Chrome浏览器中无法运行。创建一个jsfiddle将使我们能够……嗯……轻松地调试你的代码,而不仅仅是阅读它。 - 2pha
2
首先,这不应该是一个新的问题,而应该是对之前问题的更新,因为它有99%的相似度。正如@2pha所观察到的那样,你没有从之前的问题中复制animate()函数。这将解决你的问题。 - gaitat
@2pha: 怎么做?材质的加载取决于纹理,而网格则依赖于材质。 - user134055
4个回答

7
所以,代码最终发现了三个错误,分别由Falk Thiele2pha/gaitat指出:
'跨域资源共享'(CORS)问题在Firebug中引发以下错误:
SecurityError: 操作不安全。 gl.texImage2D.apply(gl, arguments);
引用Falk Thiele的说法,通过将CrossOrigin属性设置为空来解决此错误:
var loader = new THREE.TextureLoader(); loader.crossOrigin = ""; loader.load( "http://mrdoob.github.io/three.js/examples/textures/crate.gif", function (texture) { //... }, function () {}, // onProgress function function (error) { console.log(error) } // onError function );
奇怪的是,当本地加载crate.gif纹理时,如果包含loader.crossOrigin = "";这一行,也会出现CORS错误(在Chrome中)。因此,似乎只有在确定源确实是跨站点源时才应使用此行。
场景(scene)只渲染一次,而且这个渲染发生在纹理加载之前。这是因为TextureLoader.load()调用是异步的,因此在调用load完成之前,后面的代码行renderer.render(scene, camera);就已经执行了。
脚本在Chrome中无法正常工作,原因有两个:首先是上述描述的CORS问题,其次是脚本由于某种原因无法加载THREE库(Uncaught ReferenceError: THREE is not defined)。我不知道为什么THREE在Chrome中无法加载,但在纠正错误的代码中(如下所示,也可参见here),错误不再出现,所以目前原因仍然未知。
终于,我做了一个JS fiddle演示运行的代码
这是已经纠正错误的代码片段,错误1、2和3不再存在。

"use strict";

// make DOM elements:
var container = document.createElement('div');
document.body.appendChild(container);
var info = document.createElement('div');
container.appendChild(info);

// a 'Box2' geometry instance:  (see geometry implementation below)
var myBox2geom = new THREE.BoxGeometry(100, 100, 100, 10, 10, 10); // args: x,y,z-dimensions and width of their segments

// create scene:
var scene = new THREE.Scene();

// make a corresponding 'Box2' mesh:
var loader = new THREE.TextureLoader();
loader.crossOrigin = "";
loader.load("http://mrdoob.github.io/three.js/examples/textures/crate.gif",
  function(texture) {
    texture.minFilter = THREE.NearestFilter;
    var material = new THREE.MeshLambertMaterial({
      map: texture,
      side: THREE.DoubleSide
    });
    var myBox2mesh = new THREE.Mesh(myBox2geom, material);
    // add mesh to scene:
    scene.add(myBox2mesh);
  },
  function() {}, // onProgress function
  function(error) {
    console.log(error)
  } // no error gets logged
);

// make light:
var light = new THREE.PointLight(0xffffff);
light.position.set(100, 200, 300);
light.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(light);

// make camera:
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.x = 100;
camera.position.y = 200;
camera.position.z = 300;
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(camera);

// make renderer:
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);

// aaaand render, continuously!
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();



THREE.Box2Geometry = function(width, height, depth, widthSegments, heightSegments, depthSegments) {

  THREE.Geometry.call(this);

  this.parameters = {
    width: width,
    height: height,
    depth: depth,
    widthSegments: widthSegments,
    heightSegments: heightSegments,
    depthSegments: depthSegments
  };

  this.widthSegments = widthSegments || 1;
  this.heightSegments = heightSegments || 1;
  this.depthSegments = depthSegments || 1;

  var constructee = this; // constructee = the instance currently being constructed by the Box2Geometry constructor

  var width_half = width / 2; // width  = the distance along x in the absolute 3D space
  var height_half = height / 2; // height = the distance along y in the absolute 3D space
  var depth_half = depth / 2; // depth  = the distance along z in the absolute 3D space

  buildPlane('z', 'y', -1, -1, depth, height, width_half, 0); // px
  buildPlane('z', 'y', 1, -1, depth, height, -width_half, 1); // nx
  buildPlane('x', 'z', 1, 1, width, depth, height_half, 2); // py
  buildPlane('x', 'z', 1, -1, width, depth, -height_half, 3); // ny
  buildPlane('x', 'y', 1, -1, width, height, depth_half, 4); // pz
  buildPlane('x', 'y', -1, -1, width, height, -depth_half, 5); // nz

  function buildPlane(u, v, uDir, vDir, uDist, vDist, wDist_half, materialIndex) {

    var w, iu, iv,
      segU = constructee.widthSegments, // number of segments along u   // width  = x
      segV = constructee.heightSegments, // number of segments along v   // height = y
      uDist_half = uDist / 2, // the extent of the plane along u, divided by two
      vDist_half = vDist / 2, // the extent of the plane along v, divided by two
      offset = constructee.vertices.length;

    if ((u === 'x' && v === 'y') || (u === 'y' && v === 'x')) {

      w = 'z';

    } else if ((u === 'x' && v === 'z') || (u === 'z' && v === 'x')) {

      w = 'y';
      segV = constructee.depthSegments;

    } else if ((u === 'z' && v === 'y') || (u === 'y' && v === 'z')) {

      w = 'x';
      segU = constructee.depthSegments;

    }

    var segUi = segU + 1, // i = inc = incremented (by one)
      segVi = segV + 1, // i = inc = incremented (by one)
      segmentDist_u = uDist / segU,
      segmentDist_v = vDist / segV,
      normal = new THREE.Vector3();

    normal[w] = wDist_half > 0 ? 1 : -1;

    for (iv = 0; iv < segVi; iv++) {

      for (iu = 0; iu < segUi; iu++) {

        var vertex = new THREE.Vector3();
        vertex[u] = (iu * segmentDist_u - uDist_half) * uDir;
        vertex[v] = (iv * segmentDist_v - vDist_half) * vDir;
        vertex[w] = wDist_half;

        constructee.vertices.push(vertex);

      }

    }

    for (iv = 0; iv < segV; iv++) {

      for (iu = 0; iu < segU; iu++) {

        var a = iu + segUi * iv;
        var b = iu + segUi * (iv + 1);
        var c = (iu + 1) + segUi * (iv + 1);
        var d = (iu + 1) + segUi * iv;

        var uva = new THREE.Vector2(iu / segU, 1 - iv / segV);
        var uvb = new THREE.Vector2(iu / segU, 1 - (iv + 1) / segV);
        var uvc = new THREE.Vector2((iu + 1) / segU, 1 - (iv + 1) / segV);
        var uvd = new THREE.Vector2((iu + 1) / segU, 1 - iv / segV);

        var face1 = new THREE.Face3(a + offset, b + offset, d + offset);
        face1.normal.copy(normal);
        face1.vertexNormals.push(normal.clone(), normal.clone(), normal.clone());
        face1.materialIndex = materialIndex;

        constructee.faces.push(face1);
        constructee.faceVertexUvs[0].push([uva, uvb, uvd]);

        var face2 = new THREE.Face3(b + offset, c + offset, d + offset);
        face2.normal.copy(normal);
        face2.vertexNormals.push(normal.clone(), normal.clone(), normal.clone());
        face2.materialIndex = materialIndex;

        constructee.faces.push(face2);
        constructee.faceVertexUvs[0].push([uvb.clone(), uvc, uvd.clone()]);

      }

    }

  }

  this.mergeVertices();
};
THREE.Box2Geometry.prototype = Object.create(THREE.Geometry.prototype);
<script src="https://cdn.jsdelivr.net/npm/three@0.122.0/build/three.min.js"></script>

编辑:导致纹理变白(如果它本身是自发光的)或变黑(如果不是自发光的)的第四种错误类型是没有灯光,或者灯光没有被添加到场景中,或者灯光指向错误的方向(提示:使用light.lookAt()),或者灯光离网格太远。
编辑2:第五个原因是面法线没有从几何体指向外部。有关详细信息,请参阅问题three.js:自定义几何体无法贴图

纹理变黑的另一个原因是未计算顶点法线,可以通过 geometry.computeVertexNormals(); 来修复 - 这正是我的情况。 - Welder Lourenço
另一个原因是如果您正在使用BufferGeometry,则还需要分配uvs,这在此处有答案 - Gangula

1
错误控制台显示另一个CORS问题:
DOMException [SecurityError:"该操作是不安全的。"
将CrossOrigin属性设置为空:
var loader = new THREE.TextureLoader();
loader.crossOrigin = "";
loader.load("http://mrdoob.github.io/three.js/examples/textures/crate.gif",
    function( texture ) {
        //...
    },
    function () {},  // onProgress function
    function ( error ) { console.log( error ) } // onError function
);

1

从我的本地计算机上加载图片到Next.js给了我问题。Chrome的一个安全性问题,所以我不得不将图像上传到https://imgur.com/并使用链接,这样就可以解决问题了。似乎我的图片也只适用于https连接而非http。

const texture = new THREE.TextureLoader().load('https://i.imgur.com/E5ThRBu.jpeg');
    console.log(textureImage);
    this.material = new THREE.MeshBasicMaterial(
      {
        map: texture,
      })
    this.geometry = new THREE.SphereBufferGeometry(1, 30, 30);

0
我曾经遇到一个类似的问题,但原因不同:我在透明背景上写了黑色文字,结果是一个完全黑色的立方体。我通过给我的纹理PNG文件添加颜色来解决这个问题,而不是保持透明。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接