Skip to content

光照

光照使场景更有层次感,使用光照,能建立更真实的三维场景。Orillusion 引擎的光照系统主要由以下几部分组成:

光源说明
灯光组件基础光源组件:平行光,点光源 和 聚光灯
环境反射天空盒环境光,全局曝光
全局光照场景中反射或折射的间接光源

灯光组件

引擎目前内置了3种经典的光源类型:

平行光

平行光 表示的是光线从以某个方向均匀射出,光线之间是平行的,太阳照射在地球表面的光可以认为是平行光,因为太阳和地球距离的远大于地球半径,所以照射在地球的阳光可以看作是来自同一个方向的光,即平行光。平行光4 个主要个特性:

属性类型说明
lightColorColor灯光的颜色, 默认是白色 rgb(1.0,1.0,1.0)
intensityNumber光照强度,默认值为 1
directionVector3只读属性,获取平行光的方向向量
castShadowBoolean是否开启投影, 默认 false 不开启

一般使用平行光所在的 Object3Drotation 控制灯光方向

ts
let lightObj = new Object3D();
scene.addChild(lightObj);

//添加平行光
let dl = lightObj.addComponent(DirectLight);
//设置颜色
dl.lightColor = new Color(1.0, 0.95, 0.84, 1.0);
//设置强度
dl.intensity = 20;

// 通过 Object3D 设置光源的方向
lightObj.rotateX = 45;
lightObj.rotateY = 45;
// 可以通过 direction 获取方向向量
let target = dl.direction

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

<
ts
import { BoxGeometry, Camera3D, Engine3D, AtmosphericComponent, LitMaterial, HoverCameraController, MeshRenderer, Object3D, Scene3D, DirectLight, Vector3, webGPUContext, View3D } from '@orillusion/core';
import * as dat from 'dat.gui';

class Sample_Light {
    scene: Scene3D;
    hoverCameraController: HoverCameraController;
    lightObj: any;
    constructor() {}

    async run() {
        await Engine3D.init({
            canvasConfig: { devicePixelRatio: 1 }
        });
        this.scene = new Scene3D();
        let cameraObj = new Object3D();
        let mainCamera = cameraObj.addComponent(Camera3D);
        mainCamera.perspective(37, webGPUContext.aspect, 1, 10000.0);

        this.hoverCameraController = mainCamera.object3D.addComponent(HoverCameraController);
        this.scene.addChild(cameraObj);

        //set camera data
        this.hoverCameraController.setCamera(0, -45, 2000);
        this.initScene(this.scene);

        // add an Atmospheric sky enviroment
        this.scene.addComponent(AtmosphericComponent).sunY = 0.6;
        // create a view with target scene and camera
        let view = new View3D();
        view.scene = this.scene;
        view.camera = mainCamera;
        // start render
        Engine3D.startRenderView(view);
    }

