云图三维 连接你·创造的世界 致力于打造国内第一家集查看、建模、装配和渲染于一体的“云端CAD”协作设计平台。
应读者的要求,希望我们成立一个专业的、面向成渝地区的前端开发人员的webgl、Threejs行业QQ交流群,便于大家讨论问题。群里有研究webgl、Threejs大佬哦,欢迎大家加入!——点击链接加入群聊【three.js/webgl重庆联盟群】:jq.qq.com/?_wv=1027&k…
背景
3D 引擎中的雾一般是根据相机的距离,逐渐淡化为特定颜色的方式实现的。在three.js 中,您可以通过创建Fog
或FogExp2
对象并将其设置在场景的 fog
属性上来添加雾。
Fog
可以通过near
和far
设置距相机的距离。任何比near
它更近的东西都不受雾的影响。任何比far
完全雾颜色更远的东西。从它们的材料颜色到雾颜色之间的部分 near
和far
淡入淡出。
还有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
更常用,因为它可以让您选择应用雾的位置,以便您可以决定在一定距离内显示清晰的场景,然后在该距离后淡出某种颜色。
重要的是要注意雾应用于渲染的事物。它是计算对象颜色的每个像素的一部分。这意味着如果你想让你的场景淡化到某种颜色,你需要将雾和背景颜色设置为相同的颜色。背景颜色通过scene.background
属性设置 。要选择背景颜色,直接用THREE.Color
在其上附加一个即可。例如
scene.background = new THREE.Color('#F00'); // 红色
下面在之前的代码基础上添加背景色
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 = 1
,far = 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();
效果如下图所示
使用 dat.GUI进行参数修改,dat.GUI 接受一个对象和一个属性,并自动为该类型的属性创建一个接口。可以用来修改雾的near
和far
属性,注意near
大far
于是无效的。
// 我们使用这个类来传递给 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();
}
near
和far
参数设置用于调整雾的最小和最大值。
将.listen()
在最后2行的结尾告诉dat.GUI监听更改。这样,当我们near
因为对dat.GUI的编辑而far
更改或far
响应对near
dat.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');
}
您可以看到设置near
为 1.9 和far
2.0 在未雾化和完全雾化之间提供了非常清晰的过渡。其中 near
= 1.1 和far
= 2.9 应该是最平滑的,因为我们的立方体离相机旋转 2 个单位。
fog
材质上有一个布尔属性,用于确定使用该材质渲染的对象是否受到雾的影响。默认值为true
,适用于 对于大多数材料。
一个更好的例子可能是房子和房子外面的浓雾。假设雾被设置为从 2 米远(近 = 2)开始,并在 4 米(远 = 4)处完全雾化。房间超过2米,房子可能超过4米,所以你需要设置房子内部的材料不施加雾,否则当站在房子里面看房间远端的墙外时会看起来像是在雾中。
两个效果对比
雾:真实,全部
雾:真实,只有外部材料
请注意房间远端的墙壁和天花板正在应用雾。通过关闭房屋材料上的雾气,我们可以解决这个问题。
写在最后
本文介绍了Three.js的雾相关的内容,包含了介绍Fog、FogExp2具体如何使用以及最后通过一个实例对比材质与雾的关系对比,希望对你有帮助。
本文发布自 云图三维大前端团队,文章未经授权禁止任何形式的转载。