当前位置: 首页 > article >正文

threejsPBR材质与纹理贴图

1. PBR材质简介

本节课没有具体的代码,就是给大家科普一下PBR材质,所谓PBR就是,基于物理的渲染(physically-based rendering)。

Three.js提供了两个PBR材质相关的APIMeshStandardMaterial和MeshPhysicalMaterial,MeshPhysicalMaterial是MeshStandardMaterial扩展的子类,提供了更多功能属性。

光照模型

如果你有初高中最基本的物理光学知识,应该有折射、镜面反射、漫反射等基本光学概念,对于实际生活中的光学问题,Three.js会提供一些的光照模型来模拟物体表面的光照,光照模型就一种模拟光照的计算方法。MeshPhysicalMaterialMeshLambertMaterial一样都是渲染网格模型的材质,但是他们用的光照模型不同,具体点说就是材质模拟Mesh反射光照的代码算法不同,算法不同,自然模拟光照的真实程度也不同。

如果你想深入研究光照模型,可以学习下原生WebGL或WebGPU,或者看看计算机图形学相关书籍,使用threejs的大部分情况,用不着你自己实现光照模型算法,毕竟threejs通过网格模型材质帮你实现了。

PBR相关理论介绍文章

  • 半小时了解PBR:https://zhuanlan.zhihu.com/p/37639418
  • PBR知识体系整理:https://zhuanlan.zhihu.com/p/100596453
  • PBR核心知识体系总结与概览:https://zhuanlan.zhihu.com/p/53086060

网格模型材质整体回顾

  • MeshLambertMaterial: Lambert光照模型(漫反射)

  • MeshPhongMaterial:Phong光照模型(漫反射、高光反射)

  • MeshStandardMaterial和MeshPhysicalMaterial:基于物理的光照模型(微平面理论、能量守恒、菲涅尔反射...)

PBR材质相比MeshLambertMaterial和MeshPhongMaterial可以提供更逼真的、更接近生活中的材质效果,当然也会占用更多的电脑硬件资源。

通过MeshPhysicalMaterial文档,提供的资源,可以查看多个PBR材质的案例效果,系统课程中轿车展示案例也会用到PBR材质。

渲染占用资源和表现能力

整体上来看,就是渲染表现能力越强,占用的计算机硬件资源更多。

占用渲染资源 MeshBasicMaterial < MeshLambertMaterial < MeshPhongMaterial < MeshStandardMaterial < MeshPhysicalMaterial

渲染表现能力 MeshBasicMaterial < MeshLambertMaterial < MeshPhongMaterial < MeshStandardMaterial < MeshPhysicalMaterial

2. PBR材质金属度和粗糙度

本节课给大家介绍PBR材质MeshStandardMaterial金属度metalness和粗糙度roughness,再加上下节课讲解的环境贴图.envMap,给大家呈现一个金属渲染效果。

金属度metalness

金属度属性.metalness表示材质像金属的程度, 非金属材料,如木材或石材,使用0.0,金属使用1.0。

threejs的PBR材质,.metalness默认是0.5,0.0到1.0之间的值可用于生锈的金属外观

new THREE.MeshStandardMaterial({metalness: 1.0,//金属度属性
})
mesh.material.metalness = 1.0;//金属度

粗糙度roughness

生活中不同物体表面的粗糙程度不同,比如地面比较粗糙,比如镜子表面就非常非常光滑。

粗糙度roughness表示模型表面的光滑或者说粗糙程度,越光滑镜面反射能力越强,越粗糙,表面镜面反射能力越弱,更多地表现为漫反射。

粗糙度roughness,0.0表示平滑的镜面反射,1.0表示完全漫反射,默认0.5。

new THREE.MeshStandardMaterial({roughness: 0.5,//表面粗糙度
})
mesh.material.roughness = 0.5;//表面粗糙度

相关代码

// 引入three.js
import * as THREE from "three";// 引入gltf加载器
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
// 实例化一个加载器对象
const loader = new GLTFLoader();
const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景// 加载glb格式的gltf模型
loader.load("../../ClearcoatTest.glb", function (gltf) {// console.log('gltf', gltf);// model.add(gltf.scene)gltf.scene.traverse(function (obj) {// 只获取所有mesh节点if (obj.isMesh) { // 判断是否是网格模型// 查看threejs渲染gltf默认材质console.log("obj", obj.material);obj.material.metalmess = 1.0; //设置金属度obj.material.roughness = 0.9; //设置粗糙度}});model.add(gltf.scene);
});export default model;

3. 环境贴图.envMap(金属效果)

环境贴图对PBR材质渲染效果影响还是比较大,一般渲染PBR材质的模型,最好设置一个合适的环境贴图。

立方体纹理加载器CubeTextureLoader

  • TextureLoader返回Texture
  • CubeTextureLoader返回CubeTexture

通过前面学习大家知道,通过纹理贴图加载器TextureLoader.load()方法加载一张图片可以返回一个纹理对象Texture

立方体纹理加载器CubeTextureLoader.load()方法是加载6张图片,返回一个立方体纹理对象CubeTexture

立方体纹理对象CubeTexture的父类是纹理对象Texture

CubeTextureLoader加载环境贴图

所谓环境贴图,就是一个模型周围的环境的图像,比如一间房子,房子的上下左右前后分别拍摄一张照片,就是3D空间中6个角度方向的照片。

// 加载环境贴图
// 加载周围环境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表示立方体纹理对象,父类是纹理对象Texture 

MeshStandardMaterial环境贴图属性.envMap

实际生活中,一个物体表面,往往会反射周围的环境。人的眼睛看到的东西,往往反射有周围景物,所以three.js渲染模型,如果想渲染效果更好看,如果想更符合实际生活情况,也需要想办法让模型反射周围景物。

