Three.js-将多个图像映射到一个球体并控制每个图像

时间:2018-08-01 11:01:17

标签: javascript three.js

我有一个3D球面,我想将一系列图像映射到其上,并且我希望能够控制每个单独的图像,即独立地淡入/淡入每个图像。我将提供一个示例图像,说明我要实现的目标,因为我认为这是最好的解释方式。

enter image description here

因此,如您在上面看到的,每列8张图像,每行16(?)。

我已经能够通过简单地将图像映射到SphereGeometry来重新创建上述图像,但是我希望能够动态交换图像,并在不同的时间淡入它们。

到目前为止,我已经尝试过/我的想法:

  • 我尝试将8个测试图像推入一个数组并将其用作材质贴图,然后循环遍历SphereGeometry的每个面,并将材质索引指定为1到8,然后在每次8使用模,但是没有用:

    function createGlobe() {
    
        var geomGlobe = new THREE.SphereGeometry(40, 32, 16);
    
        var l = geomGlobe.faces.length;
    
        imageArray.push(new THREE.MeshBasicMaterial({map: texture1}));
        imageArray.push(new THREE.MeshBasicMaterial({map: texture2}));
        imageArray.push(new THREE.MeshBasicMaterial({map: texture3}));
        imageArray.push(new THREE.MeshBasicMaterial({map: texture4}));
        imageArray.push(new THREE.MeshBasicMaterial({map: texture5}));
        imageArray.push(new THREE.MeshBasicMaterial({map: texture6}));
        imageArray.push(new THREE.MeshBasicMaterial({map: texture7}));
        imageArray.push(new THREE.MeshBasicMaterial({map: texture8}));
    
        for (var i = 0; i < l; i++) {
            geomGlobe.faces[i].materialIndex = i % 8;
        }
    
        Globe = new THREE.Mesh(geomGlobe, imageArray);
    
        scene.add(Globe);
    }
    
  • 我想我需要每4或8个面数一次,然后设置材质 这些面孔中每个面孔的索引都相同,以便他们都使用 同一张图片,但是我不确定这些面孔是否正确对齐 这样。

基本上就是我所需要的:

一种以每列8个,每行16个的方式将图像动态添加到球体的方法,以及能够分别操纵其中每个图像的功能。

任何帮助都非常感激,因为我很困!

2 个答案:

答案 0 :(得分:1)

我建议制作一个大画布并将其用作纹理,然后为过渡到动画设置动画,然后设置texture.needsUpdate = true以在GPU上对其进行更新。

您可能会发现纹理更新花费了太多时间。在这种情况下,您可以尝试制作2个canvas +球体。并通过更改最前面的不透明度来交叉淡化。

下面是一个片段,显示了一种使用一些随机填充的画布将一个球体淡化为另一个球体的方法。

var renderer = new THREE.WebGLRenderer();
var w = 300;
var h = 200;
renderer.setSize(w, h);
document.body.appendChild(renderer.domElement);

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
  45, // Field of view
  w / h, // Aspect ratio
  0.1, // Near
  10000 // Far
);
camera.position.set(15, 10, 15);
camera.lookAt(scene.position);
controls = new THREE.OrbitControls(camera, renderer.domElement);

var light = new THREE.PointLight(0xFFFF00);
light.position.set(20, 20, 20);
scene.add(light);
var light1 = new THREE.AmbientLight(0x808080);
light1.position.set(20, 20, 20);
scene.add(light1);
var light2 = new THREE.PointLight(0x00FFFF);
light2.position.set(-20, 20, -20);
scene.add(light2);
var light3 = new THREE.PointLight(0xFF00FF);
light3.position.set(-20, -20, -20);
scene.add(light3);

var sphereGeom = new THREE.SphereGeometry(5, 16, 16);

function rnd(rng) {
  return (Math.random() * rng)
}

function irnd(rng) {
  return rnd(rng) | 0
}

function randomCanvasTexture(sz) {
  var canv = document.createElement('canvas');
  canv.width = canv.height = sz;
  var ctx = canv.getContext('2d')
  for (var i = 0; i < 100; i++) {
    ctx.fillStyle = `rgb(${irnd(256)},${irnd(256)},${irnd(256)})`
    ctx.fillRect(irnd(sz), irnd(sz), 32, 32)
  }
  var tex = new THREE.Texture(canv);
  tex.needsUpdate = true;
  return tex;
}

var material = new THREE.MeshLambertMaterial({
  color: 0x808080,
  map: randomCanvasTexture(256)
});
var mesh = new THREE.Mesh(sphereGeom, material);

var mesh1 = mesh.clone()
mesh1.material = mesh.material.clone()
mesh1.material.transparent = true;
mesh1.material.opacity = 0.5;
mesh1.material.map = randomCanvasTexture(256)
scene.add(mesh);
scene.add(mesh1);

renderer.setClearColor(0xdddddd, 1);

(function animate() {
  mesh1.material.opacity = (Math.sin(performance.now() * 0.001) + 1) * 0.5
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
})();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>

答案 1 :(得分:0)

如果不进行任何优化,则可以尝试以下操作:

textures.forEach( tex=>{

 const s = mySphere.clone()
 s.material = s.material.clone()

 tex.offset.copy(someOffset)
 tex.repeat.copy(someRepeat)

 tex.wrapS = tex.wrapT = THREE.ClampToEdgeWrapping // or something like that

 s.material.map = tex
 s.material.transparent = true

 scene.add(s)
})

这个想法是一遍又一遍地绘制相同的球体,但是用不同的偏移量遮盖。它可能不适用于.map,但可能适用于全黑或全白的alphaMap