OpenGL ES绘制3D图形以及设置视口

文章目录

    • 关于 glDrawElements
      • 基本概念
      • 使用场景
      • mode 绘制模式
      • type 索引数据类型
      • indices 索引缓冲区
      • 工作原理
      • 绘制正方体实例
    • 视口
      • 透视投影(Perspective Projection)
      • 正交投影(Orthographic Projection)
      • 正交投影和透视投影对比

关于 glDrawElements

基本概念

glDrawElements 是 OpenGL 中用于渲染图元的核心函数之一,它允许你使用索引缓冲区(Index Buffer)来指定顶点的绘制顺序,从而实现高效的渲染。这个函数在处理需要重复使用顶点数据的场景时特别有用,比如 3D 模型渲染。

使用场景

  • 渲染包含大量重复顶点的复杂模型(如立方体、地形网格)。
  • 共享顶点属性(如位置、法线、纹理坐标)。
void glDrawElements(int mode,           // 绘制模式(如 GL_TRIANGLES、GL_LINES)int count,          // 索引数量int type,           // 索引数据类型(如 GL_UNSIGNED_SHORT)java.nio.Buffer indices  // 索引缓冲区
);

mode 绘制模式

模式描述
GL_POINTS将每个顶点作为一个点绘制。
GL_LINES将顶点两两连接为线段(v0-v1, v2-v3, …)。
GL_LINE_STRIP连接所有顶点为连续线段(v0-v1-v2-…)。
GL_LINE_LOOP连接所有顶点为闭合线段(最后一个点连回第一个点)。
GL_TRIANGLES将顶点每三个一组构成三角形(v0-v1-v2, v3-v4-v5, …)。
GL_TRIANGLE_STRIP相邻三个顶点构成三角形(v0-v1-v2, v1-v2-v3, …)。
GL_TRIANGLE_FAN以第一个顶点为中心,与后续相邻顶点构成扇形三角形(v0-v1-v2, v0-v2-v3, …)。

count 索引数量

需要绘制的索引总数。例如,渲染一个立方体需要 36 个索引(12 个三角形 × 3 个顶点)。

type 索引数据类型

类型描述
GL_UNSIGNED_BYTE8 位无符号整数(范围:0~255)。
GL_UNSIGNED_SHORT16 位无符号整数(范围:0~65535)。
GL_UNSIGNED_INT32 位无符号整数(范围:0~4294967295)。
顶点数 ≤ 255:使用 GL_UNSIGNED_BYTE。
顶点数 ≤ 65535:使用 GL_UNSIGNED_SHORT(最常用)。
顶点数 > 65535:使用 GL_UNSIGNED_INT(需 OpenGL ES 3.0+)

indices 索引缓冲区

存储顶点索引的缓冲区对象(如 java.nio.Buffer)。索引值对应顶点数组中的位置。

工作原理

  • 顶点数组:定义所有顶点的属性(如位置、颜色)。
  • 索引数组:指定顶点的绘制顺序。
  • glDrawElements:根据索引从顶点数组中提取顶点,并按指定模式绘制。

绘制正方体实例

先看效果
在这里插入图片描述
先上全部代码