    initScene(scene: Scene3D) {
        {
            let dirLight = new Object3D();
            dirLight.rotationX = 30;
            dirLight.rotationZ = 30;
            let light = dirLight.addComponent(DirectLight);
            light.intensity = 20;
            light.lightColor.r = 255 / 255;
            light.lightColor.g = 157 / 255;
            light.lightColor.b = 5 / 255;
            scene.addChild(dirLight);

            let GUIHelp = new dat.GUI();
            GUIHelp.addFolder('Direct Light');
            GUIHelp.add(dirLight, 'rotationX', -180, 180, 1);
            GUIHelp.add(dirLight, 'rotationY', -180, 180, 1);
            GUIHelp.add(dirLight, 'rotationZ', -180, 180, 1);
            GUIHelp.addColor({ color: Object.values(light.lightColor).map((v) => v * 255) }, 'color').onChange((v) => {
                light.lightColor.copyFromArray(v);
            });
            GUIHelp.add(light, 'intensity', 0, 100, 1);
        }

        let mat = new LitMaterial();
        mat.baseMap = Engine3D.res.grayTexture;

        let floor = new Object3D();
        let mr = floor.addComponent(MeshRenderer);
        mr.geometry = new BoxGeometry(2000, 1, 2000);
        mr.material = mat;
        this.scene.addChild(floor);

        let box = new BoxGeometry(1, 1, 1);

        let wall_w = new Object3D();
        wall_w.localScale = new Vector3(500, 100, 10);
        wall_w.localPosition = new Vector3(0, 50, 0);
        let mrw = wall_w.addComponent(MeshRenderer);
        mrw.geometry = box;
        mrw.material = mat;
        this.scene.addChild(wall_w);

        let wall_a = new Object3D();
        wall_a.localScale = new Vector3(10, 100, 500);
        wall_a.localPosition = new Vector3(250, 50, 0);
        let mra = wall_a.addComponent(MeshRenderer);
        mra.geometry = box;
        mra.material = mat;
        this.scene.addChild(wall_a);

        let wall_d = new Object3D();
        wall_d.localScale = new Vector3(10, 100, 500);
        wall_d.localPosition = new Vector3(-250, 50, 0);
        let mrd = wall_d.addComponent(MeshRenderer);
        mrd.geometry = box;
        mrd.material = mat;
        this.scene.addChild(wall_d);
    }
}

new Sample_Light().run();

点光源

点光源 是存在于空间中的一个点,由该点向四面八方发射光线,超过有效距离的地方将无法接受到点光源的光线,并且离光源越远光照强度也会逐渐降低。通常用来模拟生活中常见的灯泡。点光源有主要以下属性:

属性类型说明
lightColorColor灯光的颜色, 默认是白色 rgb(1.0,1.0,1.0)
intensityNumber光照强度,默认值为 1
rangeNumber光照最远距离
ts
let pointLightObj = new Object3D();
// 设置光源 Object3D 的位置
pointLightObj.x = -10;
pointLightObj.y = 10;
pointLightObj.z = 10;
scene.addChild(pointLightObj);

// 设置点光源组件的半径,强度和颜色
let pointLight = pointLightObj.addComponent(PointLight);
pointLight.range = 20;
pointLight.intensity = 10;
pointLight.lightColor = new Color(1.0, 0.95, 0.84, 1.0);

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

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

class Sample_Light {
    scene: Scene3D;
    hoverCameraController: HoverCameraController;
    lightObj: any;
    constructor() {}

    async run() {
        await Engine3D.init({
            canvasConfig: { devicePixelRatio: 1 }
        });

        this.scene = new Scene3D();
        let cameraObj = new Object3D();
        let mainCamera = cameraObj.addComponent(Camera3D);
        mainCamera.perspective(37, webGPUContext.aspect, 1, 5000.0);

        this.hoverCameraController = mainCamera.object3D.addComponent(HoverCameraController);
        this.scene.addChild(cameraObj);

        //set camera data
        this.hoverCameraController.setCamera(0, -45, 1200);
        await this.initScene(this.scene);

        // add an Atmospheric sky enviroment
        this.scene.addComponent(AtmosphericComponent).sunY = 0.6;
        // create a view with target scene and camera
        let view = new View3D();
        view.scene = this.scene;
        view.camera = mainCamera;
        // start render
        Engine3D.startRenderView(view);
    }

