官網(wǎng)3D Banner效果 three.js+vue實(shí)現(xiàn)
最近沒(méi)什么事萝喘,寫(xiě)了一個(gè)3D Banner效果捻撑,給廣大前端同行們分享下管呵。
在線3D體驗(yàn)地址1:http://www.webgl3d.cn/3D/banner1/index.html
在線3D體驗(yàn)地址2:http://www.webgl3d.cn/3D/banner3/index.html
官網(wǎng)3D Banner
大部分官網(wǎng)的Banner效果一般是一張背景圖片,或者多張圖構(gòu)成的小動(dòng)畫(huà)徙赢。不過(guò)隨著webgl的流行档痪,一些官網(wǎng)會(huì)用3D模型制作官網(wǎng)的Banner效果涉枫。
3D Banner相比普通的靜態(tài)圖片,視覺(jué)效果更好腐螟,更立體愿汰。
技術(shù)棧
- 三維建模軟件:Blender
- 3D部分代碼:three.js引擎
- 普通前端部分:Vue + Vite
普通web前端部分沒(méi)什么特殊的,主要是3D部分給大家介紹下乐纸,整體的思路衬廷。
三維建模軟件:Blender
對(duì)于普通的2D網(wǎng)頁(yè),一般需要UI設(shè)計(jì)師汽绢,先出設(shè)計(jì)稿吗跋,然后前端寫(xiě)代碼。
對(duì)于3D網(wǎng)頁(yè)效果,類似的流程跌宛,首先需要3D美術(shù)酗宋,使用三維建模軟件,繪制3D效果圖疆拘,然后前端根據(jù)3D模型素材寫(xiě)相關(guān)代碼蜕猫。
建模軟件:3D建模軟件有很多可供選擇,比如C4D哎迄、Blender回右,我上面案例就是用的三維建模軟件Blender繪制。
3D代碼:Three.js
為了渲染3D效果漱挚,大家需要去了解一個(gè)WebGL相關(guān)的3D引擎庫(kù)翔烁,就是Three.js。
如果你不了解threejs棱烂,可以參考學(xué)習(xí)threejs中文網(wǎng)的課程租漂。
參考資料
Thee.js中文網(wǎng):http://www.webgl3d.cn/
步驟1. Vue+Three.js開(kāi)發(fā)環(huán)境、第一個(gè)3D案例效果
如果你對(duì)threejs有一定基礎(chǔ)了颊糜,這部分可以跳過(guò)。
threejs與前端框架結(jié)合問(wèn)題
有些同學(xué)是前端轉(zhuǎn)來(lái)過(guò)來(lái)的秃踩,受到平時(shí)開(kāi)發(fā)習(xí)慣影響衬鱼,第一反應(yīng)可能是threejs能不能與vue或react結(jié)合。
其實(shí)threejs知識(shí)點(diǎn)相對(duì)普通web前端是比較獨(dú)立的憔杨,threejs的用法鸟赫,你直接用.html文件寫(xiě),還是結(jié)合vue或React框架寫(xiě)消别,API語(yǔ)法都是一樣的抛蚤。
所以你學(xué)習(xí)threejs的重點(diǎn)不是考慮前端框架問(wèn)題,而是threejs本身寻狂,掌握了threejs岁经,剩下的事情就很簡(jiǎn)單了。
Vue與threejs結(jié)合思路
回顧下前面1.6. 第一個(gè)3D案例知識(shí)點(diǎn)
three.js執(zhí)行渲染方法.render();
會(huì)輸出一個(gè)canvas畫(huà)布renderer.domElement
蛇券,這個(gè)Canvas畫(huà)布本質(zhì)上就是一個(gè)HTML元素缀壤。
threejs與Vue結(jié)合的時(shí)候,你只需要把Canvas畫(huà)布renderer.domElement
插入到你的Vue頁(yè)面上就行纠亚,插入任何一個(gè)div或其它元素中塘慕,或者放到某個(gè)Vue組件中都行。
// WebGL渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.render(scene, camera);
//three.js執(zhí)行渲染命令會(huì)輸出一個(gè)canvas畫(huà)布(HTML元素)
document.body.appendChild(renderer.domElement);
接下來(lái)蒂胞,你寫(xiě)的threejs代碼結(jié)構(gòu)图呢,并不一定就要和我下面視頻完全一致,你可以根據(jù)你自己項(xiàng)目情況,自由調(diào)整蛤织。
腳手架Vite
使用腳手架Vite快速創(chuàng)建一個(gè)vue工程文件赴叹,具體跟著視頻可操作即可
npm create vite@latest
執(zhí)行命令npm create vite@latest
,然后選擇你想要的開(kāi)發(fā)環(huán)境即可瞳筏。
第一步是選擇你的前端框架稚瘾,第二步是選擇是否支持TS。
注意:安裝使用Vite之前姚炕,確保你電腦已經(jīng)安裝Nodejs了摊欠,盡量用最新版本的。
預(yù)覽vite項(xiàng)目默認(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時(shí)候,你可以指定你想要的版本忧侧。
// 比如安裝157版本
npm install three@0.157.0 -S
Vue中引入threejs代碼
新建index.js
文件,把threejs代碼寫(xiě)在index.js里面石窑。
index.js
文件引入three.js。
import * as THREE from 'three';
復(fù)制前面課程第一個(gè)3D案例的代碼蚓炬,粘貼到index.js
文件松逊。
// 三維場(chǎng)景
const scene = new THREE.Scene();
// 模型對(duì)象
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; //高度
// 相機(jī)
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í)行渲染命令會(huì)輸出一個(gè)canvas畫(huà)布(HTML元素),你可以插入到web頁(yè)面中
document.body.appendChild(renderer.domElement);
然后把threejs對(duì)應(yīng)的index.js
文件引入到vue的main.js
文件中肯夏。
// main.js文件
import './index.js'// 執(zhí)行threejs代碼
當(dāng)然你也可以根據(jù)需要经宏,在其它Vue組件中調(diào)用執(zhí)行threejs代碼。
設(shè)置canvas畫(huà)布全屏
上面畫(huà)布設(shè)置了固定寬高度驯击,下面改成文檔區(qū)域?qū)捀叨人咐迹簿褪撬^canvas畫(huà)布全屏
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項(xiàng)目style.css文件中
body{
// 把canvas畫(huà)布與body區(qū)域邊距設(shè)置為0
margin: 0px;
}
引入擴(kuò)展庫(kù)OrbitControls
查看文件node_modules,在目錄three/examples/jsm
中徊都,你可以看到threejs的很多擴(kuò)展庫(kù)沪斟。
對(duì)于這些擴(kuò)展庫(kù),不會(huì)一次都引入碟贾,一般你用到那個(gè)币喧,單獨(dú)引入即可,下面以OrbitControls
為例給大家展示袱耽。
OrbitControls
功能就是旋轉(zhuǎn)縮放平移杀餐,在1.9小節(jié)有具體講解:1.9. 相機(jī)控件OrbitControls,如果還沒(méi)學(xué)習(xí)朱巨,可以提前看下史翘。
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
復(fù)制1.9. 相機(jī)控件OrbitControls里面關(guān)于相機(jī)控件的代碼。
// 設(shè)置相機(jī)控件軌道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改變了相機(jī)參數(shù),重新調(diào)用渲染器渲染三維場(chǎng)景
controls.addEventListener('change', function () {
renderer.render(scene, camera); //執(zhí)行渲染操作
});//監(jiān)聽(tīng)鼠標(biāo)琼讽、鍵盤(pán)事件
步驟2:加載gltf模型
美術(shù)制作好Banner的3D模型之后必峰,可以選擇導(dǎo)出gltf格式,當(dāng)然也可以用別的格式钻蹬,只是說(shuō)gltf格式非常常用吼蚁。
然后使用threejs的gltf加載器,加載渲染Banner的gltf模型问欠,這樣肝匆,你就能在網(wǎng)頁(yè)上看到Banner的3D效果圖。
本節(jié)課顺献,以gltf格式為例旗国,給大家講解加載外部三維模型的整個(gè)過(guò)程。
場(chǎng)景注整、光源能曾、渲染器、相機(jī)控件等前面說(shuō)過(guò)基礎(chǔ)代碼肿轨,本節(jié)課不專門(mén)講解寿冕,主要是把下面三部,給大家全流程演示一遍椒袍。
- gltf模型加載器
GLTFLoader.js
- 相機(jī)參數(shù)根據(jù)需要設(shè)置
- 加載gltf的時(shí)候蚂斤,webgl渲染器編碼方式設(shè)置
1.1.引入GLTFLoader.js
// 引入gltf模型加載庫(kù)GLTFLoader.js
import { GLTFLoader } from 'three/examples/jsm//loaders/GLTFLoader.js';
1.2.gltf加載器new GLTFLoader()
執(zhí)行new GLTFLoader()
就可以實(shí)例化一個(gè)gltf的加載器對(duì)象。
// 創(chuàng)建GLTF加載器對(duì)象
const loader = new GLTFLoader();
1.3.gltf加載器方法.load()
通過(guò)gltf加載器方法.load()
就可以加載外部的gltf模型槐沼。
執(zhí)行方法.load()
會(huì)返回一個(gè)gltf對(duì)象,作為參數(shù)2函數(shù)的參數(shù)捌治,改gltf對(duì)象可以包含模型岗钩、動(dòng)畫(huà)等信息,本節(jié)課你只需要先了解gltf的場(chǎng)景屬性gltf.scene
,該屬性包含的是模型信息肖油,比如幾何體BufferGometry兼吓、材質(zhì)Material、網(wǎng)格模型Mesh森枪。
loader.load( 'gltf模型.gltf', function ( gltf ) {
console.log('控制臺(tái)查看加載gltf文件返回的對(duì)象結(jié)構(gòu)',gltf);
console.log('gltf對(duì)象場(chǎng)景屬性',gltf.scene);
// 返回的場(chǎng)景對(duì)象gltf.scene插入到threejs場(chǎng)景中
scene.add( gltf.scene );
})
相機(jī)選擇(正投影OrthographicCamera
和透視投影PerspectiveCamera
)
如果你想預(yù)覽一個(gè)三維場(chǎng)景视搏,一般有正投影相機(jī)OrthographicCamera
和透視投影相機(jī)PerspectiveCamera
可供選擇。不過(guò)大部分3D項(xiàng)目县袱,比如一般都是使用透視投影相機(jī)PerspectiveCamera
浑娜,比如游戲、物聯(lián)網(wǎng)等項(xiàng)目都會(huì)選擇透視投影相機(jī)PerspectiveCamera
式散。
如果你希望渲染的結(jié)果符合人眼的遠(yuǎn)小近大的規(guī)律筋遭,毫無(wú)疑問(wèn)要選擇透視投影相機(jī),如果不需要模擬人眼遠(yuǎn)小近大的投影規(guī)律,可以選擇正投影相機(jī)漓滔。
尺寸概念
項(xiàng)目開(kāi)發(fā)的時(shí)候编饺,程序員對(duì)一個(gè)模型或者說(shuō)一個(gè)三維場(chǎng)景要有一個(gè)尺寸的概念,不用具體值响驴,要有一個(gè)大概印象透且。
一般通過(guò)三維建模軟件可以輕松測(cè)試測(cè)量模型尺寸,比如作為程序員你可以用三維建模軟件blender打開(kāi)gltf模型豁鲤,測(cè)量尺寸秽誊。
單位問(wèn)題
three.js的世界并沒(méi)有任何單位,只有數(shù)字大小的運(yùn)算畅形。
obj养距、gltf格式的模型信息只有尺寸,并不含單位信息日熬。
不過(guò)實(shí)際項(xiàng)目開(kāi)發(fā)的時(shí)候棍厌,一般會(huì)定義一個(gè)單位,一方面甲方竖席、前端耘纱、美術(shù)之間更好協(xié)調(diào),甚至你自己寫(xiě)代碼也要有一個(gè)尺寸標(biāo)準(zhǔn)毕荐。比如一個(gè)園區(qū)束析、工廠,可以m為單位建模憎亚,比如建筑员寇、人、相機(jī)都用m為尺度去衡量第美,如果單位不統(tǒng)一蝶锋,就需要你寫(xiě)代碼,通過(guò).scale
屬性去縮放。
設(shè)置合適的相機(jī)參數(shù)
通過(guò)gltf加載完成什往,模型后扳缕,你還需要根據(jù)自身需要,設(shè)置合適的相機(jī)參數(shù)别威,就好比你拍照躯舔,你想拍攝一個(gè)石頭,肯定要把相機(jī)對(duì)著石頭省古,如果希望石頭在照片上占比大粥庄,就要離石頭近一些。
相機(jī)位置怎么設(shè)置衫樊,你就類比你的眼睛飒赃,如果你想模擬人在3D場(chǎng)景中漫游利花,那么很簡(jiǎn)單,你把相機(jī)放在地面上载佳,距離地面高度和人身高接近即可炒事。
如果你想看到工廠的全貌,你可以理解為你坐著無(wú)人機(jī)向下俯瞰蔫慧,簡(jiǎn)單說(shuō)挠乳,相比人漫游工廠,整體預(yù)覽工廠相機(jī)距離工廠距離更遠(yuǎn)一些姑躲,否則你也看不到全貌睡扬,當(dāng)然過(guò)于遠(yuǎn)了,你就看不清工廠了黍析。
以課程工廠為例卖怜,先設(shè)定一個(gè)小目標(biāo),我們希望工廠能夠居中顯示在canvas畫(huà)布上阐枣,并且保證可以整體預(yù)覽马靠。
下面以透視投影相機(jī)PerspectiveCamera
為例說(shuō)明。
2.1.相機(jī)位置.position
工廠尺寸范圍大概200米數(shù)量級(jí)蔼两,那么如果想整體預(yù)覽觀察工廠所有模型甩鳄,那很簡(jiǎn)單,第一步额划,把camera.position
的xyz值統(tǒng)統(tǒng)設(shè)置為幾百即可妙啃,比如(200, 200, 200)
。
具體xyz值俊戳,你可以通過(guò)OrbitControls可視化操作調(diào)整揖赴,然后瀏覽器控制臺(tái)記錄相機(jī)參數(shù)即可。
camera.position.set(200, 200, 200);
2.2 某位置在canvas畫(huà)布居中
你需要工廠那個(gè)位置在canavs畫(huà)布上居中抑胎,直接把camera.lookAt()
指向哪個(gè)坐標(biāo)储笑。
如果美術(shù)建模,把工廠整體居中圆恤,也就是說(shuō)模型的幾何中心,大概位于世界坐標(biāo)原點(diǎn)腔稀。你設(shè)置camera.lookAt(0,0,0)
,相機(jī)視線指向坐標(biāo)原點(diǎn)盆昙。
camera.lookAt(0, 0, 0);
注意相機(jī)控件OrbitControls會(huì)影響lookAt設(shè)置,注意手動(dòng)設(shè)置OrbitControls的目標(biāo)參數(shù)
camera.lookAt(100, 0, 0);
// 設(shè)置相機(jī)控件軌道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 相機(jī)控件.target屬性在OrbitControls.js內(nèi)部表示相機(jī)目標(biāo)觀察點(diǎn)焊虏,默認(rèn)0,0,0
// console.log('controls.target', controls.target);
controls.target.set(100, 0, 0);
controls.update();//update()函數(shù)內(nèi)會(huì)執(zhí)行camera.lookAt(controls.targe)
2.3.遠(yuǎn)裁截面far
參數(shù)
近裁截面near和遠(yuǎn)裁截面far淡喜,要能包含你想渲染的場(chǎng)景,否則超出視錐體模型會(huì)被剪裁掉诵闭,簡(jiǎn)單說(shuō)near足夠小炼团,far足夠大澎嚣,主要是far。
PerspectiveCamera(fov, aspect, near, far)
測(cè)量工廠尺寸大概幾百的數(shù)量級(jí)瘟芝,這里不用測(cè)具體尺寸易桃,有個(gè)大概數(shù)量級(jí)即可,然后far設(shè)置為3000足夠了锌俱。
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
3.紋理貼圖顏色偏差解決
three.js加載gltf模型的時(shí)候晤郑,可能會(huì)遇到three.js渲染結(jié)果顏色偏差,對(duì)于這種情況贸宏,你只需要修改WebGL渲染器默認(rèn)的編碼方式.outputEncoding
即可
//解決加載gltf格式模型紋理貼圖和原圖不一樣問(wèn)題
renderer.outputEncoding = THREE.sRGBEncoding;
注意T烨蕖!?粤贰=肓!v暄省签赃!最新版本屬性名字有改變。渲染器屬性名.outputEncoding
已經(jīng)變更為.outputColorSpace
浑侥。
查WebGL渲染器文檔姊舵,你可以看到.outputColorSpace
的默認(rèn)值就是SRGB顏色空間THREE.SRGBColorSpace
,意味著新版本代碼中寓落,加載gltf括丁,沒(méi)有特殊需要,不設(shè)置.outputColorSpace
也不會(huì)引起色差伶选。
//新版本史飞,加載gltf,不需要執(zhí)行下面代碼解決顏色偏差
renderer.outputColorSpace = THREE.SRGBColorSpace;//設(shè)置為SRGB顏色空間
步驟3:材質(zhì)效果
作為Banner仰税,肯定需要比較好的材質(zhì)效果构资,不能隨隨便便加載一個(gè)gltf模型,這樣的材質(zhì)效果不會(huì)太好陨簇。
一般來(lái)說(shuō)首先美術(shù)在Blender軟件中吐绵,可以設(shè)置PBR材質(zhì)的相關(guān)屬性,比如玻璃材質(zhì)的金屬度河绽、粗糙度己单、透射度等屬性,然后導(dǎo)出gltf格式模型的時(shí)候耙饰,這些PBR材質(zhì)屬性可以跟隨導(dǎo)出纹笼。threejs加載gltf模型的時(shí)候,會(huì)自動(dòng)解析相關(guān)的材質(zhì)屬性苟跪。
尤其是上面Banner中的玻璃材質(zhì)廷痘,為了更好的3D效果蔓涧,最好設(shè)置好環(huán)境貼圖,這樣玻璃質(zhì)感才會(huì)更強(qiáng)烈笋额。
// 加載環(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)境貼圖
補(bǔ)充內(nèi)容:PBR材質(zhì)介紹
本節(jié)課沒(méi)有具體的代碼元暴,就是給大家科普一下PBR材質(zhì),所謂PBR就是鳞陨,基于物理的渲染(physically-based rendering)昨寞。
Three.js提供了兩個(gè)PBR材質(zhì)相關(guān)的APIMeshStandardMaterial
和MeshPhysicalMaterial
,MeshPhysicalMaterial
是MeshStandardMaterial
擴(kuò)展的子類,提供了更多功能屬性厦滤。
光照模型
如果你有初高中最基本的物理光學(xué)知識(shí)援岩,應(yīng)該有折射、鏡面反射掏导、漫反射等基本光學(xué)概念享怀,對(duì)于實(shí)際生活中的光學(xué)問(wèn)題,Three.js會(huì)提供一些的光照模型來(lái)模擬物體表面的光照趟咆,光照模型就一種模擬光照的計(jì)算方法添瓷。MeshPhysicalMaterial
和MeshLambertMaterial
一樣都是渲染網(wǎng)格模型的材質(zhì),但是他們用的光照模型不同值纱,具體點(diǎn)說(shuō)就是材質(zhì)模擬Mesh反射光照的代碼算法不同鳞贷,算法不同,自然模擬光照的真實(shí)程度也不同虐唠。
如果你想深入研究光照模型搀愧,可以學(xué)習(xí)下原生WebGL或WebGPU,或者看看計(jì)算機(jī)圖形學(xué)相關(guān)書(shū)籍疆偿,使用threejs的大部分情況咱筛,用不著你自己實(shí)現(xiàn)光照模型算法,畢竟threejs通過(guò)網(wǎng)格模型材質(zhì)幫你實(shí)現(xiàn)了杆故。
PBR相關(guān)理論介紹文章
- 半小時(shí)了解PBR:https://zhuanlan.zhihu.com/p/37639418
- PBR知識(shí)體系整理:https://zhuanlan.zhihu.com/p/100596453
- PBR核心知識(shí)體系總結(jié)與概覽:https://zhuanlan.zhihu.com/p/53086060
網(wǎng)格模型材質(zhì)整體回顧
MeshLambertMaterial: Lambert光照模型(漫反射)
MeshPhongMaterial:Phong光照模型(漫反射迅箩、高光反射)
MeshStandardMaterial和MeshPhysicalMaterial:基于物理的光照模型(微平面理論、能量守恒处铛、菲涅爾反射...)
PBR材質(zhì)相比MeshLambertMaterial和MeshPhongMaterial可以提供更逼真的饲趋、更接近生活中的材質(zhì)效果,當(dāng)然也會(huì)占用更多的電腦硬件資源撤蟆。
通過(guò)MeshPhysicalMaterial文檔篙贸,提供的資源,可以查看多個(gè)PBR材質(zhì)的案例效果枫疆,系統(tǒng)課程中轎車展示案例也會(huì)用到PBR材質(zhì)。
渲染占用資源和表現(xiàn)能力
整體上來(lái)看敷鸦,就是渲染表現(xiàn)能力越強(qiáng)息楔,占用的計(jì)算機(jī)硬件資源更多寝贡。
占用渲染資源
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)一個(gè)金屬渲染效果。
金屬度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
表示模型表面的光滑或者說(shuō)粗糙程度,越光滑鏡面反射能力越強(qiáng)辆亏,越粗糙风秤,表面鏡面反射能力越弱,更多地表現(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)境貼圖對(duì)PBR材質(zhì)渲染效果影響還是比較大,一般渲染PBR材質(zhì)的模型彻磁,最好設(shè)置一個(gè)合適的環(huán)境貼圖碍沐。
立方體紋理加載器CubeTextureLoader
-
TextureLoader
返回Texture
-
CubeTextureLoader
返回CubeTexture
通過(guò)前面學(xué)習(xí)大家知道,通過(guò)紋理貼圖加載器TextureLoader
的.load()
方法加載一張圖片可以返回一個(gè)紋理對(duì)象Texture
衷蜓。
立方體紋理加載器CubeTextureLoader
的.load()
方法是加載6張圖片累提,返回一個(gè)立方體紋理對(duì)象CubeTexture
。
立方體紋理對(duì)象CubeTexture
的父類是紋理對(duì)象Texture
磁浇。
CubeTextureLoader
加載環(huán)境貼圖
所謂環(huán)境貼圖斋陪,就是一個(gè)模型周圍的環(huán)境的圖像,比如一間房子扯夭,房子的上下左右前后分別拍攝一張照片鳍贾,就是3D空間中6個(gè)角度方向的照片。
// 加載環(huán)境貼圖
// 加載周圍環(huán)境6個(gè)方向貼圖
// 上下左右前后6張貼圖構(gòu)成一個(gè)立方體空間
// '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表示立方體紋理對(duì)象骑科,父類是紋理對(duì)象Texture
MeshStandardMaterial
環(huán)境貼圖屬性.envMap
實(shí)際生活中,一個(gè)物體表面构拳,往往會(huì)反射周圍的環(huán)境咆爽。人的眼睛看到的東西,往往反射有周圍景物置森,所以three.js渲染模型斗埂,如果想渲染效果更好看,如果想更符合實(shí)際生活情況凫海,也需要想辦法讓模型反射周圍景物呛凶。
MeshStandardMaterial材質(zhì)的環(huán)境貼圖屬性是.envMap
,通過(guò)PBR材質(zhì)的貼圖屬性可以實(shí)現(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
屬性主要用來(lái)設(shè)置模型表面反射周圍環(huán)境貼圖的能力模闲,或者說(shuō)環(huán)境貼圖對(duì)模型表面的影響能力。具體說(shuō).envMapIntensity
相當(dāng)于環(huán)境貼圖的系數(shù)崭捍,環(huán)境貼圖像素值乘以該系數(shù)后尸折,在用于影響模型表面。
// envMapIntensity:控制環(huán)境貼圖對(duì)mesh表面影響程度
//默認(rèn)值1, 設(shè)置為0.0,相當(dāng)于沒(méi)有環(huán)境貼圖
obj.material.envMapIntensity = 1.0;
粗糙度roughness
為0
你可以嘗試把粗糙度roughness
設(shè)置為0殷蛇,看看模型對(duì)環(huán)境貼圖的反射效果实夹。
obj.material.roughness = 0.0;//完全鏡面反射,像鏡子一樣
選擇合適的環(huán)境貼圖
不同的明暗或景物的環(huán)境貼圖對(duì)渲染效果的影響是不一樣的粒梦,所以不僅要設(shè)置環(huán)境貼圖亮航,還要根據(jù)需要選擇合適的環(huán)境貼圖,一般實(shí)際開(kāi)發(fā)使用美術(shù)提供的環(huán)境貼圖即可谍倦。
你可以嘗試測(cè)試源碼中提供多個(gè)環(huán)境貼圖對(duì)比渲染效果差異塞赂。
紋理和渲染器顏色空間一致
//如果renderer.outputEncoding=THREE.sRGBEncoding;環(huán)境貼圖需要保持一致
textureCube.encoding = THREE.sRGBEncoding;
環(huán)境貼圖2
接著上節(jié)課的環(huán)境貼圖給大家講解。
環(huán)境貼圖作用測(cè)試
實(shí)際生活中光源照射到一個(gè)物體上昼蛀,這個(gè)物體反射出去的光線也會(huì)影響其他的物體宴猾,環(huán)境貼圖就是用一種簡(jiǎn)單方式,近似模擬一個(gè)物體周邊環(huán)境對(duì)物體表面的影響叼旋。
測(cè)試:對(duì)于PBR材質(zhì)峻呛,如果threejs三維場(chǎng)景不添加任何光源军浆,物體就是完全黑色的消痛,你可以不添加任何光源颅眶,嘗試只使用環(huán)境貼圖,你會(huì)發(fā)現(xiàn)物體表面的顏色也能看到详民,這說(shuō)明環(huán)境貼圖其實(shí)相當(dāng)于提供了物體周圍環(huán)境發(fā)射或反射的光線延欠。
測(cè)試:更換不同明暗的環(huán)境貼圖,你會(huì)發(fā)現(xiàn)場(chǎng)景中模型的明暗也有變化沈跨。
場(chǎng)景環(huán)境屬性.environment
網(wǎng)格模型可以通過(guò)材質(zhì)的.envMap
屬性設(shè)置環(huán)境貼圖由捎,如果一個(gè)gltf模型中所有的Mesh都要設(shè)置環(huán)境貼圖就需要遞歸遍歷gltf模型,給里面每個(gè)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)境貼圖影響場(chǎng)景中scene所有Mesh狞玛,可以通過(guò)Scene的場(chǎng)景環(huán)境屬性.environment
實(shí)現(xiàn),把環(huán)境貼圖對(duì)應(yīng)紋理對(duì)象設(shè)置為.environment
的屬性值即可。
// 環(huán)境貼圖紋理對(duì)象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ǔ)上擴(kuò)展出來(lái)的子類,除了繼承了MeshStandardMaterial的金屬度纠吴、粗糙度等屬性硬鞍,還新增了清漆.clearcoat
、透光率.transmission
、反射率.reflectivity
固该、光澤.sheen
碑隆、折射率.ior
等等各種用于模擬生活中不同材質(zhì)的屬性。
清漆層屬性.clearcoat
清漆層屬性.clearcoat
可以用來(lái)模擬物體表面一層透明圖層蹬音,就好比你在物體表面刷了一層透明清漆,噴了點(diǎn)水休玩。.clearcoat的范圍0到1著淆,默認(rèn)0。
const material = new THREE.MeshPhysicalMaterial( {
clearcoat: 1.0,//物體表面清漆層或者說(shuō)透明涂層的厚度
} );
清漆層粗糙度.clearcoatRoughness
清漆層粗糙度.clearcoatRoughness
屬性表示物體表面透明涂層.clearcoat
對(duì)應(yīng)的的粗糙度拴疤,.clearcoatRoughness
的范圍是為0.0至1.0永部。默認(rèn)值為0.0。
const material = new THREE.MeshPhysicalMaterial( {
clearcoat: 1.0,//物體表面清漆層或者說(shuō)透明涂層的厚度
clearcoatRoughness: 0.1,//透明涂層表面的粗糙度
} );
車外殼PBR材質(zhì)設(shè)置
在設(shè)置車外殼清漆層之前呐矾,先創(chuàng)建一個(gè)MeshPhysicalMaterial材質(zhì)苔埋,并設(shè)置好環(huán)境貼圖、金屬度蜒犯、粗糙度组橄,屬性值先根據(jù)文檔說(shuō)明給一個(gè)大概的值,具體可以通過(guò)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)境貼圖對(duì)Mesh表面影響程度
})
車外殼油漆效果
車外殼油漆效果玉工,你可以通過(guò)PBR材質(zhì)的清漆層屬性.clearcoat
和清漆層粗糙度.clearcoatRoughness
屬性模擬。
屬性值先根據(jù)文檔說(shuō)明給一個(gè)大概的值淘菩,具體可以通過(guò)gui交互界面可視化調(diào)試遵班。
const mesh = gltf.scene.getObjectByName('外殼01');
mesh.material = new THREE.MeshPhysicalMaterial( {
clearcoat: 1.0,//物體表面清漆層或者說(shuō)透明涂層的厚度
clearcoatRoughness: 0.1,//透明涂層表面的粗糙度
} );
GUI可視化調(diào)試PBR材質(zhì)屬性
關(guān)于gui的使用,在第一章節(jié)入門(mén)中詳細(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
為了更好的模擬玻璃翰萨、半透明塑料一類的視覺(jué)效果,可以使用物理透明度.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)境貼圖對(duì)Mesh表面影響程度
})
設(shè)置透光率.transmission
和折射率.ior
。
new THREE.MeshPhysicalMaterial({
transmission: 1.0, //玻璃材質(zhì)透光率冶匹,transmission替代opacity
ior:1.5,//折射率
})
GUI可視化調(diào)試PBR材質(zhì)屬性
基本參數(shù)和代碼設(shè)置好以后习劫,就是通過(guò)GUI可視化交互界面,調(diào)試PBR材質(zhì)或光源的參數(shù)嚼隘,gui.js庫(kù)的使用參考入門(mén)章節(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ì)屬性
實(shí)際開(kāi)發(fā)的時(shí)候PBR材質(zhì)的屬性,很多時(shí)候是可以在三維建模軟件中設(shè)置的飞蛹,然后通過(guò)gltf導(dǎo)出即可谤狡,這樣就不用在threejs代碼設(shè)置。
通常美術(shù)對(duì)三維場(chǎng)景渲染的了解也比大部分前端程序員多的多卧檐,只要美術(shù)在三維建模軟件設(shè)置好并導(dǎo)出包含pbr材質(zhì)屬性的gltf即可墓懂。
threejs與建模軟件對(duì)接的問(wèn)題
- gltf能否存儲(chǔ)3D建模軟件的某個(gè)材質(zhì)屬性:有些三維軟件特有的材質(zhì)屬性,不一定能通過(guò)gltf導(dǎo)出霉囚,也談不上threejs解析
- 三維建模能否導(dǎo)出PBR材質(zhì):能導(dǎo)出的話捕仔,能導(dǎo)出哪些屬性,不能導(dǎo)出哪些屬性
如果你的三維建模不能導(dǎo)出pbr材質(zhì)盈罐,或者部分pbr材質(zhì)屬性無(wú)法導(dǎo)出榜跌,那你通常需要用代碼方式添加材質(zhì),這樣就麻煩些暖呕。
Blender導(dǎo)出PBR材質(zhì)演示
首先Blender最新版導(dǎo)出gltf模型時(shí)候斜做,是可以把PBR材質(zhì)的很多屬性導(dǎo)出的,比如金屬度metalness
湾揽、粗糙度roughness
瓤逼、清漆.clearcoat
、透光率(透射度).transmission
等等库物。課件源碼中提供了blender導(dǎo)出的gltf模型你可以瀏覽器控制臺(tái)打印測(cè)試霸旗,這些PBR材質(zhì)屬性能否解析渲染。
Bledner中設(shè)置PBR材質(zhì)
你可以在Bledner中設(shè)置車外殼戚揭、車玻璃的材質(zhì)屬性
- 車外殼:清漆诱告、清漆粗糙度
- 車玻璃:透光率(透射度)
threejs解析gltf材質(zhì)規(guī)則
大家都知道,MeshPhysicalMaterial
是MeshStandardMaterial
的子類民晒,具有更多的PBR材質(zhì)屬性和功能精居。
所以,threejs解析gltf模型潜必,會(huì)用兩種材質(zhì)PBR材質(zhì)去解析靴姿,一個(gè)是標(biāo)準(zhǔn)網(wǎng)格材質(zhì)MeshStandardMaterial
,一個(gè)是物理網(wǎng)格材質(zhì)MeshPhysicalMaterial
磁滚,如果能用MeshStandardMaterial
表示就用佛吓,不能就換MeshPhysicalMaterial
宵晚。
具體說(shuō)就是,threejs解析gltf模型材質(zhì)的時(shí)候维雇,一般默認(rèn)使用標(biāo)準(zhǔn)網(wǎng)格材質(zhì)MeshStandardMaterial
淤刃,如果gltf有的材質(zhì)具有.clearcoat
、.transmission
等屬性吱型,標(biāo)準(zhǔn)網(wǎng)格材質(zhì)MeshStandardMaterial
無(wú)法表達(dá)的時(shí)候逸贾,會(huì)用物理網(wǎng)格材質(zhì)MeshPhysicalMaterial
來(lái)解析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)境貼圖
這時(shí)候清漆津滞、清漆粗糙度耕陷、透光率(透射度)等屬性Bledner都已經(jīng)設(shè)置好了,threejs可以自動(dòng)解析渲染,不用在代碼中麻煩設(shè)置了据沈,只要配上環(huán)境貼圖即可。
const mesh1 = gltf.scene.getObjectByName('外殼01');
mesh1.material.envMap = textureCube; //環(huán)境貼圖
mesh1.material.envMapIntensity = 1.0; ////環(huán)境貼圖對(duì)Mesh表面影響程度
const mesh2 = gltf.scene.getObjectByName('玻璃01');
mesh2.material.envMap = textureCube; //環(huán)境貼圖
mesh2.material.envMapIntensity = 1.0; ////環(huán)境貼圖對(duì)Mesh表面影響程度