CocosCreator-Shader-Effect-.../assets/effects/sprite-glow-inner-v2.effect
2020-06-23 18:01:57 +08:00

239 lines
6.6 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 相比起 v1 版本的变更
//
// 1. 切换了采样算法,由原来圆采样,改为矩形采样,减少大量三角函数运算,在保持差不多效果的前提下,性能得到大幅提升
CCEffect %{
techniques:
- passes:
- vert: vs
frag: fs
blendState:
targets:
- blend: true
rasterizerState:
cullMode: none
properties:
texture: { value: white }
alphaThreshold: { value: 0.5 }
# 自定义参数
glowColor: {
value: [1.0, 1.0, 0.0, 1.0],
editor: {
type: color,
tooltip: "发光颜色RGBA"
}
}
spriteWidth: {
value: 500,
editor: {
tooltip: "纹理宽度px"
}
}
spriteHeight: {
value: 500,
editor: {
tooltip: "纹理高度px"
}
}
glowRange: {
value: 20,
editor: {
tooltip: "内发光范围px"
}
}
# 发光透明度阈值
# 只有超过这个透明度的点才会发光
# 一般用于解决图像边缘存在渐变透明的时,决定超过这个透明度阈值的边缘点才点发光,具体可以操作一下
glowThreshold: {
value: 0.1,
editor: {
tooltip: "发光阈值(只有超过这个透明度的点才会发光)",
range: [0.0, 1.0]
}
}
}%
CCProgram vs %{
precision highp float;
#include <cc-global>
#include <cc-local>
in vec3 a_position;
in vec4 a_color;
out vec4 v_color;
#if USE_TEXTURE
in vec2 a_uv0;
out vec2 v_uv0;
#endif
void main () {
vec4 pos = vec4(a_position, 1);
#if CC_USE_MODEL
pos = cc_matViewProj * cc_matWorld * pos;
#else
pos = cc_matViewProj * pos;
#endif
#if USE_TEXTURE
v_uv0 = a_uv0;
#endif
v_color = a_color;
gl_Position = pos;
}
}%
CCProgram fs %{
precision highp float;
#include <alpha-test>
in vec4 v_color;
#if USE_TEXTURE
in vec2 v_uv0;
uniform sampler2D texture;
#endif
// 定义向周边搜索的圈数
#define range 5.0
#if SHOW_INNER_GLOW
uniform glow {
// 发光颜色
vec4 glowColor;
// 纹理宽度px
float spriteWidth;
// 纹理高度px
float spriteHeight;
// 内发光范围px
float glowRange;
// 发光阈值
float glowThreshold;
// 特别地,必须是 vec4 先于 float 声明
};
/**
* 获取纹理uv颜色
*
* 主要实现:超出边界的统一返回 vec4(0.0, 0.0, 0.0, 0.0)
*
* 在 Cocos Creator 2.2.1 的编辑器中超出边界的uv并不是返回 vec4(0.0, 0.0, 0.0, 0.0),实际返回为
*
* * 超出左边界的uv返回 v_uv0.x = 0 的颜色
* * 超出右边界的uv返回 v_uv0.x = 1 的颜色
* * 超出上边界的uv返回 v_uv0.y = 1 的颜色
* * 超出下边界的uv返回 v_uv0.y = 0 的颜色
*
* 和实际在浏览器上显示(超出边界即为透明)的有区别,为了统一,这里适配一下,这样子,在编辑器上预览的效果就能和实际浏览器的保持一致
*/
vec4 getTextureColor(sampler2D texture, vec2 v_uv0) {
if (v_uv0.x > 1.0 || v_uv0.x < 0.0 || v_uv0.y > 1.0 || v_uv0.y < 0.0) {
return vec4(0.0, 0.0, 0.0, 0.0);
}
return texture(texture, v_uv0);
}
/**
* 获取发光的透明度
*/
float getGlowAlpha() {
// 如果发光宽度为0直接返回0.0透明度,减少计算量
if (glowRange == 0.0) {
return 0.0;
}
// 因为我们是要做内发光,所以如果点本来是透明的或者接近透明的
// 那么就意味着这个点是图像外的透明点或者图像内透明点(如空洞)之类的
// 内发光的话,这些透明点我们不用处理,让它保持原样,否则就是会有内描边或者一点扩边的效果
// 同时也是提前直接结束,减少计算量
vec4 srcColor = getTextureColor(texture, v_uv0);
if (srcColor.a <= glowThreshold) {
return srcColor.a;
}
// 每一圈的对应的步长
float per_step_x = (1.0 / spriteWidth) * (glowRange / range);
float per_step_y = (1.0 / spriteHeight) * (glowRange / range);
// 取样周边一定范围透明度
float totalAlpha = 0.0;
for (float x = -range; x <= range; x++) {
for (float y = -range; y <= range; y++) {
totalAlpha += getTextureColor(texture, v_uv0 + vec2(x * per_step_x, y * per_step_y)).a;
}
}
totalAlpha /= (range + range + 1.0) * (range + range + 1.0);
return totalAlpha;
}
#endif
void main () {
vec4 o = vec4(1, 1, 1, 1);
#if USE_TEXTURE
o *= texture(texture, v_uv0);
#if CC_USE_ALPHA_ATLAS_TEXTURE
o.a *= texture2D(texture, v_uv0 + vec2(0, 0.5)).r;
#endif
#endif
o *= v_color;
ALPHA_TEST(o);
gl_FragColor = o;
#if SHOW_INNER_GLOW
// 目标颜色(图像)
vec4 color_dest = o;
// 获取发光透明度
// 此时我们得到的是内部透明度为1靠近边缘的为接近0的透明度其他位置为0的透明度
float alpha = getGlowAlpha();
// 而内发光是从边缘开始的,那么什么算是边缘呢?
// 如果图像边缘有大量渐变,那么如果我们取大于 0.0 点就算是图像内的话,那么可能边缘会出现锯齿
// 因此为了确定边缘,引入了发光阈值,我们只需要比较一下发光阈值就可以,大于发光阈值的点都是(图像内)发光点
if (alpha > glowThreshold) {
// 内发光是从边缘发光的是需要内部透明度为0靠近边缘的接近1的透明度
// 因此我们需要翻转一下透明度
alpha = 1.0 - alpha;
// 给点调料,让靠近边缘的更加亮
alpha = -1.0 * pow((alpha - 1.0), 4.0) + 1.0;
}
// 源颜色(内发光)
vec4 color_src = glowColor * alpha;
// 按照这个顺序,源颜色就是内发光颜色,目标颜色就是图案颜色色
// 所以命名就是 color_src, color_dest
// 按照混合颜色规则 http://docs.cocos.com/creator/manual/zh/advanced-topics/ui-auto-batch.html#blend-%E6%A8%A1%E5%BC%8F
// 要在图案上方,叠加一个内发光,将两者颜色混合起来,那么最终选择的混合模式如下:
//
// 内发光color_src: GL_SRC_ALPHA
// 原图像color_dest: GL_ONE
//
// 即最终颜色如下:
// color_src * GL_SRC_ALPHA + color_dest * GL_ONE
gl_FragColor = color_src * color_src.a + color_dest;
#endif
}
}%