OpenGL
OpenGL
Edmend Zhanghttps://blog.tryhardzhang.xyz/2024/08/02/graphics/opengl/Opengl/
https://blog.tryhardzhang.xyz/2024/07/20/intern/%E8%A7%86%E9%A2%91%E6%92%AD%E6%94%BE%E5%99%A8/
OpenGL
- 与窗口分离
OpenGL本身不负责创建窗口、管理窗口事件或者处理输入设备。这些任务通常由其他库或操作系统提供的窗口系统接口来处理。
由于viewport视口
- 右手坐标系
GLFW
GLFW(Graphics Library Framework)是一个用于创建窗口、处理输入以及管理OpenGL上下文的开源库。它简化了OpenGL程序的开发,提供了跨平台的接口,使得开发者可以专注于渲染和图形处理,而不必担心底层平台特定的窗口系统和输入处理。GLFW支持Windows、macOS和Linux等多个操作系统。
Glad 是实现函数指针的映射
opengl是状态机 保存自己渲染的状态和管线的状态
1 | glfwInit():初始化GLFW库。 |
Glad
- 加载OpenGL函数指针: OpenGL函数的地址需要在运行时通过特定的方式获取,这与直接调用标准库中的函数不同。Glad帮助开发者在运行时加载这些函数指针,以便应用程序能够调用最新的OpenGL功能。
- 管理OpenGL扩展: OpenGL是一个不断发展的标准,不同的显卡和驱动程序可能支持不同的OpenGL扩展。Glad允许你指定需要的扩展,并生成相应的加载代码,以便在应用程序中使用这些扩展。
- 跨平台支持: Glad支持多种操作系统(如Windows、Linux、macOS)和多种OpenGL版本,使得开发者可以编写跨平台的OpenGL代码。
Viewport可以定义应该在哪个视口进行渲染
双缓存
glfwSwapBuffers(window);
Shader 如何从CPU获取数据
- Layout 锚点信息 告诉信息布局 分别是什么
Shader
Vertex Buffer Object (VBO)
VBO 是一种用于存储顶点数据的缓冲区对象。它允许将顶点数据(例如顶点坐标、法线、纹理坐标等)存储在 GPU 内存中,以便快速访问和渲染。使用 VBO 可以显著提高渲染性能,因为数据传输的瓶颈被减少了。
- 获取VBO的index
- 绑定VBO
- 给VBO分配显存空间 传输数据
- 告诉shader数据解析方式
- 激活锚点
Vertex Array Object (VAO)
VAO 是一种用于存储顶点属性配置的对象。它保存了顶点属性指针的状态以及与之关联的 VBO。VAO 的作用是简化顶点属性配置的过程,通过绑定 VAO,可以快速恢复先前配置好的顶点属性状态。
程序中关系
VAO 管理和维护与绘制相关的 VBO 及其顶点属性状态。当我们创建和配置 VAO 时,会指定使用哪些 VBO 以及如何解析它们的数据。这样,当我们使用 VAO 进行绘制时,所有与 VBO 相关的状态都会自动应用。
流程
- 模型数据建立
- 发送到GPU
- GPU把顶点传输到vertexShader里
- VertexShader 进行空间变换操作,并行处理
- 到FragmentShader对各个顶点之间进行插值着色
VAO 模式绘制
1 |
|
OPENGL 部分配置问题
GLFW_OPENGL_CORE_PROFILE:
- 选择核心配置文件(Core Profile)。
- 核心配置文件排除了一些较旧的、不再推荐使用的 OpenGL 功能。它仅支持现代的 OpenGL 功能和特性。
- 使用核心配置文件时,必须使用现代的 OpenGL 函数和特性,例如顶点缓冲对象(VBO)、顶点数组对象(VAO)和着色器等。传统的固定管线功能(如直接使用 glBegin 和 glEnd 的绘图方法)在核心配置文件中不可用。
- 绑定VAO是强制的
GLFW_OPENGL_COMPAT_PROFILE:
- 选择兼容配置文件(Compatibility Profile)。
- 兼容配置文件包含了所有 OpenGL 功能,包括新的和旧的特性。这意味着你可以在一个应用程序中使用传统的固定管线功能和现代的 OpenGL 功能。
- 兼容配置文件对于需要支持老旧硬件或依赖于传统 OpenGL 功能的应用程序很有用。
1 | glfwInit(); // opengl 状态初始化 |
多个VBO的情况
VBO1和VBO2是两个 完全不同的缓存 为什么VBO2会覆盖掉VBO1的结果导VBO1不显示
因为锚定点的问题
1 | // 渲染绘制 |
多个VAO VAO 记录了锚点信息 所以不需要重新设置锚点
1 |
|
Shader与C++传输
uniform变量有限 定义了uniform如果未使用会被系统删除
1 | // 渲染绘制 |
顶点索引
剔除重复顶点 传输顶点索引 VBO VAO
EBO Element Buffer Object
EBOs are a powerful tool in OpenGL that enhance the efficiency and flexibility of rendering operations. They are especially useful for complex models and animations, allowing for more compact and efficient data representation and manipulation.
Texture
步骤
定义UV坐标 获取插值结果 与实际坐标相乘 获得实际插值
STB_IMAGE 读取图片
**
stbi_load
**从文件中加载图像,并返回图像数据的指针,同时提供图像的宽度、高度和通道数。**
stbi_load_from_memory
**从内存中加载图像数据,适用于已经在内存中的图像。**
stbi_load_from_file
**从文件指针加载图像,类似于stbi_load
,但使用FILE*
作为输入。**
stbi_load_from_callbacks
**使用自定义回调接口加载图像,适用于自定义的输入/输出系统。**
stbi_image_free
**释放由stbi_load
系列函数分配的图像数据内存。**
stbi_info
**获取图像的基本信息(宽度、高度、通道数)而不加载图像数据。**
stbi_set_flip_vertically_on_load
**设置在加载图像时是否垂直翻转图像的标志。**
stbi_failure_reason
**返回最后一次失败的原因描述。**
stbi_is_hdr
**检查图像文件是否为 HDR(高动态范围)图像。**
stbi_loadf
**加载 HDR 图像,并以浮点数据的形式返回图像数据。
Texture Wrapping
坐标超出0-1的范围 需要重复采样
Texture Filter
通过u*width
和v*height
获取坐标位小数 需要取整
Nearest 找最近的点 bilinear 找四个求平均值
图片小使用nearest 因为像素差异变大
图片大使用bilinear 更加平滑
摄像机
摄像机矩阵
1 | glUniformMatrix4fv(glGetUniformLocation(m_shaderProgram, _name.c_str()), 1, GL_FALSE, glm::value_ptr(_matrix)); |
glGetUniformLocation获取当前_name变量的shader或锚定符
glUniformMatrix4fv 向shader的location传入数据 GL_FALSE(列优先)
1 | glm::lookAt<float, glm::packed_highp>(const glm::highp_vec3 &eye, const glm::highp_vec3 ¢er, |
初始呈像
Lookat角度 并没有开启深度检测
1 | glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置清除颜色 |
加入深度检测后
摄像机的移动旋转
1 |
|
Rotate move运算规则
rotate矩阵在右边
1 | void rend() |
LookAt作用
cameraPos - cameraTarget 由减数到被减数的向量
cameraFront需要是一个单位向量
processInput每次循环都会被调用 检测是否使用按键
camera.cpp
1 |
|
1 | // 当有任何输入时 比如键盘或鼠标事件 |
摄像机转动
pitch yaw and roll
yaw x的正方向走向z的正方向为yaw的正值
yaw x的正方向走向z的负方向是负值
1 | void Camera::pitch(float _yOffset) |
需要检测是不是firstmove 记录开始的坐标 先记录再对比再剪除
1 | void Camera::onMouseMove(double _xpos, double _ypos) |
windows 的窗体位置再左上角 向上坐标减小 向下坐标增大 所以会相反
1 |
|
光照
Phong Shader 模型
环境光 Shader
1 | Fragment Shader |
漫反射 Shader
平移部分不会影响法线向量的计算 为0可以消除平移 的影响
gl_Position 将顶点的坐标转换到视图空间之中 FragPos是世界坐标
漫反射 法线矩阵证明
在这里 dot可以转化为转置后的矩阵乘法
补充
1 | _modelMatrix = glm::translate(_modelMatrix, glm::vec3(3.0f, 0.0f, -3.0f)); // 与上次的结果产生了叠加 |
可以使用 _modelMatrix = glm::mat4(1.0f) 清空
transpose(inverse(_modelMatrix))
对于法线向量的正确变换,仅仅使用模型矩阵的逆矩阵是不够的。需要使用逆矩阵的转置矩阵来确保法线的正确性。
- 为什么使用逆矩阵? 对于位置向量,我们使用模型矩阵来变换它们。然而,对于法线向量,特别是在非均匀缩放的情况下,如果直接使用模型矩阵来变换,法线方向可能会失真。因此,使用逆矩阵来反向缩放,确保法线向量的方向不受不均匀缩放的影响。
- 为什么转置? 转置操作将行向量转换为列向量(或反之),这是确保法线方向保持正确的必要步骤,因为它对法线向量施加了正确的几何变换。
1 |
|
镜面反射
材质系统
材质和光都有自己的属性
更改光照和材质属性重写
1 |
|
材质简化策略 纹理RGB可以代替环境光和漫反射的材质
1 | // 环境光材质与diffuse材质合并 漫反射材质利用物体本身的材质进行代表 |
1 | // 环境光 |
光照分类 平行光 点光源 聚光灯计算方案
平行光 通过w区分
Fragment得到的是图元 插值获取
1 | vec3 lDir = normalize(mylight.m_direction); |
点光源
聚光灯
else 中diffuse和ambient都为贴图整合 实际上是环境光的值
聚光灯边缘优化
边缘较为生硬 且光照吧随着距离没有衰减
优化1
lightDir 指向光源的向量 light.direction 光本身的方向
优化2
多光源Shader编写
1 |
|