MeshStandardMaterial材质的环境贴图属性是.envMap,通过PBR材质的贴图属性可以实现模型表面反射周围景物,这样渲染效果更好。

// 加载环境贴图
const textureCube = new THREE.CubeTextureLoader().setPath('./环境贴图/环境贴图0/').load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
new THREE.MeshStandardMaterial({metalness: 1.0,roughness: 0.5,envMap: textureCube, //设置pbr材质环境贴图
})    
obj.material.envMap = textureCube; //设置环境贴图 

环境贴图反射率.envMapIntensity

MeshStandardMaterial的.envMapIntensity属性主要用来设置模型表面反射周围环境贴图的能力,或者说环境贴图对模型表面的影响能力。具体说.envMapIntensity相当于环境贴图的系数,环境贴图像素值乘以该系数后,在用于影响模型表面。

// envMapIntensity:控制环境贴图对mesh表面影响程度
//默认值1, 设置为0.0,相当于没有环境贴图
obj.material.envMapIntensity = 1.0;

粗糙度roughness为0

你可以尝试把粗糙度roughness设置为0,看看模型对环境贴图的反射效果。

obj.material.roughness = 0.0;//完全镜面反射,像镜子一样

选择合适的环境贴图

不同的明暗或景物的环境贴图对渲染效果的影响是不一样的,所以不仅要设置环境贴图,还要根据需要选择合适的环境贴图,一般实际开发使用美术提供的环境贴图即可。

你可以尝试测试源码中提供多个环境贴图对比渲染效果差异。

纹理和渲染器颜色空间一致

//如果renderer.outputEncoding=THREE.sRGBEncoding;环境贴图需要保持一致
textureCube.encoding = THREE.sRGBEncoding;   

相关代码:可以看见区别

// 引入three.js
import * as THREE from "three";// 引入gltf加载器
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
// 实例化一个加载器对象
const loader = new GLTFLoader();
const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景const textureCude = new THREE.CubeTextureLoader().setPath("../../环境贴图/").load(["px.jpeg", "py.jpeg", "pz.jpeg", "nx.jpeg", "ny.jpeg", "nz.jpeg"]);// 加载glb格式的gltf模型
// loader.load("../../ClearcoatTest.glb", function (gltf) {
//   // console.log('gltf', gltf);
//   // model.add(gltf.scene)
//   gltf.scene.traverse(function (obj) {
//     // 只获取所有mesh节点
//     if (obj.isMesh) {
//       // 判断是否是网格模型
//       // 查看threejs渲染gltf默认材质
//       console.log("obj", obj.material);
//       obj.material.metalmess = 1.0; //设置金属度
//       obj.material.roughness = 0.9; //设置粗糙度
//       obj.material.envMap = textureCude //设置环境贴图属性的值为立方体纹理对象
//     }
//   });
//   model.add(gltf.scene);
// });loader.load("../../glTF/DamagedHelmet.gltf", function (gltf) {// gltf加载成功后返回一个对象// console.log('控制台查看gltf对象结构', gltf)gltf.scene.traverse(function (obj) {// 只获取所有mesh节点if (obj.isMesh) {// obj.material = new THREE.MeshLambertMaterial({//   color: 0xffffff,// });obj.material.metalmess = 1.0; //设置金属度obj.material.roughness = 0.9; //设置粗糙度  可以调值,范围在0-1之间,值最大,可见度更高,更亮,值越小,更暗obj.material.envMap = textureCude; //设置环境贴图属性的值为立方体纹理对象obj.material.envMapIntensity = 0}});// console.log('场景3D模型数据', gltf.scene)// console.log('gltf', gltf);model.add(gltf.scene); //三维场景添加到model组对象中
});export default model;

4. 环境贴图2

环境贴图作用测试

实际生活中光源照射到一个物体上,这个物体反射出去的光线也会影响其他的物体,环境贴图就是用一种简单方式,近似模拟一个物体周边环境对物体表面的影响。

测试:对于PBR材质,如果threejs三维场景不添加任何光源,物体就是完全黑色的,你可以不添加任何光源,尝试只使用环境贴图,你会发现物体表面的颜色也能看到,这说明环境贴图其实相当于提供了物体周围环境发射或反射的光线。

测试:更换不同明暗的环境贴图,你会发现场景中模型的明暗也有变化。

场景环境属性.environment

网格模型可以通过材质的.envMap属性设置环境贴图,如果一个gltf模型中所有的Mesh都要设置环境贴图就需要递归遍历gltf模型,给里面每个Mesh的材质设置.envMap。

loader.load("../工厂.glb", function (gltf) {// 递归遍历批量设置环境贴图gltf.scene.traverse(function (obj) {if (obj.isMesh) { //判断是否是网格模型obj.material.envMap = textureCube; //设置环境贴图}});
})

如果你希望环境贴图影响场景中scene所有Mesh,可以通过Scene的场景环境属性.environment实现,把环境贴图对应纹理对象设置为.environment的属性值即可。

环境贴图色彩空间编码.encoding

//如果renderer.outputEncoding=THREE.sRGBEncoding;环境贴图需要保持一致
textureCube.encoding = THREE.sRGBEncoding;   

相关代码:

