游戏图形学入门知识整理

分享和整理一些觉得不错的教程和理解

在此感谢各位老师!

教程:
线性代数基础:https://www.bilibili.com/video/BV1ib411t7YR?p=1
GAMES101-现代计算机图形学入门-闫令琪:https://www.bilibili.com/video/BV1X7411F744
GAMES202-高质量实时渲染-闫令琪:https://www.bilibili.com/video/BV1YK4y1T7yY
华中科技大学-计算机图形学-万琳:https://www.bilibili.com/video/BV1Zj411f7S3?p=13&spm_id_from=pageDriver
论坛:
计算机图形学与混合现实研讨会
博客:
冯乐乐(Shader入门精要)http://candycat1992.github.io/

GPU渲染管线的工作过程

image.png

几何阶段

空间变换:模型空间(左手)→世界空间(左手)→观察空间(右手)
image.png
投影准备:观察空间→观察空间(投影变换)→观察空间(裁剪空间)

经过以上变换,此时物体处在裁剪空间,
★下图需要注意,每个教材的解释略有不同,下图直接就齐次除法变成规范化的了
实际上这里只是为真正是投影(齐次除法)做准备,此时裁剪面依然是梯形四棱锥形态
目的:
1.为后面真正投影做准备,赋予W特殊意义。
2.对X,Y,Z缩放,W作为范围值,便于裁剪。
裁剪空间之前,W=0表示矢量方向,W=1表示点,投影变换之后,W的意义改变了,如下
投影变换对X,Y,Z分量进行缩放,W分量变成-z,如X,Y,Z在W范围内[-w,w],说明该顶点在裁剪空间内
Unity下,观察空间为右手坐标系,列矩阵右乘,投影变换后Z分量范围为[-w,w]
DirectX,投影变换后Z为[0,w]
进入裁剪空间后,右手坐标系,转为左手坐标系,离摄像机越远Z值越大


image.png
屏幕映射:规范化的观察空间(裁剪)→屏幕空间(真正的投影)

齐次除法=透视除法=归一化的设备坐标NDC(OpenGL)=W分量除以X,Y,Z分量
裁剪空间→NDC(经过齐次除法后,变换到一个立方体内)
立方体X,Y,Z分量的范围是[1,-1](OpenGL)
DirectX中Z分量是[0,1]
Unity=OpenGL
Unity中屏幕空间左下角是[0,0],右上角是[pixelWidth,pixelHeight]
由于当前的X,Y都是[1,-1],所以屏幕映射就是一个缩放的过程
此时Z分量经过齐次除法,也就是除W,得到的数值直接存进了深度缓冲区,这步是自动的,当然也可手动
W分量的意义主要是充当分母来得到NDC

image.png

以上就是从模型空间变换到屏幕坐标的过程

光栅化阶段

image.png
下面说下关于屏幕坐标的问题

视口空间坐标公式如下


image.png

上面公式的思想就是,首先对裁剪空间下的坐标进行齐次除法,得到范围在[ − 1, 1] 的 NDC,然后再将其映射到范围在[ 0, 1] 的视口空间下的坐标。
ComputScreenPos 用法


image.png

image.png

Unity中先在顶点着色器里将其映射到范围[ 0, W] ,然后再片段着色器中自己手动齐次除法变为[ 0, 1] 。

z和w值就是裁剪空间下的值。
详情请看ComputScreenPos ,其定义位于UnityCG.cginc中

更新:2022/8/8
CPU部分:
Unity内置管线 Build-in
调用Render()→
剔除:视锥剔除、遮挡剔除、层级剔除→
渲染顺序:按距离、渲染队列→
打包数据、参数,调用Shader:SetPassCall(渲染状态,剔除,混合模式等),DrawCall(模型数据)→
(顶点坐标,法线,UV,切线,顶点色,索引列表)
(世界变换矩阵,视角VP矩阵,fov)
(shader,材质参数,灯光信息)

GPU部分:(顶点处理→图元装配及光栅化→片元处理→输出合并→Framebuffer)
Shader处理(GPU渲染管线)→
帧缓冲区→
后处理→
再次Shader处理(GPU渲染管线)→
最终结果显示到屏幕

顶点Shader:(MVP)
模型空间→(Model Matrix)→世界空间→(View Space)→相机空间→(Projection Matrix)→裁剪空间
经过投影矩阵变换(将三维物体投影到2D平面上)


image.png

硬件操作阶段(图元装配及光栅化阶段):
裁剪空间→(裁剪操作)→


image.png

(透视除法)→NDC标准化设备坐标(Z值保留,就是光栅化后片元 深度值)→背面剔除


image.png

(视口转换,将-1,1转换到比如1920*1080的屏幕区间)→屏幕坐标→
image.png

图元装配(将点连线成面)→光栅化(填充颜色像素)→片元

片元数据(color,depth)→Alpha测试→模板测试StencilTest→深度测试DepthTest→混合Blending→帧缓冲区(colorbuffer,depthbuffer,stencilbuffer)

什么视Early-Z?
————————————————
版权声明:本文为CSDN博主「白筱风」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/whitebreeze/article/details/118688150

image.png

在以下几种情况中,Ealry-Z会不生效

开启Alpha Test 或 clip/discard 等手动丢弃片元操作
手动修改GPU插值得到的深度
开启Alpha Blend
关闭深度测试Depth Test
注:前两点情况原理类似,由于手动进行了片元的丢弃,会导致深度测试筛选出的片元也可能会被舍弃;第三点是由于开启了Alpha Blend一般会关闭深度写入,所以也不会生效;第四点关闭深度测试自然不会生效


image.png

image.png

有大量OverDraw的场景中使用Z-prepass可以很好的减小消耗
如渲染头发:
第一个pass用于生成Z Buffer:开启透明度测试仅通过不透明的测试,并关闭背面剔除,开启深度写入,关闭颜色缓冲区写入,只返回透明度值
之后的3个PASS与之前类似:第一个是渲染不透明物体,并剔除背面,设置深度测试为等于,关闭深度写入
第二个渲染背面,剔除正面,并关闭深度写入,深度测试为小于
第三个渲染正面,剔除背面,并开启深度写入,深度测试同上
DepthPrePass示范:

Pass {
            Tags{"LightMode"="DepthPrePass"}
            ZWrite On
            ColorMask 0
            Cull Off
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            struct Attributes
            {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
                float4 color : COLOR;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct Varyings
            {
                float4 pos : SV_POSITION;
                float2 uv:TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
                UNITY_VERTEX_OUTPUT_STEREO
            };
            Varyings vert (Attributes input)
            {
                Varyings output = (Varyings)0;
                float3 worldPos = WaveVertex(input.vertex, input.texcoord.xy, input.color, _WaveSpeed, _GrassWaveIntensity);
                output.pos = TransformWorldToHClip(worldPos);
                output.uv = TRANSFORM_TEX(input.texcoord.xy, _MainTex);
                return output;
            }
            float4 frag (Varyings input) : SV_Target
            {
                float4 albedo = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv)*_Color;
                float4 col = albedo * _Color;
                clip(col.a - _Cutoff);
                return 0;
            }
            ENDHLSL
        }
常用数学知识

Sin:对边比斜边
Cos:邻边比斜边
Tan:对边比临边。

你可能感兴趣的:(游戏图形学入门知识整理)