Sprite使用的shader
- vertexes
attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
void main()
{
gl_Position = CC_PMatrix * a_position;
v_fragmentColor = a_color;
v_texCoord = a_texCoord;
}
- fragment
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
void main()
{
gl_FragColor = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
}
CCRenderer.cpp
drawBatchedTriangles(){
for (int i=0; i<batchesTotal; ++i)
{
_triBatchesToDraw[i].cmd->useMaterial();
glDrawElements(GL_TRIANGLES, (GLsizei) _triBatchesToDraw[i].indicesToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (_triBatchesToDraw[i].offset*sizeof(_indices[0])) );
}
}
在draw之前,会将材质同步进行设置。
Sprite使用的TrianglesCommand,TrianglesCommand在useMaterial中会绑定纹理
void TrianglesCommand::useMaterial() const
{
//Set texture
GL::bindTexture2D(_textureID);
if (_alphaTextureID > 0)
{ // ANDROID ETC1 ALPHA supports.
GL::bindTexture2DN(1, _alphaTextureID);
}
//set blend mode
GL::blendFunc(_blendType.src, _blendType.dst);
// 将uniform,attribute同步到shader
_glProgramState->apply(_mv);
}
GL::bindTexture2D(_textureID)逻辑,里面做了cache,如果当前的纹理单元已经绑定了相同的纹理对象,就不再进行额外的active,bind逻辑
void bindTexture2D(GLuint textureId)
{
GL::bindTexture2DN(0, textureId);
}
void bindTexture2DN(GLuint textureUnit, GLuint textureId)
{
#if CC_ENABLE_GL_STATE_CACHE
CCASSERT(textureUnit < MAX_ACTIVE_TEXTURE, "textureUnit is too big");
if (s_currentBoundTexture[textureUnit] != textureId)
{
s_currentBoundTexture[textureUnit] = textureId;
activeTexture(GL_TEXTURE0 + textureUnit);
glBindTexture(GL_TEXTURE_2D, textureId);
}
#else
glActiveTexture(GL_TEXTURE0 + textureUnit);
glBindTexture(GL_TEXTURE_2D, textureId);
#endif
}
最后的_glProgramState->apply()会再次同步shader变量
void GLProgramState::apply(const Mat4& modelView)
{
applyGLProgram(modelView);
applyAttributes();
applyUniforms(); // 同步sampler的重点逻辑
}
void GLProgramState::applyUniforms()
{
// set uniforms
updateUniformsAndAttributes();
for(auto& uniform : _uniforms) {
uniform.second.apply();
}
}
uniform的同步
void UniformValue::apply()
{
if (_type == Type::CALLBACK_FN) {
switch (_uniform->type) {
case GL_SAMPLER_2D:
// 这里最终就是调用到了glUniform1i
_glprogram->setUniformLocationWith1i(_uniform->location, _value.tex.textureUnit);
// 再次确认绑定好纹理单元
GL::bindTexture2DN(_value.tex.textureUnit, _value.tex.textureId);
break;
}
}
}
总结纹理绑定的流程
在fragment中需要定义一个sampler
uniform sampler2D texture;
void bindTextureUnit(int textureUnit, GLunit textureId){
// 先激活对应的纹理单元
glActiveTexture(GL_TEXTURE0+textureUnit);
// 将创建的纹理对象绑定到已经激活的纹理单元上
glBindTexture(GL_TEXTURE_2D, textureId);
// -----------上下2部分逻辑可以分离---------
// glLinkProgram的结果,对shader的所有操作都依赖这个id
GLuint shderProgram;
// 获取shader fragment中的texture
GLuint location = glGetUniformLocation(shderProgram, "texture");
// 将sampler2D指定纹理单元
glUniform1i(location, textureUnit);
}
- textureUnit
纹理单元,从0开始
- textureId:本质上都是有
glGenTextures生成的
从cocos2dx来说,它就是
texture->getName()
从OpenGL的角度来说,
GLuint textureId;
glGenTextures(1, &textureId);
因为OpenGL是个状态机,所以textureId会记录所有的操作痕迹。
知识点
为啥这么写:GL_TEXTURE0+textureUnit
#define GL_TEXTURE0 0x84C0
#define GL_TEXTURE1 0x84C1
#define GL_TEXTURE2 0x84C2
#define GL_TEXTURE3 0x84C3
#define GL_TEXTURE4 0x84C4
#define GL_TEXTURE5 0x84C5
#define GL_TEXTURE6 0x84C6
#define GL_TEXTURE7 0x84C7
#define GL_TEXTURE8 0x84C8
#define GL_TEXTURE9 0x84C9
每个TEXTURE的id都是相邻的,对于glActiveTexture来说,只要value正确就能正常工作。