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

How To Prevent CSS Applied to One Three.JS Scene From Being Applied To All Scenes

这可能更像是一种 "you're going to have to do it the long way" 类型的交易,但它是...

当我应用 CSS 来控制只有一个 HTML 元素的不透明度时,该元素是 Three.JS 场景的容器,其中有多个元素,每个元素都是一个容器在他们自己的场景中,CSS 仅应用于包含场景的那些元素之一(即使在内联级别)正在应用于包含场景的所有元素,而不是特定的目标元素。任何 post 应用的 CSS 属性都会发生这种情况,而不仅仅是不透明度。

我之所以尝试这种方法来控制不透明度,是因为根据我的研究,没有直接的方法可以在包含 1 到 N 个网格的 Three.JS 组对象上设置不透明度。我(理论上)尝试不必定义透明度设置为 "true" 的所有材质,然后不必对 Three.JS 组对象中的所有网格进行递归更新,其中动画会消失 in/out.

我正在使用的一些组对象将在其中定义许多网格。因此,我的目标 was/is 不是更新包含在 Three.JS 组对象中的每个单独网格本身的不透明度,而是为每种类型的动画提供单独的场景,这些场景能够应用任意数量的透明度它运行然后只需调整包含该动画不透明度的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 包含元素的场景的样式会应用到所有其他包含元素的场景?我是否只需要忍受它并在控制网格不透明度方面走很长的路?

谢谢。

THREE.Group 设置透明度:

一个Group只是一个容器。因此,它有 children,这可能是其他 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>