// 引入three.js
import * as THREE from "three";// 引入gltf加载器
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
// 实例化一个加载器对象
const loader = new GLTFLoader();
const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景const textureCude = new THREE.CubeTextureLoader()// .setPath("../../环境贴图/环境贴图0/").setPath("../../环境贴图/环境贴图1/").load(["px.jpg", "py.jpg", "pz.jpg", "nx.jpg", "ny.jpg", "nz.jpg"]);// 加载glb格式的gltf模型
// "../../ClearcoatTest.glb"
loader.load("../../ClearcoatTest.glb", function (gltf) {// console.log('gltf', gltf);// model.add(gltf.scene)gltf.scene.traverse(function (obj) {// 只获取所有mesh节点if (obj.isMesh) {// 判断是否是网格模型// 查看threejs渲染gltf默认材质console.log("obj", obj.material);obj.material.metalmess = 1.0; //设置金属度obj.material.roughness = 0.9; //设置粗糙度obj.material.envMap = textureCude //设置环境贴图属性的值为立方体纹理对象}});model.add(gltf.scene);
});// loader.load("../../glTF/DamagedHelmet.gltf", function (gltf) {
//   // gltf加载成功后返回一个对象
//   // console.log('控制台查看gltf对象结构', gltf)
//   gltf.scene.traverse(function (obj) {
//     // 只获取所有mesh节点
//     if (obj.isMesh) {
//       // obj.material = new THREE.MeshLambertMaterial({
//       //   color: 0xffffff,
//       // });
//       obj.material.metalmess = 1.0; //设置金属度
//       obj.material.roughness = 0.9; //设置粗糙度  可以调值,范围在0-1之间,值最大,可见度更高,更亮,值越小,更暗
//       obj.material.envMap = textureCude; //设置环境贴图属性的值为立方体纹理对象
//       obj.material.envMapIntensity = 0
//     }
//   });
//   // console.log('场景3D模型数据', gltf.scene)
//   // console.log('gltf', gltf);
//   model.add(gltf.scene); //三维场景添加到model组对象中
// });export default model;

5. MeshPhysicalMaterial清漆层

MeshPhysicalMaterial和MeshStandardMaterial都是拥有金属度metalness、粗糙度roughness属性的PBR材质,MeshPhysicalMaterial是在MeshStandardMaterial基础上扩展出来的子类,除了继承了MeshStandardMaterial的金属度、粗糙度等属性,还新增了清漆.clearcoat、透光率.transmission、反射率.reflectivity、光泽.sheen、折射率.ior等等各种用于模拟生活中不同材质的属性。

清漆层属性.clearcoat

清漆层属性.clearcoat可以用来模拟物体表面一层透明图层,就好比你在物体表面刷了一层透明清漆,喷了点水。.clearcoat的范围0到1,默认0。

const material = new THREE.MeshPhysicalMaterial( {clearcoat: 1.0,//物体表面清漆层或者说透明涂层的厚度
} );

清漆层粗糙度.clearcoatRoughness

清漆层粗糙度.clearcoatRoughness属性表示物体表面透明涂层.clearcoat对应的的粗糙度,.clearcoatRoughness的范围是为0.0至1.0。默认值为0.0。

const material = new THREE.MeshPhysicalMaterial( {clearcoat: 1.0,//物体表面清漆层或者说透明涂层的厚度clearcoatRoughness: 0.1,//透明涂层表面的粗糙度
} );

车外壳PBR材质设置

在设置车外壳清漆层之前,先创建一个MeshPhysicalMaterial材质,并设置好环境贴图、金属度、粗糙度,属性值先根据文档说明给一个大概的值,具体可以通过gui交互界面可视化调试。

const mesh = gltf.scene.getObjectByName('外壳01');
mesh.material = new THREE.MeshPhysicalMaterial({color: mesh.material.color, //默认颜色metalness: 0.9,//车外壳金属度roughness: 0.5,//车外壳粗糙度envMap: textureCube, //环境贴图envMapIntensity: 2.5, //环境贴图对Mesh表面影响程度
})  

车外壳油漆效果

车外壳油漆效果,你可以通过PBR材质的清漆层属性.clearcoat和清漆层粗糙度.clearcoatRoughness属性模拟。

属性值先根据文档说明给一个大概的值,具体可以通过gui交互界面可视化调试。

const mesh = gltf.scene.getObjectByName('外壳01');
mesh.material = new THREE.MeshPhysicalMaterial( {clearcoat: 1.0,//物体表面清漆层或者说透明涂层的厚度clearcoatRoughness: 0.1,//透明涂层表面的粗糙度
} );

GUI可视化调试PBR材质属性

关于gui的使用,在第一章节入门中详细将结果,具体使用可以参照前面讲解。

// 范围可以参考文档
matFolder.add(mesh.material,'metalness',0,1);
matFolder.add(mesh.material,'roughness',0,1);
matFolder.add(mesh.material,'clearcoat',0,1);
matFolder.add(mesh.material,'clearcoatRoughness',0,1);
matFolder.add(mesh.material,'envMapIntensity',0,10);

效果:

.clearcoat : Float

表示clear coat层的强度,范围从0.01.0m,当需要在表面加一层薄薄的半透明材质的时候,可以使用与clear coat相关的属性,默认为0.0;

效果:与其他材质效果不一样了

 

代码:

 index.js