package com.e.openglimport android.opengl.GLSurfaceView
import android.opengl.GLU
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import java.nio.ShortBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10class CubeRenderer : GLSurfaceView.Renderer {private val vertexBuffer: FloatBufferprivate val indexBuffer: ShortBufferprivate val colorBuffer: FloatBuffer// 正方体的8个顶点坐标private val vertices = floatArrayOf(-0.5f, -0.5f, -0.5f,  // 左下后 V00.5f, -0.5f, -0.5f,  // 右下后 V10.5f, 0.5f, -0.5f,  // 右上后 V2-0.5f, 0.5f, -0.5f,  // 左上后 V3-0.5f, -0.5f, 0.5f,  // 左下前 V40.5f, -0.5f, 0.5f,  // 右下前 V50.5f, 0.5f, 0.5f,  // 右上前 V6-0.5f, 0.5f, 0.5f // 左上前 V7)// 正方体12个三角形的顶点索引(两个三角形组成一个面)private val indices = shortArrayOf(0, 1, 2, 0, 2, 3,  // 后面1, 5, 6, 1, 6, 2,  // 右面5, 4, 7, 5, 7, 6,  // 前面4, 0, 3, 4, 3, 7,  // 左面3, 2, 6, 3, 6, 7,  // 上面4, 5, 1, 4, 1, 0 // 下面)// 每个顶点的颜色(RGBA)private val colors = floatArrayOf(0.0f, 0.0f, 0.0f, 1.0f,  // V0黑色1.0f, 0.0f, 0.0f, 1.0f,  // V1红色1.0f, 1.0f, 0.0f, 1.0f,  // V2黄色0.0f, 1.0f, 0.0f, 1.0f,  // V3绿色0.0f, 0.0f, 1.0f, 1.0f,  // V4蓝色1.0f, 0.0f, 1.0f, 1.0f,  // V5紫色1.0f, 1.0f, 1.0f, 1.0f,  // V6白色0.0f, 1.0f, 1.0f, 1.0f // V7青色)private var angleX = 0fprivate var angleY = 0finit {// 初始化顶点缓冲区val vbb = ByteBuffer.allocateDirect(vertices.size * 4)vbb.order(ByteOrder.nativeOrder())vertexBuffer = vbb.asFloatBuffer()vertexBuffer.put(vertices)vertexBuffer.position(0)// 初始化索引缓冲区val ibb = ByteBuffer.allocateDirect(indices.size * 2)ibb.order(ByteOrder.nativeOrder())indexBuffer = ibb.asShortBuffer()indexBuffer.put(indices)indexBuffer.position(0)// 初始化颜色缓冲区val cbb = ByteBuffer.allocateDirect(colors.size * 4)cbb.order(ByteOrder.nativeOrder())colorBuffer = cbb.asFloatBuffer()colorBuffer.put(colors)colorBuffer.position(0)}override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {// 设置清屏颜色为灰色gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f)// 启用深度测试gl.glEnable(GL10.GL_DEPTH_TEST)// 启用顶点和颜色数组gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)gl.glEnableClientState(GL10.GL_COLOR_ARRAY)}override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {// 设置视口大小gl.glViewport(0, 0, width, height)// 设置投影矩阵gl.glMatrixMode(GL10.GL_PROJECTION)gl.glLoadIdentity()// 设置透视投影val aspectRatio = width.toFloat() / heightGLU.gluPerspective(gl, 45.0f, aspectRatio, 0.1f, 1000.0f)// 设置模型视图矩阵gl.glMatrixMode(GL10.GL_MODELVIEW)gl.glLoadIdentity()}override fun onDrawFrame(gl: GL10) {// 清除颜色和深度缓冲区gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 设置模型视图矩阵gl.glLoadIdentity()gl.glTranslatef(0.0f, 0.0f, -5.0f) // 将正方体移到屏幕中央前方// 旋转正方体angleX += 1.0fangleY += 0.5fgl.glRotatef(angleX, 1.0f, 0.0f, 0.0f) // 绕X轴旋转gl.glRotatef(angleY, 0.0f, 1.0f, 0.0f) // 绕Y轴旋转// 设置顶点和颜色指针gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer)gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer)// 使用 glDrawElements 绘制正方体gl.glDrawElements(GL10.GL_TRIANGLES, indices.size,GL10.GL_UNSIGNED_SHORT, indexBuffer)}
}

代码中都有标注,核心代码在 onDrawFrame 中,最后一行通过
gl.glDrawElements( GL10.GL_TRIANGLES, indices.size,GL10.GL_UNSIGNED_SHORT, indexBuffer)

读取顶点数据等信息,绘制正方体。

注意:    
创建Buffer时,vbb.order(ByteOrder.nativeOrder()) 一般得加上。
它是 Java NIO 缓冲区操作中的关键步骤,用于设置字节序(Byte Order),确保数据在内存中的存储方式与设备硬件一致。不添加这行有时候你会发现绘制的没啥错,就是不显示图像!!!

视口

透视投影(Perspective Projection)

基本概念
模拟人眼视觉:远处物体看起来更小,产生 "近大远小" 的效果。
视锥体(Frustum):由近平面、远平面和四个侧面组成的截头四棱锥,只有视锥体内的物体可见。
// 方法 1:使用 glFrustumf
glFrustumf(left, right, bottom, top, near, far);// 方法 2:使用 GLU.gluPerspective 内部也是使用 glFrustumf 来实现
GLU.gluPerspective(fovy, aspect, zNear, zFar);
fovy(视野角度):角度越大,视野越宽广(类似广角镜头);角度越小,视野越狭窄(类似长焦镜头)。
aspect(宽高比):需与视口宽高比匹配,否则会导致图像拉伸。
near 和 far:影响深度精度和可见距离,比值过大会导致深度冲突(Z-Fighting)

