Cocos2d-x 3.x 图形学渲染系列十六
Posted 海洋_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Cocos2d-x 3.x 图形学渲染系列十六相关的知识,希望对你有一定的参考价值。
笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。
每个引擎都有自己的处理Shader类,Cocos使用的是GLProgram类,之所以定义GLProgram类,是因为在引擎中需要有一个类管理模型的信息和矩阵信息声明。在GLProgram类中定义了模型顶点的属性,这些属性在加载模型时,用于解释模型文件内容时用于做属性标记处理,它是使用枚举定义的,枚举类代码如下所示:
enum
{
/**索引0用于定义位置*/
VERTEX_ATTRIB_POSITION,
/**索引1用于定义颜色*/
VERTEX_ATTRIB_COLOR,
/**索引2用于定义纹理坐标单元0*/
VERTEX_ATTRIB_TEX_COORD,
/**索引3用于定义纹理坐标单元1.*/
VERTEX_ATTRIB_TEX_COORD1,
/**索引4用于定义纹理坐标单元2.*/
VERTEX_ATTRIB_TEX_COORD2,
/**索引5用于定义纹理坐标单元3.*/
VERTEX_ATTRIB_TEX_COORD3,
/**索引6用于定义法线*/
VERTEX_ATTRIB_NORMAL,
/**索引7用于定义混合权重*/
VERTEX_ATTRIB_BLEND_WEIGHT,
/**索引8用于定义混合索引.*/
VERTEX_ATTRIB_BLEND_INDEX,
/**索引9用于定义正切.*/
VERTEX_ATTRIB_TANGENT,
/**索引10用于定义次法线.*/
VERTEX_ATTRIB_BINORMAL,
VERTEX_ATTRIB_MAX,
VERTEX_ATTRIB_TEX_COORDS = VERTEX_ATTRIB_TEX_COORD,
};
在后面有关模型章节时,会有这方面的实际操作,接下来GLProgram类还定义了Shader中经常用于计算矩阵转换的标记,也是以枚举形式定义的如下所示:
enum
{
/**环境颜色.*/
UNIFORM_AMBIENT_COLOR,
/**投影矩阵*/
UNIFORM_P_MATRIX,
/**模型视口矩阵.*/
UNIFORM_MV_MATRIX,
/**模型视口投影矩阵.*/
UNIFORM_MVP_MATRIX,
/**法线矩阵.*/
UNIFORM_NORMAL_MATRIX,
/**时间.*/
UNIFORM_TIME,
/**sin(时间).*/
UNIFORM_SIN_TIME,
/**cos(时间).*/
UNIFORM_COS_TIME,
/**随机数字.*/
UNIFORM_RANDOM01,
/** @{
* 对纹理的取样0-3
*/
UNIFORM_SAMPLER0,
UNIFORM_SAMPLER1,
UNIFORM_SAMPLER2,
UNIFORM_SAMPLER3,
/**@}*/
UNIFORM_MAX,
};
GLProgram类不只是只定义枚举属性,它还提供了加载顶点着色器和片段着色器接口函数,通过这些接口开发者可以知道它在加载Shader脚本时是如何解释其内容的。加载Shader脚本函数如下所示:
GLProgram* GLProgram::createWithFilenames(const std::string& vShaderFilename, const std::string& fShaderFilename, const std::string& compileTimeDefines)
{
auto ret = new (std::nothrow) GLProgram();
if(ret && ret->initWithFilenames(vShaderFilename, fShaderFilename, compileTimeDefines)) {
ret->link();
ret->updateUniforms();
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
createWithFilenames函数中的参数是顶点着色器和片段着色器的文件路径,函数内部调用了initWithFilenames函数,继续深入进去查看该函数执行细节,内容如下所示:
bool GLProgram::initWithFilenames(const std::string& vShaderFilename, const std::string& fShaderFilename, const std::string& compileTimeDefines)
{
auto fileUtils = FileUtils::getInstance();
std::string vertexSource = fileUtils->getStringFromFile(FileUtils::getInstance()->fullPathForFilename(vShaderFilename));
std::string fragmentSource = fileUtils->getStringFromFile(FileUtils::getInstance()->fullPathForFilename(fShaderFilename));
return initWithByteArrays(vertexSource.c_str(), fragmentSource.c_str(), compileTimeDefines);
}
程序加载了顶点着色器和片段着色器,也就是将Shader文件加载到内存中,接下来开始编译Shader脚本了,编译Shader脚本的函数是initWithByteArrays,它的具体实现代码如下所示:
bool GLProgram::initWithByteArrays(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray, const std::string& compileTimeDefines)
{
_program = glCreateProgram();
CHECK_GL_ERROR_DEBUG();
std::string replacedDefines = "";
replaceDefines(compileTimeDefines, replacedDefines);
_vertShader = _fragShader = 0;
if (vShaderByteArray)
{
if(!compileShader(&_vertShader, GL_VERTEX_SHADER, vShaderByteArray, replacedDefines))
{
CCLOG("cocos2d: ERROR: Failed to compile vertex shader");
return false;
}
}
// 创建和编译片段着色器
if (fShaderByteArray)
{
if(!compileShader(&_fragShader, GL_FRAGMENT_SHADER, fShaderByteArray, replacedDefines))
{
CCLOG("cocos2d: ERROR: Failed to compile fragment shader");
return false;
}
}
if (_vertShader)
{
glAttachShader(_program, _vertShader);
}
CHECK_GL_ERROR_DEBUG();
if (_fragShader)
{
glAttachShader(_program, _fragShader);
}
_hashForUniforms.clear();
CHECK_GL_ERROR_DEBUG();
return true;
}
程序加载了顶点着色器和片段着色器,也就是将Shader文件加载到内存中,接下来开始编译Shader脚本了,编译Shader脚本的函数是initWithByteArrays,它的具体实现如下所示:
bool GLProgram::compileShader(GLuint* shader, GLenum type, const GLchar* source, const std::string& convertedDefines)
{
GLint status;
if(!source)
{
return false;
}
const GLchar *sources[] = {
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
(type == GL_VERTEX_SHADER ?"precision mediump float;\\n precision mediump int;\\n" : "precision mediump float;\\n precision mediump int;\\n"),
#elif (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 && CC_TARGET_PLATFORM != CC_PLATFORM_LINUX && CC_TARGET_PLATFORM != CC_PLATFORM_MAC)
(type == GL_VERTEX_SHADER ?"precision highp float;\\n precision highp int;\\n" : "precision mediump float;\\n precision mediump int;\\n"),
#endif
COCOS2D_SHADER_UNIFORMS,
convertedDefines.c_str(),
source};
*shader = glCreateShader(type);
glShaderSource(*shader, sizeof(sources)/sizeof(*sources), sources, nullptr);
glCompileShader(*shader);
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
if (! status)
{
GLsizei length;
glGetShaderiv(*shader, GL_SHADER_SOURCE_LENGTH, &length);
GLchar* src = (GLchar*)malloc(sizeof(GLchar) * length);
glGetShaderSource(*shader, length, nullptr, src);
CCLOG("cocos2d: ERROR: Failed to compile shader:\\n%s", src);
if (type == GL_VERTEX_SHADER)
{
CCLOG("cocos2d: %s", getVertexShaderLog().c_str());
}
else
{
CCLOG("cocos2d: %s", getFragmentShaderLog().c_str());
}
free(src);
return false;
}
return (status == GL_TRUE);
}
函数的主要作用是对编写的Shader代码进行逐行解释,整个Shader底层加载编译就完成了,在明白其运行原理后,开始编写逻辑,假设已经有了顶点着色器代码zerklo.vert和片段着色器zerklo.frag,需将其传入到函数的参数中,调用的代码片段如下:
auto glprogram_Zerkalo = GLProgram::createWithFilenames("astronaut/zerkalo.vert", "astronaut/zerkalo.frag");
auto _state_Zerkalo = GLProgramState::getOrCreateWithGLProgram(glprogram_Zerkalo);
sprite_Zerkalo->setGLProgramState(_state_Zerkalo);
代码片段是加载Shader的顶点着色器和片段着色器,加载完成将其作用到材质上。当然加载Shader 的方式有很多种,也可以直接使用材质脚本加载,后面章节会有具体介绍,下面系列开始介绍顶点索引数据类。
以上是关于Cocos2d-x 3.x 图形学渲染系列十六的主要内容,如果未能解决你的问题,请参考以下文章