当前位置:首页 > 技术文章 > 正文内容

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(四)

arlanguage1个月前 (04-01)技术文章20

你知不知道

你知不知道

我等到花儿也谢了

绘制三角形已经到了第四篇了,然而三角形还是等不到踪影,不少读者已经开始按耐不住内心的躁动了,好消息来了,本文就将完成绘制三角形的任务

通过阅读本文,你将获得以下收获:

1.如何使用OpenGL es基本图元绘制点、线、三角形

2.如何使用OpenGL es更高级的图元绘制更复杂的图形

上篇回顾

上一篇博文一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)重点介绍了图元和片段着色器以及OpenGL渲染指令,憋了这么久,今天开始,让我们一起体验渲染的快感~~

绘制图形

回顾下前几篇真正执行渲染的代码:

/**        此处开始将数据传入图形渲染管线              **/
    static float triangleVer[] = {
            0.8f, -0.8f, 0.0f,
            -0.8f, -0.8f, 0.0f,
            0.0f, 0.8f, 0.0f,
    };
    
     GLuint apos = static_cast(glGetAttribLocation(program, "aPosition"));
    glEnableVertexAttribArray(apos);
    glVertexAttribPointer(apos, 3, GL_FLOAT, GL_FALSE, 0, triangleVer);
    
     /**        此处结束将数据传入图形渲染管线              **/
     
      /**        此处开始将图像渲染到屏幕              **/

    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
   
    eglSwapBuffers(display, winSurface);
    
     /**        此处结束图像渲染              **/

这里是渲染三角形的代码,glDrawArrays方法传入的图元类型是GL_TRIANGLE_STRIP,但是路要一步一步走,我们就先从最基本的绘制开始吧。

在前几篇给的代码中,使用的都是直接获取着色器变量名的方式: GLuint apos = static_cast(glGetAttribLocation(program, "aPosition"));

还记得在一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(二)GLSL一章节中曾经提到过layout修饰符:

layout是GLSL的几个修饰符中的一个,一般用来指定变量布局的,可以说是着色器和客户端程序或者其他阶段着色器的通信接口

何为变量的位置?你可以想象每一个着色器程序是一层楼,每个需要对接外部的变量住在一个个房间中,假如你现在是客户端的C++程序,你想给着色器里面某个变量传参,然后你又不知道变量名字,如果此时你知道变量所在的房间编号,那你照样还是可以将数据传给变量,就像一个蹑手蹑脚的间谍,偷偷打开对应location的房门,偷偷将一份机密文件交给里面的人(location对应变量)。所以layout加location的作用已经很明显了,你不需要房间里面住着谁(不用知道变量名称),只需要知道房间号(location),也能按成变量数值的传递

(直到今日,我依旧想为我当初的比喻点赞)

于是在今天的例子,我们统一使用这个新特性吧~

C++音视频学习资料免费获取方法:关注音视频开发T哥,+「链接」即可免费获取2023年最新C++音视频开发进阶独家免费学习大礼包!

绘制点

点的图元类型为:

GL_POINTS

表示每个顶点在屏幕上都是单独的点(想想一个点确实也玩不出其他花了)

于是渲染代码变为:

//三个点坐标
    static float pointsVer[] = {
            0.8f, -0.8f, 0.0f,
            -0.8f, -0.8f, 0.0f,
            0.0f, 0.8f, 0.0f,
    };

//旧的传输数据方式,已经out了
//    GLuint apos = static_cast(glGetAttribLocation(program, "aPosition"));

//通过layout传输数据,传给了着色器中layout为0的变量
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, pointsVer);
    //打开layout为0的变量传输开关
    glEnableVertexAttribArray(0);

    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
//从第0个顶点开始,取三个顶点,绘制成三个点到后备缓冲区
    glDrawArrays(GL_POINTS, 0, 3);
    //窗口显示,交换后备缓冲区到前台
    eglSwapBuffers(display, winSurface);

这里顶点属性数组传入的是6个浮点数,然后通过glVertexAttribPointer方法告知OpenGL如何解析这个顶点属性数组(如有不清楚,请看一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(二)中的叙述),在这里,就是将三个顶点属性依次传给顶点着色器中layout为0的变量,那么顶点着色器现在是什么样子的呢:

        #version 300 es
        layout (location = 0) 
        in vec4 aPosition;//输入的顶点坐标,会在程序指定layout将数据输入到该字段

        void main() {
           //直接把传入的坐标值作为传入渲染管线。gl_Position是OpenGL内置的表示坐标的变量
            gl_Position = aPosition;   
            //点的尺寸
            gl_PointSize = 50.0;
        }"

注意这里需要指定点的尺寸,点在这里以正方形呈现,所以指定的就是正方形的边长

