Skip to content

阴影

阴影为场景和模型添加了一定程度的深度和真实感,因为它们可以显示物体的比例和位置,使场景中的物体看起来更加立体。在引擎中,光源可以将物体的阴影投射到自身的其他部分或者附近的场景中,这些阴影可以是实时的,也可以是静态的。

配置阴影

阴影是通过光源来实现的,目前引擎中 平行光(DirectLight)点光源(PointLight)聚光灯(SpotLight) 都可以产生阴影。需要渲染阴影效果时,只需要将对应灯光的 castShadow 属性设置为 true 即可。

ts
let lightObj = new Object3D();
// 设置灯光位置
lightObj.x = 0;
lightObj.y = 0;
lightObj.z = 0;
// 设置灯光角度,绕X轴旋转45度
lightObj.rotationX = 45;
lightObj.rotationY = 0;
lightObj.rotationZ = 0;
// 添加平行光组件,并开启阴影
let lc = lightObj.addComponent(DirectLight);
lc.castShadow = true; // 默认为 false
lc.intensity = 5;
scene.addChild(lightObj);
let lightObj = new Object3D();
// 设置灯光位置
lightObj.x = 0;
lightObj.y = 0;
lightObj.z = 0;
// 设置灯光角度,绕X轴旋转45度
lightObj.rotationX = 45;
lightObj.rotationY = 0;
lightObj.rotationZ = 0;
// 添加平行光组件,并开启阴影
let lc = lightObj.addComponent(DirectLight);
lc.castShadow = true; // 默认为 false
lc.intensity = 5;
scene.addChild(lightObj);

想要看到阴影效果,我们还需要产生阴影的物体和承载阴影的物体。如果希望光线照射到某个物体上产生阴影效果,需要在物体上添加一个 MeshRenderer 组件,并且将该组件的 castShadow 属性设置为 true

ts
//创建一个box,用于产生阴影
let castShadowObj = new Object3D();
let mr1 = castShadowObj.addComponent(MeshRenderer);
mr1.geometry = new BoxGeometry();
mr1.material = new LitMaterial();
mr1.castShadow = true
scene.addChild(castShadowObj);
//创建一个box,用于产生阴影
let castShadowObj = new Object3D();
let mr1 = castShadowObj.addComponent(MeshRenderer);
mr1.geometry = new BoxGeometry();
mr1.material = new LitMaterial();
mr1.castShadow = true
scene.addChild(castShadowObj);

然后需要在接受阴影的物体上添加一个 MeshRenderer 组件,并且将该组件的 receiveShadow 属性设置为 true

ts
//创建一个plane,用于接受阴影
let receiveShadowObj = new Object3D();
let mr2 = receiveShadowObj.addComponent(MeshRenderer);
mr2.geometry  = new PlaneGeometry(1000,1000);
mr2.material =new LitMaterial();
mr2.receiveShadow = true;
scene.addChild(receiveShadowObj);
//创建一个plane,用于接受阴影
let receiveShadowObj = new Object3D();
let mr2 = receiveShadowObj.addComponent(MeshRenderer);
mr2.geometry  = new PlaneGeometry(1000,1000);
mr2.material =new LitMaterial();
mr2.receiveShadow = true;
scene.addChild(receiveShadowObj);

这样就可以在场景上看到一个 box 在平行光的照射下,产生了一个阴影并投射在了 plane 上:

平行光阴影



WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry, Color } from '@orillusion/core'

// shadow setting
Engine3D.setting.shadow.autoUpdate = true
Engine3D.setting.shadow.shadowBound = 100
Engine3D.setting.shadow.type = 'HARD'

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
})
let scene3D: Scene3D = new Scene3D()
let cameraObj: Object3D = new Object3D()
let camera = cameraObj.addComponent(Camera3D)
camera.perspective(60, Engine3D.aspect, 1, 5000.0)
let controller = cameraObj.addComponent(HoverCameraController)
controller.setCamera(-45, -45, 100, new Vector3(0, 0, 0))
scene3D.addChild(cameraObj)

