Appearance
没有环境贴图的感觉
可以看到环境贴图对PBR材质渲染效果影响还是比较大,一般渲染PBR材质的模型,最好设置一个合适的环境贴图。
立方体纹理加载器CubeTextureLoader
- TextureLoader返回Texture
- CubeTextureLoader返回CubeTexture
通过前面学习大家知道,通过纹理贴图加载器TextureLoader的.load()方法加载一张图片可以返回一个纹理对象Texture。
立方体纹理加载器CubeTextureLoader的.load()方法是加载6张图片,返回一个立方体纹理对象CubeTexture。
立方体纹理对象CubeTexture的父类是纹理对象Texture。
CubeTextureLoader加载环境贴图
所谓环境贴图,就是一个模型周围的环境的图像,比如一间房子,房子的上下左右前后分别拍摄一张照片,就是3D空间中6个角度方向的照片。
js
// 加载环境贴图
// 加载周围环境6个方向贴图
// 上下左右前后6张贴图构成一个立方体空间
// 'px.jpg', 'nx.jpg':x轴正方向、负方向贴图 p:正positive n:负negative
// 'py.jpg', 'ny.jpg':y轴贴图
// 'pz.jpg', 'nz.jpg':z轴贴图
const textureCube = new THREE.CubeTextureLoader()
.setPath('./环境贴图/环境贴图0/')
.load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
// CubeTexture表示立方体纹理对象,父类是纹理对象TextureMeshStandardMaterial环境贴图属性.envMap
实际生活中,一个物体表面,往往会反射周围的环境。人的眼睛看到的东西,往往反射有周围景物,所以three.js渲染模型,如果想渲染效果更好看,如果想更符合实际生活情况,也需要想办法让模型反射周围景物。
MeshStandardMaterial材质的环境贴图属性是.envMap,通过PBR材质的贴图属性可以实现模型表面反射周围景物,这样渲染效果更好。
js
// 加载环境贴图
// 递归遍历所有模型节点批量修改材质
loader.load('/ziyuan/金属.glb', function (gltf) {
// 递归遍历所有模型节点批量修改材质
gltf.scene.traverse(function (obj) {
if (obj.isMesh) {
//判断是否是网格模型
// console.log('obj.material',obj.material);
// 重新设置材质的金属度和粗糙度属性
obj.material.metalness = 1.0; //金属度
obj.material.roughness = 0.35; //表面粗糙度
obj.material.envMap = textureCube; //设置环境贴图
// envMapIntensity:控制环境贴图对mesh表面影响程度
obj.material.envMapIntensity = 1; //默认值1, 设置为0.0,相当于没有环境贴图
// 相当于下面这种写法
// obj.material = new THREE.MeshStandardMaterial({
// color: obj.material.color, //读取材质原来的颜色
// metalness: 1.0, //金属度
// roughness: 0.5, //粗糙度
// envMap: textureCube, //设置pbr材质环境贴图
// // envMapIntensity:控制环境贴图对mesh表面影响程度
// envMapIntensity: 0.0, //默认值1, 设置为0.0,相当于没有环境贴图
// })
}
});
model.add(gltf.scene);
});有环境贴图的效果
环境贴图反射率.envMapIntensity
MeshStandardMaterial的.envMapIntensity属性主要用来设置模型表面反射周围环境贴图的能力,或者说环境贴图对模型表面的影响能力。具体说.envMapIntensity相当于环境贴图的系数,环境贴图像素值乘以该系数后,在用于影响模型表面。
js
// envMapIntensity:控制环境贴图对mesh表面影响程度
//默认值1, 设置为0.0,相当于没有环境贴图
obj.material.envMapIntensity = 1.0;粗糙度roughness为0
你可以尝试把粗糙度roughness设置为0,看看模型对环境贴图的反射效果。
js
// obj.material.roughness = 0.5; //表面粗糙度
obj.material.roughness = 0.0;//完全镜面反射,像镜子一样镜面反射的感觉
选择合适的环境贴图
不同的明暗或景物的环境贴图对渲染效果的影响是不一样的,所以不仅要设置环境贴图,还要根据需要选择合适的环境贴图,一般实际开发使用美术提供的环境贴图即可。
纹理和渲染器颜色空间一致
js
//如果renderer.outputEncoding=THREE.sRGBEncoding;环境贴图需要保持一致
textureCube.encoding = THREE.sRGBEncoding;场景环境属性.environment
网格模型可以通过材质的.envMap属性设置环境贴图,如果一个gltf模型中所有的Mesh都要设置环境贴图就需要递归遍历gltf模型,给里面每个Mesh的材质设置.envMap。
js
loader.load("../工厂.glb", function (gltf) {
// 递归遍历批量设置环境贴图
gltf.scene.traverse(function (obj) {
if (obj.isMesh) { //判断是否是网格模型
obj.material.envMap = textureCube; //设置环境贴图
}
});
})如果你希望环境贴图影响场景中scene所有Mesh,可以通过Scene的场景环境属性.environment实现,把环境贴图对应纹理对象设置为.environment的属性值即可。
js
// 环境贴图纹理对象textureCube作为.environment属性值,影响所有模型
scene.environment = textureCube;整个背景都是贴图的感觉
代码
html
<template>
<div id="canvas"></div>
</template>
<script setup>
import * as THREE from 'three';
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { onMounted, ref } from 'vue';
import { useRoute } from 'vue-router';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const route = useRoute();
onMounted(() => {
// 创建场景
const scene = new THREE.Scene();
const loader = new GLTFLoader(); //创建一个GLTF加载器
const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景
// 加载环境贴图
// 加载周围环境6个方向贴图
// 上下左右前后6张贴图构成一个立方体空间
// 'px.jpg', 'nx.jpg':x轴正方向、负方向贴图 p:正positive n:负negative
// 'py.jpg', 'ny.jpg':y轴贴图
// 'pz.jpg', 'nz.jpg':z轴贴图
// CubeTexture表示立方体纹理对象,父类是纹理对象Texture
const textureCube = new THREE.CubeTextureLoader()
.setPath('/ziyuan/环境贴图/环境贴图1/')
.load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
textureCube.encoding = THREE.sRGBEncoding;
const geometry = new THREE.SphereGeometry(15, 32, 16);
const material = new THREE.MeshStandardMaterial({
envMap: textureCube, //设置环境贴图
metalness: 1, //金属度
roughness: 0.0 //完全镜面反射,像镜子一样
});
scene.background = textureCube;
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
// AxesHelper:辅助观察的坐标系
// const axesHelper = new THREE.AxesHelper(100);
// scene.add(axesHelper);
// tip: 环境光
const directionalLight = new THREE.DirectionalLight(0xffe1a4, 1);
directionalLight.position.set(100, 60, 50);
scene.add(directionalLight);
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient);
// const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5, 0xff0000);
// scene.add(dirLightHelper);
// width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
const width = window.innerWidth; //宽度
const height = window.innerHeight; //高度
/**
* 透视投影相机设置
*/
// 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000:远裁截面
const camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.set(12, 15, 89); //相机在Three.js三维坐标系中的位置
camera.lookAt(0, 0, 0); //相机观察目标指向Three.js坐标系原点
/**
* 创建渲染器对象
*/
const renderer = new THREE.WebGLRenderer({
antialias: true // 开启抗锯齿
});
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
renderer.render(scene, camera); //执行渲染操作
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.outputColorSpace = THREE.SRGBColorSpace; //设置为SRGB颜色空间
document.getElementById('canvas').appendChild(renderer.domElement);
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
function render() {
renderer.render(scene, camera); //执行渲染操作
// 查看相机位置 调整到一个合适的地方
// console.log('camera.position',camera.position);
requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧
}
render();
// 画布跟随窗口变化
window.onresize = function () {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
};
});
</script>