大家好,如果您还对深入OpenGL(ES)编程:掌握三角形绘制技巧不太了解,没有关系,今天就由本站为大家分享深入OpenGL(ES)编程:掌握三角形绘制技巧的知识,包括的问题都会给大家分析到,还望可以解决大家的问题,下面我们就开始吧!
渲染pipeline
pipeline 我的理解是应该翻译为“管道”,而不是常见的“管道”。因为它意味着像流水线一样工作,就是将输入的数据一步步转化为屏幕上的像素的过程。而“管道”很容易与管道联系在一起,所以我认为是错误的。 “装配线”让人注意到它本质上是一个流程、一个过程,而不是一个……管道?
任何模型(无论是3D 还是2D)的OpenGL 管道最初都是点数据。例如,正方形是其四个角的坐标,立方体是其八个点的坐标。游戏中一个复杂的角色实际上是由很多点组成的。通过搜索“3D模型”图片,您可以得到直观的感受。因此,整个过程输入的是顶点数据,即Vertex Data。最终呈现给用户的是显示屏上的图像,而图像是由像素组成的,因此输出的是每个像素的颜色。整个过程需要做的是:如何将每个坐标数据转变成屏幕上正确的像素颜色?这张图还是很直观的。蓝色部分是现代OpenGL允许我们编写和参与的部分。Shader译为“着色器”。它是进程中的子程序,负责处理某一阶段的任务。就像流水线上有很多不同的机器和人一样,他们负责部分工作。顶点着色器是第一个着色器,负责处理输入的顶点数据,例如坐标变换、几何着色器和曲面细分着色器,这些都不是必需的。片段着色器现在接受的不是顶点,而是片段,即片段,对应于一个像素单元。这个阶段主要是计算颜色,比如光照计算:当有N个光源时,这个片元的颜色是什么,光线的颜色,物体本身的颜色,片元的朝向等等予以考虑。所以要绘制三角形,需要提供3个点数据并编写顶点着色器和片段着色器。
加载shader
着色器是一个子程序。它有自己的语言glsl,需要编译后才能使用。 glsl和c类似,比如绘制三角形需要的顶点着色器:
const GLchar *vertexShaderSource=
"#版本330 核心n
vec3 位置的布局(位置=0); n
无效主(){n
gl_Position=vec4(位置, 1.0f); n
}n
";首先忽略“n”。这仅适用于多行输入字符串。着色器的内容以#version 开头。
有了着色器代码,下一步是将着色器成本加载到编译器中:
GLuint vertexShader=glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, vertexShaderSource, 0);
glCompileShader(顶点着色器);首先使用glCreateShader生成一个shader对象,然后通过glShaderSource向这个shader提供shader代码,最后编译这个shader: glCompileShader。
如果shader代码写错了,编译后会报错,所以需要检查shader的编译状态:
GLint 成功;
GLchar 信息日志[256];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, 成功);
如果(!成功){
glGetShaderInfoLog(vertexShader, sizeof(infoLog),NULL, infoLog);
std:cout "编译顶点着色器错误: " infoLog std:endl;
返回-1;
}首先使用glGetShaderiv获取着色器的编译状态。这个函数方法也比较常见:
iv 代表int 值。带有此后缀的用于区分返回值或传入值的类型。然后有一个参数表示要获取什么值。这里是GL_COMPILE_STATUS,意思是获取shader的编译状态。如果编译状态为失败,获取日志信息,查看错误发生在哪里:glGetShaderInfoLog。
片段着色器以相同的方式加载:
const GLchar *fragmentShaderSource=
"#版本330 核心n
输出vec4 颜色; n
无效主(){n
颜色=vec4(1.0f, 0.0f, 0.0f, 1.0f); n
}n
";GLuintfragmentShader=glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader,1,fragmentShaderSource,0);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, 成功);
如果(!成功){
glGetShaderInfoLog(fragmentShader, sizeof(infoLog),NULL, infoLog);
std:cout "编译片段着色器错误:" infoLog std:endl;
返回-1;
}
program
着色器加载后,需要将不同的着色器连接在一起,测试是否可以一起使用;这些着色器还需要为渲染过程生成可执行文件(executable)。
所以这个时候就需要一个shader容器,或者说管理器,也就是程序。
程序=glCreateProgram();
glAttachShader(程序, vertexShader);
glAttachShader(程序,fragmentShader);
glLinkProgram(程序);创建一个程序,然后使用glAttachShader将所有一起使用的着色器绑定到该程序中;最后使用glLinkProgram来链接程序。
最后,渲染时,使用glUseProgram(program);指定要使用的程序。
准备数据:VBO和VAO
准备好着色器后,处理流程的逻辑就准备好了。缺少的是数据。
VBO是顶点缓冲对象,是用来存储顶点数据的缓冲对象。 Buffer对象具体是什么?
缓冲区对象是OpenGL 对象,存储由OpenGL 上下文(又名: GPU)分配的未格式化内存数组。
它是GPU中申请的一块内存,用于存储数据。之前有一种直接模式:每次绘制数据都要提交。后来发展成“顶点数组”,数据存储在计算机内存中,绘图时提供位置索引。在当前的缓冲区对象中,数据存储在GPU端,这进一步加快了数据传输的速度。
老师成为缓冲对象
GLuint VBO;
glGenBuffers(1, VBO);
2.然后绑定:
````
glBindBuffer(GL_ARRAY_BUFFER, VBO);生成VBO之后,实际上和其他Buffer对象没有什么区别,所以需要做的就是谁会使用这个数据以及如何使用它。 glBindBuffer 是指定谁将使用它的问题。使用`GL_ARRAY_BUFFER`表示该缓冲区用于存储顶点属性数据。什么是顶点属性?
返回顶点着色器的代码:
vec3 位置的布局(位置=0);
顶点的坐标位置是属性之一。如果该着色器与VBO配合,则从该缓冲对象中读取位置数据。
输入数据
GLfloat 顶点[]={
-0.5f、-0.3f、0.0f、
0.5f、-0.3f、0.0f、
0.0f、0.8f、0.0f
};
glBufferData(GL_ARRAY_BUFFER, sizeof(顶点), 顶点, GL_STATIC_DRAW);
首先输入数据。 glBufferData 将为第一个参数对应的缓冲区对象创建并初始化数据。因为GL_ARRAY_BUFFER之前绑定了VBO,所以VBO输入的是顶点的数据。
4. 如何读取数据
````
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GL_FLOAT), 顶点);
glEnableVertexAttribArray(0);
````
顶点属性无法访问默认数据。使用“glEnableVertexAttribArray”打开它。该参数是属性在着色器代码中的位置。因为vec3中的`layout(location=0)position;`,将position属性的location设置为0,所以这里是开启position的读取能力。
`glVertexAttribPointer` 是决定如何读取数据的关键函数。该函数的原型为:
````
void glVertexAttribPointer( GLuint 索引,
GLint 大小,
GLenum型,
GL布尔标准化,
GLsizei大步走,
const GLvoid * 指针);
````
* 索引指定描述的是哪个属性。传入0表示描述position属性读取数据的方式。
* size为每次读取的数据大小
* type是数据类型,它和size一起决定了每次读取多少内存。这里传入的是3和GL_FLOAT,表示每个顶点的位置读取3个浮点数。
* 归一化是指数据是否需要归一化。所谓“归一化”,就是将值映射到[-1,1](有符号数)或[0,1],有符号数。这里不需要,传入GL_FALSE。
* stride 有很多顶点从这里读取数据。读完一篇后,从哪里开始读下一篇,步幅就是跳过的距离。例如:12 34 56 78,读取3个位置的内存后,如果stride设置为4,则跳转到7开始读取下一个数据。因为一个顶点有3个浮点数,下一个就在它的旁边,所以传入的是3*sizeof(GL_FLOAT),如果传入0也没关系,因为传入0的时候就读完了上一个一篇并从末尾开始阅读下一篇。
* 指针用于指定读取起始位置的偏移量。例如:12 34 56 78,12和56存储的是属性1的数据,34和78存储的是属性2的数据,那么属性2读取的起始位置就不是缓冲区对象的开头,有偏移量。
经过上面一系列的操作,顶点属性就知道了从哪里读取数据(VBO)以及如何读取数据,数据也输入到了VBO中。
5.最后是VAO,即Vertex Array Object。每次绘制对象时,都必须完成上述步骤。除了顶点数据之外,还可能存在索引数据。 VAO对这些状态进行了封装(哪些属性可以读取数据,如何读取这些属性,索引数据是什么等)。绘制时,调用`glBindVertexArray(VAO1);`,则与VAO1关联的所有状态将被启用。如果你随后调用`glBindVertexArray(VAO2);`,你可以立即切换到VAO2的所有数据。它的设计应该便于编码。
因为有了VAO,上面的数据处理可以放到准备阶段,即渲染循环之前,而不是在每个循环中处理。在渲染循环中,只需要`glBindVertexArray`来切换所需的VAO。在准备阶段,哪些州将受到VAO的约束? glBindVertexArray(VAO1);
//数据处理1
glBindVertexArray(VAO2);
//数据处理2
glBindVertexArray(0);
数据处理1中完成的所有操作将绑定到VAO1,数据处理2中完成的处理将绑定到VAO2。也就是说,现在绑定的VAO将用在谁身上。
###渲染循环glUseProgram(program);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glUseProgram 使用program 来启用与program 关联的所有着色器。 glBindVertexArray 启用与VAO 相关的所有数据和读取方法。数据和逻辑都有了,glDrawArrays绘制。
* GL_TRIANGLES表示绘制三角形,用于指定要绘制的图元类型。所有复杂的物体都是由基本图形组成的,包括点(GL_POINTS)、线(GL_LINES)等。
* 0和3指定绘图时使用的点的数据范围,从第0位开始,共3个。由于我只画一个三角形,所以使用3 个点就可以了。
###返回着色器
顶点着色器
version 330 core
布局(位置=0)在vec3 位置;
无效主(){
gl_Position=vec4(位置, 1.0f);
}
* `#version 330 core`声明版本,这里是3.3,core表示使用核心配置文件。另一个是:兼容性。 Compatibility是兼容模式,会保留之前的功能,而core会丢弃那些已经被禁用的功能。直接从核心配置文件开始学习即可。
* `layout (location=0) in vec3 position;` 声明一个vec3 类型的变量。 vec是vector的缩写,即向量。 vec3 是一个3 元素向量,例如rgb 和xyz 坐标。 layout和location用于指定该属性的位置,配合VBO数据读取。
* main函数是主函数,在这里处理顶点。 gl_Position是默认变量,用于将顶点数据输出到下一个阶段。这里的main函数中,只需将vec3改为vec4即可。
片段着色器
version 330 core
输出vec4 颜色;
无效主(){
颜色=vec4(1.0f, 0.0f, 0.0f, 1.0f);
}
* #version 同上
* color 定义一种颜色,vec4 包含rgba4 分量,out 关键字用于表示该变量用于输出到下一个阶段。如果使用in,则从上一阶段导入。
* 片段着色器将输出第一个分配的vec4 作为像素颜色。
###OpenGL ES的区别
绘制三角形时,基本上没有什么区别,只是着色器代码有两点不同:
* 版本声明中的core改为es,版本改为300。
* 由于es是针对嵌入式设备设计的,可能对内存和性能的测试比较严格,并且要求执行数据的准确性。有两种声明精度的方法:
好了,本文到此结束,如果可以帮助到大家,还望关注本站哦!
【深入OpenGL(ES)编程:掌握三角形绘制技巧】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
终于开始进入图形学的学习啦!
有11位网友表示赞同!
三角形是图形学的基础,先学会绘制它再做其他更复杂的内容。
有19位网友表示赞同!
期待看到这个三角形的渲染效果!
有8位网友表示赞同!
OpenGL(ES)这方面一直想学习,正好可以看看这篇教程。
有20位网友表示赞同!
感觉绘图库的学习需要一点耐心,但最终成果还是很值得的。
有9位网友表示赞同!
imparare OpenGL sounds pretty awesome!
有10位网友表示赞同!
绘制三角形看起来简单,但背后的原理却很复杂哦,一定要好好理解
有14位网友表示赞同!
不知道这个教程讲的是哪个版本的OpenGL(ES),方便知道的可以告诉我一下吗?
有13位网友表示赞同!
最近在学游戏开发,这篇教程刚好可以用得上!
有13位网友表示赞同!
学习绘制图形需要多少时间呢?
有15位网友表示赞同!
之前看过一些 OpenGL 的入门视频,感觉挺有帮助的,希望这篇文章能讲解得更深入一点。
有19位网友表示赞同!
OpenGL(ES) 用来做什么类型的画面渲染呢?
有16位网友表示赞同!
学习了OpenGL以后可以开发什么程序啊?可以分享一下吗?
有5位网友表示赞同!
我比较喜欢用Unity, 感觉界面比较友好,但还是想了解下OpenGL的原理
有5位网友表示赞同!
教程的代码示例能看懂吗?有没有难度?
有13位网友表示赞同!
我想问问 OpenGL(ES) 的学习资源有哪些推荐?
有13位网友表示赞同!
这个三角形是二维的吗?还是三维的?
有11位网友表示赞同!
OpenGL(ES) 相关的书籍有哪些比较经典的?
有5位网友表示赞同!
希望这篇文章能讲解简单易懂,我可以跟着步骤一步一步去实践!
有6位网友表示赞同!
期待看到更多使用 OpenGL(ES) 绘制不同图形的教程!
有5位网友表示赞同!