在上一篇文章中,我们介绍了如何绘制一个立方体,里面涉及的知识点有VBO(Vertex Buffer Object)、IBO(Index Buffer Object)和MVP(Modile-View-Projection)变换。

本文将在基于Cocos2d-x学习OpenGL ES 2.0系列——使用VBO索引(4)的基础之上,添加纹理贴图支持。最后,本文会把纹理贴图扩展至3D立方体上面。

基本方法

当我们把一张图片加载到内存里面之后,它是不能直接被GPU绘制出来的,纹理贴图过程如下

  • 首先,我们为之前的顶点添加纹理坐标属性并传到vertex shader里面去;

  • 然后,把内存里面的纹理传给GPU(glTexImage2D,可通过glBindTexture将纹理绑定到纹理对象上,这样就不用每一次都加载纹理,提高效率)

  • 最后,在fragment shader里面通过采样器,就可以根据vertex shader传递过来的纹理坐标把纹理上面的颜色值用插值的方式映射到每一个像素上去。

接下来,让我们看看具体怎么做。

准备纹理坐标(纹理坐标也叫UV坐标)

首先,我们需要修改我们的顶点属性结构体,添加一个纹理坐标属性(TexCoord):

typedef struct {
float Position[];
float Color[];
float TexCoord[];
} Vertex;

接下来,需要修改顶点数组的值,主要就是添加UV坐标:

Vertex data[] =
{
{{-,-},{,,,},{,}},
{{,-},{,,,},{,}},
{ {-,},{,,,},{,}},
{{,},{,,,},{,}}
};

注意,我们的纹理坐标的(0,0)点在图片的左上角,这个与OpenGL里面的左下角是(0,0)有所区别。所以为了让我们的图片显示正常,我们在指定左下角顶点(-1,-1)的时候,它对应的纹理坐标应该是(0,1)。其它的坐标点以此类推。

生成纹理

纹理的使用与之前的VBO使用方式差不多,都是先glGenXXX,然后glBindXXX,然后再把绑定数据。

首先,我们在头文件里面定义一个纹理的句柄:

GLuint textureId;

然后是生成纹理:

glGenTextures(, &textureId);

绑定纹理

一旦纹理生成好以后,我们就需要把实际的纹理数据通过这个id传递给GPU。在传递纹理到GPU之前,

我们首先要把一张图片读到内存里面来。因为GPU是不认识png图片的,它只认识二进制的图像数组:

Image *image = new Image;
std::string imagePath = FileUtils::getInstance()->fullPathForFilename("HelloWorld.png");
image->initWithImageFile(imagePath);

然后,我们需要设置纹理在放大或者缩小的时候的插值方法(filter)以及在指定的纹理坐标超出(0-1)的范围的时候应该采用的策略(Wrap):

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );

最后,我们把纹理数据传递给GPU。

unsigned char *imageData = image->getData();
int width = image->getWidth();
int height = image->getHeight();
//调用此方法把imageData的图像数据传递给GPU
glTexImage2D(GL_TEXTURE_2D, , GL_RGB, width, height, , GL_RGB,
GL_UNSIGNED_BYTE,//must be GL_UNSIGNED_BYTE
imageData);
//别忘了释放image内存
CC_SAFE_DELETE(image);

接下来,我需要处理Shader了。

修改Shader

首先,修改vertex shader,添加纹理坐标属性:

attribute vec2 a_position;
attribute vec4 a_color;
attribute vec2 a_coord; varying vec4 v_fragmentColor;
varying vec2 v_coord; void main()
{
gl_Position = CC_MVPMatrix * vec4(a_position.xy,,);
v_fragmentColor = a_color;
v_coord = a_coord;
}

因为纹理坐标最终要传递到fragment shader里面去,所以需要定义一个varing vec2 v_coord变量。

接下来是fragment shader的代码:

varying vec4 v_fragmentColor;
varying vec2 v_coord; uniform vec4 u_color; void main()
{
gl_FragColor = v_fragmentColor * texture2D(CC_Texture0,v_coord);
}

