知识补充
Subshader
- 可以在subshader下定义多个Tags和State
- 定义对所有Pass都生效
- 如果需要可以单独对逐个Pass进行生效
Tags
可以定义n个标签 用键值对的形式来实现
Queue队列标签
用队列标签来确定物体的渲染顺序
- Background最先进行渲染Skybox或者背景,队列号1000
- Geometry,渲染非透明的几何体所采用的队列,没有声明渲染队列的时候,unity默认使用此队列,队列号2000
- AlphaTest,Alpha测试的几何体使用的队列,在所有几何体绘制完后进行的绘制,队列号2450
- Transparent,这个队列的几何体按照从远到近的顺序进行绘制,进行Alpha混合的几何体都应该使用这个队列,3000
- Overlay:放在最后进行叠加渲染的效果,例如镜头光晕,队列号4000
也可以自己定义队列例如在Tags中写“Queue”=“Geometry+1”
除了可以在Subshader中进行指定渲染队列,还可以在Inspector中选用相应的队列或者自定义队列
渲染类型 RenderType
可以使用RenderType将Shader划分为不同的类别,用于后期进行Shader替换或者产生摄像机的深度纹理
- Opaque 用于普通Shader:不透明、自发光、反射、地形shader
- Transparent 用于半透明Shader:透明、粒子
- TransparentCutout 用于透明测试Shader 例如:植物叶子
- Background:用于Skybox Shader
- Overlay:用于GUI纹理、Halo、Flare Shader
- Tree Opaque 用于地形系统中的树干
- TreeTransparentCutout:用于地形系统中的树叶
- TreeBillboard:用于地形系统中的Billboarded树
- Grass:用于地形系统中的草
- GrassBillboard:用于地形系统中的Billboarded草
禁用批处理 DisableBatching
使用批处理的时候几何体会被变换到世界空间,模型空间会被丢弃,导致使用模型空间顶点数据的Shader无法实现所希望的效果。而开启禁用批处理可以解决这个问题
- “DisableBatching”=“True”总是禁用批处理
- “DisableBatching”=“False”不禁用批处理
- “DisableBatching”=“LODFading”当LOD效果激活的时候的时候才会禁用,主要用于地形系统上的树
禁用阴影投射ForceNoShadowCasting
将这个标签的数值设置为true,使用这个Shader的物体就不会投射阴影
忽略Projector IgnoreProjector
“IgnoreProjector”=“True”
可以让物体不受投影机的映射,半透明的shader一般都会开启这个标签
其他标签
还有很多不常用的标签
如CanUseSpriteAtlas、PreviewType
但是在实际使用中很少用到所以可以看Unity官方文档的SubShader这部分的解释
Pass的渲染状态State
- Cull:Cull Back/Cull Front/Cull 设置多边形的剔除方式,有背面剔除,正面剔除、不剔除,默认为Back
- ZTest:(Less/Greater/LEqual/GEqual/Equal/NotEqual/Always)
设置深度测试的对比方法,默认为LEqual) - Zwrite:Zwrite On是否写入深度缓存,默认为On
- Blend:Blend sourceBlendMode destBlendMode 设置渲染图像的混合方式
- ColorMask:ColorMask RGBA 默认为RGBA设置为0则无法写入任何颜色
以上就是一些常用的还有其他的去unity官方文档里看看吧~
Fallback
如果所有都执行失败了会执行的Shader
Fallback “name”
如果不写的话就直接写Fallback off 或者直接什么都不写
半透明物体的实现
关于Z-Buffer
用来解决OverDraw造成的性能浪费问题
同时实现支持正确实现透明效果
Queue 半透明队列
开启此队列后会先绘制不透明物体、再绘制半透明物体,其中半透明物体从后到前绘制
就算开启了Queue队列 事实上是先进行顶点着色器然后进行Ztest然后进行Zwrite然后进行绘制的 所以也要考虑是否开启深度写入和是否进行深度测试
解决渲染多个物体的问题
Blend混合指令
Blend Off默认不开启混合指令的时候就是关闭状态
Blend Src Det 用源乘子和目标乘子来混合实现
Blend Src Dst,SrcA DstA可以分别进行添加A的混合和RGB的混合
BlendOp 使用其他操作进行混合而不仅仅是颜色相加
计算方法是 添加色乘以添加乘子+基础色乘以基础乘子
可以用的混合系数
- Zero:数值为0表示完全不能通过
- One:数值为1表示可以完全通过
- SrcColor:可以取Source的像素颜色作为混合系数
- DstColor:把Dst的颜色用作混合系数
- SrcAlpha:吧Source的alpha用作混合系数
- DstAlpha:把dst的Apha用作混合系数
- OneMinus+SrcColor/Alpha DstColor/Apha可以对颜色以及数值反相后再用作混合系数
常用的混合指令:
Blend SrcAlpha OneMinusSrcAlpha 普通的透明叠加
Blend One OneMinusSrcAlpha 预乘透明
Blend One One 相加
Blend OneMinusDstColor One 柔和相加
Blend DstColor Zero 相乘
Blend DstColor SrcColor 2倍相乘
混合操作:
Add 将Src和Dst相加
Sub 将Src减去Dst
RevSub 用Dst减去Src
Min 类似min(Source,Dst)
Max 类似于max(Source,Dst)
一个简单的透明例子
- 先导入RGB MainTex再用其的a值乘以MainTex的a
- 记得State中加人Blend,给Tags加RenderType和Queue,再把摄像机投影给关掉
半透明物体的双面渲染(渲染背面)
同时使用两个Pass 一个Pass进行Cull Front 另一个Pass 进行Cull Back
透明测试 AlphaTest
-
综述:HLSL中有Clip指令,用于在像素着色器中丢弃数值小于0的像素,从而实现某些位置完全透明的效果,并且不会出现渲染顺序错误的现象,
将Queue队列设置为AlphaTest,将RenderType设置为TransparentCutout,并且需要IgnoreProjector忽略投影仪。在片段着色器中使用Clip()指令,将采样纹理的a通道减去一个设定的数值,
Clip(TextureColor.a-AlphaCutoffValue)在pass中进行clip减去设定的数值,如果小于0则会完全透明,否则会完全不透明
需要将alpha减少到0以下才可以被剔除 -
补充:AlphaTest进行透明剪切时候因为两边都是极端要么透明要么不透明所以容易产生很明显的锯齿现象,解决办法之一是开启MSAA,在PASS中添加AlphaToMask On开启显卡的Alpha to coverage功能用来达到消除锯齿的作用。
模板测试 StencilTest
- 模板测试:模板缓存中 每个像素都有一个模板缓存范围0-255的整数值可以被修改,后续再绘制这个像素点的时候可以对其进行判断后选择性进行像素绘制,并且绘制后可以再次修改模板缓存值
- 先对A物体进行渲染—写入A物体的缓存值—对B物体的缓存值进行自定义的比较(成功则渲染,失败就不渲染)—无论是否成功都可以对这个缓存值再次进行修改或者保留
- 具体语法:指令可以写在Subshader中也可以写在每个Pass中,要写在Stencil的代码块中,
Stencil
{
Ref referrenceValue读取参照值,用来和缓存中已经存在的模板值相比较,比较之后可以被写入缓存
ReadMask readMask 来指定哪些位的数值可以读取,默认为255也就是所有位数的都可以被读取
WriteMask writeMask同样也可以写到0到255来指定哪些位的数据可以写入
Comp comparisonFuction将参照值于缓存中的模板数值进行比较的方法,默认为always总是通过 也可以设置不同的比较方法
Pass stecilOperation如果模板测试和深度测试都通过,缓存中的深度值如何处理,默认为keep
Fail stecilOperation如果模板测试没有通过,缓存中的模板值如何处理,默认为keep
ZFail stecilOperation如果模板测试通过,但是深度测试没有通过,缓存中的模板值如何处理,默认为keep
} - 比较方法(当前渲染像素的参照值和缓存的值进行比较)
Greater大于的时候才会通过测试
GEqual当前渲染像素大于等于缓存时通过测试
Less当前渲染像素小于缓存时通过测试
LEqual当前像素小于等于缓存时才会通过测试
Equal当前像素等于缓存时候通过测试
NotEqual当前像素不等于缓存时从过测试
Always使当前渲染像素总是通过测试
Never使当前渲染像素总是不能通过测试 - 模板操作
可以对模板值进行的操作
Keep 保持缓存中的模板值
Zero 把0写进缓存
Replace把当前像素的参照值写进缓存
IncrSat递增缓存中的模板值,如果数值已经为255,则停止递增
DecrSat递减缓存中的模板值,如果数值已经为0就停止递减
Invert 将模板值按位取反
IncrWrap递增缓存值满255后Reset为0
DecrWrap递减缓存中的模板值,如果已经为0,则变为255
具体实现
Unity中编辑3D场景时候如果开启了SelectionOutline选中描边则被选中的物体会有描边效果
实现逻辑
可以用Matcap实现也可以用,后期处理实现
这里采用两层Pass来实现,先将第一层扩大然后渲染一层整体是一个颜色的,然后渲染第二层,将Queue的Transparent打开(保证描边效果不会因其他物体而被挡住),关闭第一层的深度写入保证第二层正常渲染
具体代码:
/*
* @Author: Tong
* @Date: 2022-04-25 22:31:14
* @LastEditors: Tong
* @LastEditTime: 2022-04-28 13:39:06
* @FilePath: \ShaderLab\Assets\Resoueces\Shader\OutlineShader.shader
* @Description: 可以给原本的Standard Shader添加描边效果
*
*/
Shader "Custom/OutLineShader"
{
Properties
{
[Header(OutlineSetiting)]
[Space(10)]
//定义轮廓颜色
_OutlineColor ("OutlineColor", Color) = (1,1,1,1)
//定义描边长度
_OutlineLenth ("OutlineLenth", Range(0,0.1)) = 0.05
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Transparent"}
//第一个Pass用来扩大物体本身并且渲染单个颜色
Pass
{
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
half4 _OutlineColor;
half _OutlineLenth;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
//对物体本身进行扩展沿着法线方向
v.vertex.xyz+= v.normal * _OutlineLenth ;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// 给物体上一个纯色
return _OutlineColor;
}
ENDCG
}
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) ;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
}