云图三维 | Three.js 雾化

1,522 阅读3分钟

图片来源:unsplash.com/photo......

云图三维 连接你·创造的世界 致力于打造国内第一家集查看、建模、装配和渲染于一体的“云端CAD”协作设计平台。

应读者的要求,希望我们成立一个专业的、面向成渝地区的前端开发人员的webgl、Threejs行业QQ交流群,便于大家讨论问题。群里有研究webgl、Threejs大佬哦,欢迎大家加入!——点击链接加入群聊【three.js/webgl重庆联盟群】:jq.qq.com/?_wv=1027&k…

背景

3D 引擎中的雾一般是根据相机的距离,逐渐淡化为特定颜色的方式实现的。在three.js 中,您可以通过创建FogFogExp2对象并将其设置在场景的 fog属性上来添加雾。

Fog可以通过nearfar设置距相机的距离。任何比near它更近的东西都不受雾的影响。任何比far完全雾颜色更远的东西。从它们的材料颜色到雾颜色之间的部分 nearfar淡入淡出。

还有FogExp2随着与相机的距离呈指数增长。

下面是创建雾的常见方式


 const scene = new THREE.Scene();
 {
    const color = 0xFFFFFF; // 白色
    const near = 10;
    const far = 100;
    scene.fog = new THREE.Fog(color, near, far);
 }

或者使用FogExp2


const scene = new THREE.Scene();
{

    const color = 0xFFFFFF;
    const density = 0.1;
    scene.fog = new THREE.FogExp2(color, density);
}

FogExp2效果更接近现实,但Fog更常用,因为它可以让您选择应用雾的位置,以便您可以决定在一定距离内显示清晰的场景,然后在该距离后淡出某种颜色。

image.png

重要的是要注意雾应用于渲染的事物。它是计算对象颜色的每个像素的一部分。这意味着如果你想让你的场景淡化到某种颜色,你需要将雾和背景颜色设置为相同的颜色。背景颜色通过scene.background 属性设置 。要选择背景颜色,直接用THREE.Color在其上附加一个即可。例如

scene.background = new THREE.Color('#F00'); // 红色

image.png

下面在之前的代码基础上添加背景色


 const scene = new THREE.Scene(); 

{

    const near = 1;

    const far = 2;

    const color = 'lightblue';

    scene.fog = new THREE.Fog(color, near, far);

    scene.background = new THREE.Color(color);

}

在下面的示例中,相机的near值为 0.1,其far值为 5。相机位于z = 2。立方体的大小为 1 个单位,Z = 0。这意味着雾设置为near = 1far = 2立方体将在其中心附近淡出。


function main() {

  const canvas = document.querySelector('#c');

  const renderer = new THREE.WebGLRenderer({canvas});



  const fov = 75;

  const aspect = 2;  // the canvas default

  const near = 0.1;

  const far = 5;

  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);

  camera.position.z = 2;



  const scene = new THREE.Scene();



  {

    const near = 1;

    const far = 2;

    const color = 'lightblue';

    scene.fog = new THREE.Fog(color, near, far);

    scene.background = new THREE.Color(color);

  }



  {

    const color = 0xFFFFFF;

    const intensity = 1;

    const light = new THREE.DirectionalLight(color, intensity);

    light.position.set(-1, 2, 4);

    scene.add(light);

  }



  const boxWidth = 1;

  const boxHeight = 1;

  const boxDepth = 1;

  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);



  function makeInstance(geometry, color, x) {

    const material = new THREE.MeshPhongMaterial({color});



    const cube = new THREE.Mesh(geometry, material);

    scene.add(cube);



    cube.position.x = x;



    return cube;

  }



  const cubes = [

    makeInstance(geometry, 0x44aa88,  0),

    makeInstance(geometry, 0x8844aa, -2),

    makeInstance(geometry, 0xaa8844,  2),

  ];



  function resizeRendererToDisplaySize(renderer) {

    const canvas = renderer.domElement;

    const width = canvas.clientWidth;

    const height = canvas.clientHeight;

    const needResize = canvas.width !== width || canvas.height !== height;

    if (needResize) {

      renderer.setSize(width, height, false);

    }

    return needResize;

  }



  function render(time) {

    time *= 0.001;



    if (resizeRendererToDisplaySize(renderer)) {

      const canvas = renderer.domElement;

      camera.aspect = canvas.clientWidth / canvas.clientHeight;

      camera.updateProjectionMatrix();

    }



    cubes.forEach((cube, ndx) => {

      const speed = 1 + ndx * .1;

      const rot = time * speed;

      cube.rotation.x = rot;

      cube.rotation.y = rot;

    });



    renderer.render(scene, camera);



    requestAnimationFrame(render);

  }



  requestAnimationFrame(render);

}



main();

效果如下图所示

image.png

