Skip to content

反走样 - TAAPost

一种3D渲染 抗锯齿 实现方案。3D渲染栅格化过程将显示对象按二位数组点阵的形式存储起来,得到的原始图像中物体边缘难免会有锯齿样。TAA 采用的方法为按照一定策略轻微的给相机设置一些偏移值,让物体在栅格化时会因不同的相机偏移值得到略微不同的结果。特别是在边缘的地方更为明显。最终输出到屏幕的颜色采用插值历史帧和当前帧的作为结果,且该结果用于下一次的插值。

ts
// 引擎全局配置设置
Engine3D.setting.render.postProcessing.taa.jitterSeedCount = 8;
Engine3D.setting.render.postProcessing.taa.blendFactor = 0.1;
Engine3D.setting.render.postProcessing.taa.sharpFactor = 0.6;
Engine3D.setting.render.postProcessing.taa.sharpPreBlurFactor = 0.5;
Engine3D.setting.render.postProcessing.taa.temporalJitterScale = 0.6;

//初始化引擎
await Engine3D.init();

// 添加后处理组件
let postProcessing = this.scene.addComponent(PostProcessingComponent);

// 添加TAAPost
let taaPost = postProcessing.addPost(TAAPost);

// 通过 taaPost 对象设置(引擎全局配置和此处基于 taaPost 对象设置,结果是等价的)
taaPost.jitterSeedCount = 8;
taaPost.blendFactor = 0.1;
taaPost.sharpFactor = 0.6;
taaPost.sharpPreBlurFactor = 0.5;
taaPost.temporalJitterScale = 0.6;

// 开始渲染视图
let view = new View3D();
view.scene = this.scene;
view.camera = mainCamera;
Engine3D.startRenderView(view);
// 引擎全局配置设置
Engine3D.setting.render.postProcessing.taa.jitterSeedCount = 8;
Engine3D.setting.render.postProcessing.taa.blendFactor = 0.1;
Engine3D.setting.render.postProcessing.taa.sharpFactor = 0.6;
Engine3D.setting.render.postProcessing.taa.sharpPreBlurFactor = 0.5;
Engine3D.setting.render.postProcessing.taa.temporalJitterScale = 0.6;

//初始化引擎
await Engine3D.init();

// 添加后处理组件
let postProcessing = this.scene.addComponent(PostProcessingComponent);

// 添加TAAPost
let taaPost = postProcessing.addPost(TAAPost);

// 通过 taaPost 对象设置(引擎全局配置和此处基于 taaPost 对象设置,结果是等价的)
taaPost.jitterSeedCount = 8;
taaPost.blendFactor = 0.1;
taaPost.sharpFactor = 0.6;
taaPost.sharpPreBlurFactor = 0.5;
taaPost.temporalJitterScale = 0.6;

// 开始渲染视图
let view = new View3D();
view.scene = this.scene;
view.camera = mainCamera;
Engine3D.startRenderView(view);

Engine3D.setting.render.postProcessing.taa 配置参数。

参数类型描述
jitterSeedCountnumber抖动相机随机种子采用个数,默认8个。(降低个数可以解决一些抖动太明显的问题,但是锯齿会变得更明显)
blendFactornumber合并历史帧与当前帧的系数,参数越小,当前帧占比越小。
sharpFactornumber图像锐化系数[0.1,1.9]:系数越小锐化效果越弱抗锯齿效果好,反之锐化越强抗锯齿效果越弱。
sharpPreBlurFactornumber消图像锐化采样系数缩放系数:锐化时候采样的偏移量缩放。
temporalJitterScalenumber抖动相机随机偏移值的缩放系数[0,1]:系数越小抗锯齿效果变弱,像素抖动也会变弱。


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

<
ts
import { View3D, DirectLight, Engine3D, PostProcessingComponent, LitMaterial, HoverCameraController, KelvinUtil, MeshRenderer, Object3D, PlaneGeometry, Scene3D, SphereGeometry, CameraUtil, webGPUContext, BoxGeometry, TAAPost, AtmosphericComponent } from '@orillusion/core'