// 引入threejs
import * as THREE from "three";
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import model from "./model.js";
import gui from "./gui.js";
// 创建一个三维场景scene
const scene = new THREE.Scene();
scene.add(model);// 创建一个三维坐标轴
const axesHelper = new THREE.AxesHelper(100);
scene.add(axesHelper); //将坐标轴对象添加到三维场景中// 创建一个辅助网格地面的效果
// const girdHelper = new THREE.GridHelper(600, 50, 0x00ffff,0x004444);
// scene.add(girdHelper);// 创建一个光源对象  点光源
const pointLight = new THREE.PointLight(0xffffff, 1.0);
pointLight.decay = 0.0; //不随着距离的改变而衰减
pointLight.position.set(400, 200, 300); //偏移光源位置,观察渲染效果变化
// scene.add(pointLight); //点光源添加到场景中//可视化点光源
// const pointLightHelper = new THREE.PointLightHelper(pointLight, 10);
// scene.add(pointLightHelper);
// 添加一个环境光
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient); //没有方向,也就没有立体的// 删除
// scene.remove(ambient, model);
// scene.remove(ambient)
// scene.remove(model)// 添加一个平行光
const directionalLight = new THREE.DirectionalLight(0xfffff, 0.8);
directionalLight.position.set(100, 100, 100); //棱角很弱,跟每个面的夹角都一样
directionalLight.position.set(100, 60, 50); //可以看出每个面的棱角不一样
// directionalLight.target = mesh; //默认坐标原点
scene.add(directionalLight);// 环境光子菜单
const ambinentFolder = gui.addFolder('环境光')
ambinentFolder.close() //关闭菜单
// 环境光强度
ambinentFolder.add(ambient, 'intensity', 0,2)
// 平行光子菜单
const dirFolder = gui.addFolder('平行光')
dirFolder.close() //关闭菜单
// 平行光强度
dirFolder.add(directionalLight, 'intensity', 0,2)
const dirFolder2 = dirFolder.addFolder('位置') //子菜单的子菜单
dirFolder2.close() //关闭菜单
// 平行光位置
dirFolder2.add(directionalLight.position, 'x', -400,400)
dirFolder2.add(directionalLight.position, 'y', -400,400)
dirFolder2.add(directionalLight.position, 'z', -400,400)// 定义相机输出画布的尺寸(单位:像素px)
const width = window.innerWidth;
const height = window.innerHeight;
// 设置相机的四个参数// 创建一个透视投影相机对象
const camera = new THREE.PerspectiveCamera(10, width / height, 0.001, 1000);
// 设置相机的位置
// camera.position.set(20, 20, 20); //相机在Three.js三维坐标系中的位置
camera.position.set(50, 50, 50); //相机在Three.js三维坐标系中的位置
// camera.position.set(20, 8, 9); //根据相机可视化调试camera.position
// 相机的视线,观察目标点的坐标
camera.lookAt(0, 0, 0); //坐标原点
// camera.lookAt(0.5, -0.15, 0.3);
// 创建一个WebGL渲染器
const renderer = new THREE.WebGLRenderer({antialias: true, //启用抗锯齿,线条更加流畅,减少锯齿状
});
renderer.setSize(width, height); //canvas画布的宽高度
renderer.render(scene, camera); //执行一个渲染操作,类比相机的拍照动作 咔
//把渲染结果canvas画布,也就是所谓的“照片”,添加到网页的页面上
document.body.appendChild(renderer.domElement);
// 插入到任意的html元素中
// document.getElementById("webgl").appendChild(renderer.domElement)// 设置编码方式和gltf贴图保持一致,解决渲染颜色偏差的问题
renderer.outputEncoding = THREE.sRGBEncoding;console.log("查看当前屏幕设备像素比", window.devicePixelRatio); //查看当前屏幕设备像素比 2
// 告诉threejs你的屏幕的设备像素比window.devicePixelRatio,针对与像素接近于1的设置下面的语句可能不是很明显,对于屏幕比例是2的,高清屏这种,设置的效果会很明显,减少模糊
renderer.setPixelRatio(window.devicePixelRatio); //会很清晰,遇到模糊了不要忘记设置这个
// renderer.setClearColor(0x444444);// 创建一个相机控件对象
const controls = new OrbitControls(camera, renderer.domElement);
// 渲染循环
function render() {// console.log("camera.position", camera.position);// console.log("controls.target", controls.target);// model.rotateY(0.01); //周期性旋转,每次旋转0.01弧度renderer.render(scene, camera); //周期性执行相机渲染功能,更新canvas画布上的内容requestAnimationFrame(render);
}
render();controls.target.set(0, 0, 0); //默认为0,0,0,所以更改值之后要注意更新,并且与lookAt的参数一致
// controls.target.set(0.5, -0.15, 0.3); //默认为0,0,0,所以更改值之后要注意更新,并且与lookAt的参数一致
controls.update();
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener("change", function () {// console.log(camera.position);// 每当发生改变的时候就重新渲染renderer.render(scene, camera); //执行渲染操作
});window.onresize = function () {// 更新canvas画布的尺寸renderer.setSize(window.innerWidth, window.innerHeight);// 相机的视椎体宽高比一定和画布保持一致,否则物体就会扭曲camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();
};

model.js

// 引入three.js
import * as THREE from "three";// 引入gltf加载器
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";import gui from "./gui.js";// 创建材质子菜单
const matFolder = gui.addFolder("车外壳材质");
matFolder.close(); //关闭菜单// 实例化一个加载器对象
const loader = new GLTFLoader();
const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景const textureCube = new THREE.CubeTextureLoader()// .setPath("../../环境贴图/环境贴图0/").setPath("../../环境贴图/环境贴图1/").load(["px.jpg", "py.jpg", "pz.jpg", "nx.jpg", "ny.jpg", "nz.jpg"]);//如果renderer.outputEncoding=THREE.sRGBEncoding;环境贴图需要保持一致
textureCube.encoding = THREE.sRGBEncoding;
// 加载glb格式的gltf模型
// "../../ClearcoatTest.glb"
loader.load("../../ClearcoatTest.glb", function (gltf) {// console.log('gltf', gltf);// model.add(gltf.scene)gltf.scene.traverse(function (obj) {// 只获取所有mesh节点if (obj.isMesh) {// 判断是否是网格模型// 查看threejs渲染gltf默认材质console.log("obj", obj.name);obj.material.metalmess = 1.0; //设置金属度obj.material.roughness = 0.9; //设置粗糙度obj.material.envMap = textureCube; //设置环境贴图属性的值为立方体纹理对象}});model.add(gltf.scene);const mesh = gltf.scene.getObjectByName("R2_ClearCoatSample");mesh.material = new THREE.MeshPhysicalMaterial({color: mesh.material.color,metalness: 0.9,roughness: 0.5,envMap: textureCube,envMapIntensity: 2.0,clearcoat: 1.0,clearcoatRoughness: 0.1,});// const obj = {//   color: mesh.material.color, //材质颜色// };// // 材质颜色color// matFolder.addColor(obj, "color").onChange(function (value) {//   mesh.material.color.set(value);// });// 范围可参考文档matFolder.add(mesh.material, "metalness", 0, 1);matFolder.add(mesh.material, "roughness", 0, 1);matFolder.add(mesh.material, "clearcoat", 0, 1);matFolder.add(mesh.material, "clearcoatRoughness", 0, 1);matFolder.add(mesh.material, "envMapIntensity", 0, 10);
});export default model;

