1. PBR材質簡介
本節課沒有具體的代碼,就是給大家科普一下PBR材質,所謂PBR就是,基于物理的渲染(physically-based rendering)。
Three.js提供了兩個PBR材質相關的APIMeshStandardMaterial和MeshPhysicalMaterial,MeshPhysicalMaterial是MeshStandardMaterial擴展的子類,提供了更多功能屬性。
光照模型
如果你有初高中最基本的物理光學知識,應該有折射、鏡面反射、漫反射等基本光學概念,對于實際生活中的光學問題,Three.js會提供一些的光照模型來模擬物體表面的光照,光照模型就一種模擬光照的計算方法。MeshPhysicalMaterial
和MeshLambertMaterial
一樣都是渲染網格模型的材質,但是他們用的光照模型不同,具體點說就是材質模擬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.0到1.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與建模軟件對接的問題
- gltf能否存儲3D建模軟件的某個材質屬性:有些三維軟件特有的材質屬性,不一定能通過gltf導出,也談不上threejs解析
- 三維建模能否導出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;