全局雾效
通过屏幕后处理实现雾效可以不用修改场景物体的 shader,方便自由控制
还是参照《unity shader入门精要》
// 引入雾效相关的参数
_FogStart ("FogStart", float) = 1
_FogEnd ("FogEnd", float) = 1
_FogColor ("FogColor", Color) = (1,1,1,1)
_FogDesity ("FogDesity", float) = 1
// 根据场景高度计算雾的浓度,和场景颜色做差值
float4 color = tex2D(_MainTex, i.uv);
float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);
fogDensity = saturate(fogDensity * _FogDesity);
color.rgb = lerp(color.rgb, _FogColor.rgb, fogDensity);
return color;
雾的最终效果
扫描线
// in fragment shader
float linear01Depth = Linear01Depth(depthTextureValue);
float d = distance(worldPos, _ScanCenter);
// 在场景中,并在扫描区域
if (linear01Depth < 1 && _ScanRange - d > 0)
{
// 根据坐标到中心的距离计算扫描线,加入扫描线宽度的控制
float r = saturate(1 - (_ScanRange - d) / _ScanWidth);
return _LineColor * r + color;
}
// 不在扫描区域显示默认画面
return color;
// in c#
private Vector3 mouseWorldPos;
private float scanWidth = 0f;
public float scanSpeed = 0f;
...
void Update()
{
// 鼠标点击位置为扫描区域中心
if (Input.GetMouseButtonDown(0))
{
Ray ray = currentCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
mouseWorldPos.x = hit.point.x;
mouseWorldPos.z = hit.point.y;
mouseWorldPos = hit.point;
scanWidth = 0;
}
}
// 范围随着时间扩大
scanWidth += Time.deltaTime * scanSpeed;
}
...
postEffectMaterial.SetVector("_ScanCenter", mouseWorldPos);
postEffectMaterial.SetFloat("_ScanRange", scanWidth);
最终效果
进一步:加入墙体和墙体后被遮挡的物体
利用 Stencil
可以区分绘制物体被遮挡的部分
先绘制墙后的物体,并写入Stencil
然后绘制墙,不覆盖绘制墙体后的物体
// object behind wall
Shader "Unlit/ObjectBehindWall"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_Outline ("Outline", float) = 1
_Fresnel ("Fresnel", float) = 1
}
SubShader
{
Pass
{
Tags { "RenderType"="Opaque" }
LOD 100
Stencil {
Ref 1
Comp Always
pass Replace
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 normal : NORMAL;
float3 worldPos : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Color;
float _Outline;
float _Fresnel;
v2f vert (appdata v)
{
v2f o;
float3 normal = UnityObjectToWorldNormal(v.normal);
o.vertex = UnityObjectToClipPos(v.vertex + normal * _Outline);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.normal = normal;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 V = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
float3 N = normalize(i.normal);
float NdotV = saturate(dot(V, N));
float fresnel = pow(1 - NdotV, _Fresnel);
if (fresnel < 0.2) {
discard;
}
float4 col = _Color;
col.rgb *= fresnel;
return col;
}
ENDCG
}
}
Fallback "Diffuse"
}
// wall
Shader "Unlit/wall"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Stencil {
Ref 1
Comp NotEqual
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Color;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv) * _Color;
return col;
}
ENDCG
}
}
}
墙使用的是 unlit
的材质,这样就不会写入深度
墙后的物体使用的也是 unlit
材质。在后面加上 Fallback "Diffuse"
,由于引入了阴影,就会写入深度纹理
最后的效果,在物体上绘制扫描线,墙上不绘制扫描线