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;

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/907755.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/907755.shtml
英文地址,請注明出處:http://en.pswp.cn/news/907755.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android 12系統源碼_多屏幕(四)自由窗口模式

一、小窗模式 1.1 小窗功能的開啟方式 開發者模式下開啟小窗功能 adb 手動開啟 adb shell settings put global enable_freeform_support 1 adb shell settings put global force_resizable_activities 11.2 源碼配置 copy file # add for freedom PRODUCT_COPY_FILES …

C# 將HTML文檔、HTML字符串轉換為圖片

在.NET開發中&#xff0c;將HTML內容轉換為圖片的需求廣泛存在于報告生成、郵件內容存檔、網頁快照等場景。Free Spire.Doc for .NET作為一款免費的專業文檔處理庫&#xff0c;無需Microsoft Word依賴&#xff0c;即可輕松實現這一功能。本文將深入解析HTML文檔和字符串轉圖片兩…

【HTML-15.2】HTML表單按鈕全面指南:從基礎到高級實踐

表單按鈕是網頁交互的核心元素&#xff0c;作為用戶提交數據、觸發操作的主要途徑&#xff0c;其重要性不言而喻。本文將系統性地介紹HTML表單按鈕的各種類型、使用場景、最佳實踐以及高級技巧&#xff0c;幫助開發者構建更高效、更易用的表單交互體驗。 1. 基礎按鈕類型 1.1…

吳恩達MCP課程(4):connect_server_mcp_chatbot

目錄 完整代碼代碼解釋1. 導入和初始化2. 類型定義3. MCP_ChatBot 類初始化4. 查詢處理 (process_query)5. 服務器連接管理6. 核心特性總結 示例 完整代碼 原課程代碼是用Anthropic寫的&#xff0c;下面代碼是用OpenAI改寫的&#xff0c;模型則用阿里巴巴的模型做測試 .env 文…

C++內存學習

引入 在實例化對象時&#xff0c;不管是編譯器還是我們自己&#xff0c;會使用構造函數給成員變量一個合適的初始值。 但是經過構造函數之后&#xff0c;我們還不能將其稱為成員變量的初始化&#xff1a; 構造函數中的語句只能稱為賦初值&#xff0c;而不能稱作初始化 因為初…

MySQL 大戰 PostgreSQL

一、底層架構對比 ??維度????MySQL????PostgreSQL????存儲引擎??多引擎支持&#xff08;InnoDB、MyISAM等&#xff09;單一存儲引擎&#xff08;支持擴展如Zheap、Zedstore&#xff09;??事務實現??基于UNDO日志的MVCC基于堆表(Heap)的MVCC??鎖機制??…

基于FPGA的二叉決策樹cart算法verilog實現,訓練環節采用MATLAB仿真

目錄 1.算法運行效果圖預覽 2.算法運行軟件版本 3.部分核心程序 4.算法理論概述 5.算法完整程序工程 1.算法運行效果圖預覽 (完整程序運行后無水印) MATLAB訓練結果 上述決策樹判決條件&#xff1a; 分類的決策樹1 if x21<17191.5 then node 2 elseif x21>17191…

【RAG】RAG綜述|一文了解RAG|從零開始(下)

文章目錄 5. RAG的架構5.1 Naive RAG5.2 Advanced RAG5.2.1 檢索前處理和數據索引技術5.2.2 知識分片技術5.2.3 分層索引5.2.4 檢索技術5.2.4.1 優化用戶查詢5.2.4.2 通過假想文檔嵌入修復查詢和文檔不對稱5.2.4.3 Routing5.2.4.5 自查詢檢索5.2.4.6 混合搜索5.2.4.7 圖檢索5.2…

山東大學軟件學院項目實訓-基于大模型的模擬面試系統-面試官和面試記錄的分享功能(2)

本文記錄在發布文章時&#xff0c;可以添加自己創建的面試官和面試記錄到文章中這一功能的實現。 前端 首先是在原本的界面的底部添加了兩個多選框&#xff08;后期需要美化調整&#xff09; 實現的代碼&#xff1a; <el-col style"margin-top: 1rem;"><e…

FPGA純verilog實現MIPI-DSI視頻編碼輸出,提供工程源碼和技術支持