export class Sample_TAA {
    lightObj: Object3D
    scene: Scene3D

    async run() {
        Engine3D.setting.shadow.enable = true
        Engine3D.setting.shadow.shadowBound = 100

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

        this.scene = new Scene3D()
        this.scene.addComponent(AtmosphericComponent).sunY = 0.6

        let mainCamera = CameraUtil.createCamera3DObject(this.scene, 'camera')
        mainCamera.perspective(60, webGPUContext.aspect, 1, 5000.0)
        let ctrl = mainCamera.object3D.addComponent(HoverCameraController)
        ctrl.setCamera(0, -15, 30)
        await this.initScene()

        let view = new View3D()
        view.scene = this.scene
        view.camera = mainCamera
        Engine3D.startRenderView(view)

        let postProcessing = this.scene.addComponent(PostProcessingComponent)
        postProcessing.addPost(TAAPost)
    }

    async initScene() {
        {
            this.lightObj = new Object3D()
            this.lightObj.rotationX = 15
            this.lightObj.rotationY = 110
            this.lightObj.rotationZ = 0
            let lc = this.lightObj.addComponent(DirectLight)
            lc.lightColor = KelvinUtil.color_temperature_to_rgb(5355)
            lc.castShadow = true
            lc.intensity = 10
            this.scene.addChild(this.lightObj)
        }

        {
            let mat = new LitMaterial()
            mat.baseMap = Engine3D.res.grayTexture
            mat.normalMap = Engine3D.res.normalTexture
            mat.aoMap = Engine3D.res.whiteTexture
            mat.maskMap = Engine3D.res.createTexture(32, 32, 255.0, 255.0, 0.0, 1)
            mat.emissiveMap = Engine3D.res.blackTexture
            mat.roughness = 1.0
            mat.metallic = 0.0

            let floor = new Object3D()
            let mr = floor.addComponent(MeshRenderer)
            mr.geometry = new PlaneGeometry(2000, 2000)
            mr.material = mat
            this.scene.addChild(floor)
        }

        this.createPlane(this.scene)
    }

    private createPlane(scene: Scene3D) {
        let mat = new LitMaterial()
        mat.baseMap = Engine3D.res.whiteTexture
        mat.normalMap = Engine3D.res.normalTexture
        mat.aoMap = Engine3D.res.whiteTexture
        mat.maskMap = Engine3D.res.createTexture(32, 32, 255.0, 10.0, 0.0, 1)
        mat.emissiveMap = Engine3D.res.blackTexture
        mat.roughness = 0.5
        mat.roughness_max = 0.1
        mat.metallic = 0.2
        {
            let sphereGeometry = new SphereGeometry(1, 50, 50)
            let obj: Object3D = new Object3D()
            let mr = obj.addComponent(MeshRenderer)
            mr.material = mat
            mr.geometry = sphereGeometry
            obj.x = 10
            obj.y = 2
            scene.addChild(obj)
        }

        const length = 5
        for (let i = 0; i < length; i++) {
            let cubeGeometry = new BoxGeometry(1, 10, 1)
            for (let j = 0; j < length; j++) {
                let obj: Object3D = new Object3D()
                let mr = obj.addComponent(MeshRenderer)
                mr.material = mat
                mr.geometry = cubeGeometry
                obj.localScale = obj.localScale
                obj.x = (i - 2.5) * 4
                obj.z = (j - 2.5) * 4
                obj.y = 5
                obj.rotationX = (Math.random() - 0.5) * 90
                obj.rotationY = (Math.random() - 0.5) * 90
                obj.rotationZ = (Math.random() - 0.5) * 90
                scene.addChild(obj)
            }
        }
    }
}

