基于 RenderTexture 实现多 Pass 的 Kawase Blur

基于 RenderTexture 实现多 Pass Kawase Blur

image

:woozy_face: 实现多 Pass 的操作比较骚~

:rofl: 先把代码 show 了吧,文章嘛…空了在写,一定一定~

:page_facing_up: 示例代码:

const { ccclass, property } = cc._decorator;

/**
 * 实例:Kawase 模糊(多 Pass)
 * @author 陈皮皮 (ifaswind)
 * @version 20211208
 * @see Case_MultipassKawaseBlur.ts https://gitee.com/ifaswind/eazax-cases/blob/master/assets/cases/multipassKawaseBlur/scripts/Case_MultipassKawaseBlur.ts
 * @see eazax-kawase-blur.effect https://gitee.com/ifaswind/eazax-ccc/blob/master/resources/effects/eazax-kawase-blur.effect
 * @see RenderUtil.ts https://gitee.com/ifaswind/eazax-ccc/blob/master/utils/RenderUtil.ts
 */
@ccclass
export default class Case_MultipassKawaseBlur extends cc.Component {

    @property({ type: cc.Sprite, tooltip: CC_DEV && '精灵组件' })
    protected sprite: cc.Sprite = null;

    @property({ type: cc.Material, tooltip: CC_DEV && '渲染用的材质' })
    protected material: cc.Material = null;

    /**
     * 正在使用的 RenderTexture
     */
    protected renderTexture: cc.RenderTexture = null;

    /**
     * 生命周期:开始(首次 update 前)
     */
    protected start() {
        // 目标节点
        const sprite = this.sprite,
            node = this.sprite.node;
        // 设置材质
        const material = this.material;
        material.setProperty('resolution', cc.v2(node.width, node.height));
        // 创建临时 RenderTexture
        const srcRT = new cc.RenderTexture(),
            dstRT = new cc.RenderTexture();
        // 获取初始 RenderTexture
        this.getRenderTexture(node, srcRT);
        // 多 Pass 处理
        // 注:由于 OpenGL 中的纹理是倒置的,所以双数 Pass 的出的图像是颠倒的
        this.renderWithMaterial(srcRT, dstRT, material);
        this.renderWithMaterial(dstRT, srcRT, material);
        this.renderWithMaterial(srcRT, dstRT, material);
        this.renderWithMaterial(dstRT, srcRT, material);
        this.renderWithMaterial(srcRT, dstRT, material);
        // 使用经过处理的 RenderTexture
        this.renderTexture = dstRT;
        sprite.spriteFrame = new cc.SpriteFrame(this.renderTexture);
        // 销毁不用的临时 RenderTexture
        srcRT.destroy();
    }

    /**
     * 生命周期:销毁
     */
    protected onDestroy(): void {
        // 销毁不用的 RenderTexture
        this.renderTexture && this.renderTexture.destroy();
    }

    /**
     * 获取节点的 RenderTexture
     * @param node 节点
     * @param out 输出
     * @see RenderUtil.ts https://gitee.com/ifaswind/eazax-ccc/blob/master/utils/RenderUtil.ts
     */
    protected getRenderTexture(node: cc.Node, out?: cc.RenderTexture) {
        // 检查参数
        if (!cc.isValid(node)) {
            return null;
        }
        if (!out || !(out instanceof cc.RenderTexture)) {
            out = new cc.RenderTexture();
        }
        // 获取宽高
        const width = Math.floor(node.width),
            height = Math.floor(node.height);
        // 初始化 RenderTexture
        out.initWithSize(width, height);
        // 创建临时摄像机用于渲染目标节点
        const cameraNode = new cc.Node();
        cameraNode.parent = node;
        const camera = cameraNode.addComponent(cc.Camera);
        camera.clearFlags |= cc.Camera.ClearFlags.COLOR;
        camera.backgroundColor = cc.color(0, 0, 0, 0);
        camera.zoomRatio = cc.winSize.height / height;
        // 将节点渲染到 RenderTexture 中
        camera.targetTexture = out;
        camera.render(node);
        // 销毁临时对象
        cameraNode.destroy();
        // 返回 RenderTexture
        return out;
    }

    /**
     * 使用指定材质来将 RenderTexture 渲染到另一个 RenderTexture
     * @param srcRT 来源
     * @param dstRT 目标
     * @param material 材质
     * @see RenderUtil.ts https://gitee.com/ifaswind/eazax-ccc/blob/master/utils/RenderUtil.ts
     */
    protected renderWithMaterial(srcRT: cc.RenderTexture, dstRT: cc.RenderTexture | cc.Material, material?: cc.Material) {
        // 检查参数
        if (dstRT instanceof cc.Material) {
            material = dstRT;
            dstRT = new cc.RenderTexture();
        }
        // 创建临时节点(用于渲染 RenderTexture)
        const tempNode = new cc.Node();
        tempNode.setParent(cc.Canvas.instance.node);
        const tempSprite = tempNode.addComponent(cc.Sprite);
        tempSprite.sizeMode = cc.Sprite.SizeMode.RAW;
        tempSprite.trim = false;
        tempSprite.spriteFrame = new cc.SpriteFrame(srcRT);
        // 获取图像宽高
        const width = srcRT.width,
            height = srcRT.height;
        // 初始化 RenderTexture
        dstRT.initWithSize(width, height);
        // 更新材质
        if (material instanceof cc.Material) {
            tempSprite.setMaterial(0, material);
        }
        // 创建临时摄像机(用于渲染临时节点)
        const cameraNode = new cc.Node();
        cameraNode.setParent(tempNode);
        const camera = cameraNode.addComponent(cc.Camera);
        camera.clearFlags |= cc.Camera.ClearFlags.COLOR;
        camera.backgroundColor = cc.color(0, 0, 0, 0);
        camera.zoomRatio = cc.winSize.height / height;
        // 将临时节点渲染到 RenderTexture 中
        camera.targetTexture = dstRT;
        camera.render(tempNode);
        // 销毁临时对象
        cameraNode.destroy();
        tempNode.destroy();
        // 返回 RenderTexture
        return dstRT;
    }

}

对,就酱~

拜拜~

15赞

提前来顶!

着急着下班约会?皮皮你变了

可以的,这很酷!

顶,支持皮皮

忙着干饭呢~

可以的,你也很酷!

感谢大佬!!!

芜湖~~~

无愧于Cocos Star Writer之名

MARK, 就是用2个renderTexture 在2.4X版本中实现多pass的意思吗

皮皮佬!带带弟弟!

大佬大佬,带带弟弟
1000

左右反复横跳,可以实现很多 Pass 的效果

没实力带不动啊:woozy_face:

:sob:我明白了,还是我太菜了,大佬都带不动我

这是点你呢,喊yuwei来

秀啊,有些功能常见又麻烦,收藏一波

皮皮有没有去马赛克的渲染教程,我有个朋友想学习学习

给皮皮点赞!