片段着色器和上一篇一摸一样,只是给当前片段赋颜色值:

       #version 300 es
       precision mediump float;
       out vec4 FragColor;

        void main() {
           //给当前片段赋颜色值
           FragColor = vec4(1.0,0.5,0.5,1.0);
        }

于是乎顶点着色器执行3次,得到三个顶点的最终坐标传到图形渲染管线中,执行完图元装配,光栅化之后,每个片段都执行一次片段着色器,于是乎再执行完渲染程序,三个小红点便跃然纸上:

那么绘制4个点呢?只要传入的顶点属性数组为4*3个,并通过glVertexAttribPointer方法告知OpenGL有4个顶点属性即可。以此类推~

绘制线段

说起绘制线段,OpenGL就开始展现他武器库之丰富了。

绘制线段的图元类型有3种,分别为:

GL_LINES                         
GL_LINE_LOOP                      
GL_LINE_STRIP   

三者的区别在上一篇文章图元装配一章已经有所提及:

如果现在传入顶点着色器的顶点数组的元素分别为:v0,v1,v2,v3,v4,v5,v6,v7,那么对应的图元效果图如下所示:

相信在做列位看官都能理解它们的区别了,接下来就实践分别瞥一瞥它们各自的风采。

这次传4个点,依次每种图元看看效果:

//四个点坐标
    static float lineVer[] = {
            0.8f, -0.8f, 0.0f,
            -0.8f, -0.8f, 0.0f,
            0.0f, 0.8f, 0.0f,
            0.4f, 0.8f, 0.0f,
    };

不过画线之前,别忘了要指定线段的宽度,不然宽度就是0,即一条抽象到看不见的线段。

//设置线段宽度
glLineWidth(20);

GL_LINES

glDrawArrays(GL_LINES, 0, 4);

从第0个顶点开始,读取4个顶点,每两个组成一对连成线

是屏幕下方两条红色横线,下面一条的顶点分别为0.8f, -0.8f, 0.0f-0.8f, -0.8f, 0.0f,上面的线段顶点分别为0.0f, 0.8f, 0.0f0.4f, 0.8f, 0.0f

GL_LINE_STRIP:

glDrawArrays(GL_LINE_STRIP, 0, 4);

这种是从第一个顶点开始一路连到到最后一个顶点

GL_LINE_LOOP

glDrawArrays(GL_LINE_LOOP, 0, 4);

还是这种给力,一路连成闭环

绘制三角形

画完线段,接下来就是开始令人兴奋的三角形了。而三角形,就更能体现OpenGL就开始展现他武器库之丰富,简直是东风快递、民兵、白杨皆有之。

顶点属性数组改为传入3个顶点属性(当然你要传4个也行,在后面读取的时候指定读取几个就可以了):

static float triangleVer[] = {
        0.8f, -0.8f, 0.0f,
        -0.8f, -0.8f, 0.0f,
        0.0f, 0.8f, 0.0f,
};

三角形的图元类型也是三种,我们也来一一领略一番:

三角形的就比较复杂了:

这是官网的解释:

第一种GL_TRIANGLES是每三个顶点就分别连成一个三角形,也就是说把顶点数组每三个为一组分,互相之间没有瓜葛

第二种GL_TRIANGLE_STRIP就比较牛逼了,从第0个顶点开始,每3个顶点连在一起形成三角形, v0,v1,v2为一个三角形,v1,v2,v3为一个三角形,v2,v3,v4为一个三角形依此类推。

如果顶点个数为n,则可以生成n-2个三角形

第三种GL_TRIANGLE_FAN是谁是第一个顶点谁就拥有优先的绘图权(排在第一位的果真是大佬),第一个顶点会和后面每2个顶点为一组绘制三角形。v0,v1,v2为一个三角形,v0,v2,v3为一个三角形,v0,v3,v4为一个三角形依此类推。最后形成一个扇形形状

用代码一一感受它们的风采吧:

首先用最简单的三个点绘制:

static float triangleVer[] = {
        0.8f, -0.8f, 0.0f,
        -0.8f, -0.8f, 0.0f,
        0.0f, 0.8f, 0.0f,
};
//对于三个点来说,GL_TRIANGLES,GL_TRIANGLE_STRIP和GL_TRIANGLE_FAN绘制结果都一样
glDrawArrays(GL_TRIANGLES, 0, 3);

开始玩点好玩的了

将传入的顶点属性数组改为6个元素

static float triangleVer[] = {
        0.8f, 0.8f, 0.0f,
        0.0f, 0.8f, 0.0f,
        0.4f, 0.4f, 0.0f,
        -0.8f, 0.5f, 0.0f,
        -0.4f, 0.8f, 0.0f,
        -0.8f, 0.8f, 0.0f,
};

绘制顶点数目也改为6:

glDrawArrays(GL_TRIANGLES, 0, 6);

尝试下GL_TRIANGLE_STRIP

惊奇发现已经面目全非了,几乎看不到三角形的痕迹了。