new Sample_TAA().run()
import { View3D, DirectLight, Engine3D, PostProcessingComponent, LitMaterial, HoverCameraController, KelvinUtil, MeshRenderer, Object3D, PlaneGeometry, Scene3D, SphereGeometry, CameraUtil, webGPUContext, BoxGeometry, TAAPost, AtmosphericComponent } from '@orillusion/core'

export class Sample_TAA {
    lightObj: Object3D
    scene: Scene3D

    async run() {
        Engine3D.setting.shadow.enable = true
        Engine3D.setting.shadow.shadowBound = 100

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

        this.scene = new Scene3D()
        this.scene.addComponent(AtmosphericComponent).sunY = 0.6

        let mainCamera = CameraUtil.createCamera3DObject(this.scene, 'camera')
        mainCamera.perspective(60, webGPUContext.aspect, 1, 5000.0)
        let ctrl = mainCamera.object3D.addComponent(HoverCameraController)
        ctrl.setCamera(0, -15, 30)
        await this.initScene()

        let view = new View3D()
        view.scene = this.scene
        view.camera = mainCamera
        Engine3D.startRenderView(view)

        let postProcessing = this.scene.addComponent(PostProcessingComponent)
        postProcessing.addPost(TAAPost)
    }

    async initScene() {
        {
            this.lightObj = new Object3D()
            this.lightObj.rotationX = 15
            this.lightObj.rotationY = 110
            this.lightObj.rotationZ = 0
            let lc = this.lightObj.addComponent(DirectLight)
            lc.lightColor = KelvinUtil.color_temperature_to_rgb(5355)
            lc.castShadow = true
            lc.intensity = 10
            this.scene.addChild(this.lightObj)
        }

        {
            let mat = new LitMaterial()
            mat.baseMap = Engine3D.res.grayTexture
            mat.normalMap = Engine3D.res.normalTexture
            mat.aoMap = Engine3D.res.whiteTexture
            mat.maskMap = Engine3D.res.createTexture(32, 32, 255.0, 255.0, 0.0, 1)
            mat.emissiveMap = Engine3D.res.blackTexture
            mat.roughness = 1.0
            mat.metallic = 0.0

            let floor = new Object3D()
            let mr = floor.addComponent(MeshRenderer)
            mr.geometry = new PlaneGeometry(2000, 2000)
            mr.material = mat
            this.scene.addChild(floor)
        }

        this.createPlane(this.scene)
    }

    private createPlane(scene: Scene3D) {
        let mat = new LitMaterial()
        mat.baseMap = Engine3D.res.whiteTexture
        mat.normalMap = Engine3D.res.normalTexture
        mat.aoMap = Engine3D.res.whiteTexture
        mat.maskMap = Engine3D.res.createTexture(32, 32, 255.0, 10.0, 0.0, 1)
        mat.emissiveMap = Engine3D.res.blackTexture
        mat.roughness = 0.5
        mat.roughness_max = 0.1
        mat.metallic = 0.2
        {
            let sphereGeometry = new SphereGeometry(1, 50, 50)
            let obj: Object3D = new Object3D()
            let mr = obj.addComponent(MeshRenderer)
            mr.material = mat
            mr.geometry = sphereGeometry
            obj.x = 10
            obj.y = 2
            scene.addChild(obj)
        }

        const length = 5
        for (let i = 0; i < length; i++) {
            let cubeGeometry = new BoxGeometry(1, 10, 1)
            for (let j = 0; j < length; j++) {
                let obj: Object3D = new Object3D()
                let mr = obj.addComponent(MeshRenderer)
                mr.material = mat
                mr.geometry = cubeGeometry
                obj.localScale = obj.localScale
                obj.x = (i - 2.5) * 4
                obj.z = (j - 2.5) * 4
                obj.y = 5
                obj.rotationX = (Math.random() - 0.5) * 90
                obj.rotationY = (Math.random() - 0.5) * 90
                obj.rotationZ = (Math.random() - 0.5) * 90
                scene.addChild(obj)
            }
        }
    }
}

new Sample_TAA().run()