当前位置: 首页 > news >正文

[OpenGL]简单几何类设计

目录

一、整体的设计

二、创建正方体

三、创建球体

四、几何类的代码


 

前言:刚学OpenGL,主要想要记录下学到的东西,也当作一个笔记,部分理解可能有偏差,也是不全面的,如果有发现问题的话也很高兴大家可以指正,我会尽快修改的,在后续学习过程中,也会进行相应的补充与修改。(课件截图来源于bilibili赵新政老师)

(说明:本文章主要是想要记录球体的实现,几何类只是简单的封装)

(OpenGL老师:赵新政的个人空间-赵新政个人主页-哔哩哔哩视频)


一、整体的设计

在去构建一个图形的时候,总是会需要去创建一大堆的东西,像是vbo,ebo,vao等。在很多时候,都需要去创建许许多多的图形,而为了增加代码的复用性,就完全可以将创建的这一过程封装起来 。(在这里的话将会以创建正方体与球体为例进行设计)

为了创建这样的几何图形,肯定是要去记录他的位置数据以及uv数据的,我们需要通过vbo去保存这些数据,并将他们绑定到对应vao上。同时我们需要使用ebo去记录需要去绘制的点。由于在渲染的时候我们需要绑定当前vao,所以也需要对外暴露获取vao的接口。在使用glDrawElements的时候,需要提供绘制顶点的个数,因而也需要记录绘制顶点的个数并提供对应的接口。

#pragma once#include"core.h"class Geometry
{
public:Geometry();~Geometry();static Geometry* create_box(float size);static Geometry* create_sphere(float radius);GLuint get_vao() const{return m_vao;}uint32_t get_indices_count() const{return m_indices_count;}private:GLuint m_vao{ 0 };GLuint m_pos_vbo{ 0 };GLuint m_uv_vbo{ 0 };GLuint m_ebo{ 0 };uint32_t m_indices_count{ 0 };};

二、创建正方体

对于一个正方体而言,需要什么具体的顶点数据想必大家都是清楚的,但是一个个去敲这些数据显然是太麻烦了,不过,你觉得麻烦的东西自然有的是AI做。

效果展示:

