OpenGL着色器代码理解

前言

之前使用使用opengl开发的时候,直接copy的开源代码修改后直接使用,很多代码逻辑和原理都没懂,后面有时间了系统学习了一下,做下记录,方便后续查阅

一、VBO&VAO

1.1 VBO

VBO(VertexBufferObject): 存储Mesh顶点属性数据。
存储方式:
SingleBuffer 存储方式:
在这里插入图片描述

InterleavedBuffer 存储方式:
在这里插入图片描述

1.2 VAO

VAO(VertexArrayObject): 存储一个Mesh所有属性描述。
主要解决的问题:

  • 这个Mesh中,第N个点的位置属性数据怎么读取出来?
  • 这个Mesh中,第N个点的颜色属性数据怎么读取出来?
  • 这个Mesh中,第N个点的.….属性数据怎么读取出来?

方式: 为当前Mesh的每个属性,提供具体的描述信息

  • 每个顶点xxx个数字
  • 每个数字都是xxx类型
  • 每个顶点的数据,步长为xxxbyte
  • 此属性数据在顶点数据内的偏移量 offset
  • 此属性存储在xxx号vbo

二、着色器Shader

Shader(着色器程序): 一种运行在GPU端的,类C语言,用于处理顶点数据以及决定像素片元最终着色。Shader对三角形数据的处理,分为顶点处理与片元处理,分别称为VertexShader与FragmentShader。

GLSL语言(Graphic Library Shader Language): 着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。特点如下:

  • GLSL程序本质是一种将输入转化为输出的程序;
  • GLSL程序是一种非常独立的程序,彼此之间无法通信;只能通过输入输出相互承接

2.1 顶点着色器VertexShader

在这里插入图片描述

layout (location = 0)比较难理解,这里的location = n,和glVertexAttribPointer函数的第一个参数对应;也就是说,VBO用来存放数据,可以是顶点位置、纹理、颜色等属性;而VAO是用来存放怎么从VBO里面读取数据的配置(例如:编号0存放的是位置读取配置,编号1是存放颜色读取配置等等)。

创建vao后,会使用glVertexAttribPointer将当前的顶点属性与顶点缓冲对象(VBO)关联起来。它的原型如下:

void glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);

参数说明:

  • index:指定要配置的顶点属性的编号,从0开始。例如,顶点位置、纹理、法线等属性可以分别编号为0、1、2。
  • size:指定每个顶点属性的分量数(1、2、3或4),类似于向量的维度。例如,顶点位置通常有三个分量(x, y, z),所以size为3。
  • type:指定每个分量的数据类型,可以是GL_BYTE、GL_UNSIGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT或GL_DOUBLE。
  • normalized:指定是否将数据归一化到[0,1]或[-1,1]范围内。如果为GL_TRUE,数据将被归一化;如果为GL_FALSE,数据将保持原值。
  • stride:指定连续两个顶点属性间的字节数。如果为0,则表示顶点属性是紧密排列的。
  • pointer:指向缓冲对象中第一个顶点属性的第一个分量的地址(即偏移量)。

2.1 顶点着色器VertexShader

在这里插入图片描述
gl_Position 是glsl的内置变量,负责向后续阶段输出顶点位置处理的结果(NDC坐标);

2.2 片段着色器FragmentShader

在这里插入图片描述

三、Shader编译

glShaderSource 是 OpenGL 中的一个函数,用于将着色器源码加载到指定的着色器对象中。它是创建和编译着色器程序的重要步骤之一。函数原型:

void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);

参数说明:

  • shader:指定要设置源码的着色器对象的句柄(由 glCreateShader 创建)。
  • count:表示传入的字符串数组 string 的数量。
  • string:一个指向字符串数组的指针,每个字符串都是着色器源码的一部分。
  • length:一个整型数组,表示每个字符串的长度。如果为 NULL,则假定每个字符串以空字符 \0 结尾。