使用 dat.GUI进行参数修改,dat.GUI 接受一个对象和一个属性,并自动为该类型的属性创建一个接口。可以用来修改雾的nearfar属性,注意nearfar于是无效的。


// 我们使用这个类来传递给 dat.gui

// 所以当它操纵近或远时

// 近是从不> 远和远从不< 近

class FogGUIHelper {

constructor(fog) {

this.fog = fog;

}

get near() {

return this.fog.near;

}

set near(v) {

this.fog.near = v;
this.fog.far = Math.max(this.fog.far, v);

}

get far() {

return this.fog.far;
}

set far(v) {

this.fog.far = v;

this.fog.near = Math.min(this.fog.near, v);

}

}

使用方式如下


{

const near = 1;

const far = 2;

const color = 'lightblue';

scene.fog = new THREE.Fog(color, near, far);

scene.background = new THREE.Color(color);

const fogGUIHelper = new FogGUIHelper(scene.fog);

gui.add(fogGUIHelper, 'near', near, far).listen();

gui.add(fogGUIHelper, 'far', near, far).listen();

}

nearfar参数设置用于调整雾的最小和最大值。

.listen()在最后2行的结尾告诉dat.GUI监听更改。这样,当我们near因为对dat.GUI的编辑而far 更改或far响应对neardat.GUI的编辑而更改时,将为我们更新其他属性的 UI。

能够改变雾的颜色也可能很好,但就像上面提到的,我们需要保持雾的颜色和背景颜色同步。所以,让我们为我们的助手添加另一个虚拟属性,当 dat.GUI 操作它时,它会设置两种颜色。

dat.GUI 可以通过 4 种方式操作颜色,如 CSS 6 位十六进制字符串(例如:)#112233。作为色调、饱和度、值、对象(例如:){h: 60, s: 1, v: }。作为 RGB 阵列(例如:)[255, 128, 64]。或者,作为 RGBA 数组(例如:)[127, 200, 75, 0.3]

对我们而言,使用十六进制字符串版本是最简单的,因为这样 dat.GUI 只处理单个值。幸运的是,THREE.Color 作为getHexString我们用来轻松获取这样一个字符串的方法,我们只需要在前面加上一个“#”。


// 我们使用这个类来传递给 dat.gui

// 所以当它操纵近或远时

// 近是从不> 远和远从不< 近

// 当 dat.gui 操作颜色时,我们也会

// 更新雾和背景颜色。

class FogGUIHelper {

constructor(fog, backgroundColor) {

this.fog = fog;

this.backgroundColor = backgroundColor;

}

get near() {

return this.fog.near;

}

set near(v) {

this.fog.near = v;

this.fog.far = Math.max(this.fog.far, v);

}

get far() {

return this.fog.far;
}

set far(v) {

this.fog.far = v;

this.fog.near = Math.min(this.fog.near, v);
}

get color() {
return `#${this.fog.color.getHexString()}`;

}

set color(hexString) {
this.fog.color.set(hexString);
this.backgroundColor.set(hexString);
 }
 }

然后我们调用gui.addColor为我们的助手的虚拟属性添加一个颜色 UI。


{

const near = 1;

const far = 2;

const color = 'lightblue';

scene.fog = new THREE.Fog(color, near, far);

scene.background = new THREE.Color(color); 

const fogGUIHelper = new FogGUIHelper(scene.fog, scene.background);

gui.add(fogGUIHelper, 'near', near, far).listen();

gui.add(fogGUIHelper, 'far', near, far).listen();

gui.addColor(fogGUIHelper, 'color');
}

image.png

您可以看到设置near为 1.9 和far2.0 在未雾化和完全雾化之间提供了非常清晰的过渡。其中  near= 1.1 和far= 2.9 应该是最平滑的,因为我们的立方体离相机旋转 2 个单位。

fog 材质上有一个布尔属性,用于确定使用该材质渲染的对象是否受到雾的影响。默认值为true,适用于 对于大多数材料。

一个更好的例子可能是房子和房子外面的浓雾。假设雾被设置为从 2 米远(近 = 2)开始,并在 4 米(远 = 4)处完全雾化。房间超过2米,房子可能超过4米,所以你需要设置房子内部的材料不施加雾,否则当站在房子里面看房间远端的墙外时会看起来像是在雾中。

两个效果对比

雾:真实,全部

image.png

雾:真实,只有外部材料

请注意房间远端的墙壁和天花板正在应用雾。通过关闭房屋材料上的雾气,我们可以解决这个问题。

image.png

写在最后

本文介绍了Three.js的雾相关的内容,包含了介绍Fog、FogExp2具体如何使用以及最后通过一个实例对比材质与雾的关系对比,希望对你有帮助。

本文发布自 云图三维大前端团队,文章未经授权禁止任何形式的转载。