OpenGL入门002——顶点着色器和片段着色器
文章目录
- 一些概念
- 坐标转换阶段
- 顶点着色器
- 片段着色器
- VBO
- VAO
- 实战
- 简介
- main.cpp
- CMakeLists.txt
- 最终效果
一些概念
坐标转换阶段
概述: 模型空间、世界空间、视图空间和裁剪空间是对象在3D场景中经历的不同坐标变换阶段。每个空间对应渲染管道的一个步骤,逐步将模型从其初始位置转换到最终屏幕上的位置
模型空间:
- 定义:这是对象的本地坐标系,是模型创建时的坐标
- 作用:模型空间定义了物体的基本形状和几何信息,不受场景中其他物体位置的影响。每个模型都有自己的模型空间坐标
- 转换:通过模型矩阵,可以将模型空间的坐标转换为世界空间
世界空间:
- 定义:这是整个场景的坐标系,所有模型在世界空间中都有唯一的位置和方向,可以视为“全局坐标系”
- 作用:将所有对象放置在同一个坐标系统中,确保它们相对位置正确
- 转换:通过应用视图矩阵,将世界空间的坐标转换为视图空间,视图矩阵通常基于摄像机的位置和方向
视图空间:
- 定义:又称为“摄像机空间“,是以摄像机位置为原点的坐标系,此时场景中的所有对象都以摄像机为参考重新定位
- 作用:使得所有对象相对于摄像机的位置和方向变得更直观,便于确定哪些对象可见、如何投影到屏幕
- 转换:使用投影矩阵将视图空间转换为裁剪空间,这一步决定了图像的投影类型(如透视投影或正交投影)
裁剪空间:
- 定义:应用投影变换后的空间,此时3D场景的坐标被转换为一个标准化的3D盒子,所有可见的坐标x,y,z值均被限制在-1到1之间
- 作用:裁剪空间便于对视锥外部的物体进行裁剪,只保留可见部分。裁剪后的坐标将进行透视除法,映射到2D屏幕上的坐标,即归一化设备坐标
- 转换:裁剪空间进一步转换为屏幕空间,经过视口变换,使坐标适配屏幕的分辨率和比例
顶点着色器
概述: 顶点着色器(Vertex Shader)是对输入的顶点进行处理,顶点是组成几何体的基本元素,比如三角形的每个角都是一个顶点
作用:
- 顶点位置变换:顶点着色器通常会将顶点从模型空间(即对象的局部坐标系)转换到世界空间、视图空间,最后转换到裁剪空间,以便在屏幕上正确显示
- 顶点属性处理:除了位置,顶点着色器还可以处理其他与顶点相关的属性,比如法线、纹理坐标、颜色等
- 光照计算:在某些情况下,顶点着色器可以进行基础的光照计算,如使用法线来计算顶点的光照效果
输入: 顶点的坐标、法线、纹理坐标等数据
输出: 处理后的顶点坐标,如变换后的顶点位置和其他顶点属性,供后续的图形流水线使用
片段着色器
概述: 片段着色器(Framgment Shader)是屏幕上每个像素的潜在颜色值,在光栅化阶段之后,每个几何体被转换为一系列像素片段,片段着色器负责确定这些片段的最终颜色
作用:
- 颜色计算:片段着色器通过对纹理、光照、材质等信息的处理,确定每个像素的颜色,它通常会结合插值后的顶点属性(如纹理坐标或颜色)进行复杂的颜色计算
- 光照效果:片段着色器可以进行精确的光照计算,以便生成更逼真的阴影和高光效果
- 纹理映射:片段着色器可以从纹理中采样,根据纹理坐标获取纹理颜色,并应用到片段上
输入: 每个片段的插值属性(如纹理坐标、颜色、发现等)
输出: 每个片段的最终颜色值,传递给屏幕或帧缓冲区
VBO
概述: VBO(Vertex Buffer Object)是一个存储在GPU内存中的缓冲区,用于存放顶点数据。通常,顶点数据包含顶点的坐标、颜色、法线、纹理坐标等信息。通过使用VBO,程序可以将这些顶点数据传输到GPU,这样GPU可以在渲染时快速访问它们,而不需要每帧都从CPU传输数据
关键步骤:
- 创建VBO:使用glGenBuffers()创建一个VBO
- 绑定VBO:使用glBindBuffer()绑定VBO,指定将要存储的缓冲数据类型(如顶点数据GL_ARRY_BUFFER)
- 填充VBO:使用glBufferData()将顶点数据传输到VBO中
VAO
概述: VAO(Vertex Array Object)是一个用于保存VBO配置的对象,它记录了与绘制顶点相关的所有状态信息。VAO不仅仅是VBO的一个包装器,它还保存了顶点属性指针和启用状态。例如:每个顶点的布局、数据的解释方式(如步幅、偏移量),以及使用的VBO。使用VAO可以简化渲染过程,因为当VAO被绑定时,所有与其关联的VBO和顶点属性信息都会自动生效。
关键步骤:
- 创建VAO:使用glGenVertexArrays()创建一个VAO
- 绑定VAO:使用glBindVertexArray()绑定VAO
- 设置顶点属性指针:使用glVertexAttribPointer()设置顶点属性指针(告诉OpenGL如何解释顶点数据)
- 启用顶点属性:使用glEnableVertexAttribArray()启用顶点属性
实战
简介
怎么在vscode上使用cmake构建项目,具体可以看这篇Windows上如何使用CMake构建项目 - 凌云行者的博客
目的: 绘制一个三角形
环境:
- 编译工具链:使用msys2安装的mingw-gcc
- 依赖项:glfw3:x64-mingw-static,glad:x64-mingw-static(通过vcpkg安装)
main.cpp
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>using std::cout;
using std::endl;// 屏幕宽度
const unsigned int SCR_WIDTH = 800;
// 屏幕高度
const unsigned int SCR_HEIGHT = 600;// 窗口大小改变的回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {// 确保视口与新窗口尺寸匹配,注意在视网膜显示器上,宽度和高度会显著大于指定值glViewport(0, 0, width, height);
}// 处理输入
void process_input(GLFWwindow* window) {// 按下ESC键时进入if块if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)// 关闭窗口glfwSetWindowShouldClose(window, true);
}// 顶点着色器源码
const char *vertexShaderSource = "#version 330 core\n" // 指定了GLSL(OpenGL着色器语言)的版本"layout (location = 0) in vec3 aPos;\n" // 定义了一个输入变量aPos,它是一个vec3类型的变量, 并且指定了它的位置值为0, 这意味着顶点属性数组的第一个属性将被绑定到这个变量"void main()\n""{\n"" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" // 将输入的顶点位置aPos转换为一个四维向量,gl_Postion是OpengGL固定功能管线中用于存储顶点位置的变量"}\0";// 片段着色器源码
const char *fragmentShaderSource = "#version 330 core\n" // 指定了GLSL(OpenGL着色器语言)的版本"out vec4 FragColor;\n" // 定义了一个输出变量FragColor,它是一个vec4类型的变量,表示片段颜色,out关键字表示这个变量将输出到渲染管线的下一个阶段"void main()\n""{\n"" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" // 将输出颜色设置为橙色"}\n\0";int main() {// 初始化glfwglfwInit();// 设置opengl版本glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);// 使用核心模式:确保不使用任何被弃用的功能glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// 创建glfw窗口GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "I am window title", NULL, NULL);if (window == NULL) {cout << "Failed to create glfw window" << endl;// 终止GLFWglfwTerminate();return -1;}// 设置当前窗口的上下文glfwMakeContextCurrent(window);// 设置窗口大小改变的回调函数glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// 加载opengl函数指针if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {cout << "Failed to initialize GLAD" << endl;return -1;}// 构建并编译顶点着色程序// 创建一个着色器对象,GL_VERTEX_SHADER表示顶点着色器unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);// 将着色器源码附加到着色器对象上glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);// 编译着色器glCompileShader(vertexShader);// 检查着色器是否编译成功int success;char infoLog[512];glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if (!success) {glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n"<< infoLog << endl;}// 构建并编译片段着色器// 创建一个着色器对象,GL_FRAGMENT_SHADER表示片段着色器unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);// 将着色器源码附加到着色器对象上glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);// 编译着色器glCompileShader(fragmentShader);// 检查着色器是否编译成功glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success) {glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n"<< infoLog << endl;}// 创建着色器程序对象unsigned int shaderProgram = glCreateProgram();// 将着色器对象附加到着色器程序上glAttachShader(shaderProgram, vertexShader);glAttachShader(shaderProgram, fragmentShader);// 链接程序对象glLinkProgram(shaderProgram);// 检查链接是否成功glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);if (!success) {glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n"<< infoLog << endl; }// 删除着色器对象glDeleteShader(vertexShader);glDeleteShader(fragmentShader);// 设置三角形的顶点数据float vertices[] = {-0.5f, -0.5f, 0.0f, // 左下角0.5f, -0.5f, 0.0f, // 右下角0.0f, 0.5f, 0.0f // 顶部};// 生成一个VAOunsigned int VAO;glGenVertexArrays(1, &VAO);// 绑定VAO,使其成为当前操作的VAOglBindVertexArray(VAO);// 生成一个VBOunsigned int VBO;glGenBuffers(1, &VBO);// 绑定VBO, 使其成为当前操作的VBO,GL_ARRAY_BUFFER表示顶点缓冲区glBindBuffer(GL_ARRAY_BUFFER, VBO);// 为当前绑定的VBO创建并初始化数据存储,GL_STATIC_DRAW表示数据将一次性提供给缓冲区,并且在之后的绘制过程中不会频繁更改glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 定义顶点属性的布局// - index:顶点属性的索引// - size:每个顶点属性的数量,每个顶点有三个分享// - type:数据类型// - normalized:是否将非浮点数值归一化// - stride:连续顶点属性之间的间隔// - pointer:数据在缓冲区中的偏移量glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0);// 启用顶点属性数组glEnableVertexAttribArray(0);// 解绑VBOglBindBuffer(GL_ARRAY_BUFFER, 0);// 解绑VAOglBindVertexArray(0);// 循环渲染while (!glfwWindowShouldClose(window)) { // 检查是否应该关闭窗口// 处理输入process_input(window);// 清空屏幕所用的颜色glClearColor(0.2f, 0.3f, 0.3f, 1.0f);// 清空颜色缓冲,主要目的是为每一帧的渲染准备一个干净的画布glClear(GL_COLOR_BUFFER_BIT);// 使用着色器程序glUseProgram(shaderProgram);// 绑定VAOglBindVertexArray(VAO);// 绘制三角形glDrawArrays(GL_TRIANGLES, 0 ,3);// 交换缓冲区glfwSwapBuffers(window);// 处理所有待处理事件,去poll所有事件,看看哪个没处理的glfwPollEvents();}// 删除VAOglDeleteVertexArrays(1, &VAO);// 删除VBOglDeleteBuffers(1, &VBO);// 删除着色器程序glDeleteProgram(shaderProgram);// 终止GLFW,清理GLFW分配的资源glfwTerminate();return 0;
}
CMakeLists.txt
# 设置CMake的最低版本要求
cmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(HelloTriangle)# vcpkg集成, 这里要换成你自己的vcpkg工具链文件和共享库路径
set(VCPKG_ROOT D:/software6/vcpkg/)
set(CMAKE_TOOLCHAIN_FILE ${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
set(CMAKE_PREFIX_PATH ${VCPKG_ROOT}/installed/x64-mingw-static/share)# 查找所需的包
find_package(glad CONFIG REQUIRED)
find_package(glfw3 CONFIG REQUIRED)# 添加可执行文件
add_executable(HelloTriangle main.cpp)# 链接所需的库
target_link_libraries(HelloTriangle PRIVATE glad::glad glfw)
最终效果
相关文章:

OpenGL入门002——顶点着色器和片段着色器
文章目录 一些概念坐标转换阶段顶点着色器片段着色器VBOVAO 实战简介main.cppCMakeLists.txt最终效果 一些概念 坐标转换阶段 概述: 模型空间、世界空间、视图空间和裁剪空间是对象在3D场景中经历的不同坐标变换阶段。每个空间对应渲染管道的一个步骤,…...

[数组排序] LCR 164. 破解闯关密码
文章目录 1. 题目链接2. 题目大意3. 示例4. 解题思路5. 参考代码 1. 题目链接 LCR 164. 破解闯关密码 - 力扣(LeetCode) 2. 题目大意 描述:给定一个非负整数数组 nums。 要求:将数组中的数字拼接起来排成一个数,打印…...

05 Django 框架模型介绍(一)
文章目录 1、Django 模型简介2、Django 中创建并使用模型(1)新加一个名为 myapp 的应用(2)定义模型类(2)激活模型类(3)创建数据库迁移文件(4)应用迁移文件 3、…...

【简道云 -注册/登录安全分析报告】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…...

【C++题解】1970. 判断是什么字符
欢迎关注本专栏《C从零基础到信奥赛入门级(CSP-J)》 问题:1970. 判断是什么字符 类型:字符串、字符型 题目描述: 从键盘读入一个字符,有可能是大写字母、小写字母、数字中的一种,请编程判断&…...

Python自动化操作Word文档详解
在日常办公和数据处理中,我们经常需要处理Word文档。手动操作Word文档可能会非常繁琐和耗时,而使用Python可以实现自动化操作,提高工作效率。本文将详细介绍如何使用Python自动化操作Word文档,包括读取、写入、修改和格式化等操作…...

常用滤波算法(二)-中位值滤波法
文章目录 一、中位值滤波法简介二、C语言实现中位值滤波法三、程序说明信号初始化:滤波窗口大小:内存分配:中位值滤波函数:中位值计算函数:内存释放: 四、总结 中位值滤波法,作为一种非线性滤波…...

HCIP--以太网交换安全(总实验)
实验背景 假如你是公司的网络管理员,为了提高公司网络安全性,你决定在接入交换机部署一些安全技术:端口隔、端口安全、DHCP snooping、IPSG。 实验拓扑图 实验的要求: 1.在R1、R2连接在GE0/0/1和GE0/0/2接口下,均划…...

C语言 | Leetcode C语言题解之第519题随机翻转矩阵
题目: 题解: typedef struct {unsigned long long val;UT_hash_handle hh; } Hash;typedef struct {Hash *hash;int n_rows;int n_cols; } Solution, SL;Solution* solutionCreate(int n_rows, int n_cols) {SL *obj malloc(sizeof(SL));obj->hash …...

《机器人SLAM导航核心技术与实战》第1季:第10章_其他SLAM系统
视频讲解 【第1季】10.第10章_其他SLAM系统-视频讲解 【第1季】10.1.第10章_其他SLAM系统_RTABMAP算法-视频讲解 【第1季】10.2.第10章_其他SLAM系统_VINS算法-视频讲解 【第1季】10.3.第10章_其他SLAM系统_机器学习与SLAM-视频讲解 第1季:第10章_其他SLAM系统 …...

《双指针篇》---快乐数
题目传送门 1.创建一个bitsum函数用于得到这个数每位的平方和。 2.令快指针等于bitsum(n) 3.慢指针等于n。 逐步令 fast bitSum(bitSum(fast)); slow bitSum(slow); 若最后fast等于slow,则且等于1.则return true。 否则return false。 cla…...

U盘引导丢失问题的处理办法
项目背景:在使用自制的u盘系统的时候经常遇到引导丢失的问题,那么咱们怎么解决这个问题呢,首先第一步通过手动引导u盘 进入系统,同时再进行引导区的修复这样u盘系统就可以正常工作了。 1 进入grub 的提示符下面,首先…...

layui tree customSelet选中的内容重写,查找父级
layui tree customSelet选中的内容重写,查找父级 需要重新源码 // 递归查找函数 // tree 所有数据 ,nodeId选中数据id值 function findParent(tree, nodeId, parent null) {for (let i 0; i < tree.length; i) {if (tree[i].id nodeId) {return …...

Maven 插件
Maven 插件 Maven 是一个强大的项目管理和构建自动化工具,广泛应用于 Java 项目中。它通过插件来实现各种功能,如编译、测试、打包、部署等。Maven 插件是 Maven 的核心组成部分,它们扩展了 Maven 的功能,使其能够执行各种任务。…...

MybatisPlus入门(七)MybatisPlus-DQL编程控制
一、查询投影 查询投影:查出来的东西有多少字段,设置查询出来的结果长什么样,查出的字段控制; 查询投影适用于lamda格式,使用select 查询 lqw.select(User::getId,User::getName,User::getAge); 代码示例࿱…...

K8S概念及其常见组件和整体架构
1.概念 什么是Kubernetes 官网:Kubernetes 文档 | Kubernetes K8S的本质是一组服务器集群,可以在对应服务器集群的每个节点上运行程序,来对节点中的容器进行管理 类似Master-Work方式,每个服务器上安装特定的K8S组件,…...

LabVIEW继电器视觉检测系统
随着制造业的自动化与高精度要求不断提升,传统的人工检测方法逐渐难以满足高效和高精度的需求。特别是在航空航天、医疗设备等高端领域,密封继电器推动杆部件的质量直接影响到设备的性能与可靠性。LabVIEW自动化视觉检测系统,能对推动杆部件进…...

linux操作系统进程
linux操作系统是对下的软硬件进行管理,为了能够对上提供稳定,快速,安全的服务而诞生的软件。 广义上的操作系统是包含搭载在操作系统上的软件和函数库等文件的。 狭义上的操作系统就是操作系统内核,进行进程管理,文件…...

jeecgbootvue2菜单路由配置静态文件夹(public)下的html
需求:想要在菜单配置src/assets/iconfont/chart.html显示页面(目的是打包上线以后运维依然可以修改数据) 官网没有相关数据:菜单配置说明 JeecgBoot 开发文档 看云 问题现象: 我把文件放在src/assets/iconfont/chart.html然后在vue中作为 iframe 的 src 属性&am…...

PHP反序列化原生类字符串逃逸框架反序列化利用
PHP反序列化 概念 序列化的原因:为了解决开发中数据传输和数据解析的一个情况(类似于要发送一个椅子快递,不可能整个椅子打包发送,这是非常不方便的,所以就要对椅子进行序列化处理,让椅子分成很多部分在一起打包发送…...

6.1、属性动画
使用显式动画产生布局更新动画 1.旋转动画 只修改对应的属性 rotate({angle: this.angle}) 即可达到效果 动画效果 对应实现代码 @Entry @Component struct AnimationPage {@State angle:number = 0aboutToAppear() {...

v-model还可以作用于其他表单元素的使用
1、文本输入框 直接双向绑定输入的元素值 初始化默认值为空字符串 2、复选按钮 直接双向绑定输入的元素值 初始化默认值为flase,不选中 3、单选按钮, 1.使用name分组,产生互斥效果。 2.使用value存值, 3再用v-model双向绑定…...

最短路的求解
实验类型:◆验证性实验 ◇综合性实验 ◇设计性实验 实验目的:学会使用Matlab求解最短路。 实验内容:1.熟练运用Floyd算法;2. 熟练运用Dijkstra算法;3.利用Matlab编程实现最短路的计算。 例1:已知无向图…...

四:java 基础知识(4)-- 异常 字符串
目录 1. 异常处理 1.1 什么是异常 1.2 异常的类型 1.2.1 检查异常 1.2.2 运行时异常 1.3 异常的捕获与处理 1.3.1 try-catch 语句 1.3.2 finally 块 1.3.3 throw 和 throws 关键字 1.4 自定义异常 1.5 异常的最佳实践 2. 字符串 2.1 String 类的概述 2.2 字符串的…...

Uniapp 实现app自动检测更新/自动更新功能
实现步骤 配置 manifest.json 在 manifest.json 中设置应用的基本信息,包括 versionName 和 versionCode。 一般默认0.0.1,1. 服务器端接口开发 提供一个 API 接口,返回应用的最新版本信息,版本号、下载链接。客户端检测更新 使…...

7.0、RIP
RIP (Routing Information Protocol) 简介 RIP是由Xerox在20世纪70年代开发的,最初定义在RFC1058中。RIP用两种数据包传输更新:更新和请求,每个有RIP功能的路由器在默认情况下,每隔30s利用UDP520端口向与它直连的网络邻居广播(RIP1)或组播(R…...

C#与C++结构体的交互
C#在和C进行交互时,有时候会需要传递结构体。 做一些总结,避免大家在用的时候踩坑。 一般情况 例如我们在C里定义了一个struct_basic结构体 1 struct struct_basic 2 { 3 WORD value_1; 4 LONG value_2; 5 DWORD value_3; 6 UINT v…...

sql纵表转横表
项目上有一个需求(例子): 用户表 user{ id, name, workCode } id name workCode 1 张三 WC1001 2 李四 WC1002 工作信息表 work{ id, name, workCode, workTimeSun } id name …...

数据采集-Kepware OPCUA 服务器实现
KepserverEX OPC UA server设置 系列文章目录 数据采集-Kepware 安装证书异常处理 目录 KepserverEX OPC UA server设置系列文章目录一、OPC UA(OPC Unified Architecture)二、防火墙的配置三、配置KepserverEX的OPC UA3.1 启用远程连接3.2 启动OPCUA服务器接口 四、管理OPCU…...

初识计算机网络
🌎初识计算机网络 文章目录: 初识计算机网络 计算机网络背景 网络协议 初识协议 制定协议标准的组织或公司 OSI七层模型 操作系统和计算机网络关系 再谈协议 网络传输的基本流程 …...