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

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

image.png

最近沒什么事,寫了一個3D Banner效果,給廣大前端同行們分享下。

在線3D體驗地址1:http://www.webgl3d.cn/3D/banner1/index.html

在線3D體驗地址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)成的小動畫。不過隨著webgl的流行,一些官網(wǎng)會用3D模型制作官網(wǎng)的Banner效果。

3D Banner相比普通的靜態(tài)圖片,視覺效果更好,更立體。

技術(shù)棧

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

vite使用文檔

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

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

光照模型

如果你有初高中最基本的物理光學(xué)知識,應(yīng)該有折射、鏡面反射、漫反射等基本光學(xué)概念,對于實際生活中的光學(xué)問題,Three.js會提供一些的光照模型來模擬物體表面的光照,光照模型就一種模擬光照的計算方法。MeshPhysicalMaterialMeshLambertMaterial一樣都是渲染網(wǎng)格模型的材質(zhì),但是他們用的光照模型不同,具體點說就是材質(zhì)模擬Mesh反射光照的代碼算法不同,算法不同,自然模擬光照的真實程度也不同。

如果你想深入研究光照模型,可以學(xué)習(xí)下原生WebGL或WebGPU,或者看看計算機圖形學(xué)相關(guān)書籍,使用threejs的大部分情況,用不著你自己實現(xiàn)光照模型算法,畢竟threejs通過網(wǎng)格模型材質(zhì)幫你實現(xiàn)了。

PBR相關(guān)理論介紹文章

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

MeshPhysicalMaterialMeshStandardMaterial都是擁有金屬度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與建模軟件對接的問題

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

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

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

大家都知道,MeshPhysicalMaterialMeshStandardMaterial的子類,具有更多的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表面影響程度
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,990評論 2 374

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