Shader是图形学方面的知识,不太好入门,下文用一个比较简单的例子来体验下shader。
先看创建的代码
ShaderSprite.hpp
#include "CCSprite.h"
class ShaderSprite : public cocos2d::Sprite
{
public:
void initWithShader(const char* vertexShader, const char* fragmentShader, const char* key);
private:
virtual void draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags) override;
void update(float dt) override;
private:
cocos2d::GLProgramState* m_pProgramState;
float u_time;
public:
static const char* ShaderKey;
static const char* VertexShader;
static const char* FragmentShader;
};
ShaderSprite.cpp
#include "ShaderSprite.hpp"
using namespace cocos2d;
const char* ShaderSprite::ShaderKey = "shader_key";
const char* ShaderSprite::VertexShader = " \
attribute vec4 a_position;\
attribute vec4 a_color;\
attribute vec2 a_texcoord;\
varying vec2 v_texcoord;\
varying vec4 v_fragmentColor;\
void main(){\
gl_Position = CC_PMatrix * a_position;\
v_fragmentColor = a_color;\
v_texcoord = a_texcoord;\
}\
";
const char* ShaderSprite::FragmentShader = " \
#ifdef GL_ES \n\
precision highp float; \n\
#endif \n\
varying vec2 v_texcoord; \
uniform float u_time; \
void main(){ \
vec4 incolor = texture2D(CC_Texture0, v_texcoord); \
float factor = (sin(u_time)+1.0)/3.0;\
vec4 outcolor = vec4(0.8, 0.8, 0.8, 0.8)*factor + incolor*(1.0-factor); \
gl_FragColor = vec4(outcolor.xyz, incolor.w); \
} \
";
void ShaderSprite::initWithShader(const char* vertexShader, const char* fragmentShader, const char* key)
{
GLProgram* pProgram = GLProgramCache::getInstance()->getGLProgram(key);
if (pProgram == nullptr)
{
pProgram = GLProgram::createWithByteArrays(vertexShader, fragmentShader);
GLProgramCache::getInstance()->addGLProgram(pProgram, key);
}
m_pProgramState = GLProgramState::create(pProgram);
scheduleUpdate();
}
void ShaderSprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
m_pProgramState->setUniformFloat("u_time", u_time);
setGLProgramState(m_pProgramState);
Sprite::draw(renderer, transform, flags);
}
void ShaderSprite::update(float dt)
{
u_time += dt;
}
在场景文件中添加以下代码
ShaderSprite* pSprite = new ShaderSprite();
pSprite->initWithFile("HelloWorld.png");
pSprite->setPosition(500, 500);
addChild(pSprite);
pSprite->initWithShader(ShaderSprite::VertexShader, ShaderSprite::FragmentShader, ShaderSprite::ShaderKey);
运行下代码,可以发现图片的亮度的在不断变化,可以找个工程体验一下哦。来分析分析吧。
(忽略上面代码的书写格式和简略的亮度变化方法,嘿嘿)
首先先来看ShadeSprite,继承自Sprite,复写了父类的draw函数。
在draw函数中,setGLProgramState并且调用了父类的draw函数。
我们会发现这个GLProrgamState是影响了效果,
那glprogramstate是啥呢?
/**
GLProgramState holds the ‘state’ (uniforms and attributes) of the GLProgram.
A GLProgram can be used by thousands of Nodes, but if different uniform values
are going to be used, then each node will need its own GLProgramState
*/–来自源码中的解释
//uniform和attribute是OpenGL中的变量类型,简单来说就是glprogramstate就是带有参数的glprogram
那么GLProram是啥呢?
glprogram能够编译链接我们提供的顶点着色器文件和片段着色器。
那顶点着色器和片段着色器是啥呢?
简单来说:顶点着色器就是能够在渲染的时候改变渲染顶点的位置,片段着色器能够改变片段的颜色。具体渲染管线以后再说。
ShaderSprite::VertexShader是顶点着色器,这个顶点着色器只是将持续传进来的位置直接输出,以及将颜色直接传给片段着色器,并无其它操作,大家想调调参数啥的不用动这个。
ShaderSprite::FragmentShader是片段着色器,通过这里面的两行来进行效果的改变。(可以通过动这两行来调效果)
float factor = (sin(u_time)+1.0)/3.0;
vec4 outcolor = vec4(0.8, 0.8, 0.8, 0.8)factor + incolor(1.0-factor);
其实很简单,将传进来的参数u_time对源颜色和vec4(0.8, 0.8, 0.8, 0.8)进行差值计算并输出。
在shaderSprite有一个参数u_time每帧更新,并且赋值给m_pProgramState。这就是shader外部传入参数。
本文给出了一个可运行的效果,大家可以去调效果体验一下,感受下shader的魅力。
其实上面我们能shader中的一些内置变量,内置函数,变量类型,等整理完分享一下。至于着色器原理或者说渲染管线,等我完善下知识再给大家分享,学校学的已经还给老师啦。
本来今天想再去看下事件分发机制的,正好最近效果调的比较多然后同事说分享下shader相关的,就先写了这个。