一、UniformValue
对一个uniform变量的状态(name、location、size、type)、值(union)的封装。
数据结构
class CC_DLL UniformValue {
protected:
// uniform值类型的分类
enum class Type {
VALUE, // 基本类型:int、float、vec、mat
POINTER, // 数值类型:float[1],float[2],float[3],float[4],
CALLBACK_FN // 由外部回调设置值
};
Uniform* _uniform; // 一个uniform变量除值外的状态信息(location、size、type、name),weak reference
GLProgram* _glprogram; // 这个uniform所属的GLProgram,weak reference
Type _type; // the uniform's type
union U { // 这个Uniform变量的值,用共同体结构。里面列出了所有可能的类型值,只能是其中一个
float floatValue; // uniform float shit;
int intValue; // uniform int shit;
float v2Value[2]; // uniform vec2 shit;
float v3Value[3]; // uniform vec3 shit;
float v4Value[4]; // uniform vec4 shit;
float matrixValue[16]; // uniform mat4 shit;
struct {
GLuint textureId;
GLuint textureUnit;
Texture2D* texture;
} tex; // uniform sampler2D shit;
struct {
const float* pointer;
GLsizei size;
} floatv; // uniform float[] shit;
struct {
const float* pointer;
GLsizei size;
} v2f; // uniform float[2] shit;
struct {
const float* pointer;
GLsizei size;
} v3f; // uniform float[3] shit;
struct {
const float* pointer;
GLsizei size;
} v4f; // uniform float[4] shit;
std::function<void(GLProgram*, Uniform*)> *callback; // 由于外部设置这个uniform的值
} _value;
};
class CC_DLL UniformValue
{
public:
// 设置uniform的值
void setFloat(float value);
void setInt(int value);
void setFloatv(ssize_t size, const float* pointer);
void setVec2(const Vec2& value);
void setVec2v(ssize_t size, const Vec2* pointer);
void setVec3(const Vec3& value);
void setVec3v(ssize_t size, const Vec3* pointer);
void setVec4(const Vec4& value);
void setVec4v(ssize_t size, const Vec4* pointer);
void setMat4(const Mat4& value);
// 由外部的callback来设置uniform的值
void setCallback(const std::function<void(GLProgram*, Uniform*)> &callback);
// uniform sampler2D shit;
void setTexture(Texture2D* texture, GLuint textureUnit);
void apply(); // 设置uniform的值
// 重载了赋值操作符
UniformValue& operator=(const UniformValue& o);
};
二、VertexAttribValue
VertexAttribValue是对GL函数glVertexAttribPointer及其参数的封装。相当于保存了一个通用顶点属性的状态,通过这个随时恢复这个状态。
void glVertexAttribPointer( GLuint index, // attribute的location
GLint size, // 分量数量
GLenum type, // 分量数据类型
GLboolean normalized, // 是否需要归一化
GLsizei stride, // 步进长度
const GLvoid* pointer); // 在VBO管理的数据中的偏移字节数
数据结构
class CC_DLL VertexAttribValue {
protected:
VertexAttrib* _vertexAttrib; // active的顶点属性的相关信息(name、size、type、location),weak ref
bool _enabled; // 可以执行glVertexAttribPointer了
bool _useCallback; // 见下面的联合体,用callback还是pointer。
// 方法一:要么通过pointer的数据作为参数,调用glVertexAttribPointer
// 方法二:要么由外部传入的callback自己调用glVertexAttribPointer
union U{ // 联合体
struct {
GLint size;
GLenum type;
GLboolean normalized;
GLsizei stride;
GLvoid *pointer;
} pointer;
std::function<void(VertexAttrib*)> *callback;
} _value;
};
class CC_DLL VertexAttribValue {
public:
// 方法一,参数对照上面glVertexAttribPointer的参数
void setPointer(GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLvoid *pointer);
// 方法二
void setCallback(const std::function<void(VertexAttrib*)> &callback);
void apply(); // 按两种方法中其中一种执行glVertexAttribPointer,恢复这个顶点属性状态。
};
三、GLProgramState
为什么提出State的概念?请看下面着色器,这两个着色器是实现ETC1压缩纹理的alpha支持,做法就是每个ETC1都有一张像素宽高的alpha texture,将这两个texture混合,就达到了alpha效果。
// *******************************************
// ******* 顶 点 着 色 器
// *******************************************
# version 100
uniform mat4 CC_MVPMatrix; // MVP矩阵
attribute vec4 a_position; // 顶点坐标
attribute vec4 a_color; // 顶点颜色
attribute vec2 a_texCoord; // 该顶点对应的纹理坐标
varying vec4 v_fragmentColor; // 顶点颜色传递给片段着色器
varying vec2 v_texCoord; // 顶点纹理坐标传递给片段着色器
void main()
{
gl_Position = CC_MVPMatrix;
v_fragmentColor = a_color;
v_texCoord = a_texCoord
}
// *******************************************
// ******* 片 段 着 色 器
// *******************************************
uniform sampler2D CC_Texture0; // 纹理单元GL_TEXTURE0的纹理数据的采样器
uniform sampler2D CC_Texture1; // 纹理单元GL_TEXTURE1的纹理数据的采样器
varying vec4 v_fragmentColor; // 顶点颜色传递给片段着色器
varying vec2 v_texCoord; // 顶点纹理坐标传递给片段着色器
void main()
{
vec4 texColor = texture2D(CC_Texture0, v_texCoord); // 纹理颜色
float alpha = texture2D(CC_Texture1, v_texCoord); // alpha texture的R值
gl_FragColor = v_fragmentColor * vec4(texColor.rgb * alpha, alpha);
}
实际场景中会有很多的不同的ETC1纹理需要alpha支持,对应在着色器上的区别就是uniform值、attribute值的区别,main函数主体还是一样的。我们完全可以只使用一个GLProgram,然后借助GL函数,动态的改变uniform值、attribute值,这样的话就无需创建多个GLProgram了,cocos就将动态改变的这个过程的信息封装成一个GLProgramState。
这里注意一点,GLProgramState封装的uniform中,只有user define的,并不包含cocos内置的。
1、数据结构
class CC_DLL GLProgramState : public Ref
{
bool _uniformAttributeValueDirty; // dirty flag,是否需要重新获取
// 这里缓存的都是user define uniform,并没有cocos内置的uniform
unordered_map<std::string, GLint> _uniformsByName; // name -> location
unordered_map<GLint, UniformValue> _uniforms; // location -> UniformValue
unordered_map<std::string, VertexAttribValue> _attributes; // name -> VertexAttribValue
unordered_map<std::string, int> _boundTextureUnits; // uniform name -> texture unit
int _textureUnitIndex; // 对应纹理单元:GL_TEXTURE0 +_textureUnitIndex,1 <= _textureUnitIndex <= 4,
// vertex attribute flag,每一位对应location的通用顶点属性的开关,例如:
// _vertexAttribsFlags = 1001001(二进制),则
// glEnableVertexAttribArray(location=0、3、6的attribute)
// glEnableVertexAttribArray(其他location的attribute)
uint32_t _vertexAttribsFlags;
GLProgram* _glprogram; // 关联的gl program object
Node* _nodeBinding; // render的node,weak ref
// contains uniform name and variable
// key: uniform name
// value:
std::unordered_map<std::string, std::string> _autoBindings;
// Map of custom auto binding resolvers.
static std::vector<AutoBindingResolver*> _customAutoBindingResolvers;
};
2、创建
class CC_DLL GLProgramState : public Ref
{
public:
// **************************************************
// !!!注意,只要缓存了GLProgramState,则GLProgram就在GLProgramStateCache中通过cocos2d::Map retain了
// !!!注意,如果我们要操作GLProgramState对象,务必自己retain起来,因为是auto release对象。
// **************************************************
// 创建一个全新的独立的new instance,不会缓存到GLProgramState中。
static GLProgramState* create(GLProgram* glprogram);
// 1、从缓存中查找并返回。如果没有找到,则创建新的并缓存。
static GLProgramState* getOrCreateWithGLProgram(GLProgram* glprogram);
// 1、GLProgram必须在缓存中,否则直接返回nullptr
// 2、从缓存中查找并返回。如果没有找到,则创建新的并缓存。
static GLProgramState* getOrCreateWithGLProgramName(const std::string& glProgramName );
// 这个版本慎用,你先看懂源码吧。
// 1、GLProgram必须在缓存中。
// 2、从缓存中查找并返回。如果没有找到,则创建新的并缓存。
// 3、texture森
static GLProgramState* getOrCreateWithGLProgramName(const std::string& glProgramName, Texture2D* texture);
// 1、先确保缓存了GLProgram(vertexShader + "+" + fragShader + "+" + compileTimeDefines)
// 2、然后返回一个全新独立的GLProgramState instance
static GLProgramState* getOrCreateWithShaders(const std::string& vertexShader, const std::string& fragShader, const std::string& compileTimeDefines);
// 返回一个全新独立的GLProgramState instance
GLProgramState* clone() const;
};
3、更新uniform值(状态)
注意,只能更新user define uniform。
class CC_DLL GLProgramState : public Ref
{
// 修改uniformValue的值,注意并没有立即生效,仅仅是设置到uniformValue的状态中了。调用apply生效。
void setUniformInt(const std::string& uniformName, int value);
void setUniformFloat(const std::string& uniformName, float value);
void setUniformFloatv(const std::string& uniformName, ssize_t size, const float* pointer);
void setUniformVec2(const std::string& uniformName, const Vec2& value);
void setUniformVec2v(const std::string& uniformName, ssize_t size, const Vec2* pointer);
void setUniformVec3(const std::string& uniformName, const Vec3& value);
void setUniformVec3v(const std::string& uniformName, ssize_t size, const Vec3* pointer);
void setUniformVec4(const std::string& uniformName, const Vec4& value);
void setUniformVec4v(const std::string& uniformName, ssize_t size, const Vec4* pointer);
void setUniformMat4(const std::string& uniformName, const Mat4& value);
void setUniformCallback(const std::string& uniformName, const std::function<void(GLProgram*, Uniform*)> &callback);
void setUniformTexture(const std::string& uniformName, Texture2D *texture);
void setUniformInt(GLint uniformLocation, int value);
void setUniformFloat(GLint uniformLocation, float value);
void setUniformFloatv(GLint uniformLocation, ssize_t size, const float* pointer);
void setUniformVec2(GLint uniformLocation, const Vec2& value);
void setUniformVec2v(GLint uniformLocation, ssize_t size, const Vec2* pointer);
void setUniformVec3(GLint uniformLocation, const Vec3& value);
void setUniformVec3v(GLint uniformLocation, ssize_t size, const Vec3* pointer);
void setUniformVec4(GLint uniformLocation, const Vec4& value);
void setUniformVec4v(GLint uniformLocation, ssize_t size, const Vec4* pointer);
void setUniformMat4(GLint uniformLocation, const Mat4& value);
void setUniformCallback(GLint uniformLocation, const std::function<void(GLProgram*, Uniform*)> &callback);
void setUniformTexture(GLint uniformLocation, Texture2D *texture);
}
4、更新attribute值(状态)
class CC_DLL GLProgramState : public Ref
{
// 设置vertex attribute value,见VertexAttribValue
// 注意,不会立即生效,而是设置到了状态中。
void setVertexAttribCallback(const std::string& name, const std::function<void(VertexAttrib*)> &callback);
void setVertexAttribPointer(const std::string& name, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLvoid *pointer);
}
5、apply:值更改生效
class CC_DLL GLProgramState : public Ref
{
// 调用下面的3个apply,modelView是MV
void apply(const Mat4& modelView);
// 1、glUseProgram();
// 2、更新所有内置uniform的值,modelView为MV。
// 注意哦,没有更新attribute
void applyGLProgram(const Mat4& modelView);
// 使所有attribute的通用顶点属性生效,调用glVertexAttribPointer
// applyAttribFlags: 在这之前是否需要先调用glEnableVertexAttribArray/glDisableVertexAttribArray
void applyAttributes(bool applyAttribFlags = true);
// 使所有uniform值的更改生效,调用glUniform()
void applyUniforms();
}