//DirectLight
{
    let obj = new Object3D()
    obj.rotationX = 45
    obj.rotationY = 0
    obj.rotationZ = 0
    let light = obj.addComponent(DirectLight)
    scene3D.addChild(obj)
    // enable light shadow
    light.castShadow = true
    light.intensity = 30
    scene3D.addChild(obj)
}

// create a box as shadow source
{
    let castShadowObj = new Object3D()
    castShadowObj.y = 5
    castShadowObj.rotationY = 45
    let mr = castShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(10, 10, 10)
    mr.material = new LitMaterial()
    mr.material.baseColor = new Color(1, 0, 0)
    mr.castShadow = true
    scene3D.addChild(castShadowObj)
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D()
    let mr = receiveShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(2000, 1, 2000)
    mr.material = new LitMaterial()
    mr.receiveShadow = true
    scene3D.addChild(receiveShadowObj)
}
// create a view with target scene and camera
let view = new View3D()
view.scene = scene3D
view.camera = camera
// start render
Engine3D.startRenderView(view)
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry, Color } from '@orillusion/core'

// shadow setting
Engine3D.setting.shadow.autoUpdate = true
Engine3D.setting.shadow.shadowBound = 100
Engine3D.setting.shadow.type = 'HARD'

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
})
let scene3D: Scene3D = new Scene3D()
let cameraObj: Object3D = new Object3D()
let camera = cameraObj.addComponent(Camera3D)
camera.perspective(60, Engine3D.aspect, 1, 5000.0)
let controller = cameraObj.addComponent(HoverCameraController)
controller.setCamera(-45, -45, 100, new Vector3(0, 0, 0))
scene3D.addChild(cameraObj)

//DirectLight
{
    let obj = new Object3D()
    obj.rotationX = 45
    obj.rotationY = 0
    obj.rotationZ = 0
    let light = obj.addComponent(DirectLight)
    scene3D.addChild(obj)
    // enable light shadow
    light.castShadow = true
    light.intensity = 30
    scene3D.addChild(obj)
}

// create a box as shadow source
{
    let castShadowObj = new Object3D()
    castShadowObj.y = 5
    castShadowObj.rotationY = 45
    let mr = castShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(10, 10, 10)
    mr.material = new LitMaterial()
    mr.material.baseColor = new Color(1, 0, 0)
    mr.castShadow = true
    scene3D.addChild(castShadowObj)
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D()
    let mr = receiveShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(2000, 1, 2000)
    mr.material = new LitMaterial()
    mr.receiveShadow = true
    scene3D.addChild(receiveShadowObj)
}
// create a view with target scene and camera
let view = new View3D()
view.scene = scene3D
view.camera = camera
// start render
Engine3D.startRenderView(view)

点光源阴影



WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry, Color } from '@orillusion/core'

// shadow setting
Engine3D.setting.shadow.autoUpdate = true
Engine3D.setting.shadow.shadowBound = 100
Engine3D.setting.shadow.pointShadowBias = 0.0001
Engine3D.setting.shadow.type = 'HARD'

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
})
let scene3D: Scene3D = new Scene3D()
let cameraObj: Object3D = new Object3D()
let camera = cameraObj.addComponent(Camera3D)
camera.perspective(60, Engine3D.aspect, 1, 5000.0)
let controller = cameraObj.addComponent(HoverCameraController)
controller.setCamera(0, -45, 150, new Vector3(0, 0, 0))
scene3D.addChild(cameraObj)

//PointLight
{
    let obj = new Object3D()
    let light = obj.addComponent(PointLight)
    scene3D.addChild(obj)
    obj.x = -15
    obj.y = 30
    obj.z = -20
    obj.rotationX = 0
    obj.rotationY = 0
    obj.rotationZ = 0
    light.intensity = 15
    light.range = 100
    // enable light shadow
    light.castShadow = true
    light.debug()
    light.debugDraw(true)
}