6. 物理材质透光率.transmission

如果你已经掌握上节课内容,可以继续学习物理材质MeshPhysicalMaterial的透光率属性.transmission和折射率属性.ior。

透光率(透射度).transmission

为了更好的模拟玻璃、半透明塑料一类的视觉效果,可以使用物理透明度.transmission属性代替Mesh普通透明度属性.opacity。

使用.transmission属性设置Mesh透明度,即便完全透射的情况下仍可保持高反射率。

物理光学透明度.transmission的值范围是从0.0到1.0。默认值为0.0。

const mesh = gltf.scene.getObjectByName('玻璃01')
mesh.material = new THREE.MeshPhysicalMaterial({transmission: 1.0, //玻璃材质透光率,transmission替代opacity 
})

折射率.ior

非金属材料的折射率从1.0到2.333。默认值为1.5。

不同材质的折射率,你可以百度搜索。

new THREE.MeshPhysicalMaterial({ior:1.5,//折射率
})

玻璃透光率.transmission设置

先设置玻璃金属度和粗糙度

const mesh = gltf.scene.getObjectByName('玻璃01')
mesh.material = new THREE.MeshPhysicalMaterial({metalness: 0.0,//玻璃非金属 roughness: 0.0,//玻璃表面光滑envMap:textureCube,//环境贴图envMapIntensity: 1.0, //环境贴图对Mesh表面影响程度
})

设置透光率.transmission和折射率.ior。

new THREE.MeshPhysicalMaterial({transmission: 1.0, //玻璃材质透光率,transmission替代opacity ior:1.5,//折射率
})

GUI可视化调试PBR材质属性

基本参数和代码设置好以后,就是通过GUI可视化交互界面,调试PBR材质或光源的参数,gui.js库的使用参考入门章节介绍。

const obj = {color: mesh.material.color, // 材质颜色
};
// 材质颜色color
matFolder.addColor(obj, 'color').onChange(function (value) {mesh.material.color.set(value);
});
// 范围可以参考文档
matFolder.add(mesh.material,'metalness',0,1);
matFolder.add(mesh.material,'roughness',0,1);
matFolder.add(mesh.material,'transmission',0,1);
matFolder.add(mesh.material,'ior',0,3);
matFolder.add(mesh.material,'envMapIntensity',0,10);

 代码:

model.js

// 引入three.js
import * as THREE from "three";// 引入gltf加载器
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";import gui from "./gui.js";// 创建材质子菜单
const matFolder = gui.addFolder("车外壳材质");
matFolder.close(); //关闭菜单// 实例化一个加载器对象
const loader = new GLTFLoader();
const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景const textureCube = new THREE.CubeTextureLoader()// .setPath("../../环境贴图/环境贴图0/").setPath("../../环境贴图/环境贴图1/").load(["px.jpg", "py.jpg", "pz.jpg", "nx.jpg", "ny.jpg", "nz.jpg"]);//如果renderer.outputEncoding=THREE.sRGBEncoding;环境贴图需要保持一致
textureCube.encoding = THREE.sRGBEncoding;
// 加载glb格式的gltf模型
// "../../ClearcoatTest.glb"
loader.load("../../ClearcoatTest.glb", function (gltf) {// console.log('gltf', gltf);// model.add(gltf.scene)gltf.scene.traverse(function (obj) {// 只获取所有mesh节点if (obj.isMesh) {// 判断是否是网格模型// 查看threejs渲染gltf默认材质console.log("obj", obj.name);// obj.material.metalmess = 1.0; //设置金属度// obj.material.roughness = 0.9; //设置粗糙度// obj.material.envMap = textureCube; //设置环境贴图属性的值为立方体纹理对象}});model.add(gltf.scene);const mesh = gltf.scene.getObjectByName("R2_ClearCoatSample");mesh.material = new THREE.MeshPhysicalMaterial({metalness: 0.0,roughness: 0.0,envMap: textureCube, //环境贴图envMapIntensity: 1.0, //环境贴图对Mesh表面影响程度transmission: 1.0,ior: 1.5});const obj = {color: mesh.material.color, //材质颜色};// 材质颜色colormatFolder.addColor(obj, "color").onChange(function (value) {mesh.material.color.set(value);});// 范围可参考文档matFolder.add(mesh.material, "metalness", 0, 1);matFolder.add(mesh.material, "roughness", 0, 1);matFolder.add(mesh.material, "transmission", 0, 1);matFolder.add(mesh.material, "ior", 1, 2.333);matFolder.add(mesh.material, "envMapIntensity", 0, 10);
});export default model;

效果:透明的效果

 

7. 三维软件导出PBR材质属性

实际开发的时候PBR材质的属性,很多时候是可以在三维建模软件中设置的,然后通过gltf导出即可,这样就不用在threejs代码设置。

