【问题标题】:How To Prevent CSS Applied to One Three.JS Scene From Being Applied To All Scenes如何防止应用于一个 Three.JS 场景的 CSS 应用于所有场景
【发布时间】:2019-11-03 14:07:54
【问题描述】:

这可能更像是一种“你将不得不做很长的路要走”类型的交易,但它就是这样......

当我应用 CSS 来控制只有一个 HTML 元素的不透明度时,该元素是 Three.JS 场景的容器,其中有多个元素,每个元素都是自己场景的容器,应用了 CSS(即使是在内联level) 仅应用于包含场景的元素之一,应用于包含场景的所有元素,而不是特定的目标元素。任何帖子应用的 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 不透明度渲染,而不仅仅是第一个场景本身。

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

谢谢。

【问题讨论】:

  • 跟进:如果我更改代码以对每个场景使用单独的渲染器,我会得到预期的行为,但是否可以在不实例化多个渲染的情况下做到这一点?

标签: css three.js opacity scene


【解决方案1】:

THREE.Group 设置透明度:

Group 只是一个容器。因此,它有孩子,可能是其他Groups。但是您只能将透明度应用于分配在Mesh 级别的Material,而不是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>

【讨论】:

  • 谢谢。我采用了您的代码并对其进行了修改,以便我可以在 Tween 上为不透明度设置动画,并且它可以按预期工作。
  • 作为脚注,上面的代码将对引用 Three.JS 中相同材质实例的所有组对象应用不透明度更改。因此,为了防止不透明度的变化不仅仅应用于您要定位的组,请确保组内的所有单个材质都引用它们自己的 Three.JS 材质实例,否则不透明度将级联到所有网格material 是材质的共享实例。
  • 脚注上的注释... :) 实际上,您有正确的理由,如果您的网格共享一种材料,它们会以各种方式共享它,包括属性和所有内容。它与组没有任何关系,组单独响应,前提是所有网格都有独特的材料。
  • 另外,这可能是作弊,但我发现如果我用双引号将材料的实例化,如下所示:myNameSpace.MyMaterial = "new THREE.MeshStandardMaterial({color: 0x456089, roughness: 0.5, metalness: 1, side: THREE.DoubleSide, transparent: true });"; 然后我可以调用“eval()”字符串包含对材料的实例化调用,如下所示:new THREE.Mesh(geom, eval(myNameSpace.MyMaterial)); 这只允许进行一个声明并保持每个组中的材料独立。到目前为止它有效。
  • 抱歉回复晚了。出于安全和性能原因,通常不鼓励使用evalstackoverflow.com/questions/86513/… 相反,请查看 Material.clone(),它会创建特定材料的副本。如果您对此还有其他问题,请创建一个新问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-02
  • 2021-12-09
相关资源
最近更新 更多