Shader "Unlit/遮挡边缘光描边"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Diffuse("Color",Color) = (1,1,1,1)
_Outline("Outline",Range(0,1)) = 0.1 // 描边参数控制
_OutlineColor("Outline Color",Color) = (0,0,0,0) // 描边颜色默认黑色
_Steps("Steps",Range(1,30)) = 1 // 颜色分阶参数
_ToonEffect("ToonEffect",Range(0,1)) = 0.5 // 卡通影响
_RimColor("Rim Color",Color) = (1,1,1,1) // 边缘光颜色默认白色
_RimPower("Rim Power",Range(0.001,3)) = 1 // 边缘光强度
// X-ray
_XRayColor("XRayColor",Color) = (1,1,1,1)
_XRayPower("XRayPower",Range(0.01,3)) = 1
}
SubShader
{
Tags { "Queue" = "Geometry+1000" "RenderType" = "Opaque"} // +1000在其他不透明物体之后渲染
LOD 100
Pass // 遮挡X-ray
{
Name "Xray"
Tags{"ForceNoShadowCasting" = "true"} // 无阴影
Blend SrcAlpha One // SrcAlpha现颜色a通道做原颜色的目标因子 One目标颜色做目标颜色因子 就是将这两个因子混合叠加
ZWrite Off // 不写深度
ZTest Greater // 被挡住才画
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 vertex : SV_POSITION;
float3 viewDir : TEXCOORD0; // 模型空间视角方向
fixed3 normal : TEXCOORD1; // 模型空间法线
};
fixed4 _XRayColor;
float _XRayPower;
v2f vert(appdata_base v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.viewDir = ObjSpaceViewDir(v.vertex); // 模型空间视角方向
o.normal = v.normal; // 模型空间法线
return o;
}
fixed4 frag(v2f i) : SV_Target{
float3 normal = normalize(i.normal);
float3 viewDir = normalize(i.viewDir); // 分别标准化
float rim = 1 - dot(normal, viewDir);
return _XRayColor * pow(rim , 1/ _XRayPower); // 乘上_XRayColor赋值颜色 这两步添加控制
}
ENDCG
}
Pass // 这个pass通道画图和边缘光
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc" // 不然没有 _LightColor0
#include "UnityCG.cginc"
struct v2f
{
float2 uv : TEXCOORD0;
fixed3 worldNormal : TEXCOORD1; // 世界法线向量,计算漫反射 需要世界光向量和世界法线向量点乘
float3 worldPos : TEXCOORD2; // 计算世界光向量需要世界坐标,所以这里把世界坐标传下去 计算漫反射
float4 vertex : SV_POSITION; // 顶点坐标
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Diffuse;
float _Steps;
float _ToonEffect;
fixed4 _RimColor;
float _RimPower;
v2f vert(appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex); // 顶点信息从模型转到裁剪空间再放到输出结构体
o.worldNormal = UnityObjectToWorldNormal(v.normal); // 法线从模型变换到世界坐标 法线是向量所以fixed3 为世界光向量和世界法线向量点乘计算漫反射做准备 所以要计算世界法线向量
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); // 拿到uv
o.worldPos = mul(unity_ObjectToWorld, v.vertex); // 模型的世界坐标 顶点信息从模型坐标系转到世界坐标系得到
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 环境光
fixed4 albedo = tex2D(_MainTex, i.uv); // 采样这个图
fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos); // 世界光向量 漫反射世界光向量和世界法线向量点乘
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); // 世界视角向量
float difLight = max(0,dot(worldLightDir,i.worldNormal)) * 0.5 + 0.5; // max不让这个值小于零 *0.5+0.5半兰伯特光照模型 这是漫反射光的值
// 卡通颜色
difLight = smoothstep(0, 1, difLight); // 颜色平滑在【0,1】之间
// 颜色离散化
float toon = floor(difLight * _Steps) / _Steps; // floor参数向下取整 目的:分几块
difLight = lerp(difLight, toon, _ToonEffect); // lerp前两个值插值,第三个是权值
fixed3 diffuse = _LightColor0.rgb * albedo * _Diffuse.rgb * difLight; // 漫反射
// 边缘光
float rim =1 - dot(i.worldNormal, worldViewDir); // 坐标的世界法线与世界视角方向点积 越接近0说明越垂直,也就越接近边缘 1-改逻辑为越近边值越大
fixed3 rimColor = _RimColor * pow(rim , 1/_RimPower); // pow取次方 rim是0到1,取次方放大影响
return fixed4(ambient + diffuse + rimColor,1);
}
ENDCG
}
Pass// 画边 裁剪空间外拓
{
Name "Outline"
Cull Front // 模型不画正面 防止与画图发生冲突
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Outline;
fixed4 _OutlineColor;
struct v2f {
float4 vertex : SV_POSITION; // 顶点坐标
};
v2f vert(appdata_base v) { // 顶点
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex); // 顶点信息从模型转到裁剪空间
float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal)); // 模型法线转到视角坐标系,z值变成纯粹的深度
float2 viewNormal = TransformViewToProjection(normal.xy); // 把世界法线xy轴转到投影(Projection) z值是深度就不要了
o.vertex.xy = o.vertex + viewNormal * _Outline; // 添加控制参数
return o;
}
fixed4 frag(v2f i) : SV_Target{ // 片元
return _OutlineColor;
}
ENDCG
}
}
FallBack "Diffuse" // 阴影
}
unity shader遮挡边缘光描边
最新推荐文章于 2023-06-15 16:15:33 发布