如何防止将CSS应用于一个Three.JS场景应用于所有场景

时间:2019-06-20 17:41:08

标签: css three.js opacity scene

这可能更像是“您将不得不做的很长的交易”类型的交易,但是在这里……

当我应用CSS来控制一个HTML元素(Three.JS场景的容器)的不透明度时,其中有多个元素都是各自场景的容器,则CSS被应用(即使是内联级别),仅将包含场景的那些元素之一应用于包含场景的所有元素,而不是专门针对的一个元素。发生这种情况的任何后期应用的CSS属性,而不仅仅是不透明度。

之所以尝试以这种方式控制不透明度的原因是,根据我的研究,没有直接的方法可以对包含1到N个网格物体的Three.JS组对象设置不透明度。从理论上讲,我试图不必将透明性设置为“ true”的所有材料定义出来,然后必须对Three.JS Group对象中的所有网格进行递归更新,动画会淡入/淡出。

我正在使用的某些组对象将在其中定义许多网格。因此,我的目标不是要更新Three.JS组对象中包含的每个单独网格本身的不透明度,而是要为每种类型的动画创建单独的场景,该场景能够按原样运行任何透明度然后只需调整包含该动画的opacity属性的HTML元素即可。

我尝试使用一台摄像机和多台摄像机无济于事。我也尝试过将容器嵌套在一个附加元素中,并在父元素上设置CSS,但是会发生相同的问题。我没有尝试使用多个渲染器,因为从研究中可以发现,这样做是不受欢迎的,并且可能导致性能问题和上下文限制。渲染循环还将“ autoClear”设置为false,以便所有场景一起渲染。

这是HTML语法。您会注意到,第一个元素的内联样式的不透明度设置为0.5,第二个元素未应用内联样式:

<div class="three-js-container" id="scene-container-1" style="opacity:0.5;"></div>
<div class="three-js-container" id="scene-container-2"></div>

这是Javascript代码:

/* Only one renderer instance is created */
var universalRenderer = new THREE.WebGLRenderer({antialias: true, alpha:true});

/* references to all containers are made */
var containerForScene1 = document.getElementById("scene-container-1");
var containerForScene2 = document.getElementById("scene-container-2");

/* two different cameras are created */
var cameraForScene1 = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.001, 1000);
var cameraForScene2 = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.001, 1000);

/* two different scenes are created, one for each element container */
var scene1 = new THREE.Scene();
scene1.userData.element = containerForScene1;

var scene2 = new THREE.Scene();
scene2.userData.element = containerForScene2;

/* the renderer is applied to both scene containers */
containerForScene1.appendChild(universalRenderer.domElement);
containerForScene2.appendChild(universalRenderer.domElement);

同时播放两个动画时,两个场景均以1/2不透明度渲染,而不仅仅是第一个场景本身。

是否有理由将CSS样式应用于一个包含元素的HTML场景应用于所有其他包含元素的场景?我是否只需将其吸干并在控制网格不透明度方面走长远路?

谢谢。

1 个答案:

答案 0 :(得分:1)

设置THREE.Group的透明度:

Group只是一个容器。因此,它具有子级,可能是其他Group个子级。但是您只能将透明度应用于Material级别而不是Mesh级别的Groups。但是,并不会丢失所有内容,因为您可以monkey patch Group来无缝地执行操作。

// MONKEY PATCH
Object.defineProperty(THREE.Group.prototype, "transparent", {
  set: function(newXP) {
    this.traverse(node => {
      if (node.material) {
        node.material.transparent = newXP
        node.material.opacity = (newXP) ? 0.5 : 1
      }
    })
  }
})

// Set up the renderer

const renderer = new THREE.WebGLRenderer({
  alpha: true,
  antialias: true
})
document.body.appendChild(renderer.domElement)

renderer.setSize(window.innerWidth, window.innerHeight)

const scene = new THREE.Scene()

const size = new THREE.Vector2()
renderer.getSize(size)
const camera = new THREE.PerspectiveCamera(28, size.x / size.y, 1, 1000)
camera.position.set(0, 20, 100)
camera.lookAt(scene.position)
scene.add(camera)

camera.add(new THREE.PointLight(0xffffff, 1))

function render() {
  renderer.render(scene, camera)
}

const axis = new THREE.Vector3(0, 1, 0)

function animate() {
  requestAnimationFrame(animate)
  camera.position.applyAxisAngle(axis, 0.005)
  camera.lookAt(scene.position)
  render()
}
animate()

// Populate the scene

const cubeGeo = new THREE.BoxBufferGeometry(5, 5, 5)

let opaqueCubes = []
let transparentCubes = []

const randomInRange = () => Math.random() * ((Math.random() <= 0.5) ? -10 : 10)

const opaqueGroup = new THREE.Group()
scene.add(opaqueGroup)
for (let i = 0; i < 10; ++i) {
  opaqueGroup.add(new THREE.Mesh(cubeGeo, new THREE.MeshPhongMaterial({
    color: "red"
  })))
  opaqueGroup.children[i].position.set(randomInRange(), randomInRange(), randomInRange())
}

const transparentGroup = new THREE.Group()
scene.add(transparentGroup)
for (let i = 0; i < 10; ++i) {
  transparentGroup.add(new THREE.Mesh(cubeGeo, new THREE.MeshPhongMaterial({
    color: "green"
  })))
  transparentGroup.children[i].position.set(randomInRange(-10, 10), randomInRange(-10, 10), randomInRange(-10, 10))
}

// Control the transparency from the input

const xparent = document.getElementById("xparent")
xparent.addEventListener("change", (e) => {
  transparentGroup.transparent = xparent.checked
})
html,
body {
  padding: 0;
  margin: 0;
  overflow: hidden;
}

#control {
  position: absolute;
  top: 0;
  left: 0;
  padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.js"></script>
<div id="control">
  <label>Make the green ones transparent:<input id="xparent" type="checkbox" /></label>
  <div>