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正确就能正常工作。