Unity渲染管线综述
基础概念图形API
[*]OpenGL与DirectX:用于渲染2D、3D矢量图形的API。
[*]简易流水线:重复执行一个任务,多个步骤执行完,才重新开始任务。
[*]并行流水线:重复执行一个任务,前一个步骤执行完,仍会继续进行。
[*]渲染空间:Gamma空间或线性空间。
语言类
[*]GLSL:OpenGL的跨平台(没有着色器编译器)的着色语言。
[*]HLSL:DirectX的着色语言。
[*]CG:NVIDIA的跨平台(编译成中间语言)的着色语言。
[*]Shader:GPU流水线上的可编程环节。告诉计算机如何绘制画面的程序。
[*]ShaderLab:Unity提供的编写Unity Shader的一种说明性语言。
[*]语义:映射参数的变量名。(如: float4 position : SV_POSTION)
信息类
[*]材质:色彩、纹理、光滑度、透明度、反射率、折射率、发光度等。
[*]纹理(贴图、映射):根据Kershaw的术语,通过将投影方程运用于空间中的点 ,从而得到一组称为参数空间值的关于纹理的数值。
[*]渲染图元:渲染所需的几何信息。
[*]渲染状态:定义场景网格怎样被渲染的信息(Blend,Stencil,DepthTest等)。
[*]片元:很多状态信息的数据集合,用于计算每个像素的最终颜色。
计算类
[*]着色:根据材质属性、光源信息,使用一个等式(光照模型)去计算:沿某个观察方向的出射度的过程。
[*]Draw Call:每次CPU准备数据并通知GPU的过程。消耗的是CPU,不是GPU。
[*]插值:根据已知数据点,来预测未知数据点值的方法。
<hr/>图形渲染管线
《Real-Time Rendering 3rd》 提炼总结 - 图形渲染管线 - 毛星云的文章 - 知乎
GPU渲染管线
《Real-Time Rendering 3rd》 提炼总结 - GPU渲染管线与可编程着色器 - 毛星云的文章 - 知乎
Unity - BuildIn渲染管线
[*]【教程】技术美术入门:渲染管线概述_哔哩哔哩_bilibili,以下图片均为此节截图,讲得非常好。
1.buildin渲染流程
2.1应用程序阶段:剔除、排序
2.2.1模型信息
2.2应用程序阶段:打包数据
3. GPU渲染管线流程
3.1.1顶点shader:综述
3.1.2顶点shader:拍照过程
3.2硬件操作阶段
3.3.1片元shader:综述
3.3.2片元shader:纹理技术
3.3.3片元shader:光照技术框架
3.4.1输出合并
3.4.2输出合并:深度测试
3.4.3输出合并:提前深度测试:优化
3.4.4输出合并:混合
<hr/>Scriptable Renderer Pipeline(可编程渲染管线)
在Unity通过C#脚本调用一系列API配置和执行渲染命令的方式来实现渲染流程, SRP将这些命令传递给Unity底层图形体系结构,然后再将指令发送给图形API。
为什么要用这个东西?
[*]SRP的性能比Builtin内置管线好。解决渲染性能问题(URP:PASS绘制,最大程度的减少SetPassCall;内置很容易打断合批)。
[*]SRP提供源码,容易控制,比较好扩展,让美术效果可定制(可控制),让渲染性能更容易优化。
[*]自带支持PBR渲染管线(也支持非PBR内容)。
[*]解决了内置渲染管线渲染层级问题(内置渲染管线只有修改Renderqueue,改了通常也不稳定)。
[*]减小Blit操作(Blit会进行一次全屏拷贝,相当的消耗性能和带宽),URP可以手动调用Blit。
[*]便于特效实现(扭曲一般只对不透明物体生效。火焰使用半透明,管线定制可以覆盖这些效果)。
着色器变体
[*]Shader不建议使用if(非常高的消耗),那我们需要if怎么办?Keywords(宏)。
[*]每一种宏组合就是这个shader的一个变体
[*]官方文档
[*]Shader变体收集与打包
[*]打包中裁剪Shader变体
SRP Batcher
[*]SRP Batcher工作原理探究 - Xshadow的文章 - 知乎
若要适配SRP Batcher,得满足以下几点:
1.渲染的对象要是Mesh或Skinned Mesh,不能是粒子
2.Shader的写法要适配。uniform的变量要放在UnityPerDraw或者UnityPerMaterial的CBUFFER中
3.不能使用MaterialPropertyBlocks
4.若有多pass,则必须每个Pass的CBUFFER_START中的内容一致
图片来源《黑暗之潮》技术分享
<hr/>URP渲染管线
推荐文章
[*]URP核心代码学习
[*]Unity通用渲染管线学习 - 徒花的文章 - 知乎
切换到URP渲染管线
选择SRP
[*]HDRP目前不支持移动平台
[*]URP支持移动平台也支持PC
[*]URP不支持表面着色器(如果是表面着色器写的,无法转成URP)
切换管线
设置质量参数
BuildIn批量切换成URP - Shader
定制URP的内置管线
使用RenderObject:通过实现好的RenderFeature和RenderPass,可以在不用任何代码的情况下进行定制。
《黑暗之潮》URP最终定制的渲染管线
使用RenderObject有什么用?
[*]解决半透明物体渲染的不确定性。
在透明物体之前去渲染地面的layer(单独使用一个RenderObject)。
[*]透明物体实现扭曲的效果。
将复制ColorTexture的时机挪到透明物体之后,用单独的Pass去渲染需要扭曲的特效。
变化
与内置渲染管线功能对比
[*]相机变化
[*]Shader变化
[*]参数介绍
怎么把BuildInShader转成URPShader?
ASE、ShaderGraph连的,可以直接切换,但是要调对参数。
要改代码的话,先执行这2步:
[*]先用Edit/RenderPipeLine/Universal Render Pipleline/Upgrade试试
[*]直接切换材质球的Shader为Lit
不得行,需要改Shader如下,自行尝试:
[*]CG关键字改成HLSL关键字(虽然可以用CG,但是要用Unity的HLSL库)
[*]FallBack改成:FallBack &#34;Hidden/Universal Render Pipeline/FallbackError&#34;
[*]SubShader:新增Tags{&#34;RenderPipeline&#34; = &#34;UniversalPipeline&#34;}
[*]Pass:Tags { &#34;LightMode&#34; = &#34;ForwardBase&#34; } 要改成 { &#34;LightMode&#34; = &#34;UniversalForward&#34; }(或其他更多模式)
[*]光照库替换
#include &#34;Lighting.HLSLinc&#34;
替换为
Tags{&#34;LightMode&#34; = &#34;UniversalForward&#34;}:
#include &#34;LitInput.hlsl&#34;
#include &#34;LitForwardPass.hlsl&#34;
Tags{&#34;LightMode&#34; = &#34;ShadowCaster&#34;}:
#include &#34;Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl&#34;
#include &#34;Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl&#34;
[*]查阅
内置结构体、函数
Packages/Universal RP/ShaderLibrary/Input
Packages/Universal RP/ShaderLibrary/Core
内置函数、矩阵
Packages/Universal RP/ShaderLibrary/UnityInput关于UI在Gamma空间工作,但使用了线性空间
3D美术需要在基于PBR的高清的线性空间下工作,但常规的UI美术平时习惯于在Gamma空间工作。
UI的美术不太适应线性空间的流程,也不太适用线性空间下的颜色在他们的自产工具里进行产出。(PS - Gamma转线性后,颜色差别很大)
但游戏内需要使用线性空间下的流程,所以为了解决冲突,进行了分辨率分离:
使用单独的场景摄像机在线性空间下绘制,然后与UI相机进行blend,最后输出到屏幕上。场景空间的相机最后需要进行Gamma校正,而UI相机本身就是Gamma空间,所以不需要校正。如何写一个同时兼容Build-in和URP的shader?(补全中)
UnityShader编程指南
基础渲染系列(二)——着色器(必读)
编写原则和经验
[*]不建议在编写Shader时,使用分支或者循环语句,会降低GPU并行处理操作
[*]从性能考虑,尽量把计算放在顶点着色器中去执行
[*]ShaderLab中函数和变量要先声明再调用
[*]避免重复绘制,尽可能设置渲染队列为不透明物体的渲染队列
[*]在Unity中,渲染队列小于2500(不透明)的对象,从前往后绘制;其他的队列的物体,从后往前绘制。
表面着色器
[*]Unity自创的着色器(Surf),顶点/片元着色器的子集
[*]自动实现光照模型,为我们处理很多光照细节
[*]代码量少,不需要Pass代码段。但渲染代价稍高(它在后面仍会转换成顶点/片元着色器)
[*]适合:光照数多
顶点/片元着色器
[*]代码量多,自己定义每个Pass需要使用的Shader,更灵活地控制渲染细节
[*]适合:光照数少、有很多自定义渲染效果
固定着色器
适配很旧的设备(iphone3)时才会用。
Cg/HLSL
内置变量
【变换矩阵】
模型空间 = 对象空间
投影空间 = 裁剪空间
相机空间 = 观察空间
M:模型矩阵
V:相机矩阵
P:投影矩阵
UNITY_MATRIX_MVP:模型空间->投影空间
UNITY_MATRIX_MV:模型空间->相机空间
UNITY_MATRIX_V:世界空间->相机空间
UNITY_MATRIX_P:相机空间->投影空间
UNITY_MATRIX_VP:世界空间->投影空间
_Object2World:模型空间->世界空间
_World2Object:世界空间->模型空间
UNITY_MATRIX_T_MV:UNITY_MATRIX_MV的转置矩阵
UNITY_MATRIX_IT_MV:UNITY_MATRIX_MV的逆转置矩阵
//unity5.6以前的写法
o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);
//unity5.6以后的写法
o.vertex = UnityObjectToClipPos(v.vertex);基础模板
Shader &#34;Custom/MyShader&#34; {//定义Shader的名字和位置
Properties {//声明一些能在材质面板中调整的属性
Name (&#34;displayname&#34;, PropertyType) = value
}
SubShader {
Tags{&#34;name&#34;=&#34;value&#34;}
//应用于所有Pass
Pass{//每个Pass定义一次完整渲染流程(Pass过多会造成渲染性能低下)
Tags{&#34;name&#34;=&#34;value&#34;}
//应用于此Pass
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct a2v{
float4 vertex : POSITION;
float3 noraml : NORMAL;
}
struct v2f{
float4 pos : SV_POSITION;
float3 color : COLOR;
}
v2f vert(a2v a){
v2f v;
//code
return v;
}
fixed4 frag(v2f v) : SV_Target{
fixed4 f;
//code
return f;
}
ENDCG
}
Fallback &#34;Diffuse&#34; //以上都无法应用时,使用此Shader 或者 关闭
}
}基础类型
Shader &#34;Custom/MyShader&#34; {
Properties {
//属性类型
_Int(&#34;Int&#34;,Int) = 2
_Float(&#34;Float&#34;,Float) = 1.5
_Range(&#34;Range&#34;,Range(0.0,5.0)) = 3.0
_Color(&#34;Color&#34;,Color) = (1,1,1,1)
_Vector(&#34;Vector&#34;,Vector) = (2,3,6,1)
_2D(&#34;2D&#34;,2D) = &#34;&#34;{}//花括号原本用来指定纹理属性,但在Unity5.0后被移除了
_Cube(&#34;Cube&#34;,Cube) = &#34;white&#34;{}
_3D(&#34;3D&#34;,3D) = &#34;black&#34;{}
}
SubShader {
//标签类型
Tags{&#34;Queue&#34;=&#34;Transparent+1&#34;} //指定该物体属于哪一个渲染队列
Tags{&#34;RenderType&#34;=&#34;Opaque&#34;} //对着色器进行分类
Tags(&#34;DisableBatching&#34;=&#34;True&#34;} //批处理
Tags {&#34;ForceNoShadowCasting&#34;=&#34;True&#34;} //投射阴影
Tags{&#34;IgnoreProjector&#34;=&#34;True&#34;} //半透明
Tags{&#34;CanUseSpriteAtlas&#34;=&#34;False&#34;} //Sprite图集
Tags{&#34;PreviewType&#34;=&#34;Plane&#34;} //预览类型
//状态类型
Cull Back / Front / Off 剔除模式
ZTest Less Greater|LEqual| GEqual|Equal |NotEqual | Always 设置深度测试时使用的函数
ZWrite On|Off 开启/关闭深度写入
Blend Srcactor DstFactor 开启并设置混合模式
Pass{
//状态类型 同上
//标签类型
Tags{&#34;LightMode&#34;=&#34;ForwardBase&#34;} //定义该Pass在Unity的渲染流水线中的角色
Tags {&#34;RequireOptions&#34;=&#34;Soft Vegetation&#34;} //满足某些条件时才渲染该Pass
}
}CGPROGRAM类型
Pass {
CGPROGRAM
//Unity内置文件
UnityCG.cginc 包含了最常使用的帮助函数、宏和结构体等
UnityShaderVariables.cginc编译UnityShader时,会自动包含。包含了许多内置的全局变量
Lighting.cginc 包含了各种内置的光照模型;编写的是Surface Shader的话,会自动包含进来
HLSLSupport.cginc 编译UnityShader时,会被自动包含。声明了很多用于跨平台编译的宏和定义
//ShaderLal属性类型 与 CG变量类型 匹配关系
Color,Vector - float4,half4,fixed4
Range,Float - float,half,fixed
2D - sampler2D
Cube - samplerCube
3D - sampler3D
//3种精度
float 最高精度,桌面GPU上 float = half = fixed
half 中等精度,手机GPU上 half = fixed
fixed 最低精度,尽量使用此类型(优化)
//从应用阶段传递模型数据给顶点着色器时,Unity支持的常用语义
POSITION 模型空间中的顶点位置,通常是float4类型
NORMAL 顶点法线,通常是float3类型
TANGENT 顶点切线,通常是float4类型
TEXCOORDn 该顶点的纹理坐标,n=0表示第一组纹理坐标,依此类推。通常是float2或float4类型
COLOR 顶点颜色,通常是fixed4或float4类型
//其余的全部用half
//从顶点着色器传递数据给片元着色器时,Unity使用的常用语义
SV_POSITION 裁剪空间中的顶点坐标,结构体中必须包含一个用该语义修饰的变量
COLOR0 通常用于输出第一组顶点颜色,但不是必需的
COLOR1 通常用于输出第二组顶点颜色,但不是必需的
TEXCOORD0~TEXCOORD7 通常用于输出纹理坐标,但不是必需的
//片元着色器输出时Unity支持的常用语义
SV_Target 输出值将会存储到渲染目标(render target)中
ENDCG
}<hr/>附录
[*]新版Unity shader库为什么用HLSL,而不用CG了? - 文礼的回答 - 知乎
调试
[*]在CGPROGRAM或HLSLPROGRAM内加入
[*] pragma enable_d3d11_debug_symbols
页:
[1]