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

OpenGL基本架构知识

OpenGL基本架构知识

环境:Qt Creator + C++

参考书籍:《计算机图形学编程(使用OpenGL和C++)》作者:V.斯科特.戈登 约翰.克莱维吉 (人民邮电出版社)

参考博客:https://blog.csdn.net/weixin_59876363/article/details/122570371

一、绪论

简介

  1. OpenGL是什么

    • 用于渲染2D和3D矢量图形的跨语言、跨平台的应用程序编程接口(API)
    • 提供了多级图形管线和GLSL(高级着色语言)进行模型的像素化
    • 核心库使用c编写,同时支持多种系统和语言
  2. 核心模式/可编程管线模式:
    将顶点数据经过管线处理,最终生成显示的像素点。以下为渲染顺序:

    • 顶点着色器:必须自己实现,依靠顶点数据和着色器指令控制顶点的渲染位置和方式
    • 曲面细分着色器:可以在简单图形上生成大量网格图元(常用的是三角)
    • 几何着色器:赋予程序员一次处理一个图元(多个顶点)的能力(常用的是三角)
    • 光栅化:将输入的图元转化成二维图像片段,即像素绘制的片段
    • 片段着色器:为光栅化的像素点指定颜色
    • 测试与混合
  3. GLSL运行在GPU上,OS不是总能捕捉运行的错误,通常需要进行GLSL运行日志的打印进行debug

  4. GLSL代码加载进入着色器的过程

    • C++获取GLSL着色器代码,可以在文件中读取,也可以硬编码在字符串中
    • 创建OpenGL空着色器对象并将GLSL着色器代码加载进着色器对象(一般至少要提供顶点着色器和片段着色器的代码,其他着色器代码是可选的)
    • 最后使用OpenGL命令编译链接着色器对象,并将该对象加载进硬件
  5. QOpenGLWidget提供了三个便捷的虚函数,可以进行重载实现典型的OpenGL任务

    • initializeGL:初始化设置OpenGL资源和状态
    • paintGL:渲染OpenGL场景,widget需要进行实时更新的调用
    • resizeGL:设置OpenGL视口和投影等。widget调整大小时调用
  6. 顶点着色器会在GPU上创建内存,通过GL_ARRAY_BUFFER缓冲类型的顶点缓冲对象进行管理,每一个VBO记录了一种状态。

    • 数据:从内存加载顶点缓冲对象(Vertex Buffer Objects ,VBO)到的显存中
    • 规则:通过顶点数组对象(Vertex Array Object,VAO)对顶点数据进行解释
  7. OpenGL是一个巨大的状态机,数据输入后,绘制状态参数(材质,光照,连接方式···)决定了输出的图像。OpenGL通过改变上下文变量来改变OpenGL状态,从而告知OpenGL如何绘制图像

  8. 在paintGL之外的地方调用绘制函数,没有意义,绘制图像最终将被paintGL()覆盖。如果不想被覆盖则应该使用widget的update()函数进行安排和更新

