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 的方式有很多种,也可以直接使用材质脚本加载,后面章节会有具体介绍,下面系列开始介绍顶点索引数据类。

时间: 2024-11-10 11:32:22

Cocos2d-x 3.x 图形学渲染系列十六的相关文章

Cocos2d-x 3.x 图形学渲染系列十五

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解>电子工业出版社等. 在游戏开发中或者是游戏程序员招聘中,都有关于图形学或者引擎高级程序职位.凡是涉及到这些职位的招聘,对于此职位的开发人员都需要会Shader编程或者说GPU编程,同时他们的薪资也是比较高的.目前国内掌握图形学编程的人不是很多,物以稀为贵.Shader编程的主要目的是协

Cocos2d-x 3.x 图形学渲染系列二十八

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解>电子工业出版社等. CSDN视频网址:http://edu.csdn.net/lecturer/144 昨天,刚从丈母娘家回来,继续博客的更新,接着Cocos2d-x 3.x图形学渲染系列二十七继续系列二十八的编写. 接下来读取FBX模型文件信息,首先要做的是把读取的模型信息进行归类并

Cocos2d-x 3.x 图形学渲染系列二十七

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解>电子工业出版社等. CSDN视频网址:http://edu.csdn.net/lecturer/144 笔者以前在游戏公司开发大型MMOARPG游戏时,游戏中使用的模型为了防止产品发布后被破解,程序再做模型导出插件时对模型进行了加密处理.针对模型加密的方式非常多,通常的做法是通过已经编

Hadoop运维记录系列(十六)

应了一个国内某电信运营商集群恢复的事,集群故障很严重,做了HA的集群Namenode挂掉了.具体过程不详,但是从受害者的只言片语中大概回顾一下历史的片段. Active的namenode元数据硬盘满了,满了,满了...上来第一句话就如雷贯耳. 运维人员发现硬盘满了以后执行了对active namenode的元数据日志执行了 echo "" > edit_xxxx-xxxx...第二句话如五雷轰顶. 然后发现standby没法切换,切换也没用,因为standby的元数据和日志是5月

Java设计模式菜鸟系列(十六)原型模式建模与实现

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39997337 原型模式(Prototype):该模式的思想就是将一个对象作为原型,对其进行复制.克隆,产生一个和原对象类似的新对象.而这里的复制有两种:浅复制.深复制. 浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的. 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的.简单来说,就是深复制进行了完全彻底的复制,而浅复

Cocos2D:塔防游戏制作之旅(十六)

编译运行你的app,放置一些炮塔在你的地图上吧!你将看到炮塔在敌人移动如攻击范围时如何立即开始攻击,并且敌人的血条将随着攻击不断减少知道它们被人道毁灭!胜利即将来临了! 哦!Okay,这里只有少数细节还未实现你就可以得到一个完整特性的塔防游戏啦!音效应该是一个不错的尝试.并且尽管不可战胜和极端富裕很好,你的基地还是应该有能力持续抗打的能力 - 并且你需要限制玩家的金币供给. 闪耀着的炮塔:Gotta Polish It All! 开始实现显示玩家剩余的命数 - 以及当玩家失败时发生什么! 打开H

MySQL---数据库从入门走向大神系列(十六)-JavaWeb分页技术实例演示1

分页,是一种将所有数据分段展示给用户的技术.用户每次看到的不 是全部数据,而是其中的一部分,如果在其中没有找到自己想要的内容,用户可以通过指定页码或是点上/下一页的方式进行翻页. 本例演示静态分页,也就是先设置好每页显示10行,再根据总行数,来算出总页数,将所有页数的页号都显示出来. 相关算法(技术): 总行数(num): select count(1) from stud; 每页显示的行数(n): 固定值---已知的一个常量 页数: pageSize= num/n +( (num%n==0)?

Storm系列(十六)架构分析之Executor-Bolt

准备消息循环的数据 函数原型: 1  let[executor-sampler (mk-stats-sampler (:storm-conf executor-data))] 主要功能: 定义tuple-action-fn函数,该函数会根据TaskId获得对应的Bolt对象并调用其executor方法. Bolt输入处理函数 函数原型: 1  tuple-action-fn (fn [task-id ^TupleImpl tuple]) 主要功能: 获得Bolt对应的bolt-obj,调用exe

BizTalk开发系列(十六) XML命名空间

BizTalk开发过程中如果有对XML进行开发操作,比如在自定义代码里操作XML消息或者在Mapping的时候使用Xpath对XML进行操 作.则有机会遇到XML命名空间的问题.常见的是使用Xpath选取节点的时候不知道要不要加上命名空间前缀,或者是什么时候该加什么时候不该加.为此, 做一个Sample来校验一下XML命名空间在XML操作过程中的影响. 名称空间是W3C推荐标准提供的一种统一命名XML文档中的元素和属性的机制.使用名称空间可以明确标识和组合XML文档中来自不同标记词汇表的元素和属