// create a box as shadow source
{
    let castShadowObj = new Object3D()
    castShadowObj.y = 5
    let mr = castShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(10, 10, 10)
    mr.material = new LitMaterial()
    mr.material.baseColor = new Color(1, 0, 0)
    mr.castShadow = true
    scene3D.addChild(castShadowObj)
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D()
    let mr = receiveShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(2000, 1, 2000)
    mr.material = new LitMaterial()
    mr.receiveShadow = true
    scene3D.addChild(receiveShadowObj)
}
// create a view with target scene and camera
let view = new View3D()
view.scene = scene3D
view.camera = camera
// start render
Engine3D.startRenderView(view)
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry, Color } from '@orillusion/core'

// shadow setting
Engine3D.setting.shadow.autoUpdate = true
Engine3D.setting.shadow.shadowBound = 100
Engine3D.setting.shadow.pointShadowBias = 0.0001
Engine3D.setting.shadow.type = 'HARD'

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
})
let scene3D: Scene3D = new Scene3D()
let cameraObj: Object3D = new Object3D()
let camera = cameraObj.addComponent(Camera3D)
camera.perspective(60, Engine3D.aspect, 1, 5000.0)
let controller = cameraObj.addComponent(HoverCameraController)
controller.setCamera(0, -45, 150, new Vector3(0, 0, 0))
scene3D.addChild(cameraObj)

//PointLight
{
    let obj = new Object3D()
    let light = obj.addComponent(PointLight)
    scene3D.addChild(obj)
    obj.x = -15
    obj.y = 30
    obj.z = -20
    obj.rotationX = 0
    obj.rotationY = 0
    obj.rotationZ = 0
    light.intensity = 15
    light.range = 100
    // enable light shadow
    light.castShadow = true
    light.debug()
    light.debugDraw(true)
}

// create a box as shadow source
{
    let castShadowObj = new Object3D()
    castShadowObj.y = 5
    let mr = castShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(10, 10, 10)
    mr.material = new LitMaterial()
    mr.material.baseColor = new Color(1, 0, 0)
    mr.castShadow = true
    scene3D.addChild(castShadowObj)
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D()
    let mr = receiveShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(2000, 1, 2000)
    mr.material = new LitMaterial()
    mr.receiveShadow = true
    scene3D.addChild(receiveShadowObj)
}
// create a view with target scene and camera
let view = new View3D()
view.scene = scene3D
view.camera = camera
// start render
Engine3D.startRenderView(view)

聚光灯阴影



WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry, Color } from '@orillusion/core'

// shadow setting
Engine3D.setting.shadow.autoUpdate = true
Engine3D.setting.shadow.shadowBound = 100
Engine3D.setting.shadow.pointShadowBias = 0.0001
Engine3D.setting.shadow.type = 'HARD'

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
})
let scene3D: Scene3D = new Scene3D()
let cameraObj: Object3D = new Object3D()
let camera = cameraObj.addComponent(Camera3D)
camera.perspective(60, Engine3D.aspect, 1, 5000.0)
let controller = cameraObj.addComponent(HoverCameraController)
controller.setCamera(0, -45, 150, new Vector3(0, 0, 0))
scene3D.addChild(cameraObj)

//SpotLight
{
    let obj = new Object3D()
    let light = obj.addComponent(SpotLight)
    scene3D.addChild(obj)
    obj.x = -30
    obj.y = 15
    obj.z = 40
    obj.rotationX = 0
    obj.rotationY = 145
    obj.rotationZ = 0
    light.intensity = 50
    light.range = 150
    light.outerAngle = 110
    light.innerAngle = 30
    // enable light shadow
    light.castShadow = true
}

// create a box as shadow source
{
    let castShadowObj = new Object3D()
    castShadowObj.y = 5
    let mr = castShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(10, 10, 10)
    mr.material = new LitMaterial()
    mr.material.baseColor = new Color(1, 0, 0)
    mr.castShadow = true
    scene3D.addChild(castShadowObj)
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D()
    let mr = receiveShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(2000, 1, 2000)
    mr.material = new LitMaterial()
    mr.receiveShadow = true
    scene3D.addChild(receiveShadowObj)
}
// create a view with target scene and camera
let view = new View3D()
view.scene = scene3D
view.camera = camera
// start render
Engine3D.startRenderView(view)
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry, Color } from '@orillusion/core'

