cocos2dx - shader实现任意动画的残影效果
本节主要讲利用cocos2dx机制实现opengl es shader脚本的绘制
这里先看下最终效果:
这里分别实现了灰度效果及残影的效果。
一、绘制基类
这里主要参考了cocos2dx源码中 RenderTexture 的实现,有兴趣的可以了解下。
绘制基类RenderShader主要实现以下方法:
//******************************************************************
// 文件名: RenderShader.h
// 创建人: 稀饭lei
// 版 本: 1.0
// 描 述: 特效基类
//******************************************************************
#ifndef _RenderShader_H__
#define _RenderShader_H__
#include "cocos2d.h"
USING_NS_CC; // GL纹理坐标组
static const GLfloat ccRenderTextcord[] ={
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
class RenderShader : public Node
{
public:
static RenderShader* create(); virtual bool LoadByteArray(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray); //加载 shader文件 void begin(); // 用于设置绘制开始要调用的onBegin void end(); // 用于设置绘制结束要调用的onEnd void push2Draw(Node* node); // 添加待绘制节点 virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override; // cocos2dx绘制回调 protected:
virtual void onBegin(); virtual void onEnd(); //virtual void onClear(); virtual void Render(); // 绘制实现 virtual bool init(); virtual void CreateFrameBuffer(); //创建FBO及texture RenderShader(void); virtual ~RenderShader(void); GLuint m_nFrameBuffer; // 用于当前绘制的fbo GLuint m_nFrameBufferTexture; // 用于绑定在fbo中的texture GLProgram* m_glprogram; // GL绘制管理 GLint m_nOldFBO; // 原来的fbo CustomCommand _beginCommand;
CustomCommand _endCommand;
GroupCommand _groupCommand; std::set<Node*> sRenderChild; // 需要绘制的节点 Size m_sRenderSize;
}; #endif
这里有几个主要实现:
1、我们用push2Draw(Node* node); 方法将待绘制的节点添加到sRenderChild中等待处理。
2、参考RenderTexture 的实现,我们也利用begin,end将需要绘制的节点利用visit访问添加到当前的RenderShader类所在的render层中进行绘制。
3、在实际绘制过程中调用的onBegin中缓存对应的坐标系,同时绑定我们创建的FBO使得visit进来的节点可以绘制到当前FBO中。然后在onEnd中调用render方法将我们的FBO内容绘制原来cocos2dx底层的FBO上。
4、在render()实现如何绘制当前FBO的内容到cocos2dx的FBO上。(实际shader作业的地方)
详细实现代码如下:
#include "RenderShader.h"
#define STRINGIFY(A) #A
const char* ccPositionTextureColor_frag_test = STRINGIFY(
\n#ifdef GL_ES\n
precision lowp float;
\n#endif\n varying vec4 v_fragmentColor;
varying vec2 v_texCoord; void main()
{
vec4 color = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
gl_FragColor = vec4(vec3(gray), color.a);
}
); RenderShader* RenderShader::create()
{
auto node = new RenderShader();
if (node && node->init())
{
node->autorelease();
return node;
}
delete node;
return nullptr;
} bool RenderShader::init()
{
if (m_glprogram)
{
m_glprogram->release();
m_glprogram = nullptr;
} CreateFrameBuffer(); m_glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, ccPositionTextureColor_frag_test);
m_glprogram->retain(); return true;
} RenderShader::RenderShader(void)
:m_nFrameBuffer(), m_nFrameBufferTexture(), m_glprogram(nullptr)
{
GLView* glView = Director::getInstance()->getOpenGLView();
if (!glView)
{
return;
}
//屏幕大小
m_sRenderSize = glView->getFrameSize();
} RenderShader::~RenderShader(void)
{
if (m_glprogram)
{
m_glprogram->release();
m_glprogram = nullptr;
}
if (m_nFrameBuffer)
{
glDeleteFramebuffers(, &m_nFrameBuffer);
m_nFrameBuffer = ;
}
if (m_nFrameBufferTexture)
{
glDeleteTextures(, &m_nFrameBufferTexture);
m_nFrameBufferTexture = ;
}
} bool RenderShader::LoadByteArray(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray)
{
if (m_glprogram)
{
m_glprogram->release();
m_glprogram = nullptr;
} this->CreateFrameBuffer(); m_glprogram = GLProgram::createWithByteArrays(vShaderByteArray, fShaderByteArray);
m_glprogram->retain(); return true;
} void RenderShader::onBegin()
{
if (!m_nFrameBuffer)
{
return;
}
Director* director = Director::getInstance();
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); //save old fbo and bind own
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
glBindFramebuffer(GL_FRAMEBUFFER, m_nFrameBuffer);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
} void RenderShader::onEnd()
{
if (!m_nFrameBuffer)
{
return;
} // restore viewport for render to direct fbo
Director *director = Director::getInstance();
// restore viewport
director->setViewport();
this->Render(); glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
} void RenderShader::Render()
{
if (!m_glprogram || !m_nFrameBufferTexture)
{
return;
} glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
m_glprogram->use();
m_glprogram->setUniformsForBuiltins(); //GLuint glTexture = m_glprogram->getUniformLocationForName("CC_Texture0");
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, m_nFrameBufferTexture);
//glUniform1i(glTexture, 0);
GL::bindTexture2D(m_nFrameBufferTexture);
Size sSize = Director::getInstance()->getVisibleSize();
float x = ;
float y = ;
float w = sSize.width;
float h = sSize.height;
GLfloat vertices[] = {
x, y + h,
x + w, y + h,
x, y,
x + w, y
}; glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, , GL_FLOAT, GL_FALSE, , vertices);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, , GL_FLOAT, GL_FALSE, , ccRenderTextcord); glDrawArrays(GL_TRIANGLE_STRIP, , ); /*GL::bindVAO(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);*/
} void RenderShader::CreateFrameBuffer()
{
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
if (m_nFrameBuffer == )
{
glGenFramebuffers(, &m_nFrameBuffer); if (m_nFrameBuffer == )
{
CCLOG("m_FilterFrameBuffer == 0");
return;
}
}
glBindFramebuffer(GL_FRAMEBUFFER, m_nFrameBuffer); if (m_nFrameBufferTexture == )
{
glGenTextures(, &m_nFrameBufferTexture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_nFrameBufferTexture); GLsizei nWidth = m_sRenderSize.width;
GLsizei nHeight = m_sRenderSize.height;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, , GL_RGBA, nWidth, nHeight, , GL_RGBA, GL_UNSIGNED_BYTE, );
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
CCLOG("cocos2d: Texture2D: Error uploading compressed texture glError: 0x%04X", err);
return;
} }
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_nFrameBufferTexture, ); GLenum error;
if ((error = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE)
{
CCLOG("Failed to make complete framebuffer object 0x%X", error);
return;
} glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
} void RenderShader::begin()
{
Director* director = Director::getInstance();
CCASSERT(nullptr != director, "Director is null when seting matrix stack");
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); _groupCommand.init(_globalZOrder); Renderer *renderer = Director::getInstance()->getRenderer();
renderer->addCommand(&_groupCommand);
renderer->pushGroup(_groupCommand.getRenderQueueID()); _beginCommand.init(_globalZOrder);
_beginCommand.func = CC_CALLBACK_0(RenderShader::onBegin, this); renderer->addCommand(&_beginCommand);
} void RenderShader::end()
{
_endCommand.init(_globalZOrder);
_endCommand.func = CC_CALLBACK_0(RenderShader::onEnd, this); Director* director = Director::getInstance();
CCASSERT(nullptr != director, "Director is null when seting matrix stack"); Renderer *renderer = director->getRenderer();
renderer->addCommand(&_endCommand);
renderer->popGroup(); director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); } void RenderShader::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
//Begin will create a render group using new render target
begin(); ////clear screen
//_clearCommand.init(_globalZOrder);
//_clearCommand.func = CC_CALLBACK_0(RenderShader::onClear, this);
//renderer->addCommand(&_clearCommand); //! make sure all children are drawn
{
auto it = sRenderChild.begin();
while (it != sRenderChild.end())
{
if (Node* node = *it)
{
node->visit(renderer, transform, flags);
node->release();
}
++it;
}
sRenderChild.clear();
}
//End will pop the current render group
end();
} void RenderShader::push2Draw(Node* node)
{
if (node && sRenderChild.find(node) == sRenderChild.end())
{
node->retain();
sRenderChild.insert(node);
}
else
{
CCLOG(" push same!!!");
}
}
这里shader代码实现的是灰度图的效果,其他效果可以自己修改ccPositionTextureColor_frag_test内容。
二、实际使用
1、将RenderShader当成正常的Node使用,设置父节点及index坐标等。
m_pShader = RenderShader::create();
this->addChild(m_pShader);
2、因为RenderShader在draw中对需要绘制的子节点进行了清理,所以需要每次重新对子节点进行添加,添加动画节点如下:
void CPlayer::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
if (m_pShader)
{
m_pShader->push2Draw(m_pNode);
}
}
主要事项:RenderShader在Render方法中将内容绘制到原来的FBO中,而cocos2dx对每个需要绘制的同一index层级会在开始的时候单独创建一个FBO来处理。因而如果RenderShader单独一个层级可能导致此时的FBO没有创建,导致没有绘制效果。
到这里我们就实现灰色角色的效果了,使用也依照cocos2dx的用法,相对简单。
三、残影效果扩展
主要实现代码如下:
//******************************************************************
// 文件名: RemindShader.h
// 创建人: 稀饭lei
// 版 本: 1.0
// 描 述: 残影特效
//******************************************************************
#ifndef _RemindShader_H__
#define _RemindShader_H__
#include "RenderShader.h"
USING_NS_CC;
#define REMIND_RENDER_COUNT 5 // 残影个数
class RemindShader :public RenderShader
{
public:
static RemindShader* create(); virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
private:
virtual void onBegin() override; //virtual void onClear(); virtual void Render() override; virtual bool init() override; virtual void CreateFrameBuffer() override; RemindShader(void); virtual ~RemindShader(void); GLuint m_pTextureArr[REMIND_RENDER_COUNT]; int m_nRenderFrq; // 渲染间隔帧率 int m_nRenderCount; // 渲染间隔计数 int m_nCurTexutreIndex; // 当前渲染的纹理ID
}; #endif #include "RemindShader.h" #define STRINGIFY(A) #A
const char* ccRemind_fsh = STRINGIFY(
\n#ifdef GL_ES\n
precision lowp float;
\n#endif\n varying vec2 v_texCoord;
uniform float u_alpha; void main()
{
vec4 pcolor = texture2D(CC_Texture0, v_texCoord) * u_alpha;
gl_FragColor = pcolor;
}
); RemindShader* RemindShader::create()
{
auto node = new RemindShader();
if (node && node->init())
{
node->autorelease();
return node;
}
delete node;
return nullptr;
} bool RemindShader::init()
{
if (m_glprogram)
{
m_glprogram->release();
m_glprogram = nullptr;
} CreateFrameBuffer();
m_glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, ccRemind_fsh);
m_glprogram->retain();
return true;
} RemindShader::RemindShader(void)
: m_nCurTexutreIndex(), m_nRenderFrq(), m_nRenderCount()
{
memset(m_pTextureArr, , REMIND_RENDER_COUNT);
} RemindShader::~RemindShader(void)
{
if (m_pTextureArr[])
{
glDeleteTextures(REMIND_RENDER_COUNT, m_pTextureArr);
memset(m_pTextureArr, , REMIND_RENDER_COUNT);
}
} void RemindShader::onBegin()
{
if (!m_nFrameBuffer)
{
return;
}
Director* director = Director::getInstance();
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// director->setProjection(director->getProjection());
// GLView* glView = director->getOpenGLView();
// if (!glView)
// {
// return;
// }
// Size frameSize =glView->getFrameSize();
// {
// // Calculate the adjustment ratios based on the old and new projections
// Size size = director->getWinSizeInPixels();
// float widthRatio = size.width / frameSize.width;
// float heightRatio = size.height / frameSize.height;
// //caculate the projections of size change
// Mat4 orthoMatrix;
// Mat4::createOrthographicOffCenter((float)-1.0 / widthRatio, (float)1.0 / widthRatio, (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1, 1, &orthoMatrix);
// director->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, orthoMatrix);
// }
//
// //calculate viewport
// {
// glViewport(0, 0, (GLsizei)(frameSize.width), (GLsizei)(frameSize.height));
// }
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
// 仅在计数为0才更新新的渲染
if (m_nRenderCount == )
{
glBindFramebuffer(GL_FRAMEBUFFER, m_nFrameBuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTextureArr[m_nCurTexutreIndex], );
m_nCurTexutreIndex = m_nCurTexutreIndex + < REMIND_RENDER_COUNT ? m_nCurTexutreIndex + : ; glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
}
} void RemindShader::Render()
{
if (!m_glprogram )
{
return;
} glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
m_glprogram->use();
m_glprogram->setUniformsForBuiltins(); Size sSize = Director::getInstance()->getVisibleSize();
float x = ;
float y = ;
float w = sSize.width;
float h = sSize.height; // 除当前纹理外的纹理存储的都是拖影
int nBeginIndex = m_nCurTexutreIndex;
int nCurIndex = nBeginIndex;
GLfloat falpha = 0.3f;
float addf = 0.05f; // 渲染存在m_pTextureArr队列里的拖影纹理到主场景
do
{
Point delta = _position;// this->convertToWorldSpace(_position);
GLfloat vertices[] = {
x - delta.x,y+ h - delta.y,
x+w - delta.x,y+ h - delta.y,
x - delta.x , y - delta.y,
x+w - delta.x, y - delta.y
};
//GLuint glTexture = m_glprogram->getUniformLocationForName("CC_Texture0");
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, m_pTextureArr[nCurIndex]);
//glUniform1i(glTexture, 0);
GL::bindTexture2D(m_pTextureArr[nCurIndex]); GLuint glAlpha = m_glprogram->getUniformLocationForName("u_alpha");
glUniform1f(glAlpha, falpha); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, , GL_FLOAT, GL_FALSE, , vertices);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, , GL_FLOAT, GL_FALSE, , ccRenderTextcord);
glDrawArrays(GL_TRIANGLE_STRIP, , ); falpha += addf;
addf += 0.025f;
nCurIndex = ++nCurIndex<REMIND_RENDER_COUNT ? nCurIndex : ;
} while (nCurIndex != nBeginIndex);
//
// GL::bindVAO(0);
//glBindBuffer(GL_ARRAY_BUFFER, 0);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void RemindShader::CreateFrameBuffer()
{ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
if (m_nFrameBuffer == )
{
glGenFramebuffers(, &m_nFrameBuffer); if (m_nFrameBuffer == )
{
CCLOG("m_FilterFrameBuffer == 0");
return;
}
}
GLsizei nWidth = m_sRenderSize.width;
GLsizei nHeight = m_sRenderSize.height;
glBindFramebuffer(GL_FRAMEBUFFER, m_nFrameBuffer);
if (m_pTextureArr[] == )
{
glGenTextures(REMIND_RENDER_COUNT, m_pTextureArr);
for (int i = ; i < REMIND_RENDER_COUNT; i++)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_pTextureArr[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, , GL_RGBA, nWidth, nHeight, , GL_RGBA, GL_UNSIGNED_BYTE, );
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
CCLOG("cocos2d: Texture2D: Error uploading compressed texture glError: 0x%04X", err);
return;
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTextureArr[i], );
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
}
}
glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
} void RemindShader::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
if (sRenderChild.size() <= )
{
if (m_nFrameBuffer)
{
// 清理所有缓存的纹理
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
glBindFramebuffer(GL_FRAMEBUFFER, m_nFrameBuffer);
for (int i = ; i < REMIND_RENDER_COUNT; i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTextureArr[i], );
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
}
glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
}
m_nRenderCount = m_nRenderFrq - ;
return;
}
//Begin will create a render group using new render target
begin(); if (++m_nRenderCount == m_nRenderFrq)
{
//! make sure all children are drawn
auto it = sRenderChild.begin();
while (it != sRenderChild.end())
{
if (Node* node = *it)
{
node->visit(renderer, transform, flags);
node->release();
}
++it;
}
m_nRenderCount = ; // 0 提示需要渲染进去
}
sRenderChild.clear();
//End will pop the current render group
end(); }
几个关键点:
1、利用REMIND_RENDER_COUNT控制需要创建的Texture个数,通过每次绘制到不同的Texture上来保存原来的效果。
2、shader中利用u_alpha参数控制残影的透明度,在render中绘制到原来的FBO上时调用不同的透明度来实现残影渐隐的效果。
3、通过m_nRenderFrq来控制间隔的绘制次数,来实现残影的残留效果远近。
这样就实现了残影的效果了,使用的方法还是跟基类一样。
完整代码地址:https://github.com/mydishes/cocos2dx-Ex/tree/master/Shader
cocos2dx - shader实现任意动画的残影效果的更多相关文章
- Unity3D手游开发日记(8) - 运动残影效果
2D游戏的残影很简单,美术做序列帧图片就行了,那么3D游戏的残影美术做不了,得靠程序员动态创建模型来处理. 实现原理也很简单: 1.间隔一定时间创建一个残影模型 GameObject go = Gam ...
- Unity3D-Shader-人物残影效果
[旧博客转移 - 2016年1月7日 00:24 ] 前面的话 上一篇讲了一下人物边缘发光效果,链接: Unity-ShaderLab-实现X光效果,这次我们利用这个Shader来实现人物残影效果 先 ...
- as3如何做出残影效果
在页游中,时不时能看到人物做一些快速移动动作如冲刺时,有残影效果,强化了画面表现.实际人肉眼之所以能看到残影的效果,是因为观察到的物体会在人视线中残留几十毫秒时间,当运动物体运动太快时,人肉眼所见未能 ...
- Unity3d 残影效果(狂拽炫酷叼炸天)
效果图,真的很叼啊 我根据别人的改进了一版,支持MeshFilter上的Mesh(需要确保Mesh的Read/Write是开启的否则不能正常工作) 非常感谢原作者给提供思路.http://blog.c ...
- Unity Shader : Ghost(残影) v1
前阵子组长给我提了个需求,要实现角色人物的残影.我百度google了一下,发现可以用两种方式实现这个效果:1.记录前几帧的人物位置,将其传入shader中,对每个位置进行一个pass渲染.2. 通过相 ...
- Unity运动残影技能
残影实现: 1.List<DrawMesh> list,此list中包含某一帧动画模型网格.材质 2.每过一段时间就将运动物体的模型add到list中(优化:未实现,网格合并) 3.Lat ...
- PS制作gif动图以及背景透明与消除残影
摘要: 用Photoshop制作gif动画的要点:在窗口菜单中找到“时间轴”选中打开时间轴,单击一帧,设置该帧显示持续时间在图层里将该帧要显示的图层显示,并将不该显示的层隐藏,新建一帧,接下来就是重复 ...
- Win7去除桌面残影的方法
用户升级到Win7系统后使用正常,就是系统桌面会留有残影,怎么样也去不掉,影响用户的使用,那么要如何将这些残影去掉呢?可从计算机属性中进行相关配置. 解决方法 一.在计算机面板上,右键点击“计算机”, ...
- HDOJ(HDU) 2153 仙人球的残影(谜一样的题、、、)
Problem Description 在美丽的HDU,有一名大三的同学,他的速度是众所周知的,跑100米仅仅用了2秒47,在他跑步过程中会留下残影的哎,大家很想知道他是谁了吧,他叫仙人球,既然名字这 ...
随机推荐
- 垂直居中小记 line-height table vertical-align:middle
垂直居中分两种情况:1.父元素高度确定的单行文本 2.以及父元素高度确定的多行文本. 1.垂直居中-父元素高度确定的单行文本的竖直居中的方法是通过设置父元素的 height 和 line ...
- [转载]在instagram上面如何利用电脑来上传图片
原文地址:在instagram上面如何利用电脑来上传图片作者:小北的梦呓 我们都知道instagram是一个手机版的app,instagram官方不支持通过电脑来上传图片,而利用手机又很麻烦,那么如果 ...
- PS各个工具的字母快捷键和英…
原文地址:PS各个工具的字母快捷键和英文全名作者:Tycho 选框-Marquee(M) 移动-move(V) 套索-Lasso(L) 魔棒-Wand(W) 喷枪-in ...
- 201521123006 《java程序设计》 第8周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 1.2 选做:收集你认为有用的代码片段 class ArrayAlg { public static < ...
- Java课程设计——GUI密码生成器201521123035
1.团队课程设计博客链接 (http://www.cnblogs.com/wuling15/p/7061857.html) 2.个人负责模块或任务说明 (1)确定课题并进行任务分工 (2)编写随机数产 ...
- 201521123096《Java程序设计》第十周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 1.finally 题目4-2 1.1 截图你的提交结果(出 ...
- 【DDD】业务建模实践 —— 删除帖子
本文是基于上一篇‘业务建模战术’的实践,主要讲解‘删除帖子’场景的业务建模,包括:业务建模.业务模型.示例代码:示例代码会使用java编写,文末附有github地址.相比于<领域驱动设计> ...
- 如何在mybatis 中使用In操作
如何在mybatis 中使用In操作 假如我们想使用这样一个sql 语句,但是这样的sql语句有IN这样的操作.在我们的mybatis中有相对应的操作 SELECT * FROM product_db ...
- Web 项目更改项目名
简单的记录web开发中基本的操作. 更改项目名 直接修改 找到原项目中的.project 文件,更改中项目名称.然后在同目录下找到.mymetadata 文件 并更改name.context-root ...
- 初学者一些常用的SQL语句(一)
一.数据库的创建create database 数据库名create database bbb二.表的创建 ***[]:可选项*** null:空值 not null 不为空***只有字符型能指定长度 ...