通常美术对三维场景渲染的了解也比大部分前端程序员多的多,只要美术在三维建模软件设置好并导出包含pbr材质属性的gltf即可。

threejs与建模软件对接的问题

  1. gltf能否存储3D建模软件的某个材质属性:有些三维软件特有的材质属性,不一定能通过gltf导出,也谈不上threejs解析
  2. 三维建模能否导出PBR材质:能导出的话,能导出哪些属性,不能导出哪些属性

如果你的三维建模不能导出pbr材质,或者部分pbr材质属性无法导出,那你通常需要用代码方式添加材质,这样就麻烦些。

Blender导出PBR材质演示

首先Blender最新版导出gltf模型时候,是可以把PBR材质的很多属性导出的,比如金属度metalness、粗糙度roughness、清漆.clearcoat、透光率(透射度).transmission等等。课件源码中提供了blender导出的gltf模型你可以浏览器控制台打印测试,这些PBR材质属性能否解析渲染。

Bledner中设置PBR材质

你可以在Bledner中设置车外壳、车玻璃的材质属性

车外壳:清漆、清漆粗糙度
车玻璃:透光率(透射度)

threejs解析gltf材质规则

大家都知道,MeshPhysicalMaterial是MeshStandardMaterial的子类,具有更多的PBR材质属性和功能。

所以,threejs解析gltf模型,会用两种材质PBR材质去解析,一个是标准网格材质MeshStandardMaterial,一个是物理网格材质MeshPhysicalMaterial,如果能用MeshStandardMaterial表示就用,不能就换MeshPhysicalMaterial。

具体说就是,threejs解析gltf模型材质的时候,一般默认使用标准网格材质MeshStandardMaterial,如果gltf有的材质具有.clearcoat、.transmission等属性,标准网格材质MeshStandardMaterial无法表达的时候,会用物理网格材质MeshPhysicalMaterial来解析gltf材质。

查看threejs解析的PBR材质

gltf.scene.traverse(function(obj) {if (obj.isMesh) {console.log('obj.material',obj.material);}
});
console.log('外壳',mesh1.material);
console.log('玻璃',mesh2.material);

设置环境贴图

这时候清漆、清漆粗糙度、透光率(透射度)等属性Bledner都已经设置好了,threejs可以自动解析渲染,不用在代码中麻烦设置了,只要配上环境贴图即可。

const mesh1 = gltf.scene.getObjectByName('外壳01');
mesh1.material.envMap = textureCube; //环境贴图
mesh1.material.envMapIntensity = 1.0; 环境贴图对Mesh表面影响程度
const mesh2 = gltf.scene.getObjectByName('玻璃01');
mesh2.material.envMap = textureCube; //环境贴图
mesh2.material.envMapIntensity = 1.0; 环境贴图对Mesh表面影响程度

相关代码:

// 引入three.js
import * as THREE from "three";// 引入gltf加载器
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";import gui from "./gui.js";// 创建材质子菜单
const matFolder = gui.addFolder("车外壳材质");
matFolder.close(); //关闭菜单// 实例化一个加载器对象
const loader = new GLTFLoader();
const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景const textureCube = new THREE.CubeTextureLoader()// .setPath("../../环境贴图/环境贴图0/").setPath("../../环境贴图/环境贴图1/").load(["px.jpg", "py.jpg", "pz.jpg", "nx.jpg", "ny.jpg", "nz.jpg"]);//如果renderer.outputEncoding=THREE.sRGBEncoding;环境贴图需要保持一致
textureCube.encoding = THREE.sRGBEncoding;
// 加载glb格式的gltf模型
// "../../ClearcoatTest.glb"
loader.load("../../ClearcoatTest.glb", function (gltf) {// console.log('gltf', gltf);gltf.scene.traverse(function (obj) {// 只获取所有mesh节点if (obj.isMesh) {// 判断是否是网格模型// 查看threejs渲染gltf默认材质console.log("obj", obj.name);console.log("obj", obj.material);}});model.add(gltf.scene);const mesh1 = gltf.scene.getObjectByName("R2_ClearCoatSample");mesh1.material.envMap = textureCube; //环境贴图mesh1.material.envMapIntensity = 1.0; //环境贴图对Mesh表面影响程度// mesh1.material.transmission = 1.0;const mesh2 = gltf.scene.getObjectByName("R1_ClearCoatSample");mesh2.material.envMap = textureCube; //环境贴图mesh2.material.envMapIntensity = 1.0; //环境贴图对Mesh表面影响程度// mesh2.material.transmission = 1.0;console.log("r2", mesh1.material);console.log("r3", mesh2.material);
});export default model;

相关文章:

threejsPBR材质与纹理贴图

1. PBR材质简介 本节课没有具体的代码&#xff0c;就是给大家科普一下PBR材质&#xff0c;所谓PBR就是&#xff0c;基于物理的渲染(physically-based rendering)。 Three.js提供了两个PBR材质相关的APIMeshStandardMaterial和MeshPhysicalMaterial,MeshPhysicalMaterial是Mes…...

深兰科技董事长陈海波受邀出席2025苏商高质量发展(常州)峰会,共话AI驱动产业升级

5月29日&#xff0c;2025苏商高质量发展峰会在常州隆重开幕。本次峰会聚焦新质生产力培育与产业创新转型&#xff0c;汇聚了众多江苏省内知名企业家、专家学者及政府代表。深兰科技创始人、董事长陈海波作为人工智能领域的领军企业代表&#xff0c;受邀出席盛会并参与重要活动环…...

【计算机网络】子网划分

