Laya Air - 如何在Laya3D中实现屏幕后期特效?

前言

最近由于项目的原因,不得不去学习了一下Laya这款游戏引擎,在学习使用过程中,我发现在Laya官方文档中关于对屏幕后期处理相关的介绍比较少,就只有官方提供了一个Bloom全屏泛光的后期特效。
而且也没有关于自定义后期处理相关接口的介绍!所以我不得不去查看了一下Laya的源码,参考了一下Bloom的实现,最后也终于整理了出来。

实现

其实Laya给我们提供了一个 后期处理 的类Laya.PostProcessEffect,我们自定义的只需要继承该类,并且重写 render 方法即可轻松实现后期处理。

下面我简单的实现一个修改屏幕颜色的后期处理shader

废话不多说,直接上代码

创建一个 SimpleColorEffect 脚本

export class SimpleColorEffect extends Laya.PostProcessEffect {
    // 通过Shader属性名称获得唯一ID。
    public static TestColor = Laya.Shader3D.propertyNameToID("u_TestColor");

    private _shader: Laya.Shader3D;
    private _shaderData: Laya.ShaderData = new Laya.ShaderData();

    constructor() {
        super();
        this.init();
    }
    // 
    public get shader(): Laya.Shader3D {
        return this._shader;
    }

    public get shaderData(): Laya.ShaderData {
        return this._shaderData;
    }

    // 设置颜色
    public set color(v: Laya.Vector4) {
        this._shaderData.setVector(SimpleColorEffect.TestColor, v);
    }


    // 初始化
    public init(): void {
        //所有的attributeMap属性
        var attributeMap: Object = {
            'a_PositionTexcoord': Laya.VertexMesh.MESH_POSITION0,
            'a_Normal': Laya.VertexMesh.MESH_NORMAL0,
            'a_Texcoord0': Laya.VertexMesh.MESH_TEXTURECOORDINATE0
        };
        //所有的uniform属性
        var uniformMap: Object = {
            'u_MainTex': Laya.Shader3D.PERIOD_MATERIAL,
            'u_AutoExposureTex': Laya.Shader3D.PERIOD_MATERIAL,
            'u_TestColor': Laya.Shader3D.PERIOD_MATERIAL,
        };
        // 创建shader
        this._shader = Laya.Shader3D.add("SimpleColorEffect");
        var subShader = new Laya.SubShader(attributeMap, uniformMap);
        this._shader.addSubShader(subShader);
        // 读取顶点和片元着色器
        let vs = `
            #include "Lighting.glsl";

            attribute vec4 a_PositionTexcoord;
            attribute vec2 a_Texcoord;
            varying vec2 v_Texcoord0;

            void main() {
                gl_Position = vec4(a_PositionTexcoord.xy, 0.0, 1.0);
                v_Texcoord0 = a_PositionTexcoord.zw;
                gl_Position = remapGLPositionZ(gl_Position);
            }
        `;
        let fs = `
            #ifdef GL_FRAGMENT_PRECISION_HIGH
            precision highp float;
            #else
            precision mediump float;
            #endif
            
            #include "Colors.glsl";
            #include "Sampling.glsl";
            
            uniform sampler2D u_MainTex;

            varying vec2 v_Texcoord0;
            uniform vec4 u_TestColor;
            
            void main() {
                vec4 resColor = vec4(0., 0., 0., 0.);
                resColor = texture2D(u_MainTex, v_Texcoord0.xy);
                gl_FragColor = resColor*u_TestColor;
            }
        `;
        //添加着色器pass
        subShader.addShaderPass(vs, fs);
    }
	//重写render - 每一帧camera 捕获到的图像 渲染到屏幕之前执行
    render(context: Laya.PostProcessRenderContext) {
        let command = context.command;
        let source = context.source;
        let destination = context.destination;
        // 要渲染到目标纹理必须通过以下
        var rt1 = Laya.RenderTexture.createFromPool(source.width, source.height, 0, 3);
        command.blitScreenTriangle(source, rt1, null, this._shader, this._shaderData);
        command.blitScreenTriangle(rt1, source);
        // 释放
        Laya.RenderTexture.recoverToPool(rt1);
    }
}

使用如下:

  var postProcess = new Laya.PostProcess();
  //创建一个简单的颜色滤镜效果
  let simple = new SimpleColorEffect();
  //设置颜色
  simple.color = new Laya.Vector4(1,0,0,1); //红色
  //添加一个后期处理特效
  postProcess.addEffect(simple);
  camera.postProcess = postProcess;

效果如下: 左边是未处理 ,右边 红色滤镜处理
Laya Air - 如何在Laya3D中实现屏幕后期特效?_第1张图片

如果熟悉Unity Shader的同学 应该就能看出来上面重写的 render 函数类似于 Unity 中的 OnRenderImage 函数。

不过有点问题,按理说 最终输出到屏幕的指令应该是这么写

command.blitScreenTriangle(source, destination,null,this._shader, this._shaderData);

从源纹理(source) 经过shader处理 然后输出到目标纹理(destination),但是经过测试最终是无效的, 需要先创建一个临时纹理, 然后渲染到临时纹理,再渲染到源纹理,才有效果。
如下:

render(context: Laya.PostProcessRenderContext) {
        let command = context.command;
        let source = context.source;
        let destination = context.destination;
        // 要渲染到目标纹理必须通过以下
        var rt1 = Laya.RenderTexture.createFromPool(source.width, source.height, 0, 3);
        command.blitScreenTriangle(source, rt1, null, this._shader, this._shaderData);
        command.blitScreenTriangle(rt1, source);
        // 释放
        Laya.RenderTexture.recoverToPool(rt1);
    }

不知道这是Laya的bug?还是就是这么设计的?我个人感觉有点不合理。

哎,不管如何,我们现在可以轻松地自定义后期处理shader了,比如可以实现一些高斯模糊等特效

你可能感兴趣的:(LayaAir)