最近由于项目的原因,不得不去学习了一下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;
如果熟悉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了,比如可以实现一些高斯模糊等特效