惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
人人都是产品经理
人人都是产品经理
IT之家
IT之家
Cyberwarzone
Cyberwarzone
T
Troy Hunt's Blog
有赞技术团队
有赞技术团队
阮一峰的网络日志
阮一峰的网络日志
T
Threat Research - Cisco Blogs
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - 叶小钗
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
The Hacker News
The Hacker News
Jina AI
Jina AI
T
Tor Project blog
V
Vulnerabilities – Threatpost
酷 壳 – CoolShell
酷 壳 – CoolShell
Spread Privacy
Spread Privacy
博客园_首页
C
Cybersecurity and Infrastructure Security Agency CISA
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Simon Willison's Weblog
Simon Willison's Weblog
Security Latest
Security Latest
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 司徒正美
V2EX - 技术
V2EX - 技术
I
Intezer
The Cloudflare Blog
Cisco Talos Blog
Cisco Talos Blog
SecWiki News
SecWiki News
博客园 - 【当耐特】
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
Lohrmann on Cybersecurity
Scott Helme
Scott Helme
Google Online Security Blog
Google Online Security Blog
量子位
The Last Watchdog
The Last Watchdog
AI
AI
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
Security Affairs
P
Palo Alto Networks Blog
S
Secure Thoughts
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Attack and Defense Labs
Attack and Defense Labs

音视频开发进阶