这边也定义了一个同样的varing变量,同时我们看到有一个texture2D函数,它可以通过CC_Texture0这个采样器和纹理坐标(v_coord)计算出对应的颜色值。

修改draw call

在调用draw call之前,我们需要绑定纹理。我们只需要在glDrawElements方法之前调用下列方法就可以了:

GL::bindTexture2D(textureId);

运行结果

接下来,我们需要把立方体的六个面都添加这张纹理。

让立方体不再裸奔

这个过程大部分代码都是一样的,惟一的区别就是顶点数组的修改,我们需要为每一个面的顶点都指定UV坐标:

#define TEX_COORD_MAX   1
Vertex Vertices[] = {
// Front
{{, -, }, {, , , }, {TEX_COORD_MAX, }},
{{, , }, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{-, , }, {, , , }, {, TEX_COORD_MAX}},
{{-, -, }, {, , , }, {, }},
// Back
{{, , -}, {, , , }, {TEX_COORD_MAX, }},
{{-, -, -}, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{, -, -}, {, , , }, {, TEX_COORD_MAX}},
{{-, , -}, {, , , }, {, }},
// Left
{{-, -, }, {, , , }, {TEX_COORD_MAX, }},
{{-, , }, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{-, , -}, {, , , }, {, TEX_COORD_MAX}},
{{-, -, -}, {, , , }, {, }},
// Right
{{, -, -}, {, , , }, {TEX_COORD_MAX, }},
{{, , -}, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{, , }, {, , , }, {, TEX_COORD_MAX}},
{{, -, }, {, , , }, {, }},
// Top
{{, , }, {, , , }, {TEX_COORD_MAX, }},
{{, , -}, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{-, , -}, {, , , }, {, TEX_COORD_MAX}},
{{-, , }, {, , , }, {, }},
// Bottom
{{, -, -}, {, , , }, {TEX_COORD_MAX, }},
{{, -, }, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{-, -, }, {, , , }, {, TEX_COORD_MAX}},
{{-, -, -}, {, , , }, {, }}
};

下面是立方体的六个面贴上纹理之后的效果:

结语

其实我们在生成纹理的时候,还可以使用下面两种简化的方法:

//method2: the easier way
Texture2D *texture = new Texture2D;
texture->initWithImage(image);
textureId = texture->getName();
//method3: the easiest way
Sprite *sprite = Sprite::create("HelloWorld.png");
textureId = sprite->getTexture()->getName();

3D旋转立方体(带纹理贴图)源代码下载

单个图片的纹理贴图源码下载

Reference

http://open.gl/textures

纹理立方体

来源网址:http://4gamers.cn/archives/551