Geometry* Geometry::create_box(float size)
{Geometry* geometry = new Geometry();float half_size = size / 2.0f;float positions[] = {// 前面-half_size, -half_size,  half_size,half_size, -half_size,  half_size,half_size,  half_size,  half_size,-half_size,  half_size,  half_size,// 后面-half_size, -half_size, -half_size,-half_size,  half_size, -half_size,half_size,  half_size, -half_size,half_size, -half_size, -half_size,// 上面-half_size,  half_size, -half_size,-half_size,  half_size,  half_size,half_size,  half_size,  half_size,half_size,  half_size, -half_size,// 下面-half_size, -half_size, -half_size,half_size, -half_size, -half_size,half_size, -half_size,  half_size,-half_size, -half_size,  half_size,// 右面half_size, -half_size, -half_size,half_size,  half_size, -half_size,half_size,  half_size,  half_size,half_size, -half_size,  half_size,// 左面-half_size, -half_size, -half_size,-half_size, -half_size,  half_size,-half_size,  half_size,  half_size,-half_size,  half_size, -half_size};float uvs[] = {// 前面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0,// 后面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0,// 上面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0,// 下面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0,// 右面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0,// 左面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0};unsigned int indices[] = {// 前面0, 1, 2,2,3,0,// 后面4, 5, 6,6,7,4,// 上面8, 9, 10,10, 11,8,// 下面12, 13, 14,14,15,12,// 右面16, 17, 18,18, 19,16,// 左面20, 21, 22,22, 23,20};//2. 创建VBOGLuint &pos_vbo=geometry->m_pos_vbo,&uv_vbo=geometry->m_uv_vbo,&ebo=geometry->m_ebo;glGenBuffers(1, &pos_vbo);glBindBuffer(GL_ARRAY_BUFFER, pos_vbo);glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);glGenBuffers(1, &uv_vbo);glBindBuffer(GL_ARRAY_BUFFER, uv_vbo);glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_STATIC_DRAW);//3. 创建EBOglGenBuffers(1, &ebo);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);geometry->m_indices_count = sizeof(indices) / sizeof(indices[0]);//4. 创建VAOglGenVertexArrays(1, &geometry->m_vao);glBindVertexArray(geometry->m_vao);glBindBuffer(GL_ARRAY_BUFFER, pos_vbo);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);glBindBuffer(GL_ARRAY_BUFFER, uv_vbo);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)0);//5. 绑定EBOglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->m_ebo);glBindVertexArray(0);return geometry;
}

三、创建球体

在进行学习之前,对于创建球体我是没有任何思路的,我认为这本身就是抽象的,相较于创建棱角分明的正方体,球体的曲面令人无从下手,如何去获取顶点数据成了一个难题。

球体可以看作是经纬线分布组成的集合体,每一个经线跟纬线的焦点,都可以视作一个顶点,而确定了经线条数(long)与纬线条数(latitude)就可以锁定球体的精度(肯定是越多显得越浑圆)。(课件截图来源于bilibili赵新政)

在这个基础上,就已经显现出计算顶点位置的思路了,我们只需要选取一条经线与一条纬线,去计算其交点的位置即可。结合几何知识,从图中的表示可以得出:

y=radius*cos(phi)

x=radius*sin(phi)*cos(theta)

z=radius*sin(phi)*sin(theta)

但是,在上述的表达式中,phi和theta角是未知的,那么接下来的问题便是解决角度的计算问题。

在计算角度的过程当中,这里是将球体看作半条经线(一个半圆)绕着圆周旋转一圈所得到。(★)

取纬线截面进行观察,可以发现每个小角都是被纬线等分得到,可知纬线角=Π/latitude。同理可得经线角=2Π/long。(这个公式与标★那句是紧密相关的)最终的phi/theta也就是经线角/纬线角乘以当前线的索引(第几条)。因而,只需要使用两层循环遍历纬线和经线,就可以得计算得到坐标了。(long为经线段数,latitude为纬线段数)

那么接下来需要处理uv数据。从展开图去分析,uv数据实际上也是被经纬线所等分,那么u=j/long,v=i/latitude。(j,i为当前遍历到的第几条经纬线)由于这里演示的uv坐标轴与实际的uv坐标轴是放过来的,因而需要取补。实际的u=1.0-j/long,v=1.0-i/latitude。

最后,就只需要取计算索引了。取出展开图中的一个矩形,记其右上角坐标为p1,那么右下角坐标就是由p1经过了纬线条数个顶点+1后所得到的顶点(展开图左右/上下的线实际上是同一条,两条线是重合的,因而在计算时需要+1),p2=p1+long+1,相应的左上角顶点就是p1+1,左下角顶点就是p2+1。

代码与效果演示:

Geometry* Geometry::create_sphere(float radius)
{Geometry* geometry = new Geometry();//1.位置 2.uv 3.索引//1.主要变量声明std::vector<GLfloat>positions{};std::vector<GLfloat>uvs{};std::vector<GLuint>indices{};//声明经线与纬线的数量int numLatLines = 60;int numLongLines = 60;//2.通过两层循环(纬线在外,经线在内)===>位置、uvfor (int i = 0; i <= numLatLines; i++){for (int j = 0; j <= numLongLines; j++){float phi = glm::pi<float>() * i / numLatLines;float theta = 2 * glm::pi<float>() * j / numLongLines;float y = radius * cos(phi);float x = radius * sin(phi) * cos(theta);float z = radius * sin(phi) * sin(theta);positions.push_back(x);positions.push_back(y);positions.push_back(z);float u = 1.0 - (float)j / (float)numLongLines;float v = 1.0 - (float)i / (float)numLatLines;uvs.push_back(u);uvs.push_back(v);}}//3.通过两层循环得到顶点索引for (int i = 0; i < numLatLines; i++){for (int j = 0; j < numLongLines; j++){int p1 = i * (numLongLines + 1) + j;int p2 = p1 + numLongLines + 1;int p3 = p1 + 1;int p4 = p2 + 1;indices.push_back(p1);indices.push_back(p2);indices.push_back(p3);indices.push_back(p3);indices.push_back(p2);indices.push_back(p4);}}//4.生成vbo与vaoGLuint& pos_vbo = geometry->m_pos_vbo, & uv_vbo = geometry->m_uv_vbo, & ebo = geometry->m_ebo;glGenBuffers(1, &pos_vbo);glBindBuffer(GL_ARRAY_BUFFER, pos_vbo);glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(float), positions.data(), GL_STATIC_DRAW);glGenBuffers(1, &uv_vbo);glBindBuffer(GL_ARRAY_BUFFER, uv_vbo);glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(float), uvs.data(), GL_STATIC_DRAW);glGenBuffers(1, &ebo);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW);glGenVertexArrays(1, &geometry->m_vao);glBindVertexArray(geometry->m_vao);glBindBuffer(GL_ARRAY_BUFFER, pos_vbo);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);glBindBuffer(GL_ARRAY_BUFFER, uv_vbo);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->m_ebo);glBindVertexArray(0);geometry->m_indices_count = indices.size();return geometry;
}

四、几何类的代码

我想你一定不会忘记在析构的时候把相关数据释放掉的~

geometry.h

#pragma once#include"core.h"class Geometry
{
public:Geometry();~Geometry();static Geometry* create_box(float size);static Geometry* create_sphere(float radius);GLuint get_vao() const{return m_vao;}uint32_t get_indices_count() const{return m_indices_count;}private:GLuint m_vao{ 0 };GLuint m_pos_vbo{ 0 };GLuint m_uv_vbo{ 0 };GLuint m_ebo{ 0 };uint32_t m_indices_count{ 0 };};

geometry.cpp

#include"geometry.h"
#include<vector>Geometry::Geometry()
{}
Geometry::~Geometry()
{if(m_vao!=0)glDeleteVertexArrays(1, &m_vao);if(m_pos_vbo!=0)glDeleteBuffers(1, &m_pos_vbo);if(m_uv_vbo!=0)glDeleteBuffers(1, &m_uv_vbo);if(m_ebo!=0)glDeleteBuffers(1, &m_ebo);
}Geometry* Geometry::create_box(float size)
{Geometry* geometry = new Geometry();float half_size = size / 2.0f;float positions[] = {// 前面-half_size, -half_size,  half_size,half_size, -half_size,  half_size,half_size,  half_size,  half_size,-half_size,  half_size,  half_size,// 后面-half_size, -half_size, -half_size,-half_size,  half_size, -half_size,half_size,  half_size, -half_size,half_size, -half_size, -half_size,// 上面-half_size,  half_size, -half_size,-half_size,  half_size,  half_size,half_size,  half_size,  half_size,half_size,  half_size, -half_size,// 下面-half_size, -half_size, -half_size,half_size, -half_size, -half_size,half_size, -half_size,  half_size,-half_size, -half_size,  half_size,// 右面half_size, -half_size, -half_size,half_size,  half_size, -half_size,half_size,  half_size,  half_size,half_size, -half_size,  half_size,// 左面-half_size, -half_size, -half_size,-half_size, -half_size,  half_size,-half_size,  half_size,  half_size,-half_size,  half_size, -half_size};float uvs[] = {// 前面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0,// 后面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0,// 上面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0,// 下面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0,// 右面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0,// 左面0.0, 0.0,1.0, 0.0,1.0, 1.0,0.0, 1.0};unsigned int indices[] = {// 前面0, 1, 2,2,3,0,// 后面4, 5, 6,6,7,4,// 上面8, 9, 10,10, 11,8,// 下面12, 13, 14,14,15,12,// 右面16, 17, 18,18, 19,16,// 左面20, 21, 22,22, 23,20};//2. 创建VBOGLuint &pos_vbo=geometry->m_pos_vbo,&uv_vbo=geometry->m_uv_vbo,&ebo=geometry->m_ebo;glGenBuffers(1, &pos_vbo);glBindBuffer(GL_ARRAY_BUFFER, pos_vbo);glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);glGenBuffers(1, &uv_vbo);glBindBuffer(GL_ARRAY_BUFFER, uv_vbo);glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_STATIC_DRAW);//3. 创建EBOglGenBuffers(1, &ebo);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);geometry->m_indices_count = sizeof(indices) / sizeof(indices[0]);//4. 创建VAOglGenVertexArrays(1, &geometry->m_vao);glBindVertexArray(geometry->m_vao);glBindBuffer(GL_ARRAY_BUFFER, pos_vbo);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);glBindBuffer(GL_ARRAY_BUFFER, uv_vbo);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)0);//5. 绑定EBOglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->m_ebo);glBindVertexArray(0);return geometry;
}
Geometry* Geometry::create_sphere(float radius)
{Geometry* geometry = new Geometry();//1.位置 2.uv 3.索引//1.主要变量声明std::vector<GLfloat>positions{};std::vector<GLfloat>uvs{};std::vector<GLuint>indices{};//声明经线与纬线的数量int numLatLines = 60;int numLongLines = 60;//2.通过两层循环(纬线在外,经线在内)===>位置、uvfor (int i = 0; i <= numLatLines; i++){for (int j = 0; j <= numLongLines; j++){float phi = glm::pi<float>() * i / numLatLines;float theta = 2 * glm::pi<float>() * j / numLongLines;float y = radius * cos(phi);float x = radius * sin(phi) * cos(theta);float z = radius * sin(phi) * sin(theta);positions.push_back(x);positions.push_back(y);positions.push_back(z);float u = 1.0 - (float)j / (float)numLongLines;float v = 1.0 - (float)i / (float)numLatLines;uvs.push_back(u);uvs.push_back(v);}}//3.通过两层循环得到顶点索引for (int i = 0; i < numLatLines; i++){for (int j = 0; j < numLongLines; j++){int p1 = i * (numLongLines + 1) + j;int p2 = p1 + numLongLines + 1;int p3 = p1 + 1;int p4 = p2 + 1;indices.push_back(p1);indices.push_back(p2);indices.push_back(p3);indices.push_back(p3);indices.push_back(p2);indices.push_back(p4);}}//4.生成vbo与vaoGLuint& pos_vbo = geometry->m_pos_vbo, & uv_vbo = geometry->m_uv_vbo, & ebo = geometry->m_ebo;glGenBuffers(1, &pos_vbo);glBindBuffer(GL_ARRAY_BUFFER, pos_vbo);glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(float), positions.data(), GL_STATIC_DRAW);glGenBuffers(1, &uv_vbo);glBindBuffer(GL_ARRAY_BUFFER, uv_vbo);glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(float), uvs.data(), GL_STATIC_DRAW);glGenBuffers(1, &ebo);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW);glGenVertexArrays(1, &geometry->m_vao);glBindVertexArray(geometry->m_vao);glBindBuffer(GL_ARRAY_BUFFER, pos_vbo);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);glBindBuffer(GL_ARRAY_BUFFER, uv_vbo);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->m_ebo);glBindVertexArray(0);geometry->m_indices_count = indices.size();return geometry;
}

 

 

http://www.dtcms.com/a/395475.html

相关文章:

  • 堆排序的应用
  • python13——异常处理
  • AXI_CAN IP 简单使用。(仿真、microblaze)
  • zabbix-mcp-server:使用自然语言操作Zabbix
  • 【Makefile】Linux内核模块编译
  • Qt 系统相关 - 音视频
  • Go基础:Go语言中的指针详解:在什么情况下应该使用指针?
  • ReactNative性能优化实践方案
  • 大数据数仓面试问题
  • 深入理解Java中的==、equals与hashCode:区别、联系
  • Qt笔记:QString::toLocal8Bit的理解
  • 第12章 机器学习 - 局限性
  • ​​[硬件电路-320]:模拟电路与数字电路,两者均使用晶体管(如BJT、MOSFET),但模拟电路利用其线性区,数字电路利用其开关特性。
  • 今日行情明日机会——20250922
  • 智能交通拥堵检测系统详解(附视频+代码资源)
  • LLM 数据安全:筑牢数据防线
  • AI 在医疗领域的十大应用:从疾病预测到手术机器人
  • 零序电流/电压(面向储能变流器应用)
  • 【系统分析师】2024年上半年真题:综合知识-答案及详解(回忆版)
  • 给工业通信装“耐达讯自动化翻译器”:电表说Modbus,主控听Profibus,全靠它传话
  • 不同品牌PLC如何接入云平台?御控多协议物联网网关一站式集成方案
  • 深入理解指针(最终章):指针运算本质与典型试题剖析
  • SCI 期刊验证!苏黎世大学使用 ALINX FPGA 开发板实现分子动力学模拟新方案
  • C# OnnxRuntime yolov8 纸箱分割
  • SQLite3的API调用实战例子
  • LeetCode 60. 排列序列
  • springboot2.7.11 + quartz2.3.2,单机,集群实战,增删改查任务,项目一启动就执行任务
  • Hive 调优
  • 王晨辉:RWA注册登记平台赋能资产数字化转型
  • 周末荐读:美 SEC 推出加密货币 ETF 上市标准,Base 发币在即