不要管为什么这个笔记的P1是从二开始的。第一节课是大纲介绍and吹水环节2333
现代GPU图形学管线
Graphics Pipeline
- 顶点处理:Vertex Precessing:将所有顶点根据当前空间和给定参数进行MVP对应变换至一个归一化的立方体当中。
- 面连接:Triaggle Processing:将上一步给定的顶点坐标根据原来的面片相关信息重新串联成形。
- 光栅化:将三角形片元离散化为屏幕空间上的一系列像素(锯齿和走样问题一般的出现场景就在此处)
(这个阶段包括片元剔除,遮挡关系考虑等处理) - 着色:计算其对应的着色模型等的出的颜色结果并赋值给对应的图像。(利用Z-buffer判断取哪一个颜色信息进行装配)
- 缓冲区操作:将计算好的帧加入缓冲区,将对应帧的缓冲区颜色显示在屏幕上。
先进行着色还是先进行颜色管理这种视情况而定,不是绝对的;其取决于实际的引擎渲染管线。
相关的一些知识点
- Bling-Phong着色模型:环境光、高光、反射光照(对全局的光效拟合效果差强人意)
特别的,利用GPU进行计算的时候计算速度显著快于cpu,其主要归功于相应的并行计算特性
- uv贴图映射:利用中心插值坐标采用里面的纹理坐标用于基础的着色值等。
OPENGL
- 一种负责调度GPU的CPU指令集,本质上是一套API,与语言没有显著关系。(有跨平台的显著优势)
- 相同生态:vulkan和Directx,后者经常使用但是windows独占。
严格来说,现代流水管线中的每一步都可以在OPENGL中找到相应的封装。
一些weekness:版本更新不固定,社区化琐碎版本比较复杂;
C语言风格语言,偏底层,不是很易于使用,封装程度低(现在优化程度相对较高,这方面相对缺点较小)
基础的MVP变换等都被封装成函数了,但是基础的效果需要自己封装之后在进行更深入的使用,未来可能还是在Vulkan部分。
OpenGL的渲染流程
放置物体\模型
- 把物体和模型加载在自己的坐标空间中并摆放好(将模型读入传给GPU)
(我们使用VBO——Vertex Buffer Object对这部分信息进行存储:其中主要用来记录纹理坐标,法线信息以及根据其相应的编号来确定顶点的链接关系) - 利用矩阵变换进行空间变换以及平移、缩放、旋转逻辑:转化到屏幕空间
本质上OpenGL就是一个第三方API,用于简化这些计算
视口变换
- 根据相机的摆位、朝向进行相应的视口变换(正交投影?透视投影?)-只需要定义相机位置,朝向角度,zfar,znear(可以唯一确定一个视锥体)即可。
- 创建一个帧缓冲区,用于缓存相应的渲染区域
渲染
指定好FrameBuffer后,我们可以用一个平面渲染多个结果,每次对场景进行一次渲染,也就是指定FrameBuffer输出纹理到平面,但是同一个Buffer现在可以渲染多个图(通过不同的Pass渲染到不同的缓冲区中)比如一个shader中有多个Pass-分别将其渲染到颜色缓冲、深度缓冲、法线缓冲等等不同的Buffer当中(也就是可以输出多张纹理图)(一般不直接将渲染的结果输出到平面,否则容易生成画面撕裂——垂直同步技术就是用于解决这一问题)
还有一种解决方案,就是使用双重、三重缓冲,即将所有正在渲染的输出都加入缓冲区,等到完全渲染好再将其输出到屏幕平面上。——会降低相应的帧率和性能
输出至屏幕空间
- 通过顶点和片元着色器的自定义操作,来决定对应位置的颜色着色信息。
- 顶点:做MVP处理和一些插值操作,将结果枝传入片元着色器
- 片段:定义一个片段着色器进行着色;缺省或者自己为其定义相应的z-buffer
- 渲染:进行实际化的光栅化
OPENGL-Pass
在每个Pass当中:
- 物体(obj)、镜头、MVP矩阵等等信息
- 相关的帧缓冲区和输入输出纹理
- 专门编写的顶点和片元着色器
- 在渲染直线,指定并给好对应GPU型号及其信息
(本质上他是以状态机模型进行的AC自动机)
个人的看法上,我更加考虑学习一下vulkan和Directx相应的内容。
更高的思考:多Pass
利用先前的渲染结果进行作为后续pass的基础用于实现一些问题:例如阴影,描边等相应效果。
(weakness:每一个光源都需要进行一趟shadow map处理)
GLSL:OpenGL Shading Language
在unity和虚幻引擎中,普通Build-In管线中允许使用包裹后的GLSL进行Shader书写;但是在更现代的流水线管线当中,GLSL会被翻译为HLSL在进行编译(例如Unity中的URP以及HDRP管线中就会在保存后被自动转写,可能有莫名bug2333)
上古时期还得用汇编语言在GPU上写循环 无敌了
对于DirectX,这种着色器语言为HLSL,他们都是需要被编译为汇编语言的相应上层语言。
Shader的运作流程
这一流程和C风格的代码无二,类似于编写CPU侧的相应程序。
- 创建Shader
- 编译Shader
- 将Shader加入项目
- 链接并使用
代码含义与相应作用
以一个顶点着色器为例:
- vec是向量简写其中后面的相应数字就是相应维度,而mat就是方矩阵相应关键字,数字为其对应的一些维度。
- varying所定义的部分需要进行相应传递,会将后面的从顶点着色器传递到相应的片元着色器;在片元着色器中访问对应的变量就能找到对应的变量
- uniform——相当于从cpu直接调用的变量,为全局变量,有uniform调用东西就是固定好的东西,类似图片中定一个模型和透视等,一旦相机固定其就不会有相关变化
- highp为高精度,也就是其存储的对应变量的精度较高
相应的片元着色器:
类sampler2D类型的变量,就是一个含有uv坐标,里面是四通道(RGB,Alpha)的结构体。
在返回值部分,我们需要用gl_FragColor记录返回值,加入缓冲区。从变量的形式我们不难理解,我们可以在缓冲区中的uv相关内容使用多套采样的TexCoord进行对应的解决。
Shader Debug方法
上古时期:HLSL软件模拟以及GLSL效果
不好调试的原因:GPU的高并行计算以及其通信概念不同,片段着色器负责的部分只负责着色,无法输出其他的结果,需要和cpu进行通信后才能得到相应debug信息
现代调试方式:
NSIGHT Graphics:跨平台 but NVIDIA ONLY(AMD NOOOOOOO!)
RenderDoc:跨平台,对GPU没有限制
Profiler:工业界常用的API之一。
邪门调试方法:
将深度以灰度图绘制,预期的错误结果return一个特殊颜色渲染输出(特殊的调试用于判断其是否渲染成功和相应结果)利用ColorPicker解决