缓冲区对象

  1. 缓冲区绑定流程

    // 1.声明VBO和VAO
    GLuint vao[1];// GLuint实际就是unsigned int
    GLuint vbo[2];
    // 2.创建缓冲区,并将返回的ID存入VAO和VBO
    glGenVertexArrays(1, &VAO);
    glGenBuffers(2, &VBO);// (创建ID的数目, 用来保存返回ID的数组)
    // 3.将VAO和VBO绑定成缓冲区对象
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // 4. 初始化缓冲区数据
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);
    // 5. 告诉GPU如何解释VBO中的属性信息
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), nullptr);
    // 6. 启用0号顶点属性
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float),  (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    // 7. 使用glDrawArrays()绘制对象
    
  2. 着色器的绑定:每个缓冲区需要有在顶点着色器中声明的相应的顶点属性变量

    layout(location = 0) in vec3 position
    // 位置值为0的顶点属性指针指向的数据,每次抓取3个到position中
    
    • layout修饰符是将顶点属性和特定缓冲区关联的方法,这里的0就是绑定的0号ID的VBO
    • in关键字表示顶点属性从缓冲区接收数值
    • vec3的意思是着色器每次调用会抓取三个浮点类型的值,即一个坐标(x, y, z)
    • 变量名称是position
  3. 通常把顶点数据放在一个缓冲区中,并把这个缓冲区和着色器中声明的顶点属性相关联

  4. 在OpenGL中,缓冲区被包含在顶点缓冲对象(Vertex Buffer Object, VBO)中,VBO在C++/OpenGL应用程序中被声明和实例化。一个场景可能需要很多个VBO,通常我们在初始化的阶段生成并填充若干个VBO,方便后续直接使用。

  5. 使用uniform关键字在着色器中声明统一变量

GLSL

  1. GLSL(OpenGL Shading Language)使用C语言作为基础高阶着色语言,避免了使用汇编语言或硬件规格语言的复杂性。

    // 典型程序
    #version version_number
    in type in_variable_name;
    in type in_variable_name;
    out type out_variable_name;
    uniform type uniform_name;
    
    void main(){
        out_variable_name = weird_stuff_we_processed;
    }
    
  2. OpenGL确保至少有16个包含4分量的顶点属性可以使用,但是可以声明的顶点属性数量存在上限

  3. GLSL的容器

    • 向量(Vetor)
      • vecn:n个float类型
      • bvecn:n个boolean类型
      • ivecn:n个intgers类型
      • uvecn:n个无符号整形
      • dvecn:n个double类型
    • 矩阵(Matrix)
  4. 向量重组

    vec2 vect = vex2(0.5, 0.7);
    vec4 result = vex4(vect, 0.0, 0.0);
    
  5. 如果类型和名字都一致,OpenGL可以把不同程序文件变量都链接在一起

    
    #version 330 core
    // 顶点着色器定义和声明变量
    layout (location = 0) in vec3 aPos;
    out vec4 vertexColor;
    void main() {
        gl_Position = vec4(aPos, 1.0);
        vertexColor = vec4(0.5, 0.0, 0.0, 1.0);
    }
    
    
    #version 330 core
    // 片段着色器接受变量
    out vec4 FragColor;
    in vec4 vertexColor;// 接收的变量
    void main() {
        FragColor = vertexColor; 
    }
    

    如果将它们封装在单独的资源文件中,使用shaderProgram.addShaderFromSourceFile()函数进行连接,则首行必须是版本号

  6. paintGL()以外的位置绘制openGL

    makeCurrent();
    // openGL绘制函数
    doneCurrent();
    update();
    

    如果不使用上述函数进行包含,绘制的图像将会被paintGL( )覆盖

  7. 顶点着色器的输入来源于openGL形式化的数据,使用layout(location = 0)链接到顶点数据,可以在cpu上配置顶点属性

  8. GPU和CPU之间的数据传输

    • 使用glGetAttribLocation查村属性位置值
    • 绑定GPU的属性值,告诉它向cpu解析的属性值
    // 1.属性值查询的方式
    shaderProgram.bind();
    // 查询属性位置值,如果查询不到返回-1
    Glint posLocation = shaderProgram.attributeLocation("aPos");
    // 告诉显卡如何解析缓冲里的属性值
    glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float),(void*)0);
    
    // 2.绑定的方式
    shaderProgram.bind();
    GLint posLocation = 2;
    shaderProgram.bindAttributeLocation("aPos", poslocation);
    glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0);
    // 开始VAO管理的第三个属性值
    glENableVertexAttribArray(posLocation);
    
  9. uniform是一种CPU向GPU中着色器发送数据的方式,是全局的Global,可以被任意着色器程序在任意阶段访问

    • 如果声明一个uniform却没有用过,编译器会默认移除这个变量,导致最后编译出的版本中并不会包含它,这可能导致几个非常麻烦的错误
    • OpenGL在其核心是一个c库,所以不支持类型的重载 ,在函数参数类型不同时,需要为其定义新的函数。
  10. 在Qt中可以使用QTimer的timeout信号槽,传递一个随着时间改变的值

  11. 将顶点着色器中的数据传到片段着色器中

    // 顶点着色器
    #version 330 core
    layout(location = 0) in vec3 aPos;
    layout(location = 1) in vec3 aColor;
    out vec3 ourColor;# 传递的值
    void main(){
        gl_Position = vex4(aPos, 1.0);
        ourColor = aColor;
    }
    
    // 片段着色器
    #version 330 core
    out vec4 FragColor;
    in vec3 ourColor;
    
    void main(){
        FragColor = vec4(ourColor, 1.0);
    }
    

纹理处理

  1. 当出现复杂图形绘制时候通常采用纹理贴图采样的方式,即对一个图片进行裁剪后的部分显示

https://blog.csdn.net/weixin_59876363/article/details/122807398

相关文章:

  • 神奇的卡尔曼滤波,行人追踪的福音
  • 第三章 教育法律法规
  • MATLAB | 全网唯一,使用MATLAB绘制好看的韦恩图(venn)
  • 2D Transpose算子GPU实现和优化
  • 软件复杂性的来源与应对
  • 11.9 表达式求值
  • 09-排序3 Insertion or Heap Sort(浙大数据结构)
  • java-python+vue社区防疫服务管理系统网站
  • sparksql insertinto 源码解析
  • 反射之获取Class
  • 【博客477】prometheus-----数值数据编码(varint与zigzag)
  • LCMXO2-2000HC-4FTG256C FPGA MachXO2系列 256-FTBGA 现场可编程门阵列
  • 初始Cpp之 四、数据类型
  • office2019如何自定义安装位置?
  • java基于ssm的汽车维修保养管理系统
  • [PHP内核探索]PHP中的哈希表
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • 《Java编程思想》读书笔记-对象导论
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • co.js - 让异步代码同步化
  • Docker: 容器互访的三种方式
  • java8 Stream Pipelines 浅析
  • JavaScript DOM 10 - 滚动
  • Java超时控制的实现
  • js 实现textarea输入字数提示
  • Promise面试题2实现异步串行执行
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Redux 中间件分析
  • text-decoration与color属性
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 二维平面内的碰撞检测【一】
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 构建工具 - 收藏集 - 掘金
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 力扣(LeetCode)21
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • 微信小程序开发问题汇总
  • 我建了一个叫Hello World的项目
  • 一些关于Rust在2019年的思考
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • $().each和$.each的区别
  • (3)nginx 配置(nginx.conf)
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (转)3D模板阴影原理
  • (转)fock函数详解
  • (转)Oracle存储过程编写经验和优化措施
  • (转)机器学习的数学基础(1)--Dirichlet分布
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • .Mobi域名介绍
  • .Net Remoting常用部署结构