学习OpenGLES第一天:如何绘制一个正方形
学习OpenGLES第一天:如何绘制一个正方形
介绍
通过绘制一个正方形来理解一下OpenGLES的基础知识,查漏补缺,增强理解。
OpenGLES坐标系统
首先OpenGL的坐标系是一个归一化的笛卡尔坐标系,即中心点的坐标值是0,其最大最小值范围是[-1,1]。因此无论屏幕多大,最终都会被映射到[-1,1]上。那openGL是怎么样知道坐标的映射关系呢?通过视口。
glViewport(x, y, width, height);
glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)。
通过这四个参数还有上面的说明可以看出来视口是个矩形,中心点坐标是0,最大最小值是[-1,1],那么视口的位置和大小就映射到了最终的坐标值上。
既然有映射,那么肯定就有转换。一张图片如果希望按照自身正常的比例绘制到视口内,需要进行一系列的矩阵运算。
着色器
说明
着色器是OpenGL里面最重要的东西,也是必须掌握的东西。如何理解着色器?每一个像素点在显示到画面上之前都会经过着色器的运算,我觉得可以把着色器理解为一种加窗行为。
顶点着色器
#version 300 es
precision highp float;
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec3 aTexAttr;
uniform highp mat4 mvp;
out mediump vec2 vTexPosition;
void main()
{
gl_Position = mvp * vec4(aPosition, 1.0);
vTexPosition = aTexAttr.xy;
}
像素着色器
#version 300 es
precision mediump float;
in vec2 vTexPosition;
uniform float uAlpha;
uniform sampler2D uTexture;
out vec4 fragColor;
void main()
{
fragColor = texture(uTexture, vTexPosition) * uAlpha;
}
浮点的精度规则
- highp – 32位浮点格式,适合用于顶点变换,但性能最慢。
- mediump – 16位浮点格式,适用于纹理UV坐标和比highp 大约快两倍
- lowp – 8位的顶点格式,适合对颜色,照明计算和其它高性能操作,速度大约是highp 的4倍
着色器代码中第一行定义了当前使用的着色器的版本,不同的版本有着不同的关键字、方法名和内置变量,我这里使用的是3.0版本的着色器。第二行呢则是浮点精度规则,如上所示。其它的则是顶点坐标、纹理坐标、矩阵相关的一些东西。texture方法呢,则是从纹理里面取出vTexPosition位置的像素。这些东西可以看一下glsl相关的书籍。
数组
顶点数组
顶点数组是我要在画面上画什么形状而这个形状的顶点坐标是什么的数组。因为我要画的是一个正方形,所以我就定义一个正方形的数据。
const GLfloat rectPositions[] = {
1.0f, -1.0f, 0.0f, //右下
1.0f, 1.0f, 0.0f, //右上
-1.0f, 1.0f, 0.0f, //左上
-1.0f, -1.0f, 0.0f, //左下
};
那么现在问题来了,一个正方形能够有几种定义顶点数组的方式?这个得根据使用不同的api或者不同的参数有不同的定义顶点数组的方式。比如 三角形、三角形扇 和 三角形带 决定了顶点的绘制顺序,glDrawElements 和 glDrawArrays 则决定了是否使用索引的方式去绘制。
纹理数组
纹理数组就是我需要按照一个什么顺序,从这个纹理上读取数据。那怎么从纹理上读数据,
就是按照左上角是[0,0],右下角是[width, height]这么个逻辑来读取数据,在opengl上也是一组归一化的坐标,且与顶点坐标有着相同的顺序。
const GLfloat texPosition[] = {
1.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
};
索引数组
最后就是索引数组,索引数组决定了上面两个数据内数据的顺序。那根据上面的坐标,不同的绘制三角形的方式定义的索引数组不一样。
GL_TRIANGLE_STRIP 定义的索引数组是0132
GL_TRIANGLE_FAN 定义的索引数组是0123
GL_TRIANGLE 定义的索引就很多样化了,只要能组合成矩形的三角形都可以
为什么这么定义,可以去看下opengl 三角形序列的绘制方式。
缓冲区
既然定义了各个数据,那么就需要缓冲区去保存起来了,不用缓冲区行不行啊。也行!!!但是每次绘制都要把各个数组从cpu内存拷贝到gpu显存,有点浪费资源。所以就出现了各种各样的缓冲区,能够把这些数据缓存在显存中。
VBO
顶点缓冲对象,也没啥好说的,就初始化个缓冲区,然后把数组都丢进去
glGenBuffers(1, this->vbo);
//顶点缓冲区,绑定VBO,保存顶点数据
glBindBuffer(GL_ARRAY_BUFFER, this->vbo[0]);
//初始化缓冲区的大小
glBufferData(GL_ARRAY_BUFFER, sizeof(rectPositions) + sizeof(texPosition), NULL, GL_STATIC_DRAW);
//把数据都丢进去
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(rectPositions), cubePositions);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(rectPositions), sizeof(texPosition), color);
IBO
索引缓冲区,使用方式和VBO一样
glGenBuffers(1, this->ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->ebo[0]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indexs), Indexs, GL_STATIC_DRAW);
矩阵计算
这个可以看一本书叫做<<3D数学基础:图形与游戏开发>>
绘制
opengles有两种类型的绘制。一种是DrawElements* 这种类型主要是使用索引坐标。另一种是DrawArrays 这种类型是不使用索引坐标。废话!!!那两者在定义顶点坐标的时候就有所不一样,前者的数组会比后者小很多。因为前者只需要确定好图形的顶点,然后通过索引坐标组成三角形。而后者则需要在顶点坐标里就把所有的三角形组装好。
代码
懒得写。
预告
学习OpenGLES第二天:如何绘制一个正方体