目錄
Three.js入門
Three.js光源
Three.js陰影
Three.js紋理貼圖
使用燈光后,場景中就會產生陰影。物體的背面確實在黑暗中,這稱為核心陰影(core shadow)。我們缺少的是落下的陰影(drop shadow),即對象在其他對象上創建陰影。本文主要探索是落下的陰影(drop shadow)的相關內容。
支持陰影的光源類型
前面的文章我們有介紹過,只有部分光源支持陰影:
- 平行光(
DirectionalLight
) - 點光源(
PointLight
) - 聚光燈(
SpotLight
)
它們對應的陰影在three.js中也有對應的實現類:
- 平行光陰影(
DirectionalLightShadow
) - 點光源陰影(
PointLightShadow
) - 聚光燈陰影(
SpotLightShadow
)
陰影的代碼實現
在入門系列中有介紹陰影的基本代碼實現,這里再簡單敘述一遍。主要分為四個步驟:
- 告訴渲染器顯示陰影貼圖;
renderer.shadowMap.enabled = true;
- 對需要投影的物體設置
castShadow
為true
;sphere.castShadow = true;
- 對需要接收投影的物體設置
receiveShadow
為true
;plane.receiveShadow = true;
- 最后,對可以產生陰影的光源設置
castShadow
為true
;light.castShadow = true;
至此,我們應該就可以在接收投影的物體上看到陰影了!
陰影的實現原理
每次渲染時,three.js 將為每個支持陰影的光源都會做一次渲染。
- 光源具有一個相機,以這個相機為視角進行渲染;
- 光源的渲染結果被作為紋理存儲起來,也就是 陰影貼圖(shadow map);
- 陰影貼圖將被用于所有需要接受陰影的材質(material),并投影到幾何體(geometry)上。
有些材質不接受光照,所以材質也會影響陰影的顯示。
陰影的優化和調整
陰影貼圖大小
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
- 陰影貼圖越大約清晰,但是越消耗資源;
- 512和1024大小對比圖,就可以看出來對比效果是1024更清晰(運行效果更明顯)。
相機設置
幅度
我們先來看一個陰影不完整的現象:
正交相機OrthographicCamera
平行光源用的是正交相機進行渲染:
export class DirectionalLight extends Light<DirectionalLightShadow> {shadow: DirectionalLightShadow;
}export class DirectionalLightShadow extends LightShadow<OrthographicCamera> {camera: OrthographicCamera;
}
- 用相機輔助線看一下光線的陰影的相機視角:
const cameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera);
scene.add(cameraHelper);
球體超過了陰影的相機視角的上邊緣。
- 擴大相機的上邊緣
directionalLight.shadow.camera.top = 8; // 這個值不是固定的,視場景不同
far
和相機的渲染一致,設置相機的far
,可以控制是否顯示陰影。
directionalLight.shadow.camera.far = 100;
Blur 模糊
我們可以使用 radius 屬性控制陰影模糊:
directionalLight.shadow.radius = 20
陰影的邊緣會有模糊的效果。
陰影的類型
在Three.js
中,ShadowMapType
枚舉定義了幾種陰影映射的類型:
BasicShadowMap
:基本陰影映射類型,使用簡單的陰影投影算法生成陰影。這是默認的陰影映射類型。
性能優秀但是質量不好(默認)
PCFShadowMap
:使用Percentage-Closer Filtering (PCF)
技術生成陰影,以獲得更平滑的陰影邊緣效果。
性能稍差但是擁有光滑的邊緣
PCFSoftShadowMap
:使用軟陰影技術生成陰影,通過多次采樣和模糊處理來產生更柔和的陰影效果。
性能稍差但是擁有更 soft 的邊緣
VSMShadowMap
:使用Variance Shadow Mapping (VSM)
技術生成陰影,以獲得更高質量的陰影效果和更柔和的陰影邊緣。
性能稍差,更多限制,有著意想不到的效果
代碼實現:
renderer.shadowMap.type = THREE.BasicShadowMap;
我項目中使用,倒是看不出來有啥大的區別。