官網(wǎng)3D Banner效果 three.js+vue實現(xiàn)
最近沒什么事,寫了一個3D Banner效果,給廣大前端同行們分享下。
在線3D體驗地址1:http://www.webgl3d.cn/3D/banner1/index.html
在線3D體驗地址2:http://www.webgl3d.cn/3D/banner3/index.html
官網(wǎng)3D Banner
大部分官網(wǎng)的Banner效果一般是一張背景圖片,或者多張圖構(gòu)成的小動畫。不過隨著webgl的流行,一些官網(wǎng)會用3D模型制作官網(wǎng)的Banner效果。
3D Banner相比普通的靜態(tài)圖片,視覺效果更好,更立體。
技術(shù)棧
- 三維建模軟件:Blender
- 3D部分代碼:three.js引擎
- 普通前端部分:Vue + Vite
普通web前端部分沒什么特殊的,主要是3D部分給大家介紹下,整體的思路。
三維建模軟件:Blender
對于普通的2D網(wǎng)頁,一般需要UI設(shè)計師,先出設(shè)計稿,然后前端寫代碼。
對于3D網(wǎng)頁效果,類似的流程,首先需要3D美術(shù),使用三維建模軟件,繪制3D效果圖,然后前端根據(jù)3D模型素材寫相關(guān)代碼。
建模軟件:3D建模軟件有很多可供選擇,比如C4D、Blender,我上面案例就是用的三維建模軟件Blender繪制。
3D代碼:Three.js
為了渲染3D效果,大家需要去了解一個WebGL相關(guān)的3D引擎庫,就是Three.js。
如果你不了解threejs,可以參考學(xué)習(xí)threejs中文網(wǎng)的課程。
參考資料
Thee.js中文網(wǎng):http://www.webgl3d.cn/
步驟1. Vue+Three.js開發(fā)環(huán)境、第一個3D案例效果
如果你對threejs有一定基礎(chǔ)了,這部分可以跳過。
threejs與前端框架結(jié)合問題
有些同學(xué)是前端轉(zhuǎn)來過來的,受到平時開發(fā)習(xí)慣影響,第一反應(yīng)可能是threejs能不能與vue或react結(jié)合。
其實threejs知識點相對普通web前端是比較獨立的,threejs的用法,你直接用.html文件寫,還是結(jié)合vue或React框架寫,API語法都是一樣的。
所以你學(xué)習(xí)threejs的重點不是考慮前端框架問題,而是threejs本身,掌握了threejs,剩下的事情就很簡單了。
Vue與threejs結(jié)合思路
回顧下前面1.6. 第一個3D案例知識點
three.js執(zhí)行渲染方法.render();
會輸出一個canvas畫布renderer.domElement
,這個Canvas畫布本質(zhì)上就是一個HTML元素。
threejs與Vue結(jié)合的時候,你只需要把Canvas畫布renderer.domElement
插入到你的Vue頁面上就行,插入任何一個div或其它元素中,或者放到某個Vue組件中都行。
// WebGL渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.render(scene, camera);
//three.js執(zhí)行渲染命令會輸出一個canvas畫布(HTML元素)
document.body.appendChild(renderer.domElement);
接下來,你寫的threejs代碼結(jié)構(gòu),并不一定就要和我下面視頻完全一致,你可以根據(jù)你自己項目情況,自由調(diào)整。
腳手架Vite
使用腳手架Vite快速創(chuàng)建一個vue工程文件,具體跟著視頻可操作即可
npm create vite@latest
執(zhí)行命令npm create vite@latest
,然后選擇你想要的開發(fā)環(huán)境即可。
第一步是選擇你的前端框架,第二步是選擇是否支持TS。
注意:安裝使用Vite之前,確保你電腦已經(jīng)安裝Nodejs了,盡量用最新版本的。
預(yù)覽vite項目默認(rèn)效果
命令行執(zhí)行
npm i
,安裝所有默認(rèn)依賴命令行執(zhí)行
npm run dev
,查看vite里面Vue代碼默認(rèn)渲染效果。
現(xiàn)在你可以把默認(rèn)的HTML和CSS代碼刪掉,然后在引入threejs代碼。
npm安裝threejs
安裝threesjs時候,你可以指定你想要的版本。
// 比如安裝157版本
npm install three@0.157.0 -S
Vue中引入threejs代碼
新建index.js
文件,把threejs代碼寫在index.js里面。
index.js
文件引入three.js。
import * as THREE from 'three';
復(fù)制前面課程第一個3D案例的代碼,粘貼到index.js
文件。
// 三維場景
const scene = new THREE.Scene();
// 模型對象
const geometry = new THREE.BoxGeometry(50, 50, 50);
const material = new THREE.MeshBasicMaterial({
color: 0x0000ff,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// AxesHelper:輔助觀察的坐標(biāo)系
const axesHelper = new THREE.AxesHelper(250);
scene.add(axesHelper);
const width = 800; //寬度
const height = 500; //高度
// 相機
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
camera.position.set(292, 223, 185);
camera.lookAt(0, 0, 0);
// WebGL渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.render(scene, camera);
//three.js執(zhí)行渲染命令會輸出一個canvas畫布(HTML元素),你可以插入到web頁面中
document.body.appendChild(renderer.domElement);
然后把threejs對應(yīng)的index.js
文件引入到vue的main.js
文件中。
// main.js文件
import './index.js'// 執(zhí)行threejs代碼
當(dāng)然你也可以根據(jù)需要,在其它Vue組件中調(diào)用執(zhí)行threejs代碼。
設(shè)置canvas畫布全屏
上面畫布設(shè)置了固定寬高度,下面改成文檔區(qū)域?qū)捀叨龋簿褪撬^canvas畫布全屏
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
// 可以放到vite項目style.css文件中
body{
// 把canvas畫布與body區(qū)域邊距設(shè)置為0
margin: 0px;
}
引入擴展庫OrbitControls
查看文件node_modules,在目錄three/examples/jsm
中,你可以看到threejs的很多擴展庫。
對于這些擴展庫,不會一次都引入,一般你用到那個,單獨引入即可,下面以OrbitControls
為例給大家展示。
OrbitControls
功能就是旋轉(zhuǎn)縮放平移,在1.9小節(jié)有具體講解:1.9. 相機控件OrbitControls,如果還沒學(xué)習(xí),可以提前看下。
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
復(fù)制1.9. 相機控件OrbitControls里面關(guān)于相機控件的代碼。
// 設(shè)置相機控件軌道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改變了相機參數(shù),重新調(diào)用渲染器渲染三維場景
controls.addEventListener('change', function () {
renderer.render(scene, camera); //執(zhí)行渲染操作
});//監(jiān)聽鼠標(biāo)、鍵盤事件
步驟2:加載gltf模型
美術(shù)制作好Banner的3D模型之后,可以選擇導(dǎo)出gltf格式,當(dāng)然也可以用別的格式,只是說gltf格式非常常用。
然后使用threejs的gltf加載器,加載渲染Banner的gltf模型,這樣,你就能在網(wǎng)頁上看到Banner的3D效果圖。
本節(jié)課,以gltf格式為例,給大家講解加載外部三維模型的整個過程。
場景、光源、渲染器、相機控件等前面說過基礎(chǔ)代碼,本節(jié)課不專門講解,主要是把下面三部,給大家全流程演示一遍。
- gltf模型加載器
GLTFLoader.js
- 相機參數(shù)根據(jù)需要設(shè)置
- 加載gltf的時候,webgl渲染器編碼方式設(shè)置
1.1.引入GLTFLoader.js
// 引入gltf模型加載庫GLTFLoader.js
import { GLTFLoader } from 'three/examples/jsm//loaders/GLTFLoader.js';
1.2.gltf加載器new GLTFLoader()
執(zhí)行new GLTFLoader()
就可以實例化一個gltf的加載器對象。
// 創(chuàng)建GLTF加載器對象
const loader = new GLTFLoader();
1.3.gltf加載器方法.load()
通過gltf加載器方法.load()
就可以加載外部的gltf模型。
執(zhí)行方法.load()
會返回一個gltf對象,作為參數(shù)2函數(shù)的參數(shù),改gltf對象可以包含模型、動畫等信息,本節(jié)課你只需要先了解gltf的場景屬性gltf.scene
,該屬性包含的是模型信息,比如幾何體BufferGometry、材質(zhì)Material、網(wǎng)格模型Mesh。
loader.load( 'gltf模型.gltf', function ( gltf ) {
console.log('控制臺查看加載gltf文件返回的對象結(jié)構(gòu)',gltf);
console.log('gltf對象場景屬性',gltf.scene);
// 返回的場景對象gltf.scene插入到threejs場景中
scene.add( gltf.scene );
})
相機選擇(正投影OrthographicCamera
和透視投影PerspectiveCamera
)
如果你想預(yù)覽一個三維場景,一般有正投影相機OrthographicCamera
和透視投影相機PerspectiveCamera
可供選擇。不過大部分3D項目,比如一般都是使用透視投影相機PerspectiveCamera
,比如游戲、物聯(lián)網(wǎng)等項目都會選擇透視投影相機PerspectiveCamera
。
如果你希望渲染的結(jié)果符合人眼的遠小近大的規(guī)律,毫無疑問要選擇透視投影相機,如果不需要模擬人眼遠小近大的投影規(guī)律,可以選擇正投影相機。
尺寸概念
項目開發(fā)的時候,程序員對一個模型或者說一個三維場景要有一個尺寸的概念,不用具體值,要有一個大概印象。
一般通過三維建模軟件可以輕松測試測量模型尺寸,比如作為程序員你可以用三維建模軟件blender打開gltf模型,測量尺寸。
單位問題
three.js的世界并沒有任何單位,只有數(shù)字大小的運算。
obj、gltf格式的模型信息只有尺寸,并不含單位信息。
不過實際項目開發(fā)的時候,一般會定義一個單位,一方面甲方、前端、美術(shù)之間更好協(xié)調(diào),甚至你自己寫代碼也要有一個尺寸標(biāo)準(zhǔn)。比如一個園區(qū)、工廠,可以m為單位建模,比如建筑、人、相機都用m為尺度去衡量,如果單位不統(tǒng)一,就需要你寫代碼,通過.scale
屬性去縮放。
設(shè)置合適的相機參數(shù)
通過gltf加載完成,模型后,你還需要根據(jù)自身需要,設(shè)置合適的相機參數(shù),就好比你拍照,你想拍攝一個石頭,肯定要把相機對著石頭,如果希望石頭在照片上占比大,就要離石頭近一些。
相機位置怎么設(shè)置,你就類比你的眼睛,如果你想模擬人在3D場景中漫游,那么很簡單,你把相機放在地面上,距離地面高度和人身高接近即可。
如果你想看到工廠的全貌,你可以理解為你坐著無人機向下俯瞰,簡單說,相比人漫游工廠,整體預(yù)覽工廠相機距離工廠距離更遠一些,否則你也看不到全貌,當(dāng)然過于遠了,你就看不清工廠了。
以課程工廠為例,先設(shè)定一個小目標(biāo),我們希望工廠能夠居中顯示在canvas畫布上,并且保證可以整體預(yù)覽。
下面以透視投影相機PerspectiveCamera
為例說明。
2.1.相機位置.position
工廠尺寸范圍大概200米數(shù)量級,那么如果想整體預(yù)覽觀察工廠所有模型,那很簡單,第一步,把camera.position
的xyz值統(tǒng)統(tǒng)設(shè)置為幾百即可,比如(200, 200, 200)
。
具體xyz值,你可以通過OrbitControls可視化操作調(diào)整,然后瀏覽器控制臺記錄相機參數(shù)即可。
camera.position.set(200, 200, 200);
2.2 某位置在canvas畫布居中
你需要工廠那個位置在canavs畫布上居中,直接把camera.lookAt()
指向哪個坐標(biāo)。
如果美術(shù)建模,把工廠整體居中,也就是說模型的幾何中心,大概位于世界坐標(biāo)原點。你設(shè)置camera.lookAt(0,0,0)
,相機視線指向坐標(biāo)原點。
camera.lookAt(0, 0, 0);
注意相機控件OrbitControls會影響lookAt設(shè)置,注意手動設(shè)置OrbitControls的目標(biāo)參數(shù)
camera.lookAt(100, 0, 0);
// 設(shè)置相機控件軌道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 相機控件.target屬性在OrbitControls.js內(nèi)部表示相機目標(biāo)觀察點,默認(rèn)0,0,0
// console.log('controls.target', controls.target);
controls.target.set(100, 0, 0);
controls.update();//update()函數(shù)內(nèi)會執(zhí)行camera.lookAt(controls.targe)
2.3.遠裁截面far
參數(shù)
近裁截面near和遠裁截面far,要能包含你想渲染的場景,否則超出視錐體模型會被剪裁掉,簡單說near足夠小,far足夠大,主要是far。
PerspectiveCamera(fov, aspect, near, far)
測量工廠尺寸大概幾百的數(shù)量級,這里不用測具體尺寸,有個大概數(shù)量級即可,然后far設(shè)置為3000足夠了。
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
3.紋理貼圖顏色偏差解決
three.js加載gltf模型的時候,可能會遇到three.js渲染結(jié)果顏色偏差,對于這種情況,你只需要修改WebGL渲染器默認(rèn)的編碼方式.outputEncoding
即可
//解決加載gltf格式模型紋理貼圖和原圖不一樣問題
renderer.outputEncoding = THREE.sRGBEncoding;
注意!!!?。。?!最新版本屬性名字有改變。渲染器屬性名.outputEncoding
已經(jīng)變更為.outputColorSpace
。
查WebGL渲染器文檔,你可以看到.outputColorSpace
的默認(rèn)值就是SRGB顏色空間THREE.SRGBColorSpace
,意味著新版本代碼中,加載gltf,沒有特殊需要,不設(shè)置.outputColorSpace
也不會引起色差。
//新版本,加載gltf,不需要執(zhí)行下面代碼解決顏色偏差
renderer.outputColorSpace = THREE.SRGBColorSpace;//設(shè)置為SRGB顏色空間
步驟3:材質(zhì)效果
作為Banner,肯定需要比較好的材質(zhì)效果,不能隨隨便便加載一個gltf模型,這樣的材質(zhì)效果不會太好。
一般來說首先美術(shù)在Blender軟件中,可以設(shè)置PBR材質(zhì)的相關(guān)屬性,比如玻璃材質(zhì)的金屬度、粗糙度、透射度等屬性,然后導(dǎo)出gltf格式模型的時候,這些PBR材質(zhì)屬性可以跟隨導(dǎo)出。threejs加載gltf模型的時候,會自動解析相關(guān)的材質(zhì)屬性。
尤其是上面Banner中的玻璃材質(zhì),為了更好的3D效果,最好設(shè)置好環(huán)境貼圖,這樣玻璃質(zhì)感才會更強烈。
// 加載環(huán)境貼圖
const textureCube = new THREE.CubeTextureLoader()
.setPath('./環(huán)境貼圖/環(huán)境貼圖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, //設(shè)置pbr材質(zhì)環(huán)境貼圖
})
obj.material.envMap = textureCube; //設(shè)置環(huán)境貼圖
補充內(nèi)容:PBR材質(zhì)介紹
本節(jié)課沒有具體的代碼,就是給大家科普一下PBR材質(zhì),所謂PBR就是,基于物理的渲染(physically-based rendering)。
Three.js提供了兩個PBR材質(zhì)相關(guān)的APIMeshStandardMaterial
和MeshPhysicalMaterial
,MeshPhysicalMaterial
是MeshStandardMaterial
擴展的子類,提供了更多功能屬性。
光照模型
如果你有初高中最基本的物理光學(xué)知識,應(yīng)該有折射、鏡面反射、漫反射等基本光學(xué)概念,對于實際生活中的光學(xué)問題,Three.js會提供一些的光照模型來模擬物體表面的光照,光照模型就一種模擬光照的計算方法。MeshPhysicalMaterial
和MeshLambertMaterial
一樣都是渲染網(wǎng)格模型的材質(zhì),但是他們用的光照模型不同,具體點說就是材質(zhì)模擬Mesh反射光照的代碼算法不同,算法不同,自然模擬光照的真實程度也不同。
如果你想深入研究光照模型,可以學(xué)習(xí)下原生WebGL或WebGPU,或者看看計算機圖形學(xué)相關(guān)書籍,使用threejs的大部分情況,用不著你自己實現(xiàn)光照模型算法,畢竟threejs通過網(wǎng)格模型材質(zhì)幫你實現(xiàn)了。
PBR相關(guān)理論介紹文章
- 半小時了解PBR:https://zhuanlan.zhihu.com/p/37639418
- PBR知識體系整理:https://zhuanlan.zhihu.com/p/100596453
- PBR核心知識體系總結(jié)與概覽:https://zhuanlan.zhihu.com/p/53086060
網(wǎng)格模型材質(zhì)整體回顧
MeshLambertMaterial: Lambert光照模型(漫反射)
MeshPhongMaterial:Phong光照模型(漫反射、高光反射)
MeshStandardMaterial和MeshPhysicalMaterial:基于物理的光照模型(微平面理論、能量守恒、菲涅爾反射...)
PBR材質(zhì)相比MeshLambertMaterial和MeshPhongMaterial可以提供更逼真的、更接近生活中的材質(zhì)效果,當(dāng)然也會占用更多的電腦硬件資源。
通過MeshPhysicalMaterial文檔,提供的資源,可以查看多個PBR材質(zhì)的案例效果,系統(tǒng)課程中轎車展示案例也會用到PBR材質(zhì)。
渲染占用資源和表現(xiàn)能力
整體上來看,就是渲染表現(xiàn)能力越強,占用的計算機硬件資源更多。
占用渲染資源
MeshBasicMaterial < MeshLambertMaterial < MeshPhongMaterial < MeshStandardMaterial < MeshPhysicalMaterial渲染表現(xiàn)能力
MeshBasicMaterial < MeshLambertMaterial < MeshPhongMaterial < MeshStandardMaterial < MeshPhysicalMaterial
PBR材質(zhì)金屬度和粗糙度(金屬效果)
本節(jié)課給大家介紹PBR材質(zhì)MeshStandardMaterial
金屬度metalness
和粗糙度roughness
,再加上下節(jié)課講解的環(huán)境貼圖.envMap
,給大家呈現(xiàn)一個金屬渲染效果。
金屬度metalness
金屬度屬性.metalness
表示材質(zhì)像金屬的程度, 非金屬材料,如木材或石材,使用0.0,金屬使用1.0。
threejs的PBR材質(zhì),.metalness
默認(rèn)是0.5,0.0到1.0之間的值可用于生銹的金屬外觀
new THREE.MeshStandardMaterial({
metalness: 1.0,//金屬度屬性
})
mesh.material.metalness = 1.0;//金屬度
粗糙度roughness
生活中不同物體表面的粗糙程度不同,比如地面比較粗糙,比如鏡子表面就非常非常光滑。
粗糙度roughness
表示模型表面的光滑或者說粗糙程度,越光滑鏡面反射能力越強,越粗糙,表面鏡面反射能力越弱,更多地表現(xiàn)為漫反射。
粗糙度roughness
,0.0表示平滑的鏡面反射,1.0表示完全漫反射,默認(rèn)0.5。
new THREE.MeshStandardMaterial({
roughness: 0.5,//表面粗糙度
})
mesh.material.roughness = 0.5;//表面粗糙度
環(huán)境貼圖.envMap(金屬效果)
環(huán)境貼圖對PBR材質(zhì)渲染效果影響還是比較大,一般渲染PBR材質(zhì)的模型,最好設(shè)置一個合適的環(huán)境貼圖。
立方體紋理加載器CubeTextureLoader
-
TextureLoader
返回Texture
-
CubeTextureLoader
返回CubeTexture
通過前面學(xué)習(xí)大家知道,通過紋理貼圖加載器TextureLoader
的.load()
方法加載一張圖片可以返回一個紋理對象Texture
。
立方體紋理加載器CubeTextureLoader
的.load()
方法是加載6張圖片,返回一個立方體紋理對象CubeTexture
。
立方體紋理對象CubeTexture
的父類是紋理對象Texture
。
CubeTextureLoader
加載環(huán)境貼圖
所謂環(huán)境貼圖,就是一個模型周圍的環(huán)境的圖像,比如一間房子,房子的上下左右前后分別拍攝一張照片,就是3D空間中6個角度方向的照片。
// 加載環(huán)境貼圖
// 加載周圍環(huán)境6個方向貼圖
// 上下左右前后6張貼圖構(gòu)成一個立方體空間
// 'px.jpg', 'nx.jpg':x軸正方向、負(fù)方向貼圖 p:正positive n:負(fù)negative
// 'py.jpg', 'ny.jpg':y軸貼圖
// 'pz.jpg', 'nz.jpg':z軸貼圖
const textureCube = new THREE.CubeTextureLoader()
.setPath('./環(huán)境貼圖/環(huán)境貼圖0/')
.load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
// CubeTexture表示立方體紋理對象,父類是紋理對象Texture
MeshStandardMaterial
環(huán)境貼圖屬性.envMap
實際生活中,一個物體表面,往往會反射周圍的環(huán)境。人的眼睛看到的東西,往往反射有周圍景物,所以three.js渲染模型,如果想渲染效果更好看,如果想更符合實際生活情況,也需要想辦法讓模型反射周圍景物。
MeshStandardMaterial材質(zhì)的環(huán)境貼圖屬性是.envMap
,通過PBR材質(zhì)的貼圖屬性可以實現(xiàn)模型表面反射周圍景物,這樣渲染效果更好。
// 加載環(huán)境貼圖
const textureCube = new THREE.CubeTextureLoader()
.setPath('./環(huán)境貼圖/環(huán)境貼圖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, //設(shè)置pbr材質(zhì)環(huán)境貼圖
})
obj.material.envMap = textureCube; //設(shè)置環(huán)境貼圖
環(huán)境貼圖反射率.envMapIntensity
MeshStandardMaterial
的.envMapIntensity
屬性主要用來設(shè)置模型表面反射周圍環(huán)境貼圖的能力,或者說環(huán)境貼圖對模型表面的影響能力。具體說.envMapIntensity
相當(dāng)于環(huán)境貼圖的系數(shù),環(huán)境貼圖像素值乘以該系數(shù)后,在用于影響模型表面。
// envMapIntensity:控制環(huán)境貼圖對mesh表面影響程度
//默認(rèn)值1, 設(shè)置為0.0,相當(dāng)于沒有環(huán)境貼圖
obj.material.envMapIntensity = 1.0;
粗糙度roughness
為0
你可以嘗試把粗糙度roughness
設(shè)置為0,看看模型對環(huán)境貼圖的反射效果。
obj.material.roughness = 0.0;//完全鏡面反射,像鏡子一樣
選擇合適的環(huán)境貼圖
不同的明暗或景物的環(huán)境貼圖對渲染效果的影響是不一樣的,所以不僅要設(shè)置環(huán)境貼圖,還要根據(jù)需要選擇合適的環(huán)境貼圖,一般實際開發(fā)使用美術(shù)提供的環(huán)境貼圖即可。
你可以嘗試測試源碼中提供多個環(huán)境貼圖對比渲染效果差異。
紋理和渲染器顏色空間一致
//如果renderer.outputEncoding=THREE.sRGBEncoding;環(huán)境貼圖需要保持一致
textureCube.encoding = THREE.sRGBEncoding;
環(huán)境貼圖2
接著上節(jié)課的環(huán)境貼圖給大家講解。
環(huán)境貼圖作用測試
實際生活中光源照射到一個物體上,這個物體反射出去的光線也會影響其他的物體,環(huán)境貼圖就是用一種簡單方式,近似模擬一個物體周邊環(huán)境對物體表面的影響。
測試:對于PBR材質(zhì),如果threejs三維場景不添加任何光源,物體就是完全黑色的,你可以不添加任何光源,嘗試只使用環(huán)境貼圖,你會發(fā)現(xiàn)物體表面的顏色也能看到,這說明環(huán)境貼圖其實相當(dāng)于提供了物體周圍環(huán)境發(fā)射或反射的光線。
測試:更換不同明暗的環(huán)境貼圖,你會發(fā)現(xiàn)場景中模型的明暗也有變化。
場景環(huán)境屬性.environment
網(wǎng)格模型可以通過材質(zhì)的.envMap
屬性設(shè)置環(huán)境貼圖,如果一個gltf模型中所有的Mesh都要設(shè)置環(huán)境貼圖就需要遞歸遍歷gltf模型,給里面每個Mesh的材質(zhì)設(shè)置.envMap
。
loader.load("../工廠.glb", function (gltf) {
// 遞歸遍歷批量設(shè)置環(huán)境貼圖
gltf.scene.traverse(function (obj) {
if (obj.isMesh) { //判斷是否是網(wǎng)格模型
obj.material.envMap = textureCube; //設(shè)置環(huán)境貼圖
}
});
})
如果你希望環(huán)境貼圖影響場景中scene所有Mesh,可以通過Scene的場景環(huán)境屬性.environment
實現(xiàn),把環(huán)境貼圖對應(yīng)紋理對象設(shè)置為.environment
的屬性值即可。
// 環(huán)境貼圖紋理對象textureCube作為.environment屬性值,影響所有模型
scene.environment = textureCube;
環(huán)境貼圖色彩空間編碼.encoding
//如果renderer.outputEncoding=THREE.sRGBEncoding;環(huán)境貼圖需要保持一致
textureCube.encoding = THREE.sRGBEncoding;
MeshPhysicalMaterial清漆層.clearcoat
MeshPhysicalMaterial
和MeshStandardMaterial
都是擁有金屬度metalness
、粗糙度roughness
屬性的PBR材質(zhì),MeshPhysicalMaterial是在MeshStandardMaterial基礎(chǔ)上擴展出來的子類,除了繼承了MeshStandardMaterial的金屬度、粗糙度等屬性,還新增了清漆.clearcoat
、透光率.transmission
、反射率.reflectivity
、光澤.sheen
、折射率.ior
等等各種用于模擬生活中不同材質(zhì)的屬性。
清漆層屬性.clearcoat
清漆層屬性.clearcoat
可以用來模擬物體表面一層透明圖層,就好比你在物體表面刷了一層透明清漆,噴了點水。.clearcoat的范圍0到1,默認(rèn)0。
const material = new THREE.MeshPhysicalMaterial( {
clearcoat: 1.0,//物體表面清漆層或者說透明涂層的厚度
} );
清漆層粗糙度.clearcoatRoughness
清漆層粗糙度.clearcoatRoughness
屬性表示物體表面透明涂層.clearcoat
對應(yīng)的的粗糙度,.clearcoatRoughness
的范圍是為0.0至1.0。默認(rèn)值為0.0。
const material = new THREE.MeshPhysicalMaterial( {
clearcoat: 1.0,//物體表面清漆層或者說透明涂層的厚度
clearcoatRoughness: 0.1,//透明涂層表面的粗糙度
} );
車外殼PBR材質(zhì)設(shè)置
在設(shè)置車外殼清漆層之前,先創(chuàng)建一個MeshPhysicalMaterial材質(zhì),并設(shè)置好環(huán)境貼圖、金屬度、粗糙度,屬性值先根據(jù)文檔說明給一個大概的值,具體可以通過gui交互界面可視化調(diào)試。
const mesh = gltf.scene.getObjectByName('外殼01');
mesh.material = new THREE.MeshPhysicalMaterial({
color: mesh.material.color, //默認(rèn)顏色
metalness: 0.9,//車外殼金屬度
roughness: 0.5,//車外殼粗糙度
envMap: textureCube, //環(huán)境貼圖
envMapIntensity: 2.5, //環(huán)境貼圖對Mesh表面影響程度
})
車外殼油漆效果
車外殼油漆效果,你可以通過PBR材質(zhì)的清漆層屬性.clearcoat
和清漆層粗糙度.clearcoatRoughness
屬性模擬。
屬性值先根據(jù)文檔說明給一個大概的值,具體可以通過gui交互界面可視化調(diào)試。
const mesh = gltf.scene.getObjectByName('外殼01');
mesh.material = new THREE.MeshPhysicalMaterial( {
clearcoat: 1.0,//物體表面清漆層或者說透明涂層的厚度
clearcoatRoughness: 0.1,//透明涂層表面的粗糙度
} );
GUI可視化調(diào)試PBR材質(zhì)屬性
關(guān)于gui的使用,在第一章節(jié)入門中詳細(xì)將結(jié)果,具體使用可以參照前面講解。
// 范圍可以參考文檔
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);
物理材質(zhì)透光率.transmission
如果你已經(jīng)掌握上節(jié)課內(nèi)容,可以繼續(xù)學(xué)習(xí)物理材質(zhì)MeshPhysicalMaterial
的透光率屬性.transmission
和折射率屬性.ior
。
透光率(透射度).transmission
為了更好的模擬玻璃、半透明塑料一類的視覺效果,可以使用物理透明度.transmission
屬性代替Mesh普通透明度屬性.opacity
。
使用.transmission
屬性設(shè)置Mesh透明度,即便完全透射的情況下仍可保持高反射率。
物理光學(xué)透明度.transmission
的值范圍是從0.0到1.0。默認(rèn)值為0.0。
const mesh = gltf.scene.getObjectByName('玻璃01')
mesh.material = new THREE.MeshPhysicalMaterial({
transmission: 1.0, //玻璃材質(zhì)透光率,transmission替代opacity
})
折射率.ior
非金屬材料的折射率從1.0到2.333。默認(rèn)值為1.5。
不同材質(zhì)的折射率,你可以百度搜索。
new THREE.MeshPhysicalMaterial({
ior:1.5,//折射率
})
玻璃透光率.transmission
設(shè)置
先設(shè)置玻璃金屬度和粗糙度
const mesh = gltf.scene.getObjectByName('玻璃01')
mesh.material = new THREE.MeshPhysicalMaterial({
metalness: 0.0,//玻璃非金屬
roughness: 0.0,//玻璃表面光滑
envMap:textureCube,//環(huán)境貼圖
envMapIntensity: 1.0, //環(huán)境貼圖對Mesh表面影響程度
})
設(shè)置透光率.transmission
和折射率.ior
。
new THREE.MeshPhysicalMaterial({
transmission: 1.0, //玻璃材質(zhì)透光率,transmission替代opacity
ior:1.5,//折射率
})
GUI可視化調(diào)試PBR材質(zhì)屬性
基本參數(shù)和代碼設(shè)置好以后,就是通過GUI可視化交互界面,調(diào)試PBR材質(zhì)或光源的參數(shù),gui.js庫的使用參考入門章節(jié)介紹。
const obj = {
color: mesh.material.color, // 材質(zhì)顏色
};
// 材質(zhì)顏色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);
三維建模軟件導(dǎo)出PBR材質(zhì)屬性
實際開發(fā)的時候PBR材質(zhì)的屬性,很多時候是可以在三維建模軟件中設(shè)置的,然后通過gltf導(dǎo)出即可,這樣就不用在threejs代碼設(shè)置。
通常美術(shù)對三維場景渲染的了解也比大部分前端程序員多的多,只要美術(shù)在三維建模軟件設(shè)置好并導(dǎo)出包含pbr材質(zhì)屬性的gltf即可。
threejs與建模軟件對接的問題
- gltf能否存儲3D建模軟件的某個材質(zhì)屬性:有些三維軟件特有的材質(zhì)屬性,不一定能通過gltf導(dǎo)出,也談不上threejs解析
- 三維建模能否導(dǎo)出PBR材質(zhì):能導(dǎo)出的話,能導(dǎo)出哪些屬性,不能導(dǎo)出哪些屬性
如果你的三維建模不能導(dǎo)出pbr材質(zhì),或者部分pbr材質(zhì)屬性無法導(dǎo)出,那你通常需要用代碼方式添加材質(zhì),這樣就麻煩些。
Blender導(dǎo)出PBR材質(zhì)演示
首先Blender最新版導(dǎo)出gltf模型時候,是可以把PBR材質(zhì)的很多屬性導(dǎo)出的,比如金屬度metalness
、粗糙度roughness
、清漆.clearcoat
、透光率(透射度).transmission
等等。課件源碼中提供了blender導(dǎo)出的gltf模型你可以瀏覽器控制臺打印測試,這些PBR材質(zhì)屬性能否解析渲染。
Bledner中設(shè)置PBR材質(zhì)
你可以在Bledner中設(shè)置車外殼、車玻璃的材質(zhì)屬性
- 車外殼:清漆、清漆粗糙度
- 車玻璃:透光率(透射度)
threejs解析gltf材質(zhì)規(guī)則
大家都知道,MeshPhysicalMaterial
是MeshStandardMaterial
的子類,具有更多的PBR材質(zhì)屬性和功能。
所以,threejs解析gltf模型,會用兩種材質(zhì)PBR材質(zhì)去解析,一個是標(biāo)準(zhǔn)網(wǎng)格材質(zhì)MeshStandardMaterial
,一個是物理網(wǎng)格材質(zhì)MeshPhysicalMaterial
,如果能用MeshStandardMaterial
表示就用,不能就換MeshPhysicalMaterial
。
具體說就是,threejs解析gltf模型材質(zhì)的時候,一般默認(rèn)使用標(biāo)準(zhǔn)網(wǎng)格材質(zhì)MeshStandardMaterial
,如果gltf有的材質(zhì)具有.clearcoat
、.transmission
等屬性,標(biāo)準(zhǔn)網(wǎng)格材質(zhì)MeshStandardMaterial
無法表達的時候,會用物理網(wǎng)格材質(zhì)MeshPhysicalMaterial
來解析gltf材質(zhì)。
查看threejs解析的PBR材質(zhì)
gltf.scene.traverse(function(obj) {
if (obj.isMesh) {
console.log('obj.material',obj.material);
}
});
console.log('外殼',mesh1.material);
console.log('玻璃',mesh2.material);
設(shè)置環(huán)境貼圖
這時候清漆、清漆粗糙度、透光率(透射度)等屬性Bledner都已經(jīng)設(shè)置好了,threejs可以自動解析渲染,不用在代碼中麻煩設(shè)置了,只要配上環(huán)境貼圖即可。
const mesh1 = gltf.scene.getObjectByName('外殼01');
mesh1.material.envMap = textureCube; //環(huán)境貼圖
mesh1.material.envMapIntensity = 1.0; ////環(huán)境貼圖對Mesh表面影響程度
const mesh2 = gltf.scene.getObjectByName('玻璃01');
mesh2.material.envMap = textureCube; //環(huán)境貼圖
mesh2.material.envMapIntensity = 1.0; ////環(huán)境貼圖對Mesh表面影響程度