音视频教程-第三节 音视频教程-第二节 真的,AI 可能就是新时代的信息差 充值 Cursor 之后,工作有了哪些变化?🤔 个人'蒸馏'大模型能做哪些有意思的事情 DeepSeek 大模型在 Mac 上的部署和运行 音视频教程-第一节 【WebRTC 专栏】-- Android 开发集成 WebRTC 库的几种方式 【WebRTC 专栏】-- 在 Mac M1 等系列芯片编译和开发 WebRTC-Android 库 Meta Llama3 大模型在 Mac 上的部署和运行 iOS VideoToolBox 解码 HEVC Open-GOP 视频的问题排查 Flutter 状态管理之 InheritedWidget 使用和分析 用 ChatGPT 回答技术问题怎么样 ? 音视频开发系统入门大致路线 UE 4.27 添加自定义 ShadingModel 用 UE4 虚幻引擎做个捏脸小功能~~ UE4 材质练习 之 凹凸贴图偏移的使用 UE4 材质练习系列基础 OpenGL上下文创建以及共享机制 007 | 播放器系列专栏-解析 MP4 文件读取信息 006 | 播放器系列专栏-在 Mac 上查看 MP4 格式信息 干货 | 快速抽取缩略图是怎么练成的? 关于直播、WebRTC、FFmpeg 的那些事 005 | 播放器系列专栏-在 Windows 上查看 MP4 格式信息 将音视频中的花屏、绿屏、黑屏问题一网打尽 关于音视频里面的解码帧率和渲染帧率 004 | 播放器系列专栏-认识MP4视频(下) 003 | 播放器系列专栏-认识MP4视频(上) 入门或者转行音视频,应该要怎么做? H264视频文件如何缩放分辨率 002 | 播放器系列专栏-FFmpeg依赖库的配置 001 | 播放器系列专栏-关于播放器项目的一个小实践 Seek策略以及在有B帧情况下的处理 目前流媒体开发工程师工作内容主要是什么? 一个音视频领域专业问答的小圈子 干货收藏 || Vulkan Game Engine 视频教程 音视频春节假期内卷指南(实操) Vulkan 在 FFmpeg 中的支持 Windows 下 FFmpeg 和 LibX264 的编译和配置 Metal 开发 | 使用 C++ 进行接口调用 音视频开发工作经验分享 || 视频版 FFmpeg 调用 MediaCodec 硬解码到 Surface 上 代码吸猫 | 用 OpenGL 图像渲染的养猫计划 百倍变速--解码到底能不能丢 非参考帧 ?FFmpeg 有话说!!! 老生常谈-FFmpeg 的编译问题轻松搞定 FFmpeg 调用 Android MediaCodec 进行硬解码(附源码) 【WebRTC 专栏】--创建相机预览 Unity Shader 光照基础之 Half Lambert 光照模型 Unity Shader 光照基础之Lambert光照模型 Unity Shader 光照基础内容 Unity Shader 显示一张图片纹理 UnityShader 的基本概念 Unity 物体的基本操作 C++ 模板系列小结07-尾置返回类型 C++ 模板系列小结06-可变参数模板特性 C++ 中的多线程的使用和线程池建设 C++ 模板系列小结05-模板类型作为模板参数 C++ 模板系列小结04-类模板中的成员模板 C++ 模板系列小结03-在模板中指定变量类型 C++ 模板系列小结02-非类型模板参数 C++ 模板系列小结01-函数模板和类模板 从零打造渲染引擎系列01-什么是渲染引擎 iOS开发 - 在 Swift 中去调用 C/C++ 代码 2021 技术新番 - 从零打造渲染引擎系列 iOS 音视频开发的一些基础准备工作 音视频交流群又来啦~~~ 【WebRTC 专栏】WebRTC & Android 开发学习环境搭建~ 【喜大普奔】域名终于备案通过啦 Shader 优化 | OpenGL 绘制网格效果 【音视频连载-011】第二季 FFmpeg 一层一层获取文件信息 KodeLife | Shader 实时编辑预览的强大工具使用实践 推荐几个堪称教科书级别的 Android 音视频入门项目 【音视频连载-010】第二季 FFmpeg 日志打印 【音视频连载-008】基础学习篇-SDL 播放 PCM 音频文件(下) 【音视频连载-007】基础学习篇-SDL 播放 PCM 音频文件(上) 【音视频连载-006】基础学习篇-SDL 播放 YUV 视频文件 【音视频连载-005】基础学习篇-SDL 加载 YUV 文件并显示 【音视频连载-004】基础学习篇-SDL 加载图片并显示 【音视频连载-003】基础学习篇-SDL 消息循环和事件响应 【音视频连载-002】基础学习篇-SDL 创建窗口并显示颜色 【音视频连载-001】基础学习篇- SDL 介绍以及工程配置 LearnOpenGL 源码在 MAC 上的编译与调试 2019 年终总结与回顾 Android NDK 开发的免费技术视频来啦~~ OpenGL 实现视频编辑中的转场效果 OpenGL 实践之贝塞尔曲线绘制 图像库 libjpeg-turbo 编译与实践 图像库 libpng 编译与实践 rust 开发编译 Android 动态库实践 Android NDK 开发 —— 从 Assets 文件夹加载图片并上传纹理 简单易用的图像解码库介绍 —— stb_image 博客图床迁移记 进击的 Vulkan 移动开发之 SwapChain 进击的 Vulkan 移动开发之 Command Buffer 进击的 Vulkan 移动开发之 Instance & Device & Queue 进击的 Vulkan 移动开发(一)之今生前世 Java 显式锁 Lock 与条件队列 C++ 标准容器库小结 一文读懂 YUV 的采样与格式 《OpenGL ES 3.x 游戏开发》碰撞检测之 AABB 包围盒
OpenGL 的 glDrawElements 绘制方法
音视频开发进阶 · 2018-05-25 · via 音视频开发进阶

一个专注音视频领域的小圈子

避免图片丢失,建议阅读微信原文:

https://mp.weixin.qq.com/s/WcWdYE5j8Ycw2dtJYS-Cxg 在之前的绘制中,我们都是通过 glDrawArrays 方法来实现的,它会按照我们传入的顶点顺序和指定的绘制方式进行绘制。 回顾一下之前提到的绘制类型:

绘制类型 绘制方式
GL_POINTS 将传入的顶点坐标作为单独的点绘制
GL_LINES 将传入的坐标作为单独线条绘制,ABCDEFG六个顶点,绘制AB、CD、EF三条线
GL_LINE_STRIP 将传入的顶点作为折线绘制,ABCD四个顶点,绘制AB、BC、CD三条线
GL_LINE_LOOP 将传入的顶点作为闭合折线绘制,ABCD四个顶点,绘制AB、BC、CD、DA四条线。
GL_TRIANGLES 将传入的顶点作为单独的三角形绘制,ABCDEF绘制ABC,DEF两个三角形
GL_TRIANGLE_STRIP 将传入的顶点作为三角条带绘制,ABCDEF绘制ABC,BCD,CDE,DEF四个三角形
GL_TRIANGLE_FAN 将传入的顶点作为扇面绘制,ABCDEF绘制ABC、ACD、ADE、AEF四个三角形
假设要绘制一个立方体,以 GL_TRIANGLES 的类型进行绘制,那么六个面,每个面由两个三角形组成,就得向渲染管线传入 36 个顶点,36 个顶点按照顺序进行绘制,而实际上,一个矩形也就才 8 个顶点而已。

