LearnOpenGL笔记
LearnOpenGL笔记
本文仅为学习OpenGL过程中的学习笔记,OpenGL细节参考https://learnopengl-cn.github.io/。
OpenGL流水线
在gl中坐标系是3D的,但是窗口却是2D像素数组,这导致gl大部分工作都是关于把3D坐标转变为适应屏幕的2D像素。3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线(Graphics Pipeline,大多译为管线,指的是一堆原始图形数据途经一个输送管道,期间经过各种变化处理最终出现在屏幕的过程)管理的。图形渲染管线可以被划分为两个主要部分:第一部分把你的3D坐标转换为2D坐标(几何阶段);第二部分是把2D坐标转变为实际的有颜色的像素(光栅化阶段)。
着色器
着色器(Shader)是运行在GPU上的小程序。这些小程序为图形渲染管线的某个特定部分而运行。从基本意义上来说,着色器只是一种把输入转化为输出的程序。着色器也是一种非常独立的程序,因为它们之间不能相互通信;它们之间唯一的沟通只有通过输入和输出。着色器程序是由GLSL语言编写,openGL会编译成具体的着色程序。常规的着色器有以下几种。顶点和片段着色器是最常用的着色器程序。
顶点着色器
几何着色器
片段着色器
顶点着色器
顶点着色器作为顶点数据的入口,顶点数据一般包含位置坐标(xyz轴),纹理坐标,法向量(normal),tangent(切线),副切线(bitangent)。
顶点着色器应该接收的是一种特殊形式的输入。client会通过openGL来声明好顶点数据的结构,顶点着色器程序就可以访问到相应偏移量的数据,顶点着色器作为第一个着色器,将处理好的数据传递传递给下一个着色器。关键字in
, out
描述着色器的输入输出。
片段着色器
片段着色器需要生成一个最终输出的颜色。如果你在片段着色器没有定义输出颜色,OpenGL会把你的物体渲染为黑色(或白色)。片段着色器的主要作用就是描述片段的颜色(可以理解为像素点)。实际使用过程中会配合纹理使用,同时也会涉及到光学模型,让渲染的结果更逼真。
片段着色器的输入由上一个着色器提供,一般为顶点着色器。
几何着色器
几何着色器处理的是图元。比如说渲染的方式是三角形,则输入的是3个顶点数据。几何着色器可以在顶点发送到下一着色器阶段之前对它们随意变换。然而,几何着色器最有趣的地方在于,它能够将(这一组)顶点变换为完全不同的图元,并且还能生成比原来更多的顶点。
几何着色器可以实现法向量可视化,毛发等细节效果。
纹理
diffuse,specular,normal,depth
纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节;你可以想象纹理是一张绘有砖块的纸,无缝折叠贴合到你的3D的房子上,这样你的房子看起来就像有砖墙外表了。因为我们可以在一张图片上插入非常多的细节,这样就可以让物体非常精细而不用指定额外的顶点。
使用纹理坐标获取纹理颜色叫做采样,纹理坐标[0, 1]。
纹理的资源叫做贴图,其实就是图片。常规的贴图有以下几种:
- 漫反射贴图
- 镜面光贴图
- 法线贴图
- 视差贴图
- 放射光贴图
具体细节在光照模型中会具体描述,物理的颜色主要取决于反射或射入我们眼睛中颜色决定。
切线空间
在一个不同的坐标空间中进行光照,这个坐标空间里,法线贴图向量总是指向这个坐标空间的正z方向;所有的光照向量都相对与这个正z方向进行变换。这样我们就能始终使用同样的法线贴图,不管朝向问题。这个坐标空间叫做切线空间(tangent space)。
法线贴图中的法线向量定义在切线空间中,在切线空间中,法线永远指着正z方向。切线空间是位于三角形表面之上的空间:法线相对于单个三角形的本地参考框架。它就像法线贴图向量的本地空间;它们都被定义为指向正z方向,无论最终变换到什么方向。使用一个特定的矩阵我们就能将本地/切线空间中的法线向量转成世界或视图空间下,使它们转向到最终的贴图表面的方向。
使用法线贴图也是一种提升你的场景的表现的重要方式。在使用法线贴图之前你不得不使用相当多的顶点才能表现出一个更精细的网格,但使用了法线贴图我们可以使用更少的顶点表现出同样丰富的细节。
坐标系统
标准化设备坐标(Normalized Device Coordinate, NDC)。也就是说,每个顶点的x,y,z坐标都应该在**-1.0到1.0**之间,超出这个坐标范围的顶点都将不可见。我们通常会自己设定一个坐标的范围,之后再在顶点着色器中将这些坐标变换为标准化设备坐标。然后将这些标准化设备坐标传入光栅器(Rasterizer),将它们变换为屏幕上的二维坐标或像素。
将坐标变换为标准化设备坐标,接着再转化为屏幕坐标的过程通常是分步进行的,也就是类似于流水线那样子。在流水线中,物体的顶点在最终转化为屏幕坐标之前还会被变换到多个坐标系统(Coordinate System)。将物体的坐标变换到几个过渡坐标系(Intermediate Coordinate System)的优点在于,在这些特定的坐标系统中,一些操作或运算更加方便和容易,这一点很快就会变得很明显。对我们来说比较重要的总共有5个不同的坐标系统:
- 局部空间(Local Space,或者称为物体空间(Object Space))
- 世界空间(World Space)
- 观察空间(View Space,或者称为视觉空间(Eye Space))
- 裁剪空间(Clip Space)
- 屏幕空间(Screen Space)
为了将坐标从一个坐标系变换到另一个坐标系,我们需要用到几个变换矩阵,最重要的几个分别是模型(Model)、观察(View)、投影(Projection)三个矩阵。有了上述3个变化矩阵,就可以实现旋转,平移等效果。
光照模型
冯氏光照模型(Phong Lighting Model)。冯氏光照模型的主要结构由3个分量组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。物体的颜色则为3个分量的向量和。
- 环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
- 漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
- 镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。
漫反射和镜面光照都和片段表面的法向量和光线向量的夹角相关,需要通过计算获得具体的计算因子。
除冯氏光照模型外,在冯氏模型的拓展还有Blinn-Phong着色模型,引入了半程向量概念,在镜面反射分量上有一个更好的表现。
3D模型数据
3D文件通常包括网格模型、UV纹理、材质贴图。常见的3D模型格式有如下几种
- OBJ,适用于3D软件模型之间的互导,主要支持多边形模型。
- DAE,纯文本的模型格式,其本质是一个xml文件,可以实现动态模型。
- FBX,最好的互导方案
- STL,文件格式简单, 应用非常广泛
一个非常流行的模型导入库是Assimp。当使用Assimp导入一个模型的时候,它通常会将整个模型加载进一个场景(Scene)对象,它会包含导入的模型/场景中的所有数据。Assimp会将场景载入为一系列的节点(Node),每个节点包含了场景对象中所储存数据的索引,每个节点都可以有任意数量的子节点。Assimp数据结构的(简化)模型如下:
- 和材质和网格(Mesh)一样,所有的场景/模型数据都包含在Scene对象中。Scene对象也包含了场景根节点的引用。
- 场景的Root node(根节点)可能包含子节点(和其它的节点一样),它会有一系列指向场景对象中mMeshes数组中储存的网格数据的索引。Scene下的mMeshes数组储存了真正的Mesh对象,节点中的mMeshes数组保存的只是场景中网格数组的索引。
- 一个Mesh对象本身包含了渲染所需要的所有相关数据,像是顶点位置、法向量、纹理坐标、面(Face)和物体的材质。
- 一个网格包含了多个面。Face代表的是物体的渲染图元(Primitive)(三角形、方形、点)。一个面包含了组成图元的顶点的索引。由于顶点和索引是分开的,使用一个索引缓冲来渲染是非常简单的(见[你好,三角形](https://learnopengl-cn.github.io/01 Getting started/04 Hello Triangle/))。
- 最后,一个网格也包含了一个Material对象,它包含了一些函数能让我们获取物体的材质属性,比如说颜色和纹理贴图(比如漫反射和镜面光贴图)。