// shadow setting
Engine3D.setting.shadow.autoUpdate = true
Engine3D.setting.shadow.shadowBound = 100
Engine3D.setting.shadow.pointShadowBias = 0.0001
Engine3D.setting.shadow.type = 'HARD'

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
})
let scene3D: Scene3D = new Scene3D()
let cameraObj: Object3D = new Object3D()
let camera = cameraObj.addComponent(Camera3D)
camera.perspective(60, Engine3D.aspect, 1, 5000.0)
let controller = cameraObj.addComponent(HoverCameraController)
controller.setCamera(0, -45, 150, new Vector3(0, 0, 0))
scene3D.addChild(cameraObj)

//SpotLight
{
    let obj = new Object3D()
    let light = obj.addComponent(SpotLight)
    scene3D.addChild(obj)
    obj.x = -30
    obj.y = 15
    obj.z = 40
    obj.rotationX = 0
    obj.rotationY = 145
    obj.rotationZ = 0
    light.intensity = 50
    light.range = 150
    light.outerAngle = 110
    light.innerAngle = 30
    // enable light shadow
    light.castShadow = true
}

// create a box as shadow source
{
    let castShadowObj = new Object3D()
    castShadowObj.y = 5
    let mr = castShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(10, 10, 10)
    mr.material = new LitMaterial()
    mr.material.baseColor = new Color(1, 0, 0)
    mr.castShadow = true
    scene3D.addChild(castShadowObj)
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D()
    let mr = receiveShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(2000, 1, 2000)
    mr.material = new LitMaterial()
    mr.receiveShadow = true
    scene3D.addChild(receiveShadowObj)
}
// create a view with target scene and camera
let view = new View3D()
view.scene = scene3D
view.camera = camera
// start render
Engine3D.startRenderView(view)

阴影类型

通过设置阴影类型,可以控制阴影的表现效果。目前支持的阴影类型有:

  • HARD:硬阴影,是一种比较锐利的阴影,通常用于阴影距离物体比较近的情况。
  • SOFT:软阴影,是一种比较柔和、模糊的阴影,阴影边缘做了模糊处理,通常用于阴影离物体比较远的情况。
  • PCF:PCF(Percentage-Closer Filtering) 是一种常见的软阴影处理算法,通过对当前像素和周围的阴影深度进行采样并按距离进行加权平均,从而得到一种人工伪造的柔和阴影效果。目前引擎默认使用这个类型的阴影。

配置阴影类型:

ts
Engine3D.setting.shadow.type = 'HARD'; // 默认 PCF
Engine3D.setting.shadow.type = 'HARD'; // 默认 PCF


WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Color } from '@orillusion/core'
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry } from '@orillusion/core'
import * as dat from 'dat.gui'

// shadow setting
Engine3D.setting.shadow.autoUpdate = true
Engine3D.setting.shadow.shadowBound = 100
Engine3D.setting.shadow.pointShadowBias = 0.0001
Engine3D.setting.shadow.type = sessionStorage._shadow_type || 'HARD'

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
})
let scene3D: Scene3D = new Scene3D()
let cameraObj: Object3D = new Object3D()
let camera = cameraObj.addComponent(Camera3D)
camera.perspective(60, Engine3D.aspect, 1, 5000.0)
let controller = cameraObj.addComponent(HoverCameraController)
controller.setCamera(0, -45, 50, new Vector3(0, 0, 0))
scene3D.addChild(cameraObj)

//PointLight
{
    let obj = new Object3D()
    let light = obj.addComponent(PointLight)
    scene3D.addChild(obj)
    obj.x = -15
    obj.y = 30
    obj.z = -20
    obj.rotationX = 0
    obj.rotationY = 0
    obj.rotationZ = 0
    light.intensity = 30
    light.radius = 1
    light.range = 100
    light.castShadow = true
}

