javascript - THREE.js:模拟 MeshBasicMaterial,同时允许彩色灯光

标签 javascript three.js lighting phong

我正在使用 Three.js 制作一个地下城爬行者类型的游戏。我使用 MeshBasicMaterial 使所有内容都变得“truebright”,以使地下城始终可见。然而,我想在门口或墙上的缝隙下添加“额外”的灯光来营造气氛。但 BasicMaterial 上不显示光,因此我切换到 Phong 来测试我地板上的光。现在我的地板是黑色的!很可能是因为没有全局光。

有什么方法可以模拟 MeshBasicMaterial 的属性,同时允许不同颜色的光?地牢的四个侧面都是封闭的,所以我想放置一个非常大的全局光会在各处转换阴影或覆盖地面上的颜色。

这不是我问题的主要焦点,但另外:我该如何做到让光线被墙壁阻挡而不是仅仅穿过它们?墙壁只是由映射系统生成的 1x1x1 3d 网格立方体。

With MeshBasicMaterial

With MeshPhongMaterial

With MeshPhongMaterial

最佳答案

一旦切换到MeshPhongMaterial, Material 就会变成阴影。您可以设置一些参数使其更接近 MeshBasicMaterial,但您仍然会获得渐变照明,无论如何,这确实是您正在寻找的“奖励”照明。在下面的代码中,我将 shininess 属性设置为 0,这消除了 Phong 着色的强光效果。

为了让光线不透过墙壁,您需要实现阴影转换。这在 THREE.js 中实际上非常简单,并且网上有大量文章描述如何做到这一点,所以我不会在这里重复任何内容。但正如您在我的简单示例中看到的,您需要将网格体设置为转换和接收阴影(分别为 castShadows/receiveShadows),并设置灯光来转换它们以及(castShadows)。

var renderer, scene, camera, controls, stats;

var WIDTH = window.innerWidth,
  HEIGHT = window.innerHeight,
  FOV = 70,
  NEAR = 1,
  FAR = 1000;

function populateScene() {
  var cfgeo = new THREE.PlaneBufferGeometry(100, 100),
    lwallgeo = new THREE.PlaneBufferGeometry(20, 20),
    rwallgeo = new THREE.PlaneBufferGeometry(50, 20),
    farwallgeo = new THREE.PlaneBufferGeometry(50, 20),
    bumpgeo = new THREE.PlaneBufferGeometry(10, 10);

  var mat = new THREE.MeshPhongMaterial({
    color: 0xcccccc,
    emissive: new THREE.Color(0x0c0c0c),
    shininess: 0,
    side: THREE.DoubleSide
  });

  var ceiling = new THREE.Mesh(cfgeo, mat),
    floor = new THREE.Mesh(cfgeo, mat),
    lwall = new THREE.Mesh(lwallgeo, mat),
    rwall = new THREE.Mesh(rwallgeo, mat),
    farwall = new THREE.Mesh(farwallgeo, mat),
    bump1 = new THREE.Mesh(bumpgeo, mat),
    bump2 = new THREE.Mesh(bumpgeo, mat);
  ceiling.castShadow = true;
  ceiling.receiveShadow = true;
  floor.castShadow = true;
  floor.receiveShadow = true;
  lwall.castShadow = true;
  lwall.receiveShadow = true;
  rwall.castShadow = true;
  rwall.receiveShadow = true;
  farwall.castShadow = true;
  farwall.receiveShadow = true;
  bump1.castShadow = true;
  bump1.receiveShadow = true;
  bump2.castShadow = true;
  bump2.receiveShadow = true;

  ceiling.position.y = 10;
  ceiling.rotation.x = Math.PI / 2;
  floor.position.y = -10;
  floor.rotation.x = Math.PI / -2;
  lwall.rotation.y = Math.PI / 2;
  lwall.position.x = -10;
  rwall.rotation.y = Math.PI / -2;
  rwall.position.x = 10;
  rwall.position.y = 2;
  farwall.position.z = -20;
  bump1.rotation.y = Math.PI / -2;
  bump2.rotation.y = Math.PI / -2;
  bump1.position.set(10, -10, -15);
  bump2.position.set(10, -10, 5);

  scene.add(ceiling);
  scene.add(floor);
  scene.add(lwall);
  scene.add(rwall);
  scene.add(farwall);
  scene.add(bump1);
  scene.add(bump2);

  var bonus = new THREE.SpotLight(0xcccc00, 0.5);
  bonus.position.set(15, -7, -5);
  bonus.castShadow = true;
  bonus.distance = 20;
  var tgt = new THREE.Object3D();
  tgt.position.set(0, -10, -10);
  bonus.target = tgt;
  scene.add(bonus);
  scene.add(tgt);
}

function init() {
  document.body.style.backgroundColor = "slateGray";

  renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true
  });
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;

  document.body.appendChild(renderer.domElement);
  document.body.style.overflow = "hidden";
  document.body.style.margin = "0";
  document.body.style.padding = "0";

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
  camera.position.z = 15;
  scene.add(camera);

  controls = new THREE.TrackballControls(camera, renderer.domElement);
  controls.dynamicDampingFactor = 0.5;
  controls.rotateSpeed = 3;

  var light = new THREE.PointLight(0xffffff, 1, Infinity);
  camera.add(light);

  stats = new Stats();
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.top = '0';
  document.body.appendChild(stats.domElement);

  resize();
  window.onresize = resize;

  populateScene();

  animate();
}

function resize() {
  WIDTH = window.innerWidth;
  HEIGHT = window.innerHeight;
  if (renderer && camera && controls) {
    renderer.setSize(WIDTH, HEIGHT);
    camera.aspect = WIDTH / HEIGHT;
    camera.updateProjectionMatrix();
    controls.handleResize();
  }
}

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

function animate() {
  requestAnimationFrame(animate);
  render();
  controls.update();
  stats.update();
}

function threeReady() {
  init();
}

(function() {
  function addScript(url, callback) {
    callback = callback || function() {};
    var script = document.createElement("script");
    script.addEventListener("load", callback);
    script.setAttribute("src", url);
    document.head.appendChild(script);
  }

  addScript("https://threejs.org/build/three.js", function() {
    addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function() {
      addScript("https://threejs.org/examples/js/libs/stats.min.js", function() {
        threeReady();
      })
    })
  })
})();

关于javascript - THREE.js:模拟 MeshBasicMaterial,同时允许彩色灯光,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44480512/

相关文章:

opengl - 每片段光照坐标系

OpenGL 聚光灯与 Cg

javascript - 如何在 JavaScript 中为 D3 构建数据集?

javascript - Vuejs 和 Drupal

javascript - Three.js 项目,在自上而下 View 中保持相机位于对象中心

javascript - three.js CameraHelper 旋转速度比 PerspectiveCamera 快

c++ - 为什么我的 3d 点积在另一个方向上变黑了?

javascript - 获取点击相对于最近亲属的位置

javascript - 全日历 v4 : addEvent() not working in custom view

javascript - Three.js 没有从我的星球构造函数渲染我的纹理