官網(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è)效果,類(lèi)似的流程,首先需要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è)置,你就類(lèi)比你的眼睛,如果你想模擬人在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;
注意!!!!!!!最新版本屬性名字有改變。渲染器屬性名.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ò)展的子類(lèi),提供了更多功能屬性。
光照模型
如果你有初高中最基本的物理光學(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)課程中轎車(chē)展示案例也會(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
的父類(lèi)是紋理對(duì)象Texture
。
CubeTextureLoader
加載環(huán)境貼圖
所謂環(huán)境貼圖,就是一個(gè)模型周?chē)沫h(huán)境的圖像,比如一間房子,房子的上下左右前后分別拍攝一張照片,就是3D空間中6個(gè)角度方向的照片。
// 加載環(huán)境貼圖
// 加載周?chē)h(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ì)象,父類(lèi)是紋理對(duì)象Texture
MeshStandardMaterial
環(huán)境貼圖屬性.envMap
實(shí)際生活中,一個(gè)物體表面,往往會(huì)反射周?chē)沫h(huán)境。人的眼睛看到的東西,往往反射有周?chē)拔铮詔hree.js渲染模型,如果想渲染效果更好看,如果想更符合實(shí)際生活情況,也需要想辦法讓模型反射周?chē)拔铩?/p>
MeshStandardMaterial材質(zhì)的環(huán)境貼圖屬性是.envMap
,通過(guò)PBR材質(zhì)的貼圖屬性可以實(shí)現(xiàn)模型表面反射周?chē)拔铮@樣渲染效果更好。
// 加載環(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è)置模型表面反射周?chē)h(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)于提供了物體周?chē)h(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)的子類(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,//透明涂層表面的粗糙度
} );
車(chē)外殼PBR材質(zhì)設(shè)置
在設(shè)置車(chē)外殼清漆層之前,先創(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,//車(chē)外殼金屬度
roughness: 0.5,//車(chē)外殼粗糙度
envMap: textureCube, //環(huán)境貼圖
envMapIntensity: 2.5, //環(huán)境貼圖對(duì)Mesh表面影響程度
})
車(chē)外殼油漆效果
車(chē)外殼油漆效果,你可以通過(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
為了更好的模擬玻璃、半透明塑料一類(lèi)的視覺(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è)置車(chē)外殼、車(chē)玻璃的材質(zhì)屬性
- 車(chē)外殼:清漆、清漆粗糙度
- 車(chē)玻璃:透光率(透射度)
threejs解析gltf材質(zhì)規(guī)則
大家都知道,MeshPhysicalMaterial
是MeshStandardMaterial
的子類(lèi),具有更多的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表面影響程度