为了优化绘制的效率,减少数据的传递,于是就有了 glDrawElements 绘制方法。

glDrawElements 方法还是需要传递顶点数据,但只需要传递物体实际上的顶点数据,也就是最少的,不重复的顶点数据。

然后再向渲染管线传递要绘制的顶点数据的索引,根据索引从顶点数据中取出对应的顶点,然后再按照指定的方式进行绘制。

如下图所示,图片截自《OpenGL ES 3.x 游戏开发上卷》:

由三个三角形组成的倒置的梯形,实际上只有五个顶点 $ {v0,v1,v2,v3,v4}$,因此也只传递了五个顶点,接下来就是确定这个五个顶点的索引顺序。

索引顺序和我们要绘制的方式有很大的关系,不同绘制方式的索引顺序不同。

采用 GL_TRIANGLE_STRIP 的类型绘制,那么索引顺序就是 ${0,1,2,3,4}$。

具体方法调用情况代码:

	// 函数原型
    public static native void glDrawElements(
        int mode, // 绘制方式
        int count, // 绘制数量
        int type, // 索引的数据类型
        java.nio.Buffer indices // 索引缓冲
    );
	// 定义顶点的索引数据
    private byte[] front = {
            // 前面索引
            0, 1, 2, 3
    };
    // 索引数据传递到缓冲区
    private ByteBuffer frontBuffer = ByteBuffer.allocateDirect(indices.length *Constants.BYTES_PRE_BYTE)
									 .put(indices)
	frontBuffer.position(0)								 
	// glDrawElements 绘制
	GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP,front.size,GLES20.GL_UNSIGNED_BYTE, frontBuffer)
	// glDrawArrays 绘制
	// GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)

在函数原型中定义了要传入的参数,根据要绘制的方法和索引缓冲区,找到对应的点进行绘制。

对于绘制时有重复点,采用这种方式,就可以减少向渲染管线中传递重复的点了。

而且,在定义一个顶点时,大都是 float 类型,它是四个字节,而对于绘制量比较小,顶点数量在 byte 所能表达整数范围内,可以采用 byte 类型定义索引顺序,它只占一个字节,减少了内存的使用。

glDrawElements 和 glDrawArrays 的对比

glDrawElements 方法的 count 的参数定义了要取多少个索引出来绘制,而且这个绘制是连续的,必须要把 count 数量的顶点绘制完。

这里就有一个很有意思的地方了,有一些小游戏是这样的:要求一笔绘制完一个形状,而且不允许交叉。

比如,要求一笔绕过立方体的六个面,而且不允许交叉,这就很难做到的了。

而使用 glDrawElements 方法同样会这样,采用索引不能一次不交叉的把图形全部绘制完,得采取两次绘制。

比如在实践绘制一个立方体时,采用了如下的方式:

	// 前面索引
    private byte[] front = {0, 1, 2, 3};
    // 后面索引
    private byte[] back = { 4, 5, 6, 7};
	// 上面索引
    private byte[] top = {0, 1, 4, 5};
    // 下面索引
    private byte[] bottom = { 2, 3, 6, 7};
    // 左面索引
    private byte[] left = {0, 4, 2, 6};
    // 右侧索引
    private byte[] right = {1, 5, 3, 7};

把立方体分解成了六个面的内容进行绘制,也就是采用了六个索引缓存。

而对于使用 glDrawArrays的方式,可以一次性把所有顶点传到渲染管线,并且可以选择绘制的开始和结尾点,这样就只要一个缓冲区就好了,不过代码就是要多占用内存空间了。

所以说,能用 glDrawElements 方式的还是要采用的。

具体代码详情,可以参考我的 Github 项目:

https://github.com/glumes/AndroidOpenGLTutorial

原创文章,转载请注明来源:    OpenGL 的 glDrawElements 绘制方法