使用shader graph过程中,想要实现一些多pass效果,虽然使用render feature能够一定程度上解决需求,但我认为render feature始终是一个在特定阶段做一些简单操作的实用工具,在实现多pass效果的时候总会有一些不如意,所以总结了unity官网上的CG/HLSL规范,也是下一步准备浏览大佬作品的前置条件。如果连基本规则都不懂,即使是看到一些简单操作也会一头雾水。
Shader "URPShaderTest"
{
Properties
{
//Color(4,线性SRGB)、Vector(4)、Float、2D(texture)、Cube(cubmap)、Integer、Range、2DArray、3D、CubeArray
//对应下面声明fixed4、float4、float、sampler2D、samplerCUBE
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
_MainTex("Texture", 2D) = "white" {}
[Toggle(Switch)]
_Switch("Switch", Integer) = 0
_OffsetUnitScale("OffsetUnitScale", Integer) = 0
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//特性
/*
[Gamma] 指示浮点或矢量特性使用sRGB值
[HDR] 指示纹理或颜色特性使用高动态范围(HDR)值
[HideInInspector] 藏
[MainTexture] 设置材质的主纹理,可以使用Material.mainTexture访问该材质。默认名称为_MainTex的纹理为主纹理
[MainColor] 设置材质的主颜色,可以使用Material.color访问该材质。默认名称为_color的颜色为主颜色
[NoScaleOffset] 为该纹理属性隐藏平铺和偏移字段
[Normal] 指示法线贴图纹理
[PerRendererData] 指示纹理特性将来自MaterialPropertyBlock,inspector将显示为只读
*/
//MaterialProperty类 https://docs.unity3d.com/2021.3/Documentation/ScriptReference/MaterialProperty.html Material.GetFloat, Material.SetFloat等等
//编辑器扩展MaterialPropertyDrawer、MaterialEditor、MaterialProperty、ShaderGUI https://docs.unity3d.com/2021.3/Documentation/Manual/SL-CustomEditor.html
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//保留名称
//_TransparencyLM ("Transmissive Texture", 2D) = "white" {} //在光照贴图过程中启用自定义RGB透明度
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
// Cg/HLSL 可以接受uniform值 https://docs.unity3d.com/2021.3/Documentation/Manual/SL-PropertiesInPrograms.html 矩阵和数组只能作为非序列化的运行时属性存在(因为无法在properties块中定义它们)
}
SubShader
{
//SubShaderTags
//RenderPipeline:UniversalRenderPipeline/HighDefinitionRenderPipeline 主要控制渲染用的管线
//Queue:Background(1000)Geometry(default)(2000)AlphaTest(2450)Transparent(3000)Overlay(4000) 或者可以写具体值1300或者Background+300等等 主要控制渲染顺序
//RenderType 没有默认值 替换材质时可以使用这个值作标识:Camera.SetReplacementShader(ShaderType,“ReplaceMePlease”);平时不必使用 貌似也用作深度图生成?
//ForceNoShadowCasting true false 是否阻止阴影投射,有时也阻止接收
//DisableBatching True False默认 LODFading当Lod值非0时阻止,反之不阻止 阻止unity对使用这个SubShader的几何体应用动态批处理 动态批处理会将所有几何体转换为世界空间,着色器程序无法再访问对象空间。可以避免依赖对象空间的着色器程序无法正确渲染的问题
//IgnoreProjector True False默认 不希望任何投影类型材质或者贴图,影响我们的物体或者着色器 UI上用的多
//PreviewType Sphere Plane Skybox 检视预览用什么方式展示
//CanUseSpriteAtlas 警告用户着色器依赖于原始纹理坐标,因此不应将其纹理打包到图集中 多用于使用Legacy Sprite Packer的项目中
Tags {"RenderPipeline" = "UniversalRenderPipeline" "Queue" = "Geometry+100" "RenderType" = "ReplaceMePlease" "ForceNoShadowCasting" = "True" "DisableBatching" = "False" "IgnoreProjector" = "False" "PreviewType" = "Sphere"}
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//这个值和与相机的距离没有关系,Unity不会自动计算着色器LOD,必须手动设置最大着色器LOD,表示它在计算上的要求有多高
// 当有多个subshader时,LOD必须按降序排列,这是因为Unity查找选择第一个有效的SubShader,所以如果它首先找到一个LOD较低的子着色器,它将始终使用它。看来是值越高计算要求越高
LOD 200
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//为了支持SRP Batcher,Shader中要将所有暴露出的参数(贴图除外)给包含到CBUFFER_START(UnityPerMaterial)与CBUFFER_END之间。并且为了保证之后的每个Pass都能拥有一样的CBUFFER
//这一段代码需要写在SubShader之内,其它Pass之前。
//CBUFFER_START(UnityPerMaterial)
// float4 _MainTex_ST;
// float _Switch;
//CBUFFER_END
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//插入来自另一个着色器的命名Pass,减少Pass代码重复 这里插入的是自己的
UsePass "URPShaderTest/URPShaderTestPass0"
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//抓取帧缓冲区纹理到这个命名纹理中去,可以在Pass中声明使用这张纹理。如果不写名称会抓到_GrabTexture中去,但是使用自定义命名可以减少屏幕抓取次数,故尽量使用自命名
GrabPass
{
"_BackgroundTexture"
}
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
Pass
{
// 某些着色器需要在不同的管线下运行,需要这个模块 这个模块必须在Pass或者subshader的最开始声明,看你想单独控制pass还是整个subshader
// 这些版本必须和package manager里对应上,否则Pass或者subshader无法正常工作
// 不同的写法有不同的行为https://docs.unity3d.com/Manual/SL-PackageRequirements.html
PackageRequirements
{
"com.unity.render-pipelines.universal": "[13.0.1,14.0.7]"
"com.unity.textmeshpro" : "3.0.6"
}
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
Name "URPShaderTestPass0"
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//指令:也可以单独控制pass或整个subshader,但是subshader的会被pass覆盖
//部分一:渲染状态
//AlphaToMask On Off 根据片段着色器输出中的alpha值按比例修改multisample覆盖遮罩 在不使用MSAA的情况下启用结果可能是不可预测的
AlphaToMask Off
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//Blend Off默认 这里要花多行讲解一下
/*混合公式为:finalValue = sourceFactor * sourceValue operation destinationFactor * destinationValue *这里的operation是下面的BlendOp 默认是Add*
Blend的签名分为三种,State、Render Target、Factor。
其中State(Off)和Render Target(0到7的整数)只会出现一次 【简单来讲,渲染目标(Render Target)就是一种可以在运行时写入的纹理。从引擎的角度讲,渲染目标会存储颜色、法线以及AO等信息】
Factor总共有四个,出现顺序从左到右按此解读: ,
比如Blend 1 One Zero, Zero One就会这么解读 ,
但是:任何指定渲染目标的签名都需要OpenGL 4.0+、GL_ARB_draw_buffers_blend或OpenGL ES 3.2。单独的RGB和alpha混合与高级OpenGL混合操作不兼容。*/
/*One 值为1,使用此因子来让帧缓冲区源颜色或是目标颜色完全的通过。1
Zero 值为0,使用此因子来删除帧缓冲区源颜色或目标颜色的值。0
SrcColor 使用此因子为将当前值乘以帧缓冲区源颜色的值 A
SrcAlpha 使用此因子为将当前值乘以帧缓冲区源颜色Alpha的值。A*alpha
DstColor 使用此因子为将当前值乘以帧缓冲区目标颜色的值。B
DstAlpha 使用此因子为将当前值乘以帧缓冲区目标颜色Alpha分量的值。B*alpha
OneMinusSrcColor 使用此因子为将当前值乘以(1 -帧缓冲区源颜色值) 1- A
OneMinusSrcAlpha 使用此因子为将当前值乘以(1 -帧缓冲区源颜色Alpha分量的值) 1-A*lpha
OneMinusDstColor 使用此因子为将当前值乘以(1 –目标颜色值) 1-B
OneMinusDstAlpha使用此因子为将当前值乘以(1 –目标Alpha分量的值) 1- B*alpha*/
//src是这个通道渲染出来的颜色值,des是帧缓冲中已经存在的颜色值,跟当前pass的渲染时间有关系,如果这个pass是在最后渲染,那么帧缓冲则是屏幕上渲染完所有其他物体后的颜色值
Blend Zero One // 这样就是舍弃这个通道产生的颜色值而使用帧缓冲中的颜色值,也就是把这个物体透过去了
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//*添加BlendOp要保证已经存在Blend指令,且并非所有设备都支持所有混合操作!!!要注意的如下*
/*1、Min and Max require GL_EXT_blend_minmax on OpenGL ES 2.
2、Logical operations require DX 11.1+ or Vulkan.
3、Advanced OpenGL blending operations require GLES3.1 AEP+, GL_KHR_blend_equation_advanced, or GL_NV_blend_equation_advanced. They can only be used with standard RGBA blending; they are not compatible with separate RGB and alpha blending.*/
BlendOp Max // 或者使用这样的混合操作标识 Add(默认), Sub, RevSub, Min, Max等等值非常多,甚至带逻辑运算,具体看这里吧,感觉不常用 https://docs.unity3d.com/Manual/SL-BlendOp.html
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//默认情况下,GPU会写入所有通道(RGBA)。对于某些效果,您可能希望不修改某些通道,这时就需要ColorMask
//ColorMask
//channels代表颜色通道,值有0、R、G、B、A或是任意组合如:RA、RGB *写哪个代表允许写入哪个通道,值0代表可以写入所有通道,和RGBA应该是一样的*
//render target如上所说,值为0到7
ColorMask RGBA
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//是否开启保守光栅化,GPU不考虑覆盖范围,只要有一部分被三角形覆盖的像素就进行光栅化,等于说生成的像素会变多
//使用SystemInfo.supportsConservativeRaster API检查硬件是否支持,不支持会被忽略
Conservative False
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//裁剪:Back(默认)、Front、Off
Cull Back
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//深度偏移:调整深度偏移以强制GPU在相同深度的其他几何体之上绘制几何体,这个东西能防止z-fighting(相同深度面片渲染打架)
//Offset ,
//factor:缩放最大Z坡度(不平行于近剪裁平面和远剪裁平面的多边形具有Z坡度),范围【-1,1】
//units:缩放最小可解析深度缓冲区值,以产生恒定的深度偏移,范围【-1,1】 负值表示GPU将多边形绘制得更靠近摄影机。正值表示GPU绘制的多边形离摄影机更远。
Offset -1, -1 // 一般来说这样就可以有效防止z-fighting
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
Offset -1, [_OffsetUnitScale] // *想用属性往任意指令里面写值的话*
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//模版缓冲区为每个像素存储一个8位整数值,模板测试失败GPU将跳过该像素,可以执行两种操作---写入和测试,它们甚至可以在一条指令中同时执行
//使用Ref、ReadMask和Comp参数来配置模版测试。使用Ref、WriteMask、Pass、Fail和ZFail参数来配置模版写入。
//(ref & readMask) comparisonFunction (stencilBufferValue & readMask) 测试方程,写入的话只是(ref & readMask)在通过或失败情况下怎么做
Stencil{
Ref 2 //这是传进去的int值(默认0【0,255】注意这是二进制11111111),如果在测试就用这个值和模板值(如果没有写入过默认0)作比较,受readMask影响、如果在写入就是写入这个值,受writeMask影响
ReadMask 255 //这是传进去的int值(默认0【0,255】注意这是二进制掩码11111111,与Ref的二进制相与)这个掩码应该是为了不让自己写入的值影响自己,所以一般两个掩码应该是不同的,一般有通道单独写模板值,所以读取通道掩码应和那个写入通道的掩码匹配
WriteMask 155 //这是传进去的int值(默认0【0,255】注意这是二进制掩码11111111,与Ref的二进制相与)
Comp Always //何时通过模板测试,这个和写入没有任何关系,还有CompBack和CompFront专门针对背面和正面,如果存在Comp则会覆盖他们,值为:Never、Less、Equal、LEqual、Greater、NotEqual、GEqual、Always
Pass Replace //设置对通过模板测试的像素怎么处理,还有PassBack和PassFront专门针对背面和正面,如果存在Pass则会覆盖他们,值为:Keep(保持原值)、Zero(写入模板值0)、Replace(写入Ref值)、IncrSat(加上去但不超过255)、DecrSat(相减但不小于0)、Invert(所有位取反)、IncrWrap(加上去但超过255会从0重新开始)、DecrWrap(相减但小于0会从255重新开始)
Fail Keep //设置对未通过模板测试的像素怎么处理,还有FailBack和FailFront专门针对背面和正面,如果存在Fail则会覆盖他们,值和Pass的相同
ZFail Keep //当像素通过模板测试但未通过深度测试时怎么处理,还有ZFailBack和ZFailFront专门针对背面和正面,如果存在ZFail则会覆盖他们,值和Pass的相同
}
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//是否开启深度裁剪 该模式确定GPU如何处理近平面和远平面之外的片段
ZClip False //True(默认,直接切掉)、False(clamp操作,低于近平面设为近平面,远于远平面设为远平面)
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//设置深度测试通过规则 Less、LEqual(默认)、Equal、GEqual、Greater、NotEqual、Always
ZTest LEqual
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//深度写入 On(默认)、Off
ZWrite On
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//通道内的Tags只能配置一个LightMode
//URP通道的LightMode可选值:
//UniversalForward 正向渲染 计算灯光 根据所有光源照亮一个物体,之后再渲染下一个物体
//UniversalForwardOnly 如果URP使用“延迟渲染”但某个Pass必须使用“正向渲染”渲染对象,请使用此值
//UniversalGBuffer 延迟渲染 不计算灯光 先将所有物体都先绘制到屏幕空间的缓冲(G-buffer几何缓冲区),再逐光源对该缓冲进行着色的过程 将本来在物空 间(三维空间)进行光照计算放到了像空间(二维空间)进行处理。
//Universal2D 2D渲染用,计算灯光
//ShadowCaster 渲染阴影贴图使用
//DepthOnly 渲染深度图使用
//Meta 当选用此值时,unity仅在灯光烘焙阶段执行此Pass,而在构建时会忽略此Pass
//SRPDefaultUnlit 默认值,多Pass时可以用它
Tags {"LightMode" = "SRPDefaultUnlit"}
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
HLSLINCLUDE
HLSL code that you want to share goes here
ENDHLSL
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
HLSLPROGRAM
#pragma enable_d3d11_debug_symbols // debug信息
#pragma target 4.0 // 默认2.5 target的详细信息 https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html
//#pragma require [value] 添加某些支持 https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html
#pragma vertex vert
//#pragma hull hul // 曲面细分阶段 这两个都需要相当高的target5.0的Tessellation,应该还用不上
//#pragma domain dom // 曲面细分阶段
#pragma geometry geo // 几何阶段,需要target4.0
#pragma fragment frag
//声明关键字shader_feature、multi_compile、dynamic_branch,前两个都是编译时确定,有变体。shader_feature会自动剔除没有使用的。后面是实时的,没有变体,可以使用关键字更改着色器行为
//全局关键字限制256个,unity已经用了六十几个,为了避免不够用可以使用本地关键字【声明】_local
#pragma multi_compile_local MyShaderKeyWord1 MyShaderKeyWord2 //这里生成了两个关键字,如果都没有去激活则默认使用第一个,否则使用激活的那个
//删除哪些不需要的关键字
#pragma skip_variants SKIP1 SKIP2
//只想为某些图形API编译某些着色器程序,或是在使用并非所有平台都支持的功能时可以使用这个
#pragma only_renderers gles gles3 d3d11 glcore vulkan
#pragma exclude_renderers metal ps4 xboxone ps5
//For More #pragma https://docs.unity3d.com/Manual/SL-PragmaDirectives.html
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//https://github.com/Unity-Technologies/Graphics/tree/master/Packages/com.unity.render-pipelines.universal/ShaderLibrary
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
struct appdata
{
//顶点着色器语义 https://learn.microsoft.com/zh-cn/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics?redirectedfrom=MSDN
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tan : TANGENT;
float2 uv : TEXCOORD0;
};
struct v2f
{
//像素着色器语义 https://learn.microsoft.com/zh-cn/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics?redirectedfrom=MSDN
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 normal : NORMAL;
};
//从片段着色器的返回结构,SV_Target,SV_Depth等,不知道这两个之外还有能用的不。SV_Target可以从0到7,在多渲染目标(MRT)时很有用
struct fragOutput {
//语义 https://learn.microsoft.com/zh-cn/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics?redirectedfrom=MSDN
float4 color1 : SV_Target;
float4 color2 : SV_Target1;
float depth : SV_Depth; //用自定义深度覆盖Z缓冲区值,不知道啥用,还会关闭深度缓冲的优化,能不开别开
};
//如果是纹理属性:
//1.可以添加一个_ST标识平铺和偏移。xy为xy平铺值,zw为xy偏移值
//2.可以添加_TexelSize(float4)获取大小信息。xy为1/宽、1/高,zw为宽高
//3.纹理_HDR,如何解码潜在HDR的信息(例如RGBM编码的)纹理,这取决于所使用的颜色空间 见UnityCG.cginc着色器包含文件中的DecodeHDR函数
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
float4 _MainTex_HDR;
v2f vert(appdata v, uint vid : SV_VertexID) // 获取顶点 ID,必须为 uint
{
v2f o;
o.vertex = TransformObjectToHClip(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.normal = v.normal;
return o;
}
[maxvertexcount(3)]
void geo(triangle v2f p[3], inout LineStream istream)
{
for (int i = 0; i < 3; i++)
{
v2f o = (v2f)0;
o.vertex = p[i].vertex;
o.normal = p[i].normal;
o.uv = p[i].uv;
//将每个顶点添加到LineStream流里
istream.Append(o);
}
}
//https://docs.unity3d.com/cn/current/Manual/SL-ShaderSemantics.html
//特殊:屏幕uv坐标screenPos(float2)、是否面向摄像机facing,大于0是面向,小于0是反向
//UNITY_VPOS_TYPE screenPos : VPOS,屏幕空间像素位置,感觉用起来有弊端,SV_POSITION不能放在结构体
fragOutput frag(v2f i, float facing : VFACE) : SV_Target
{
fragOutput o;
o.color1 = tex2D(_MainTex, i.uv);
o.color2 = float4(i.uv, 0, 0);
o.depth = 50;
return o;
}
ENDHLSL
}
}
Fallback Off
}