前言
前一篇介绍了三角形的绘制,这一篇我们来绘制矩形,并加载本地图片转换为纹理显示到矩形上。
正文
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);// 进行纹理采样
}
第一步当然是把shader代码写好,相比上一篇,去掉了顶点颜色,加上了纹理坐标,在片元着色器里使用texture2D进行纹理采样。
2.矩形
Rectangle.h:
#ifndef OPENGLES_RECTANGLE_H
#define OPENGLES_RECTANGLE_H
#include "Platform.h"
#include "Shader.hpp"
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
在C++代码中添加着色器中对应的引用,添加了setTexture函数,用于设置纹理。
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);
}
顶点坐标有四个,按顺序分别是矩形的左上角、左下角、右下角、右上角,纹理坐标也是四个一一对应,注意由于屏幕的坐标系与OpenGL ES纹理坐标系不一样,屏幕坐标系原点在左上角,OpenGL ES纹理坐标系原点在左下角,即两个刚好Y轴是反的,所以顶点纹理坐标要按照屏幕坐标系来,否则显示出来得图像是倒的,可以看到顶点(-0.5, 0.5)对应的纹理坐标是(0, 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端图片转纹理
iOS端图片转纹理更方便,使用GLKTextureLoader,代码如下:
- (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);
}
}
运行效果如下(左iOS 右Android):
![](https://img.haomeiwen.com/i3914288/6564db342be708c5.png)
后记
完整工程下载
网友评论