补充说明:实际使用过程中,可能是多个Shader搭配一起使用;例如:有A、B、C三个Shader,使用过程中,可能是A和C一起搭配使用,glShaderSource的count则需要传2,length则需要传数组,目前简单使用只会用到一个Shader。

在这里插入图片描述

1、 将vs与fs的源代码装入字符串

	const char* vertexShaderSource =
		"#version 460 core\n"
		"layout (location = 0) in vec3 aPos;\n"
		"void main()\n"
		"{\n"
		" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
		"}\n\0";
		
	const char* fragmentShaderSource = 
		"#version 330 core\n"
		"out vec4 FragColor;\n"
		"void main()\n"
		"{\n"
		" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
		"}\n\0";

2、创建 Shader 程序(vs、fs)

	GLuint vertex, fragment;
	
	vertex = glCreateShader(GL_VERTEX_SHADER);

	fragment = glCreateShader(GL_FRAGMENT_SHADER);

3、为 shader 程序输入 shader 代码

	glShaderSource(vertex, 1, &vertexShaderSource, NULL);
	
	glShaderSource(fragment, 1, &fragmentShaderSource, NULL);

4、执行 shader 代码编译

	glCompileShader (vertex);
	
	int success = 0;
	char infoLog[1024];//该字符串用来承接错误与否并输出
	// 检查 vertex 编译结果,并把结果放在success里面
	glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(vertex, 1024, NULL, infoLog);
		std::cout << "Error: SHADER COMPILE ERROR --VERTEX" << "\n" << infoLog << std::endl;
	}

	glCompileShader(fragment);
	
	// 检查 fragment 编译结果,并把结果放在success里面
	glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(fragment, 1024, NULL, infoLog);
		std::cout << "Error: SHADER COMPILE ERROR --FRAGMENT" << "\n" << infoLog << std::endl;
	}

5、创建一个Program壳子

	GLuint program = 0;

	program = glCreateProgram();

6、将vs与fs编译好的结果放到program这个壳子里

	glAttachShader(program, vertex);

	glAttachShader(program, fragment);

7、执行program的链接操作,形成最终可执行shader程序

	glLinkProgram(program);

	//检查链接错误
	glGetProgramiv(program,GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(program, 1024, NULL, infoLog);
		std::cout << "Error: SHADER LINK ERROR " << "\n" << infoLog << std::endl;
	}

8、shader链接完之后要清理,最终想拿到的是program,可以理解为将shader代码编译成一个exe文件,后续只需要使用该exe文件就行,也就是program

	glDeleteShader(vertex);

	glDeleteShader(fragment);
内容概要:本文围绕可变桨叶四旋翼无人机的规范控制与点对点运动模拟展开,重点研究优化推力分配策略在翻转动作中的应用与性能比较。通过Matlab代码实现,构建了四旋翼动力学模型,并设计了多种控制算法以实现精确的姿态调整与轨迹跟踪。研究对比了不同推力分配方案在执行高机动性翻转动作时的稳定性、能耗效率与响应速度,旨在提升无人机在复杂飞行任务中的动态性能与控制精度。该仿真研究为无人机飞控系统的设计与优化提供了理论依据和技术支持。; 适合人群:具备一定自动控制理论基础和Matlab编程能力,从事无人机控制、飞行器动力学或机器人系统研究的科研人员及研究生。; 使用场景及目标:① 实现四旋翼无人机在三维空间中的精确点对点运动控制;② 对比分析不同推力分配策略在执行翻转等高难度动作时的控制效果与能耗表现,优化飞行性能;③ 为无人机自主飞行、特技飞行及复杂环境下的机动控制提供算法验证平台。; 阅读建议:此资源以Matlab仿真为核心,建议读者结合相关控制理论知识,深入理解代码实现细节,重点关注动力学建模、控制律设计与推力分配模块。在学习过程中,应动手调试参数,复现文中翻转动作的仿真结果,并尝试拓展至其他复杂飞行任务,以加深对无人机控制机理的理解
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li_Zhi_Yao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值