uniform
方案和assembler
方案的实现。
GT
在Assembler 源码解读及使用 一文中提到自定义渲染可以实现很多酷炫的 shader 特效,目前常用的有两种方法:
创建自定义材质,给材质增加参数。这个参数会作为 uniform
变量传入 shader 由于渲染合批要求材质参数保持一致,所以如果大量对象使用自定义材质时,并且材质参数各不相同,是无法进行合批渲染的,一个对象占一个 draw call
。
创建自定义 assembler
,在顶点数据输入渲染管道前修改它的值。
本文将用这两种方案实现亮度/饱和度/对比度的调整。(注意效果图中不同方式的draw call不同)
新建 Sprite
组件,选择材质 BrightSaturaContrastUniform.mtl
,添加用户脚本 BrightSaturaContrastUniform.ts
,调整对应参数即可。
新建一个节点,添加用户脚本 BrightSaturaContrastAssemblerSprite.ts
,选择材质 BrightSaturaContrastAssembler.mtl
,调整对应参数即可。
调色效果的逻辑参考《Unity Shader入门精要》中的介绍。
亮度的调整只需要把原颜色乘以亮度系数
brightness
即可。然后,我们计算该像素对应的亮度值(luminance
),这是通过对每个颜色分量乘以一个特定的系数再相加得到的。我们使用该亮度值创建了一个饱和度为0的颜色值,并使用saturation
属性在其和上一步得到的颜色之间进行插值,从而得到希望的饱和度颜色。对比度的处理类似,我们首先创建一个对比度为0的颜色值(各分量均为0.5),再使用contrast
属性在其和上一步得到的颜色之间进行插值,从而得到最终的处理结果。
新建一个 BrightSaturaContrastUniform.effect
。
在片元着色器中定义一个 uniform
块。
uniform lamyoung_com {
float brightness;
float saturation;
float constrast;
};
写一个计算插值方法。
vec3 lerp(vec3 a, vec3 b, float w){
return a + w*(b-a);
}
参考《Unity Shader入门精要》中的介绍,翻译一下计算过程即可,主要代码如下。
CCTexture(texture, v_uv0, o);
// apply brightness
vec3 finnalColor = o.rgb * brightness;
// apply saturation
float luminance = 0.2125 * o.r + 0.7154 * o.g + 0.0721 * o.b;
vec3 luminanceColor = vec3(luminance, luminance, luminance);
finnalColor = lerp(luminanceColor, finnalColor, saturation);
// apply constrast
vec3 avgColor = vec3(0.5, 0.5, 0.5);
finnalColor = lerp(avgColor, finnalColor, constrast);
o.rgb = finnalColor.rgb;
接着新建材质 BrightSaturaContrastUniform.mtl
,选择 BrightSaturaContrastUniform.effect
。
最后写一个 BrightSaturaContrastUniform.ts
脚本去控制 uniform
参数,主要代码如下。
this._sprite.getMaterial(0).setProperty('brightness', this._brightness);
this._sprite.getMaterial(0).setProperty('saturation', this._saturation);
this._sprite.getMaterial(0).setProperty('constrast', this._constrast);
新建一个 BrightSaturaContrastAssembler.effect
。
因为这里用的是顶点数据,所以在顶点着色器中定义一些要传入片元着色器的属性。
in float a_brightness;
out float v_brightness;
in float a_saturation;
out float v_saturation;
in float a_constrast;
out float v_constrast;
并把这些属性值传给片元着色器。
v_brightness = a_brightness;
v_saturation = a_saturation;
v_constrast = a_constrast;
片元着色器接收这些属性。
in float v_brightness;
in float v_saturation;
in float v_constrast;
在片元着色器的处理和uniform
的类似。
CCTexture(texture, v_uv0, o);
// apply brightness
vec3 finnalColor = o.rgb * v_brightness;
// apply saturation
float luminance = 0.2125 * o.r + 0.7154 * o.g + 0.0721 * o.b;
vec3 luminanceColor = vec3(luminance, luminance, luminance);
finnalColor = lerp(luminanceColor, finnalColor, v_saturation);
// apply constrast
vec3 avgColor = vec3(0.5, 0.5, 0.5);
finnalColor = lerp(avgColor, finnalColor, v_constrast);
o.rgb = finnalColor.rgb;
新建材质 BrightSaturaContrastAssembler.mtl
,选择 BrightSaturaContrastAssembler.effect
。
新建 BrightSaturaContrastAssembler.ts
继承Assembler 源码解读及使用 中的 GTSimpleSpriteAssembler2D
。
因为要用到亮度/饱和度/对比度这些属性,所以定义顶点属性的时候要加上这些属性。
// 自定义顶点格式,在vfmtPosUvColor基础上,加入 a_brightness a_saturation a_constrast
let gfx = cc.gfx;
const vfmtCustom = new gfx.VertexFormat([
{ name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },
{ name: gfx.ATTR_UV0, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },
{ name: gfx.ATTR_COLOR, type: gfx.ATTR_TYPE_UINT8, num: 4, normalize: true },
{ name: 'a_brightness', type: gfx.ATTR_TYPE_FLOAT32, num: 1 },
{ name: 'a_saturation', type: gfx.ATTR_TYPE_FLOAT32, num: 1 },
{ name: 'a_constrast', type: gfx.ATTR_TYPE_FLOAT32, num: 1 },
]);
更新顶点数据时,只需要找到对应的偏移量就可以了。
let dstOffset;
let verts = this._renderData.vDatas[0];
for (let i = 0; i < this.verticesCount; ++i) {
// fill
let floatsOffset = this.floatsPerVert * i;
dstOffset = floatsOffset + this.brightnessOffset;
verts[dstOffset] = this.brightness;
dstOffset = floatsOffset + this.saturationOffset;
verts[dstOffset] = this.saturation;
dstOffset = floatsOffset + this.constrastOffset;
verts[dstOffset] = this.constrast;
}
最后写一个 BrightSaturaContrastAssemblerSprite.ts
使用BrightSaturaContrastAssembler.ts
,并在需要的时候更新数据。更新数据的代码如下。
assembler.brightness = this.brightness;
assembler.constrast = this.constrast;
assembler.saturation = this.saturation;
this.setVertsDirty();
需要注意的是
drawcall
数量并不是越少越好,最佳性能往往是CPU
与GPU
负载均衡的结果。
以上为白玉无冰使用 Cocos Creator v2.4.0
实现 "亮度/饱和度/对比度的调整"
的技术分享。欢迎分享给身边的朋友!
任何行动往往都比没有行动好,特别是当你一直停滞在不愉快的情势下很长时间的时候。如果这是一个错误,至少你学到了一些东西。这样一来,它就不再是一个错误。如果你仍然选择停滞不前,那么你就学不到任何东西。
做或者不做。
转一转
赞一赞
看一看