学习OpenGL过程中
发现运动模糊这种画面加强特效十分有趣
运动模糊可以从几个方面来理解
1.人眼看到快速移动的东西看不清,就是运动模糊
2.相机拍照快速移动的东西,画面虚化,是更加直观的运动模糊
3.在游戏中,移动物体周围,用算法加入加强的模糊效果,提高画面表现力,这是我们下面要讨论的内容
转载注明http://www.cnblogs.com/billyrun/articles/5945653.html
源码地址https://github.com/novemrain/Cocos-OpenGL
运动模糊原理
1.在一帧里面多次绘制运动物体
在当前帧取得运动物体此前若干帧的顶点变换信息,
一并进行渲染(渲染多次)
需要控制每一帧的强度,需要适当虚化(减弱颜色和透明度)才能还原运动模糊效果
这种方式会出现明显的拖影
2.纹理采样加权模糊
这种模糊效果在FragmentShader中实现
获取运动速度,并把速度作为一个向量传入shader
纹理采样时,在速度方向上采样多次并取颜色(加权)平均值
这种方式会出现明显的模糊
3.通过glReadPixels(PBO)
OpenGL超级宝典上的实例方法
大概思路是读取/保存像素
然后把前几帧的像素绘制在屏幕上
与方法1比较相似但也有差别
方法1保存了之前的变换信息进行再次绘制
本方法在绘制本帧时直接保存了像素信息
很直观的这样可以做全屏幕的模糊
不知道能不能做单一物体的模糊(对背景等不需要模糊的部分不做重复绘制)
运动模糊实现
以下我们结合cocos,使用方法1实现Sprite/Armature的运动模糊
运动模糊开启后,元素会在Move,Jump等动作时表现明显的拖影效果
1.Shader
const char* motionBlur_frag = STRINGIFY( varying vec4 v_fragmentColor; varying vec2 v_texCoord; uniform float u_vOpacity; void main() { vec4 color0 = texture2D(CC_Texture0, v_texCoord); gl_FragColor = v_fragmentColor * color0 * u_vOpacity; } );
与Sprite所使用的shader相比,多乘了一个u_vOpacity
避免多次混合导致图像颜色过强
2.渲染次数的控制
首先定义一个最大的模糊渲染次数
比如说10次,那么我们需要最多保存10个过去的变换
其次还需要知道当前的模糊渲染次数
物体运动的不同阶段,身后的模糊是不同的
刚运动时拖影数是1,2,3,4,5..这样递增的
// motion blur // blur effect on/off bool bMotionBlur; // current blurs int currentMotions; // blurs to be set unsigned short uFrame; // transforms for blurs cocos2d::Mat4 motions[MOTION_FRAME_MAX];
注意这里uFrame才是本次设置的最大模糊渲染次数
cocos2d::Mat4 motions[MOTION_FRAME_MAX]并不一定是满的!
3.运动模糊开启与关闭
void MotionBlurSprite::setMotionBlur(bool bBlur, unsigned short frame) { // 目前的做法,直接切换了shader不会保存最后几帧 if (bMotionBlur != bBlur) { bMotionBlur = bBlur; currentMotions = 0; } uFrame = frame; if (bMotionBlur) { setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_MOTION_BLUR)); auto program = getGLProgram(); _uvOpacityLoc = glGetUniformLocation(program->getProgram(), "u_vOpacity"); setIntensity(1.5f); } else { setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP)); } }
注意切换时,尤其是从关到开,一定要把currentMotions置0
防止以前的过期拖影重复显示在运动初始阶段
确保运动的初始阶段是"干净"的
4.模糊特效的绘制顺序
先绘制快要过期的拖影,再绘制最近的拖影,最后绘制本帧
这样的顺序保证了正确的显示层级
在本帧绘制完成后,还要更新运动模糊的transform数组
void MotionBlurSprite::onDraw(const Mat4 &transform, uint32_t flags) { //1.渲染顺序反向 //2.混合颜色透明度调整 auto glProgramState = getGLProgramState(); glProgramState->apply(transform); GL::blendFunc(_blendFunc.src, _blendFunc.dst); GL::bindTexture2D(_texture->getName()); GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); #define kQuadSize sizeof(_quad.bl) size_t offset = (size_t)&_quad; // 对于vertex绑定我们变换过的顶点信息 // 对于texCoods和color绑定_quad中的信息 // vertex int diff = offsetof(V3F_C4B_T2F, vertices); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff)); // texCoods diff = offsetof(V3F_C4B_T2F, texCoords); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff)); // color diff = offsetof(V3F_C4B_T2F, colors); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff)); Mat4 currentTransform; bool bLast = false; for (int i = currentMotions - 1; i >= 0 || bLast == false; i--) { bLast = i < 0; currentTransform = bLast ? transform : motions[i]; float fOpacity = 1.0f / (currentMotions + 1); if (bMotionBlur)fOpacity = MIN(fOpacity * _intensity, 1.0f); glUniform1f(_uvOpacityLoc, fOpacity); //绘制三角形 glProgramState->apply(currentTransform); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); CHECK_GL_ERROR_DEBUG(); CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 4); } // 处理运动模糊帧 if (bMotionBlur) currentMotions = MIN(currentMotions + 1, uFrame); else currentMotions = MAX(currentMotions - 1, 0); for (int i = currentMotions - 1; i >= 0; i--) { motions[i] = i == 0 ? transform : motions[i - 1]; } }
5.Armature处理办法
Armature由一个个Bone组成
Bone包含了Skin,Skin的处理方式和Sprite十分相似
处理Skin之后,Armture会在各种Action(Move/Jump)发生时出现模糊效果
而在动画播放的本身不会产生模糊(动画播放的过程中transform不改变)
我们为Armature添加一个运动模糊的开关
CC_SYNTHESIZE(bool, bMotionBlur, MotionBlur);
然后在Armature::draw方法中,将bMotionBlur值传入每一个Skin
这样来控制Skin是否表现模糊效果
6.Sprite/Armature运动模糊效果图
转载注明http://www.cnblogs.com/billyrun/articles/5945653.html
源码地址https://github.com/novemrain/Cocos-OpenGL