修改上面的代码,使用循环多绘制一些正方体

   override fun onDrawFrame(gl: GL10) {// 清除颜色和深度缓冲区gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 旋转正方体angleX += 1.0fangleY += 0.5f// 设置顶点和颜色指针gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer)gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer)for (i in 0..10) {// 使用 glDrawElements 绘制正方体// 设置模型视图矩阵gl.glLoadIdentity()gl.glTranslatef(0.0f, -1f, -(5.0f * i.toFloat())) // 修改平移距离使绘制看起来远近的效果gl.glRotatef(angleX, 1.0f, 0.0f, 0.0f) // 绕X轴旋转gl.glRotatef(angleY, 0.0f, 1.0f, 0.0f) // 绕Y轴旋转gl.glDrawElements(GL10.GL_TRIANGLES, indices.size,GL10.GL_UNSIGNED_SHORT, indexBuffer)}}

效果如下图,有一种越远越小的感觉。

在这里插入图片描述

正交投影(Orthographic Projection)

基本概念
平行投影:光线从无限远处平行照射物体,物体大小与距离无关。
保持比例:物体的真实尺寸和角度在投影后保持不变,平行线投影后仍平行。
// 方法 1:3D 正交投影(OpenGL ES 1.x/2.0)
void glOrthof(float left, float right, float bottom, float top, float near, float far);// 方法 2:2D 正交投影(OpenGL ES 1.x,简化版)
void GLU.gluOrtho2D(float left, float right, float bottom, float top);

将上面透视投影的方法换成正交投影,同样绘制多个正方体。

override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {// 设置视口大小gl.glViewport(0, 0, width, height)// 设置投影矩阵gl.glMatrixMode(GL10.GL_PROJECTION)gl.glLoadIdentity()// 设置透视投影val aspectRatio = width.toFloat() / height// GLU.gluPerspective(gl, 45.0f, aspectRatio, 0.1f, 1000.0f)// 正交投影GLU.gluOrtho2D(gl, -5F, 5F, -5F, 5F)// 设置模型视图矩阵gl.glMatrixMode(GL10.GL_MODELVIEW)gl.glLoadIdentity()}

效果图如下,只能看到最前面的一个正方体

在这里插入图片描述

正交投影和透视投影对比

特性正交投影透视投影
特性正交投影透视投影
视觉效果无近大远小,深度感弱近大远小,深度感强
投影矩阵线性变换(仅缩放和平移)非线性变换(包含除法)
深度精度均匀分布近平面精度高,远平面精度低
物体大小与距离无关随距离增加而减小
适用场景2D 游戏、UI、地图、CAD3D 游戏、VR、真实感渲染

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/83448.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SAS求解多元回归方程】REG多元回归分析-多元一次回归

多元一次回归是一种统计方法,用于分析多个自变量(解释变量)与一个因变量(响应变量)之间的线性关系。 目录 【示例】 基本语法 SAS代码 参数估计 方差分析 回归统计量 y的拟合诊断 y的回归变量值 【示例】 设Y…

卡通幼儿园教育通用可爱PPT模版分享

幼儿园教育通用PPT模版,教育教学PPT模版,卡通教育PPT模版,可爱卡通教学课件PPT模版,小清新动物卡通通用PPT模版,教学说课通用PPT模版,开学季PPT模版,国学颂歌PPT模版,可爱简约风PPT模…

力扣HOT100之技巧:75. 颜色分类

这道题实际上就是让我们不用sort()函数来实现对原数组的排序,这里我直接使用快速排序对原数组进行排序了,也是复习一下基于快慢指针的快速排序写法。面试手撕快排的思路参考这个视频。 用时击败100%,还行。下面直接贴代码。 class Solution …

离线部署openstack 2024.1 keystone

控制节点身份服务 离线下载 apt-get install --download-only keystone python3-openstackclient apache2 libapache2-mod-wsgi-py3mkdir /controller/keystone mv /var/cache/apt/archives/*.deb /controller/keystone/ dpkg -i /controller/keystone/*.deb在一个控制节点操…

帆软 BI 从入门到实战全攻略(一):安装激活与添加数据

一、帆软 BI 产品概述​ 在当今大数据时代,数据分析与可视化成为企业洞察业务、驱动决策的关键利器。帆软软件有限公司作为中国专业的大数据 BI 和分析平台提供商,自 2006 年成立以来,凭借其在商业智能和数据分析领域的深耕细作,…

网络协议通俗易懂详解指南

目录 1. 什么是网络协议? 1.1 协议的本质 1.2 为什么需要协议? 1.3 协议分层的概念 2. TCP协议详解 - 可靠的信使 📦 2.1 TCP是什么? 2.2 TCP的核心特性 🔗 面向连接 🛡️ 可靠传输 📊 流量控制 2.3 TCP三次握手 - 建立连接 2.4 TCP四次挥手 - 断开连接…

量子加速器切入 AI 底层架构!能源焦虑时代,ORCA 正在改写数据中心的计算逻辑

内容来源:量子前哨(ID:Qforepost) 文丨浪味仙 排版丨浪味仙 行业动向:2000字丨5分钟阅读 人工智能的飞速发展,令计算需求呈现爆炸式增长,也催生出专为 AI 设计的新型计算基础设施形态——AI…

< 买了个麻烦 (二) 618 京东云--轻量服务器 > “可以为您申请全额退订呢。“ 工单记录:可以“全额退款“

事情进展是这样的: 海外接听 一分钟 1-2 元,具体多少要问联通。 这几天接电话,有点儿心烦,看见来自 010-86310548 以为是 ICP 备案,结果接起来全是 VPS (轻量应用服务器)这个工单,就…

接口限频算法:漏桶算法、令牌桶算法、滑动窗口算法

文章目录 限频三大算法对比与选型建议一、漏桶算法(Leaky Bucket Algorithm)1.核心原理2.实现3.为什么要限制漏桶容量4.优缺点分析 二、令牌桶算法(Token Bucket Algorithm)1.核心原理2.实现(1)单机实现&am…

HTML5 盒子模型

1. 盒子模型的概念 2. 边框(border) 边框颜色(border-color) 边框粗细(border-width) 边框样式(border-style) border简写(border:) 3. 外边距&am…

【Linux】Linux高级I/O

参考博客:https://blog.csdn.net/sjsjnsjnn/article/details/128345976 一、五种IO模型 阻塞式I/O非阻塞式I/OI/O复用(多路转接)信号驱动式I/O异步I/O I/O我们并不陌生,简单的说就是输入输出;对于一个输入操作通常包…

关于界面存在AB测试后UI刷新空白的问题

问题描述: 在同一页面存在AB面,A和B同时都有一个rv,然后A面的rv填充不了数据,B面的可以。 问题解决: header_task布局里的include_new_gift_sign里有一个和外层一样id的recyclerview include的标签的作用是。在infl…

Go 协程(Goroutine)入门与基础使用

一、什么是协程(Goroutine)? 简单来说,协程是由 Go 语言运行时管理的轻量级线程。相比系统线程,它的调度开销极小,内存占用非常少(默认只需 2KB 栈空间)。 你可以在一个程序中轻松…

matlab 各种智能优化算法

1. 优化算法相关 蚁群优化算法(ACO) 蚁群优化算法是一种模拟蚂蚁觅食行为的优化技术。以下是一个简化版的ACO用于解决旅行商问题(TSP)的MATLAB代码: function [bestRoute, minDist] acoTsp(distMatrix, numAnts, n…

Hilt -> Android 专属依赖注入(DI)框架

Hilt 是 Google 基于 Dagger 封装的 Android 专属依赖注入(DI)框架,显著简化了依赖管理流程,提升代码可维护性和可测试性。以下是核心要点及使用指南: dagger2: Dagger 2 原理和使用-CSDN博客 Hilt vs Dagger2&…

AISHELL-5 全球首套智能驾舱中文语音交互数据集开源

随着汽车成为人们日常生活中不可或缺的一部分,而驾驶舱中传统的触摸交互方式容易分散驾驶员的注意力,存在安全风险,因此,车内基于语音的交互方式得到重视。与通常家庭或会议场景中的语音识别系统不同,驾驶场景中的系统…

openstack之neutron(一)

NFV基础 neutron是对二层物理网络的抽象与管理,实例的网络功能由连接到vSwitch的端口上的vNIC共同实现,再通过物理服务器的物理网卡访问外部的物理网络。 NFV实现 网卡虚拟化:tap、tun、veth; 交换机虚拟化:linuxbri…

【Java】Arrays.sort:TimSort

一,概述 书接前文【Java】Arrays.sort:DualPivotQuicksort-CSDN博客 Arrays.sort对基本数据类型使用了双轴快速排序,但是对Object[]类型,则使用了TimSort,TimSort是稳定的排序,它整合了插入排序归并排序,…

一个n8n构建的能和LLM对话的Agent

一个n8n构建的能和LLM对话的Agent 1.OLLAMA1.1.下载和安装1.2.设置环境变量1.3.重启ollama1.4.测试1.5.拉取模型2.n8n部署2.1. 镜像拉取和启动2.2.注册和登录2.3.新建一个工作流3.说在后面的话环境搭建说明: windows(RTX 5090)+VM CENTOS 采用本地化的ollama运行LLM n8n是一…

升级 Ubuntu Linux 内核的几种不同方法

方法 1 - 使用 dpkg 升级 Linux 内核(手动方式) 这个方法可以帮助你从 kernel.ubuntu.com 网站手动下载可用的最新 Linux 内核。如果你打算安装最新版(而不是稳定版或者正式发布版),那这种方法…