再看下GL_TRIANGLE_FAN

更加没有三角形痕迹了,v5还被抛弃了什么鬼。

为什么呢?按照前面对GL_TRIANGLE_FAN的讲解,v0,v4,v5会连成一个三角形,不过仔细看坐标就会发现,它们三个点是在一条直线上的,连不了三角形,索性直接抛弃了v5

其实OpenGL之前是有四边形的图元类型的,但是在OpenGL3.1之后就被移除了,那这次就不讲了,那么问题来了,四边形要怎么绘制呢?

C++音视频学习资料免费获取方法:关注音视频开发T哥,+正在跳转即可免费获取2023年最新C++音视频开发进阶独家免费学习大礼包!

其实答案已经在上面了,通过GL_TRIANGLE_STRIP或者GL_TRIANGLE_FAN,只要给四个任何三个点不共线的点,就可以绘制出来。

三角形是最基本的图形,三角形三个点必然是共面的,所以在OpenGL的世界里,任何图形都可以通过三角形来构建。

举个例子,画个正方形瞧瞧?

static float triangleVer[] = {
        0.8f, 0.8f, 0.0f,
        0.8f, -0.8f, 0.0f,
        -0.8f, 0.8f, 0.0f,
        -0.8f, -0.8f, 0.0f,
};

使用GL_TRIANGLE_STRIP绘制:

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

show you the rectangle:

嗯?说好的正方形怎么变成长方形了。坐标上确实是正方形,但是因为坐标是按照比例系数传的并且手机屏幕是长方形的,所以无奈被拉长。不过也是有办法解决的,后面讲矩阵的博文就告诉你们答案。

总结

本文主要详细介绍了OpenGL es各种基本图元的绘制,通过本文的学习,各种基本图图元的绘制相信不在话下了,而且通过一些图元类型还可以玩出一些花了。现在距离一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(一)中绘制三角形的终极任务只差那种看起来酷毙的绚烂的渐变色彩了,别急,还是那句话,*欲知后事如何*,*且听下回分解* 哈哈。下一篇文章一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(五)

代码地址

opengl-es-study-demo (不断更新中)

参考

OpenGL官网Primitives Learn OpenGL

作者:半岛铁盒里的猫 链接:
https://juejin.cn/post/7144335420644392991 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在开发的路上你不是一个人,欢迎加入C++音视频开发交流群「链接」大家庭讨论交流!

扫描二维码推送至手机访问。

版权声明:本文由AR编程网发布,如需转载请注明出处。

本文链接:http://www.arlanguage.com/post/3780.html

分享给朋友:

“一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(四)” 的相关文章

nginx监控与调优(三)

nginx监控通常有两种方法:一是status监控;二是ngxtop监控。一、status监控使用status监控的步骤:1.确定nginx中status模块是否已安装[root@localhost sbin]# nginx -V nginx version: nginx/1.13.7 built...

K8S灰度环境频繁出现502 k8s ingress 灰度发布

公司一个需求版本开发、测试结束,升级到灰度环境之后频繁出现502公司使用的是php + nginx 所以分析也是按照这个方向来分析的。nginx报错:NGINX ERROR 10.9.95.75 !!!2020/07/21 17:05:12 [error] 7#7: *38808 recv() fa...

linux系统安全,多人共用服务器,防止别人干‘坏事’

我们知道,在开发一个应用的过程中,需要涉及代码、操作系统、网络和数据库等多个方面。所以,只是了解代码安全肯定是不够的,我们还需要了解常见的基础环境和工具中的安全机制,学会通过正确地配置这些安全机制,来提升安全保障。谈到 Linux,我相信你每天都在使用 Linux 进行各种开发和运维操作。但是,大多...

VUE3前端开发入门系列教程

一直以来使用ThinkJS开发,使用Semantic-UI手写代码,又缺少一些table等插件,好累。平时使用NodeJS开发后端较多,一直有接触VUE想法,总是不得入门(可能是思维固化了),再次深入研究,直接从VUE3入手,并借这次机会写个入门系列教程,一是做个笔记备查,二是与大家分享。初次入门V...

Nginx从入门到放弃05-访问日志与日志切割

设置访问日志当我们访问nginx服务时,nginx会记录日志,nginx日志分两种,一种是访问日志,一种是错误日志,访问日志记录在”access.log”文件中,错误日志记录在”error.log”文件中。笔者把自己总结的文档分为几遍,合集在 https://g.xgss.net/nginx/自定义...

使用docker在Kylin v10下安装zabbix 6.4

环境操作系统:Kylin v10内核版本: 4.19.90-52.22.v2207.ky10.x86_64zabbix server版本: 6.4.11mysql 版本: 8.0docker版本:23.0.6安装docker这里是本次安装需要的所有文件,若为离线环境可以提前下载,docker镜像可以...