文章目录 【计算机网络】子网划分&#xff08;知识点详细&#xff09;一、子网划分基础概念1. **为什么需要子网划分&#xff1f;**2. **关键术语** 二、子网划分核心原理1. **借位规则**2. **子网划分步骤** 三、子网划分实战案例案例1&#xff1a;标准C类网划分&#xff08;等…...

Git入门到精通:30分钟掌握核心技巧

目录 一、基础理论片 Git简介 Git安装 Git仓库 Git基本命令用法 仓库别名 二、实操命令篇 远程分支 分支的新建和合并 实操演示 1 本地新建仓库 2 gitee新建仓库 3 建立关系 4 新建分支 5 开发新功能 6 推送新分支 7 合并新分支到主分支 三、可视化工具篇 G…...

Redis7底层数据结构解析

redisObject 在 Redis 的源码中&#xff0c;Redis 会将底层数据结构&#xff08;如 SDS、hash table、skiplist 等&#xff09;统一封装成一个对象&#xff0c;这个对象叫做 redisObject&#xff0c;也简称 robj。 typedef struct redisObject {unsigned type : 4; // 数…...

Android 异步编程中协程的完整实战示例

一、全链路数据加载&#xff1a;网络请求 数据库缓存 在实际开发中&#xff0c;数据加载通常需要先检查本地缓存&#xff0c;若缓存失效则从网络获取&#xff0c;并将结果更新到本地。以下是完整的 MVVM 架构示例&#xff1a; 1. 项目结构 app/ ├── data/ …...

多部手机连接同一wifi的ip一样吗?

在家庭和办公环境中&#xff0c;多台手机同时连接同一个WiFi路由器已成为常态。不少用户会产生疑问&#xff1a;这些设备的IP地址会相同吗&#xff1f;下面就一起来了解一下吧。 一、多部手机连接同一WiFi的IP‌一样吗 多部手机连接同一WiFi时的IP地址是否相同&#xff0c;需要…...

大语言模型值ollama使用(1)

ollama为本地调用大语言模型提供了便捷的方式。下面列举如何在windows系统中快捷调用ollama。 winR打开运行框&#xff0c;输入cmd 1、输入ollama list 显示已下载模型 2、输入ollama pull llama3 下载llama3模型 3、 输入 ollama run llama3 运行模型 4、其他 ollama li…...

大模型应用开发之Langchain

一、框架简述 Langchain 是一个用于构建和管理 LLM 应用的开发框架。它为开发者提供了工具和接口&#xff0c;以便于更轻松地将大语言模型集成到应用程序中&#xff0c;并处理语言模型生成的响应、管理对话状态、执行链式调用、处理多步任务等。 二、Langchain主要模块 1、M…...

thc-ssl-dos:SSL 压力测试的轻量级工具!全参数详细教程!Kali Linux教程!

简介 THC-SSL-DOS 是一款用于验证 SSL 性能的工具。 建立安全的 SSL 连接需要服务器比客户端高 15 倍的处理能力。 THC-SSL-DOS 利用这种不对称特性&#xff0c;通过使服务器过载并使其断网。 此问题影响当今所有 SSL 实现。供应商自 2003 年以来就已意识到这个问题&#x…...

什么是内网ip证书

内网IP证书是一种基于公钥基础设施&#xff08;PKI&#xff09;技术的数字证书&#xff0c;专门用于保护企业内部网络中通过IP地址访问服务的通信安全。以下是对内网IP证书的详细解析&#xff1a; 一、核心定义与用途 定义&#xff1a;内网IP证书是SSL/TLS证书的一种特殊类型…...

【速通RAG实战:进阶】17、AI视频打点全攻略:从技术实现到媒体工作流提效的实战指南

一、AI视频打点的技术底层与数据处理流程 (一)视频内容结构化的核心技术栈 AI视频打点的本质是将非结构化视频数据转化为带时间戳的结构化信息,其技术流程涵盖音视频处理、语音识别、自然语言处理三大核心模块,形成“数据采集-内容解析-智能标记-协同应用”的完整闭环。 …...

立控信息智能装备柜:科技赋能军队装备管理现代化

在军事装备管理领域&#xff0c;高效、安全、智能化的存储解决方案至关重要。传统的人工管理模式不仅效率低下&#xff0c;还容易因人为疏忽导致装备丢失或管理混乱。​LKONE智能装备柜凭借先进的物联网技术、生物识别安全系统和智能管理功能&#xff0c;为军队提供了一套高效、…...

【freertos-kernel】queue(发送)

文章目录 补充各种yeildTCB的xStateListItem和xEventListItem xQueueGenericSendprvCopyDataToQueueprvNotifyQueueSetContainervTaskInternalSetTimeOutStatevTaskSuspendAllxTaskResumeAllprvLockQueueprvUnlockQueueprvIncrementQueueTxLockvTaskPlaceOnEventListprvAddCurr…...

【华为云物联网】如何实现在 MQTT.fx 上模拟数据间隔上传一次,并按设定系数变动数据

虽然 MQTT.fx 本身不支持定时循环脚本发送消息,但可以通过以下方式 实现在 MQTT.fx 上模拟设备参数每隔 1 分钟上传一次,并按设定系数变动数据: ✅ 推荐方式:使用 Python 脚本+MQTT.fx 联动观察 你将用 Python 自动发送数据,MQTT.fx 订阅对应主题观察是否发送成功。 🧩…...

破解高原运维难题:分布式光伏智能监控系统的应用研究

安科瑞刘鸿鹏 摘要 高原地区光照资源丰富&#xff0c;具有发展分布式光伏发电的巨大潜力。然而&#xff0c;该地区复杂的气候环境、地形地貌和运维条件对光伏电站的运行与维护带来严峻挑战。本文结合Acrel1000DP分布式光伏监控系统的技术特点和典型应用案例&#xff0c;探讨其…...