目錄 1、前言工程概述免責聲明 2、相關方案推薦我已有的所有工程源碼總目錄----方便你快速找到自己喜歡的項目我這里已有的 MIPI 編解碼方案 3、設計思路框架工程設計原理框圖FPGA內部彩條RGB數據位寬轉換RGB數據緩存MIPI-DSI協議層編碼MIPI-DPHY物理層串化MIPI-LVDS顯示屏工程…

LXQt修改開始菜單高亮

開始菜單紅色高亮很難看 mkdir -p ~/.local/share/lxqt/palettes/ mkdir -p ~/.local/share/lxqt/themes/ cp /usr/share/lxqt/palettes/Dark ~/.local/share/lxqt/palettes/Darker cp -p /usr/share/lxqt/themes/dark ~/.local/share/lxqt/themes/darker lxqt-panel.qss L…

DeepSeek-R1-0528-Qwen3-8B 本地ollama離線運行使用和llamafactory lora微調

參考: https://huggingface.co/deepseek-ai/DeepSeek-R1-0528-Qwen3-8B 量化版本: https://huggingface.co/unsloth/DeepSeek-R1-0528-Qwen3-8B-GGUF https://docs.unsloth.ai/basics/deepseek-r1-0528-how-to-run-locally 1、ollama運行 升級ollama版本到0.9.0 支持直接…

vue3 + WebSocket + Node 搭建前后端分離項目 開箱即用

[TOC](vue3 WebSocket Node 搭建前后端分離項目) 開箱即用 前言 top1&#xff1a;vue3.5搭建前端H5 top2&#xff1a;Node.js koa搭建后端服務接口 top3&#xff1a;WebSocket 長連接實現用戶在線聊天 top4&#xff1a;接口實現模塊化 Mysql 自定義 top5&#xff1a;文件上…

Vue 前端代碼規范實戰:ESLint v9、Prettier 與 Stylelint 集成指南與最佳實踐

&#x1f680; 作者主頁&#xff1a; 有來技術 &#x1f525; 開源項目&#xff1a; youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template &#x1f33a; 倉庫主頁&#xff1a; GitCode︱ Gitee ︱ Github &#x1f496; 歡迎點贊 &#x1f44d; 收藏 ?評論 …

docker docker-ce docker.io

Ubuntu安裝 ??更新軟件包列表?? 首先確保軟件包列表是最新的&#xff1a; sudo apt-get update 使用正確的卸載命令?? 替換 docker-engine 為 docker-ce 或 docker.io&#xff1a; sudo apt-get remove docker docker-ce docker.io containerd runc ??檢查已安裝的 Do…

C++ 初階 | 類和對象易錯知識點(下)

目錄 0.引言 1.初始化列表 2.static 靜態成員變量&#xff1a; 靜態成員函數&#xff1a; 3.友元函數 4.內部類 定義&#xff1a; 特點&#xff1a; 應用&#xff1a; 5.優化寫法 6.例題 求和12...n (不能用for/while/if/else等關鍵字) 7.總結 0.引言 今天&…

使用yocto搭建qemuarm64環境

環境 yocto下載 # 源碼下載 git clone git://git.yoctoproject.org/poky git reset --hard b223b6d533a6d617134c1c5bec8ed31657dd1268 構建 # 編譯鏡像 export MACHINE"qemuarm64" . oe-init-build-env bitbake core-image-full-cmdline 運行 # 跑虛擬機 export …

AWS WebRTC:獲取ICE服務地址(part 3):STUN服務和TURN服務的作用

STUN服務和TURN服務的作用&#xff1a; 服務全稱作用是否中繼流量適用場景STUNSession Traversal Utilities for NAT 協助設備發現自己的公網地址&#xff08;srflx candidate&#xff09; ? 不中繼&#xff0c;僅輔助NAT 穿透成功時使用TURNTraversal Using Relays around N…

分析XSSstrike源碼

#用于學習web安全自動化工具# 我能收獲什么&#xff1f; 1.XSS漏洞檢測機制 學習如何構造和發送XSS payload如何識別響應中的回顯&#xff0c;WAF&#xff0c;過濾規則等如何使用詞典&#xff0c;編碼策略&#xff0c;上下文探測等繞過過濾器 2.Python安全工具開發技巧 使…

npm run build 報錯:Some chunks are larger than 500 KB after minification

當我們的 Vue 項目太大&#xff0c;使用 npm run build 打包項目的時候&#xff0c;就有可能會遇到以下報錯&#xff1a; (!) Some chunks are larger than 500 kB after minification. Consider: - Using dynamic import() to code-split the application - Use build.rollup…