基于Cocos2d-x学习OpenGL ES 2.0系列——纹理贴图(6)的更多相关文章

  1. 基于Cocos2d-x学习OpenGL ES 2.0系列——使用VBO索引(4)

    在上一篇文章中,我们介绍了uniform和模型-视图-投影变换,相信大家对于OpenGL ES 2.0应该有一点感觉了.在这篇文章中,我们不再画三角形了,改为画四边形.下篇教程,我们就可以画立方体了, ...

  2. 基于Cocos2d-x学习OpenGL ES 2.0系列——你的第一个三角形(1)

    前言 在本系列教程中,我会以当下最流行的2D引擎Cocos2d-x为基础,介绍OpenGL ES 2.0的一些基本用法.本系列教程的宗旨是OpenGL扫盲,让大家在使用Cocos2d-x过程中,知其然 ...

  3. 基于Cocos2d-x学习OpenGL ES 2.0系列——OpenGL ES渲染之Shader准备(7)

    Cocos2d-x底层图形绘制是使用OpenGL ES协议的.OpenGL ES是什么呢? OpenGL ES(OpenGl for Embedded System)是OpenGL三维图形API的子集 ...

  4. 基于Cocos2d-x学习OpenGL ES 2.0系列——编写自己的shader(2)

    在上篇文章中,我给大家介绍了如何在Cocos2d-x里面绘制一个三角形,当时我们使用的是Cocos2d-x引擎自带的shader和一些辅助函数.在本文中,我将演示一下如何编写自己的shader,同时, ...

  5. 基于Cocos2d-x学习OpenGL ES 2.0系列——OpenGL ES渲染之LayerColor(8)

    在前面文章中讲述了Cocos2d-x引擎OpenGL渲染准备Shader方面,本文主要讲解使用LayerColor来讲述OpenGL的渲染过程. 1.LayerColor对象创建 添加LayerCol ...

  6. 基于Cocos2d-x学习OpenGL ES 2.0系列——你的第一个立方体(5)

    在上篇文章中,我们介绍了VBO索引的使用,使用VBO索引可以有效地减少顶点个数,优化内存,提高程序效率. 本教程将带领大家一起走进3D--绘制一个立方体.其实画立方体本质上和画三角形没什么区别,所有的 ...

  7. 基于Cocos2d-x学习OpenGL ES 2.0系列——初识MVP(3)

    在上一篇文章中,我在介绍vertex shader的时候挖了一个坑:CC_MVPMatrix.它其实是一个uniform,每一个Cocos2d-x预定义的shader都包含有这个uniform,但是如 ...

  8. 基于Cocos2d-x学习OpenGL ES 2.0之多纹理

    没想到原文出了那么多错别字,实在对不起观众了.介绍opengl es 2.0的不多.相信介绍基于Cocos2d-x学习OpenGL ES 2.0之多纹理的,我是独此一家吧.~~ 子龙山人出了一个系列: ...

  9. OpenGL ES课程VI之纹理贴图(原文对照)

    http://www.educity.cn/wenda/92368.html OpenGL ES教程VI之纹理贴图(原文对照) OpenGL ES Tutorial for Android – Par ...

随机推荐

  1. java面试2

    1.面向对象的特征有哪些方面   1.抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节.抽 ...

  2. sparkStreaming 练习

    val updateFunc2 = (iter:Iterator[(String,Seq[Int],Option[Int])])=>{ iter.map{case (x,y,z) => / ...

  3. linux中的ll(转)

    linux中的ll(转) 操作系统:ubuntu 9.04 ll并不是linux下一个基本的命令,它实际上是ls -l的一个别名. Ubuntu默认不支持命令ll,必须用 ls -l,这样使用起来不是 ...

  4. 【高可用HA】Apache (4) —— Mac下配置Apache Httpd负载均衡(Load Balancer)之mod_jk

    Mac下配置Apache Httpd负载均衡(Load Balancer)之mod_jk httpd版本: httpd-2.4.17 jk版本: tomcat-connectors-1.2.41 参考 ...

  5. mysql pdo事务

    /* 开始一个事务,关闭自动提交 */直到调用commit结束事务时才提交 $dbh->beginTransaction(); bool PDO::commit ( void ) 提交一个事务, ...

  6. R语言基于S4的面向对象编程

    前言 本文接上一篇文章 R语言基于S3的面向对象编程,本文继续介绍R语言基于S4的面向对象编程. S4对象系统具有明显的结构化特征,更适合面向对象的程序设计.Bioconductor社区,以S4对象系 ...

  7. 使用Backbone构建精美应用的7条建议

    我们在Bizzabo使用Backbone.js已经有两年的时间了.我们从一个小的网页应用为开始,这些小应用是由Backbone驱动的,而且他们看上去非常友好. Backbone天生就不固执己见.你从文 ...

  8. jump display

    查找了数据库,再在while里拼接成json是很麻烦的,所以,jump display 获得数组 <?php header("Content-Type:text/html; chars ...

  9. 关于Cocos2d-x中多边形物理刚体的设置

    1.如果想要设置某个物体有多边形的刚体,这样可以更精确地进行碰撞检测,可以用以下的方法 auto hero = PlaneHero::create(); addChild(hero, 0, HERO_ ...

  10. 图像边缘检測--OpenCV之cvCanny函数

    图像边缘检測--OpenCV之cvCanny函数 分类: C/C++ void cvCanny( const CvArr* image, CvArr* edges, double threshold1 ...