// create a box as shadow source
{
    let castShadowObj = new Object3D()
    castShadowObj.y = 5
    let mr = castShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(10, 10, 10)
    mr.material = new LitMaterial()
    mr.material.baseColor = new Color(1,0,0)
    mr.castShadow = true
    scene3D.addChild(castShadowObj)
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D()
    let mr = receiveShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(2000, 1, 2000)
    mr.material = new LitMaterial()
    mr.receiveShadow = true
    scene3D.addChild(receiveShadowObj)
}
// create a view with target scene and camera
let view = new View3D()
view.scene = scene3D
view.camera = camera
// start render
Engine3D.startRenderView(view)

let gui = new dat.GUI()
gui.add(Engine3D.setting.shadow, 'type', ['PCF', 'SOFT', 'HARD']).onChange((v)=>{
    sessionStorage._shadow_type = v
    location.reload()
})
import { Color } from '@orillusion/core'
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry } from '@orillusion/core'
import * as dat from 'dat.gui'

// shadow setting
Engine3D.setting.shadow.autoUpdate = true
Engine3D.setting.shadow.shadowBound = 100
Engine3D.setting.shadow.pointShadowBias = 0.0001
Engine3D.setting.shadow.type = sessionStorage._shadow_type || 'HARD'

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
})
let scene3D: Scene3D = new Scene3D()
let cameraObj: Object3D = new Object3D()
let camera = cameraObj.addComponent(Camera3D)
camera.perspective(60, Engine3D.aspect, 1, 5000.0)
let controller = cameraObj.addComponent(HoverCameraController)
controller.setCamera(0, -45, 50, new Vector3(0, 0, 0))
scene3D.addChild(cameraObj)

//PointLight
{
    let obj = new Object3D()
    let light = obj.addComponent(PointLight)
    scene3D.addChild(obj)
    obj.x = -15
    obj.y = 30
    obj.z = -20
    obj.rotationX = 0
    obj.rotationY = 0
    obj.rotationZ = 0
    light.intensity = 30
    light.radius = 1
    light.range = 100
    light.castShadow = true
}

// create a box as shadow source
{
    let castShadowObj = new Object3D()
    castShadowObj.y = 5
    let mr = castShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(10, 10, 10)
    mr.material = new LitMaterial()
    mr.material.baseColor = new Color(1,0,0)
    mr.castShadow = true
    scene3D.addChild(castShadowObj)
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D()
    let mr = receiveShadowObj.addComponent(MeshRenderer)
    mr.geometry = new BoxGeometry(2000, 1, 2000)
    mr.material = new LitMaterial()
    mr.receiveShadow = true
    scene3D.addChild(receiveShadowObj)
}
// create a view with target scene and camera
let view = new View3D()
view.scene = scene3D
view.camera = camera
// start render
Engine3D.startRenderView(view)

let gui = new dat.GUI()
gui.add(Engine3D.setting.shadow, 'type', ['PCF', 'SOFT', 'HARD']).onChange((v)=>{
    sessionStorage._shadow_type = v
    location.reload()
})

阴影大小

TODO

联级阴影贴图(Cascaded Shadow Maps)

TODO

阴影属性

属性类型说明
enableBoolean是否启用阴影,默认 false
typeString阴影类型,默认 PCF
shadowQualityNumber阴影渲染品质
shadowBoundNumber阴影区域范围
shadowSizeNumber平行光阴影贴图尺寸大小,默认1024,数值越小性能开销越低,但阴影锯齿感越明显
pointShadowSizeNumber点光源阴影贴图大小尺寸,默认1024
pointShadowBiasNumber点光源和聚光灯的阴影偏移值
autoUpdateBoolean是否自动更新阴影,默认 false
csmMarginNumber?
csmScatteringExpNumber?
csmAreaScaleNumber?