    initScene(scene: Scene3D) {
        {
            let sp = new SphereGeometry(5, 30, 30);
            let pointLight = new Object3D();
            pointLight.y = 200;
            let mr = pointLight.addComponent(MeshRenderer);
            mr.geometry = sp;
            mr.material = new LitMaterial();

            let light = pointLight.addComponent(PointLight);
            light.intensity = 100;
            light.range = 300;
            light.lightColor.r = 255 / 255;
            light.lightColor.g = 157 / 255;
            light.lightColor.b = 5 / 255;
            scene.addChild(pointLight);

            let GUIHelp = new dat.GUI();
            GUIHelp.addFolder('Direct Light');
            GUIHelp.add(pointLight, 'x', -180, 180, 1);
            GUIHelp.add(pointLight, 'y', -180, 180, 1);
            GUIHelp.add(pointLight, 'z', -180, 180, 1);
            GUIHelp.addColor({ color: Object.values(light.lightColor).map((v) => v * 255) }, 'color').onChange((v) => {
                light.lightColor.copyFromArray(v);
            });
            GUIHelp.add(light, 'intensity', 0, 100, 1);
            GUIHelp.add(light, 'range', 100, 500, 1);
        }

        let mat = new LitMaterial();
        mat.baseMap = Engine3D.res.grayTexture;

        let floor = new Object3D();
        let mr = floor.addComponent(MeshRenderer);
        mr.geometry = new BoxGeometry(2000, 1, 2000);
        mr.material = mat;
        this.scene.addChild(floor);

        let box = new BoxGeometry(1, 1, 1);
        let wall_w = new Object3D();
        wall_w.localScale = new Vector3(500, 100, 10);
        wall_w.localPosition = new Vector3(0, 50, 0);
        let mrw = wall_w.addComponent(MeshRenderer);
        mrw.geometry = box;
        mrw.material = mat;
        this.scene.addChild(wall_w);

        let wall_a = new Object3D();
        wall_a.localScale = new Vector3(10, 100, 500);
        wall_a.localPosition = new Vector3(250, 50, 0);
        let mra = wall_a.addComponent(MeshRenderer);
        mra.geometry = box;
        mra.material = mat;
        this.scene.addChild(wall_a);

        let wall_d = new Object3D();
        wall_d.localScale = new Vector3(10, 100, 500);
        wall_d.localPosition = new Vector3(-250, 50, 0);
        let mrd = wall_d.addComponent(MeshRenderer);
        mrd.geometry = box;
        mrd.material = mat;
        this.scene.addChild(wall_d);
    }
}

new Sample_Light().run();

聚光灯

聚光灯点光源 类似,但是它的光线不是朝四面八方发射,而是朝某个方向范围,就像现实生活中的手电筒发出的光。聚光灯有几个主要特性:

属性类型说明
lightColorColor灯光的颜色, 默认是白色 rgb(1.0,1.0,1.0)
intensityNumber光照强度,默认值为 1
directionVector3只读属性,获取聚光的方向向量
rangeNumber光照最远距离
innerAngleNumber光锥内切角,聚光在小于这个角度的范围内有光线
outerAngleNumber光锥外切角,光线会在内切角到外切角的范围内逐步衰减到0
ts
let spotLightObj = new Object3D();
// 设置光源 Object3D 的位置
spotLightObj.y = 100;
spotLightObj.rotationX= 90;
scene.addChild(spotLightObj);

// 设置聚光灯组件的属性
let spotLight = spotLightObj.addComponent(SpotLight);
spotLight.lightColor = new Color(1.0, 0.95, 0.84, 1.0);
spotLight.intensity = 20;
spotLight.range = 200;
spotLight.innerAngle = 20;
spotLight.outerAngle = 35;

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

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

class Sample_Light {
    scene: Scene3D;
    hoverCameraController: HoverCameraController;
    lightObj: any;
    constructor() {}

    async run() {
        await Engine3D.init({
            canvasConfig: { devicePixelRatio: 1 }
        });

        this.scene = new Scene3D();
        let cameraObj = new Object3D();
        let mainCamera = cameraObj.addComponent(Camera3D);
        mainCamera.perspective(37, webGPUContext.aspect, 1, 5000.0);

        this.hoverCameraController = mainCamera.object3D.addComponent(HoverCameraController);
        this.scene.addChild(cameraObj);

        //set camera data
        this.hoverCameraController.setCamera(0, -45, 1000);
        this.initScene(this.scene);

        // add an Atmospheric sky enviroment
        this.scene.addComponent(AtmosphericComponent).sunY = 0.6;
        // create a view with target scene and camera
        let view = new View3D();
        view.scene = this.scene;
        view.camera = mainCamera;
        // start render
        Engine3D.startRenderView(view);
    }

