# OpenGLES2之Android&iOS跨平台开发教程（四）绘制矩形并显示纹理

## 正文

#### 1.着色器

attribute vec4 aPosition;                  // 应用程序传入顶点着色器的顶点位置
attribute vec2 aTextureCoord;              // 应用程序传入顶点着色器的顶点纹理坐标
varying vec2 vTextureCoord;                // 用于传递给片元着色器的顶点纹理数据

void main() {
gl_Position = aPosition;
vTextureCoord = aTextureCoord;
}

precision mediump float;       // 设置工作精度
varying vec2 vTextureCoord;    // 接收从顶点着色器过来的纹理坐标
uniform sampler2D uTexture;    // 纹理采样器，代表一幅纹理

void main() {
gl_FragColor = texture2D(uTexture, vTextureCoord);// 进行纹理采样
}

#### 2.矩形

Rectangle.h:

#ifndef OPENGLES_RECTANGLE_H
#define OPENGLES_RECTANGLE_H

#include "Platform.h"

class Rectangle {
public:
GLuint program; // 着色器程序引用
GLuint aPosition; // 顶点位置引用
GLuint aTextureCoord; // 纹理坐标引用
GLuint uTexture;    // 纹理引用
GLuint textureId = 0;

int vertexCount = 0; // 顶点数量

float* vertices = NULL;
float* uv = NULL;

Rectangle(); // 构造函数
~Rectangle();
void init();
void draw();
void setTexture(GLuint texId);

};

#endif //OPENGLES_RECTANGLE_H

Rectangle.cpp:

#include "Rectangle.h"

Rectangle::Rectangle()
{
init();
}

Rectangle::~Rectangle()
{
delete [] vertices;
delete [] uv;
}

void Rectangle::init()
{
vertexCount = 4;
// 顶点坐标
vertices = new float[vertexCount * 3]
{
-0.5, 0.5, 0,
-0.5, -0.5, 0,
0.5, -0.5, 0,
0.5, 0.5, 0,
};

// 纹理坐标
uv = new float[vertexCount * 2]
{
0, 0,
0, 1,
1, 1,
1, 0,
};

// 顶点着色器
char vsh[] = "attribute vec4 aPosition;                  // 应用程序传入顶点着色器的顶点位置\n"
"attribute vec2 aTextureCoord;              // 应用程序传入顶点着色器的顶点纹理坐标\n"
"varying vec2 vTextureCoord;                // 用于传递给片元着色器的顶点纹理数据\n"
"\n"
"void main() {\n"
"    gl_Position = aPosition;\n"
"    vTextureCoord = aTextureCoord;\n"
"}";
// 片元着色器
char fsh[] = "precision mediump float;       // 设置工作精度\n"
"varying vec2 vTextureCoord;    // 接收从顶点着色器过来的纹理坐标\n"
"uniform sampler2D uTexture;    // 纹理采样器，代表一幅纹理\n"
"\n"
"void main() {\n"
"    gl_FragColor = texture2D(uTexture, vTextureCoord);// 进行纹理采样\n"
"}";
// 创建着色器程序
program = createProgram(vsh, fsh);
// 获取着色器中的属性引用
aPosition = glGetAttribLocation(program, "aPosition");
aTextureCoord = glGetAttribLocation(program, "aTextureCoord");
uTexture = glGetUniformLocation(program, "uTexture");
// 使用着色器程序
glUseProgram(program);
// 给着色器传递顶点坐标数据
glVertexAttribPointer(aPosition, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(aPosition);
// 给着色器传递纹理坐标数据
glVertexAttribPointer(aTextureCoord, 2, GL_FLOAT, GL_FALSE, 0, uv);
glEnableVertexAttribArray(aTextureCoord);
}

void Rectangle::draw()
{
// 绘制三角形
glDrawArrays(GL_TRIANGLE_FAN, 0, vertexCount);
}

void Rectangle::setTexture(GLuint texId)
{
textureId = texId;
// 激活纹理单元0
glActiveTexture(GL_TEXTURE0);
// 绑定纹理
glBindTexture(GL_TEXTURE_2D, textureId);
// 给着色器传递纹理数据
glUniform1i(uTexture, 0);
}

#### 3.修改Renderer

Renderer中把绘制三角形的换成矩形，添加了一个setTexture接口函数

#include "Renderer.h"
#include "Platform.h"
#include "Triangle.h"
#include "Rectangle.h"

Triangle *triangle;
Rectangle *rectangle;

void surfaceCreated() {
// 指定刷新颜色缓冲区的颜色
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);

//triangle = new Triangle();
rectangle = new Rectangle();
}

void surfaceChanged(int w, int h) {
ESLog("viewport: %d, %d", w, h);
// 设置视口
glViewport(0, 0, w, h);
}

void drawFrame() {
// 清除颜色缓冲区
glClear(GL_COLOR_BUFFER_BIT);

//triangle->draw();
rectangle->draw();
}

void setTexture(unsigned int texId)
{
rectangle->setTexture(texId);
}

#### 4.Android端图片转纹理

Android端加载本地图片当然就使用Bitmap了，GLUtils中有将Bitmap转换为texture的方法，代码如下：

    public void loadImage(final String path) {
queueEvent(new Runnable() {
@Override
public void run() {
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
int textureId = textures[0];
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeFile(path);
} catch (Exception e) {
e.printStackTrace();
}
if (bitmap != null) {
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
setTexture(textureId);
bitmap.recycle();
}
}
});
}

queueEvent表示将当前操作插入GL线程运行，因为使用了OpenGL相关函数，必须在GL线程中运行，否则会报没有OpenGL上下文错误。

#### 5.iOS端图片转纹理

- (void)loadImage:(NSString*)path
{
NSError* error;
GLKTextureInfo* texture = [GLKTextureLoader textureWithContentsOfFile:path options:nil error:&error];
if (error)
{
NSLog(@"Texture Error:%@", error);
} else {
GLuint name = texture.name;
setTexture(name);
}
}