官網(wǎng)3D Banner效果 three.js+vue實(shí)現(xiàn)

官網(wǎng)3D Banner效果 three.js+vue實(shí)現(xiàn)

image.png

最近沒(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

【視頻展示】 https://www.bilibili.com/video/BV1Ci4y1e7XX/?share_source=copy_web&vd_source=026f0cd0b145ec9bc2c005d9eaf67b0b

官網(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ù)棧

  1. 三維建模軟件:Blender
  2. 3D部分代碼:three.js引擎
  3. 普通前端部分: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)境即可瞳筏。

vite使用文檔

第一步是選擇你的前端框架稚瘾,第二步是選擇是否支持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)講解寿冕,主要是把下面三部,給大家全流程演示一遍椒袍。

  1. gltf模型加載器GLTFLoader.js
  2. 相機(jī)參數(shù)根據(jù)需要設(shè)置
  3. 加載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)的APIMeshStandardMaterialMeshPhysicalMaterial,MeshPhysicalMaterialMeshStandardMaterial擴(kuò)展的子類,提供了更多功能屬性厦滤。

光照模型

如果你有初高中最基本的物理光學(xué)知識(shí)援岩,應(yīng)該有折射、鏡面反射掏导、漫反射等基本光學(xué)概念享怀,對(duì)于實(shí)際生活中的光學(xué)問(wèn)題,Three.js會(huì)提供一些的光照模型來(lái)模擬物體表面的光照趟咆,光照模型就一種模擬光照的計(jì)算方法添瓷。MeshPhysicalMaterialMeshLambertMaterial一樣都是渲染網(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)理論介紹文章

網(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

MeshPhysicalMaterialMeshStandardMaterial都是擁有金屬度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)題

  1. gltf能否存儲(chǔ)3D建模軟件的某個(gè)材質(zhì)屬性:有些三維軟件特有的材質(zhì)屬性,不一定能通過(guò)gltf導(dǎo)出霉囚,也談不上threejs解析
  2. 三維建模能否導(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ì)屬性

  1. 車外殼:清漆诱告、清漆粗糙度
  2. 車玻璃:透光率(透射度)

threejs解析gltf材質(zhì)規(guī)則

大家都知道,MeshPhysicalMaterialMeshStandardMaterial的子類民晒,具有更多的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表面影響程度
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饺蔑,一起剝皮案震驚了整個(gè)濱河市锌介,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猾警,老刑警劉巖孔祸,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異发皿,居然都是意外死亡崔慧,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)穴墅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)惶室,“玉大人,你說(shuō)我怎么就攤上這事玄货』食” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵松捉,是天一觀的道長(zhǎng)夹界。 經(jīng)常有香客問(wèn)我,道長(zhǎng)隘世,這世上最難降的妖魔是什么可柿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮丙者,結(jié)果婚禮上复斥,老公的妹妹穿的比我還像新娘。我一直安慰自己蔓钟,他們只是感情好永票,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般侣集。 火紅的嫁衣襯著肌膚如雪键俱。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天世分,我揣著相機(jī)與錄音编振,去河邊找鬼。 笑死臭埋,一個(gè)胖子當(dāng)著我的面吹牛踪央,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瓢阴,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼畅蹂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了荣恐?” 一聲冷哼從身側(cè)響起液斜,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎叠穆,沒(méi)想到半個(gè)月后少漆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硼被,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年示损,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嚷硫。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡检访,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仔掸,到底是詐尸還是另有隱情烛谊,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布嘉汰,位于F島的核電站丹禀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鞋怀。R本人自食惡果不足惜双泪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望密似。 院中可真熱鬧焙矛,春花似錦、人聲如沸残腌。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蟆盹,卻和暖如春孩灯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背逾滥。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工峰档, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寨昙。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓讥巡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親舔哪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子欢顷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容