    initScene(scene: Scene3D) {
        {
            let sp = new SphereGeometry(5, 30, 30);
            let spotLight = new Object3D();
            let mr = spotLight.addComponent(MeshRenderer);
            mr.geometry = sp;
            mr.material = new LitMaterial();

            let light = spotLight.addComponent(SpotLight);
            spotLight.y = 200;
            spotLight.z = 50;
            spotLight.rotationX = 120;
            light.lightColor.r = 255 / 255;
            light.lightColor.g = 157 / 255;
            light.lightColor.b = 5 / 255;
            light.intensity = 100;
            light.range = 500;
            light.outerAngle = 110;
            light.innerAngle = 30;
            scene.addChild(spotLight);

            let GUIHelp = new dat.GUI();
            GUIHelp.addFolder('Direct Light');
            GUIHelp.add(spotLight, 'x', -180, 180, 1);
            GUIHelp.add(spotLight, 'y', -180, 180, 1);
            GUIHelp.add(spotLight, 'z', -180, 180, 1);
            GUIHelp.add(spotLight, 'rotationX', -180, 180, 1);
            GUIHelp.add(spotLight, 'rotationY', -180, 180, 1);
            GUIHelp.add(spotLight, 'rotationZ', -180, 180, 1);
            GUIHelp.addColor({ color: Object.values(light.lightColor).map((v) => v * 255) }, 'color').onChange((v) => {
                light.lightColor.copyFromArray(v);
            });
            GUIHelp.add(light, 'intensity', 0, 100, 1);
            GUIHelp.add(light, 'range', 100, 500, 1);
            GUIHelp.add(light, 'outerAngle', 0, 180, 1);
            GUIHelp.add(light, 'innerAngle', 0, 100, 1);
        }

        let mat = new LitMaterial();
        mat.baseMap = Engine3D.res.grayTexture;

        let floor = new Object3D();
        let mr = floor.addComponent(MeshRenderer);
        mr.geometry = new BoxGeometry(2000, 1, 2000);
        mr.material = mat;
        this.scene.addChild(floor);

        let box = new BoxGeometry(1, 1, 1);
        let wall_w = new Object3D();
        wall_w.localScale = new Vector3(500, 100, 10);
        wall_w.localPosition = new Vector3(0, 50, 0);
        let mrw = wall_w.addComponent(MeshRenderer);
        mrw.geometry = box;
        mrw.material = mat;
        this.scene.addChild(wall_w);

        let wall_a = new Object3D();
        wall_a.localScale = new Vector3(10, 100, 500);
        wall_a.localPosition = new Vector3(250, 50, 0);
        let mra = wall_a.addComponent(MeshRenderer);
        mra.geometry = box;
        mra.material = mat;
        this.scene.addChild(wall_a);

        let wall_d = new Object3D();
        wall_d.localScale = new Vector3(10, 100, 500);
        wall_d.localPosition = new Vector3(-250, 50, 0);
        let mrd = wall_d.addComponent(MeshRenderer);
        mrd.geometry = box;
        mrd.material = mat;
        this.scene.addChild(wall_d);
    }
}

new Sample_Light().run();

IES 灯光信息

照明工程学会(IES)定义了一种文件格式,可以描述真实灯光在现实世界的光照强度分布情况。IES 文件描述了各种类型的灯具的光线强弱度,衰减曲线,模拟灯珠的透射,折射等光线变化行为,最终解码成指定的2D数据图进行3D空间的灯光映射。

IES 灯光示例

ies_0ies_1ies_2

加载 IES 贴图

除了常规的光源类型设置,引擎还支持通过加载预设的 IES 贴图来设置复杂的光线分布:

ts
// 加载 IES 贴图
let iesTexture = await Engine3D.res.loadTexture("https://cdn.orillusion.com/ies/ies_2.png");
// 创建 IES 对象
let iesPofiles = new IESProfiles();
iesPofiles.IESTexture = iesTexture;
let light = new Object3d()
let pointLight = light.addComponent(PointLight);
// 设置灯光 IES 分布
pointLight.iesPofile = iesPofiles;

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

<
ts
import { BoxGeometry, Camera3D, Engine3D, View3D, LitMaterial, HoverCameraController, BitmapTexture2D, MeshRenderer, Object3D, Scene3D, SphereGeometry, PointLight, Vector3, webGPUContext, IESProfiles, AtmosphericComponent } from '@orillusion/core';

class Sample_LightIES {
    scene: Scene3D;
    hoverCameraController: HoverCameraController;
    lightObj: any;
    constructor() {}

    async run() {
        Engine3D.setting.shadow.pointShadowBias = 0.0001;
        Engine3D.setting.shadow.type = `HARD`;

        await Engine3D.init({
            canvasConfig: { devicePixelRatio: 1 }
        });

        this.scene = new Scene3D();
        let cameraObj = new Object3D();
        let mainCamera = cameraObj.addComponent(Camera3D);
        mainCamera.perspective(37, webGPUContext.aspect, 1, 5000.0);

        this.hoverCameraController = mainCamera.object3D.addComponent(HoverCameraController);
        this.scene.addChild(cameraObj);

        //set camera data
        this.hoverCameraController.setCamera(0, -45, 200);
        await this.initScene(this.scene);

        // add an Atmospheric sky enviroment
        this.scene.addComponent(AtmosphericComponent).sunY = 0.6;
        // create a view with target scene and camera
        let view = new View3D();
        view.scene = this.scene;
        view.camera = mainCamera;
        // start render
        Engine3D.startRenderView(view);
    }

    async initScene(scene: Scene3D) {
        // load ies texture
        let iesTexture = await Engine3D.res.loadTexture('https://cdn.orillusion.com/ies/ies_2.png');
        var iesPofiles = new IESProfiles();
        iesPofiles.IESTexture = iesTexture;

        {
            let po = new Object3D();
            let pl = po.addComponent(PointLight);
            pl.intensity = 10;
            pl.range = 100;
            pl.castShadow = true;
            pl.realTimeShadow = true;
            pl.iesProfiles = iesPofiles;
            po.x = 0;
            po.y = 22;
            po.z = 15;
            this.scene.addChild(po);
        }

        let ball: Object3D;
        {
            let mat = new LitMaterial();
            mat.roughness = 0.5;
            mat.metallic = 0.2;

            ball = new Object3D();
            let mr = ball.addComponent(MeshRenderer);
            mr.geometry = new SphereGeometry(6, 20, 20);
            mr.material = mat;
            this.scene.addChild(ball);
            ball.transform.x = -17;
            ball.transform.y = 10;
            ball.transform.z = 10;

            //wall
            let back_wall = new Object3D();
            let mr2 = back_wall.addComponent(MeshRenderer);
            mr2.geometry = new BoxGeometry(500, 500, 10);
            mr2.material = mat;
            this.scene.addChild(back_wall);
        }
    }
}

new Sample_LightIES().run();

获取 IES 贴图

社区中有大量优质的 IES 资源分享社区,一些灯光设备厂商也会分享专业的 IES 文件,它们一般都是免费的,比如:

社区中同样有很多专业的 IES 预览/转换的软件,比如 IESviewer,您也可以使用专业的3D建模软件来将 IES 文件转换到普通的 png 贴图文件,最后加载到引擎中来。

环境光

除了直接的光源,引擎通过设置 Scene3D.evnMap 天空盒贴图进行基本的环境光渲染,详情参考 天空盒 相关介绍

全局光照

一般光照系统只考虑光源直接照射到物体表面所产生的效果,不会计算光源经过物体表面反射或折射的光线,即间接光照。全局光照系统能够对间接光照进行建模,实现更加逼真的光线效果。详情参考 高级 GI