图标变白,开始菜单栏无法打开程序(以jupyter为例)

不知道是本人删了一些东西导致的还是什么原因&#xff0c;总之现在本人的jupyter只能通过命令行打开&#xff0c;以往我是从开始菜单栏打开。琢磨了一段时间&#xff0c;发现是.ico文件没有了。重新在网上下载图片&#xff0c;用网站图片转 ico 图标 - 锤子在线工具 转换一下格…...

大语言模型(LLM)入门 - (1) 相关概念

文章来自&#xff1a;大语言模型(LLM)小白入门自学项目-TiaoYu-1 GitHub - tiaoyu1122/TiaoYu-1: For People! For Freedom!For People! For Freedom! Contribute to tiaoyu1122/TiaoYu-1 development by creating an account on GitHub.https://github.com/tiaoyu1122/TiaoYu…...

行为型:访问者模式

目录 1、核心思想 2、实现方式 2.1 模式结构 2.2 实现案例 3、优缺点分析 4、适用场景 1、核心思想 目的&#xff1a;数据结构稳定的情况下&#xff0c;解决数据与算法的耦合问题。适用于对象结构稳定但需频繁扩展操作的场景。 实现&#xff1a;在访问数据时根据数据类…...

C++数据结构 : 哈希表的实现

C数据结构 &#xff1a; 哈希表的实现 目录 C数据结构 &#xff1a; 哈希表的实现引言1. 哈希概念1.1 直接定址法1.2 哈希冲突1.3 负载因子 2. 哈希函数2.1 除法散列法/除留余数法2.2 乘法散列法&#xff08;了解&#xff09;2.3 全域散列法&#xff08;了解&#xff09; 3. 处…...

抖音电商客户端一面面经

抖音电商客户端一面面经 时间&#xff1a; 25.05.30 岗位&#xff1a; 抖音电商客户端开发工程师 形式&#xff1a; 技术一面 刚刚结束了字节跳动抖音电商客户端开发工程师岗位的技术一面&#xff0c;整体感觉考察范围非常全面&#xff0c;涵盖了基础、项目、算法、系统设计等…...

JavaScript 在 AcroForm 中的广泛应用

在Adobe表单(特别是SAP Interactive Forms by Adobe)中使用JavaScript的各种技巧和方法,下面这些代码片段可以帮助开发者更高效地处理表单逻辑和交互。 1. 获取数据内容 从上下文结构中获取数据 var LV_DATA = xfa.resolveNode("$record.IM_TEST.FIELDNAME").val…...

Socket编程之TCP套件字

基于的TCP套件字编程流程 1. Socket套接字 Socket是一个编程接口&#xff08;网络编程接口&#xff09;&#xff0c;是一种特殊的文件描述符&#xff08;write/read&#xff09;。Socket并不 仅限于TCP/IP Socket独立于具体协议的编程接口&#xff0c;这个接口位于TCP/IP四层…...

AD9268、AD9643调试过程中遇到的问题

Ad9268芯片 AD9268是一款双通道、16位、80 MSPS/105 MSPS/125 MSPS模数转换器(ADC)。AD9268旨在支持要求高性能、低成本、小尺寸和多功能的通信应用。双通道ADC内核采用多级差分流水线架构&#xff0c;集成输出纠错逻辑。每个ADC都具有宽带宽、差分采样保持模拟输入放大器&…...

Java-File类基本方法使用指南

Java-File类基本方法使用指南 一、File类基础概念1.1 什么是File类1.2 File类的构造函数 二、文件和目录的创建与删除2.1 创建文件 - createNewFile()2.2 创建目录 - mkdir() 和 mkdirs()2.3 删除文件或目录 - delete() 三、文件和目录的查询与判断3.1 存在性判断 - exists()3.…...

Python爬虫实战:研究PyQuery库相关技术

1. 引言 1.1 研究背景与意义 随着互联网的快速发展,网络上的数据量呈爆炸式增长。如何高效地从海量的网页数据中提取有价值的信息,成为当前信息技术领域的一个重要研究方向。网络爬虫作为一种自动获取网页内容的程序,能够按照一定的规则,自动地抓取万维网信息,在搜索引擎…...

第九篇:MySQL 安全加固与访问控制策略实战

数据库的安全不仅仅是防止外部入侵&#xff0c;更包括合理配置账户权限、日志审计、网络加密、配置加固等。本文将系统性梳理 MySQL 的安全机制与实战加固方法&#xff0c;助你构建安全可靠的数据库运行环境。 一、数据库安全风险面 数据库常面临的威胁&#xff1a; 弱口令或默…...

神经网络-Day40

目录 单通道图片的规范写法图像任务中的张量形状NLP任务中的张量形状1. **Flatten操作**2. **view/reshape操作** 总结彩色图片的规范写法 图像数据的格式以及模型定义的过程&#xff0c;和之前结构化数据的略有不同&#xff0c;主要差异体现在2处 模型定义的时候需要展平图像由…...

WindowServer2022下docker方式安装dify步骤

WindowServer2022下docker方式安装dify步骤&#xff08;稳定后考虑部署至linux中&#xff09; 教程&#xff1a;https://blog.csdn.net/qq_49035156/article/details/143264534 0、资源要求 ---windows&#xff1a;8核CPU、16G内存、200G500G存储 ---10.21.31.122/administra…...

Java五种方法批量处理List元素全解

Java:如何优雅批量处理List中的每个元素 一、场景分析&#xff1a;为什么需要批量处理List&#xff1f;二、核心方法&#xff1a;五种实现方式对比2.1 普通for循环&#xff08;最直接的方式&#xff09;代码示例&#xff1a;优缺点&#xff1a; 2.2 Java 8 replaceAll&#xff…...