0.前言
通过之前的努力,我们达到了如下效果
现在我们想在这个基础上添加描边效果,也就外轮廓线。
1.核心思想
这里要用到的知识点就是我们之前提到的,一个SubShader可以有多个Pass
想实现描边效果,我们需要用到两个Pass渲染,在一个Pass中渲染一个球,然后在另一个Pass中渲染一个大一圈的黑色球,然后再对大一圈的黑球进行正面剔除
2.核心代码
1.剔除正面
Cull Front
2.让球扩大一圈
v2f vert (appdata v)
{
v2f o;
// 获取法线
float3 normal = v.normal;
// 顶点加 0.0.2 倍的法线
v.vertex.xyz += normal * 0.02;
// 转换坐标到裁剪坐标
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
3.返回一个黑色球
float4 frag (v2f i) : SV_Target
{
// 全部设置呈黑色
return fixed4(0,0,0,1);
}
4.用两个Pass渲染
Shader "MyShader/Cartoon"
{
Properties
{
...
}
SubShader
{
Pass
{
Tags { "LightMode" = "ForwardBase"}
// 剔除掉球的正面
Cull Front
CGPROGRAM
...
v2f vert (appdata v)
{
...
}
float4 frag (v2f i) : SV_Target
{
...
}
ENDCG
}
Pass
{
CGPROGRAM
...
v2f vert (appdata v)
{
...
}
float4 frag (v2f i) : SV_Target
{
...
}
ENDCG
}
}
}
3.完整的代码实现
Shader "MyShader/Cartoon"
{
Properties
{
_Diffuse("Diffuse",Color) = (1,1,1,1)
}
SubShader
{
Pass
{
Tags { "LightMode" = "ForwardBase"}
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float3 worldNormal:TEXCOORD0;
float4 pos: SV_POSITION;
};
float4 _Diffuse;
v2f vert (appdata v)
{
v2f o;
// 获取法线
float3 normal = v.normal;
// 顶点加 0.0.2 倍的法线
v.vertex.xyz += normal * 0.02;
// 转换坐标到
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
float4 frag (v2f i) : SV_Target
{
return fixed4(0,0,0,1);
}
ENDCG
}
Pass
{
Tags { "LightMode" = "ForwardBase"}
Cull Back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float3 worldNormal:TEXCOORD0;
float4 pos: SV_POSITION;
};
float4 _Diffuse;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
// 表面法线
o.worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
return o;
}
float4 frag (v2f i) : SV_Target
{
// 环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// 顶点到光源方向
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
float NdotL = 0.5 + 0.5 * dot(i.worldNormal, worldLight);
if (NdotL > 0.9)
{
NdotL = 1;
}
else if (NdotL > 0.5)
{
NdotL = 0.6;
}
else if (NdotL > 0.2)
{
NdotL = 0.3;
}
else
{
NdotL = 0.1;
}
// Cdiffuse = (Clight * Mdiffuse)max(0,N^*L^)
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * NdotL;
return fixed4(ambient + diffuse,1.0);
}
ENDCG
}
}
}
4.总结
虽然我们完成了一个看起效果还不错的卡通渲染球,但是还是存在很多问题,比如外轮廓不平滑,没有高光效果,只能渲染一些表面平滑并且没有棱角的物体,当然之后通过更深入的研究我们也会继续完善这个卡通渲染效果。
所有文章的目录
StarryFun:独立游戏爱好者的成长记录zhuanlan.zhihu.com