模糊效果在游戏中经常会用到,有的为了突出前景会把背景给模糊化,有的是因为一些技能需要模糊效果。模糊是shader中较为简单的一种应用。
原理
把一个点的像素值用它周围的点的像素值的加权平均代替。
先说下这个模糊算法的大致思路,我们在片段着色器中可以得到当前像素点的颜色值,要想让这个颜色变得模糊,就要让它与它周围的像素点的颜色稍微接近一点,那么我们就需要拿到这个像素点周围的像素点的颜色值,我们把这些个像素点的值加起来取平均值,就得到了一个区域内的平均颜色。
如果直接使用这个颜色的话,最终的效果会变得很模糊,如果我们只是想稍微模糊一点的话,就要让这个平均值更接近于当前像素点原本的颜色,为此,我们取均值的时候对每个像素点增加了一个权重的定义,当前像素点的权重最高,依次向周围减弱,使得最后得到的均值的颜色更接近于当前像素点原始的颜色。
直接上代码
建一个顶点着色器命名为Blur.vert
//顶点着色器
//v_fragmentColor是从顶点着色器设置的颜色经过光栅化阶段的线性插值后传给片段着色器的颜色。
//v_texCoord同样是经过线性插值而来的纹理坐标。
attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
//varying 顶点shader和片段shader之间相互传递的参数。
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
void main()
{
gl_Position = CC_PMatrix * a_position;
v_fragmentColor = a_color;
v_texCoord = a_texCoord;
}
precision mediump float是open es特有的精度限定符,原本的浮点数精度是double,opengl es为了提高渲染效率,限定精度为float类型。
v_fragmentColor是从顶点着色器设置的颜色经过光栅化阶段的线性插值后传给片段着色器的颜色。
v_texCoord同样是经过线性插值而来的纹理坐标。
CC_Texture0是一个采样器,在加载Shader的时候,引擎会预先把这些uniform变量给加载进来。
下面这部分代码就是引擎预先加载进来的uniform变量:
static const char * COCOS2D_SHADER_UNIFORMS =
"uniform mat4 CC_PMatrix;\n"
"uniform mat4 CC_MVMatrix;\n"
"uniform mat4 CC_MVPMatrix;\n"
"uniform mat3 CC_NormalMatrix;\n"
"uniform vec4 CC_Time;\n"
"uniform vec4 CC_SinTime;\n"
"uniform vec4 CC_CosTime;\n"
"uniform vec4 CC_Random01;\n"
"uniform sampler2D CC_Texture0;\n"
"uniform sampler2D CC_Texture1;\n"
"uniform sampler2D CC_Texture2;\n"
"uniform sampler2D CC_Texture3;\n"
"//CC INCLUDES END\n\n";
这些变量在shader里面如果没有用到的话,会被引擎给优化掉。
texture2D()是shader的内建方法,作用是从CC_Texture0采样器中进行纹理采样,得到当前片段的颜色值。
gl_FragColor是shader的内建变量,表示当前片段的颜色,代码中是把从采样器中拿到的颜色值进行一个变灰处理后,最后得到的颜色值再赋值给gl_FragColor。gl_FragColor就是最终的颜色。
建一个FragmentShader命名为Blur.frag
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform vec2 resolution;//模糊对象的实际分辨率
uniform float blurRadius;//半径
uniform float sampleNum;//间隔的段数
vec4 blur(vec2);
void main(void)
{
vec4 col = blur(v_texCoord);
gl_FragColor = vec4(col) * v_fragmentColor;
}
vec4 blur(vec2 p)
{
if (blurRadius > 0.0 && sampleNum > 1.0)
{
vec4 col = vec4(0);
vec2 unit = 1.0 / resolution.xy;//单位坐标
float r = blurRadius;
float sampleStep = r / sampleNum;
float count = 0.0;
//遍历一个矩形,当前的坐标为中心点,遍历矩形中每个像素点的颜色
for(float x = -r; x < r; x += sampleStep)
{
for(float y = -r; y < r; y += sampleStep)
{
float weight = (r - abs(x)) * (r - abs(y));//权重,p点的权重最高,向四周依次减少
col += texture2D(CC_Texture0, p + vec2(x * unit.x, y * unit.y)) * weight;
count += weight;
}
}
//得到实际模糊颜色的值
return col / count;
}
return texture2D(CC_Texture0, p);
}
使用示例
//精灵
auto splash = Sprite::create("HelloWorld.png");
splash->setPosition(100, 100);
this->addChild(splash, 1);
//vert:顶点着色器 frag:片段着色器
auto glprogram = GLProgram::createWithFilenames("Shaders/test/Blur.vert", "Shaders/test/Blur.frag");
auto glprogramstate = GLProgramState::getOrCreateWithGLProgram(glprogram);
splash->setGLProgramState(glprogramstate);
//设置属性
Size size = sprite->getTexture()->getContentSizeInPixels();
glprogramstate->setUniformVec2("resolution", size); //设置图片分辨率值
glprogramstate->setUniformFloat("blurRadius", 5);//像素点模糊处理的参考矩形的半径
glprogramstate->setUniformFloat("sampleNum", 5);//选择像素点的间隔的数量
效果
参考资料:https://www.cnblogs.com/cxiaojia/p/5195858.html