1.卡通渲染
//卡通渲染
//轮廓线渲染原理:渲染物体背面后向外拓展
//轮廓线弃用方案:根据视线与物体法线的夹角大小判断轮廓。理由:应用于正方体等规整物体上时效果极差
Shader "MyToon/Toon-Fragment" {
Properties {
_MainTex ("MainTex", 2D) = "white" {}
_Ramp ("Ramp Texture", 2D) = "white" {}
_Tooniness ("Tooniness", Range(0.1,20)) = 4
_Outline ("Outline", Range(0,0.1)) = 0.01
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
//下面是渲染背面的pass
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Front//剔除正面
Lighting Off
ZWrite On
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
float _Outline;
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : POSITION;
};
v2f vert (a2v v)
{
v2f o;
float4 pos = mul( UNITY_MATRIX_MV, v.vertex); //顶点位置从模型空间转换到观察空间(摄像机空间)
float3 normal = mul(v.normal, (float3x3)UNITY_MATRIX_T_MV);//将法线(向量)从模型空间变换到观察空间。
normal.z = -0.5;//Z方向的值扁平化(使其是一个较小的定值),防止喧宾夺主,挡住正面的渲染结果(不能关闭深度写入来解决)
//将法线与顶点转到观察空间也是为了这一步的扁平化。否则不需要(根据法线与视线夹角的那个方法)
pos = pos + float4(normalize(normal),0) * _Outline;//v.vertex沿着v.normal的方向扩展_Outline倍
o.pos = mul(UNITY_MATRIX_P, pos);//把新的位置转换到投影坐标系中
return o;
}
float4 frag(v2f i) : COLOR
{
return float4(0, 0, 0, 1); //将背面渲染成黑色就返回
}
ENDCG
}
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Back
Lighting On
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityShaderVariables.cginc"
sampler2D _MainTex;
sampler2D _Ramp;
float4 _MainTex_ST;
float _Tooniness;
struct a2v {
float4 vertex : POSITION;//语义POSITION指模型空间下的顶点位置
float3 normal :NORMAL;//语义指模型空间下法线
float4 texcoord:TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;// SV_POSITION指裁剪空间下定点位置
float3 WorldNormal:TEXCOORD0;
float3 WorldPos : TEXCOORD1;
float2 uv:TEXCOORD2;//用于存储纹理坐标
};
v2f vert(a2v v) {
v2f f;
f.WorldNormal= UnityObjectToWorldNormal(v.normal);//法线世界化
//f.WorldNormal = normalize(mul(unity_ObjectToWorld, v.normal));
f.WorldPos = mul(unity_ObjectToWorld, v.vertex);//点坐标世界化
//点坐标世界化,是方便通过世界坐标下的点作为一些内置函数的参数,获取光照方向,视线方向等
f.pos = UnityObjectToClipPos(v.vertex);//点坐标片元化
f.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
//f.uv=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;
//_MainTex_ST.xy对纹理进行缩放,_MainTex_ST.zw对纹理偏移
return f;
}
float4 frag(v2f f) : COLOR
{
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(f.WorldPos));
fixed3 worldNormal=normalize(f.WorldNormal);
fixed3 albedo =tex2D(_MainTex,f.uv).rgb;
albedo.rgb = (floor(albedo.rgb*_Tooniness)/_Tooniness);//合并颜色
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo;
fixed halfLambert =dot( worldNormal,lightDir)*0.5+0.5;//半兰伯特
fixed3 diffuseColor=tex2D(_Ramp,fixed2(halfLambert,halfLambert)).rgb;
//让半兰伯特的取值范围映射在(0,1),用其数值构建一套纹理坐标,并用这套坐标对渐变纹理采样。
fixed3 diffuse=diffuseColor*_LightColor0.rgb;
return fixed4(ambient + diffuse , 1);
}
ENDCG
}
Pass {
Tags { "LightMode"="ForwardAdd" }
Cull Back
Lighting On
Blend One One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdadd
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityShaderVariables.cginc"
sampler2D _MainTex;
sampler2D _Ramp;
float4 _MainTex_ST;
float _Tooniness;
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
};
struct v2f
{
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float3 normal : TEXCOORD1;
half3 lightDir : TEXCOORD2;
LIGHTING_COORDS(3,4)
};
v2f vert (a2v v)
{
v2f o;
//Transform the vertex to projection space
o.pos = UnityObjectToClipPos( v.vertex);
o.normal = mul((float3x3)unity_ObjectToWorld, SCALED_NORMAL);
o.lightDir = WorldSpaceLightDir( v.vertex );
//Get the UV coordinates
o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
// pass lighting information to pixel shader
TRANSFER_VERTEX_TO_FRAGMENT(o);
return o;
}
float4 frag(v2f i) : COLOR
{
//Get the color of the pixel from the texture
float4 c = tex2D (_MainTex, i.uv);
//Merge the colours
c.rgb = (floor(c.rgb*_Tooniness)/_Tooniness);
//Based on the ambient light
float3 lightColor = float3(0,0,0);
//Work out this distance of the light
float atten = LIGHT_ATTENUATION(i);
//Angle to the light
float diff = dot (normalize(i.normal), normalize(i.lightDir));
diff = diff * 0.5 + 0.5;
//Perform our toon light mapping
diff = tex2D(_Ramp, float2(diff, 0.5));
//Update the colour
lightColor += _LightColor0.rgb * (diff * atten);
//Product the final color
c.rgb = lightColor * c.rgb * 2;
return c;
}
ENDCG
}
}
FallBack "Diffuse"
}
2.溶解
Shader "Unlit/Melt"
{
Properties{
_MainTex("Base(rgb)", 2D) = "white"{}
_NoiseMap("NoiseMap", 2D) = "white"{}//噪声贴图
_MeltThreshold("MeltThreshold", Range(0, 1)) = 0//烧毁闸值
_EdgeLength("EdgeLength",Range(0, 0.3))=0.1
_EdgeColorStart("EdgeColorStart", Color) = (1,1,1,1)
_EdgeColorEnd("EdgeColorEnd", Color) = (0,0,0,0)
//_Erode("Erode", Range(0.0, 1.0)) = 0.98
//_ErodeThreshold("ErodeThreshold", Range(0.0, 1.0)) = 0.71
}
SubShader{
CGINCLUDE
#include "Lighting.cginc"
#include "UnityCG.cginc"
#include "AutoLight.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _NoiseMap;
float _MeltThreshold;//消融阈值
float _EdgeLength;
//消融边缘起始颜色
fixed4 _EdgeColorStart;
//最终颜色
fixed4 _EdgeColorEnd;
控制侵蚀程度
//float _Erode;
//控制侵蚀颜色阈值
//float _ErodeThreshold;
struct a2v{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
SHADOW_COORDS(3)
};
v2f vert(a2v v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target{
fixed3 melt = tex2D(_NoiseMap, i.uv).rgb;//melt意味溶解
//使用噪声图采样
clip(melt.r - _MeltThreshold);
//采样阈值与设定阈值比较,小于设定的阈值就裁剪掉该片元
//如何理解:clip函数相当于返回一个裁剪后的部分可见模型。而哪些地方裁剪哪些不,完全取决于噪声贴图(与主图共用uv)与闸值的比较
//常规光照计算部分
fixed3 albedo = tex2D(_MainTex, i.uv).rgb;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, -worldLightDir));
fixed3 lightColor = diffuse + ambient;
//下面处理边缘的烧融部分
float result =1- (melt.r - _MeltThreshold);
//melt.r - _MeltThreshold得到了未被裁剪的剩余部分。即半透明中(半黑)更可见的部分
//注意剩余部分对应的噪声贴图不均。(白色为1,黑色为0.介于之间为半透明)
//1-剩余部分 即得到透明值(噪声图黑色含量值)。0为不透明,反之更透明。
if(result > (1-_EdgeLength)){//1-_EdgeLength是不会出现烧融的部分,即“内陆”。
//result 大于1-_EdgeLength的部分即为烧融边界
fixed4 edgeColor=lerp(_EdgeColorStart,_EdgeColorEnd,(result-(1-_EdgeLength))/_EdgeLength);
//result-(1-_EdgeLength))/_EdgeLength是将烧融部分规范到(0,1)
//lerp(a,b,f):线性插值函数,返回值为(1-f)*a+b*f
return edgeColor;
}
//直接返回光照后颜色
return fixed4(lightColor, 1);
}
ENDCG
//为了看见整个模型的消融过程,我选择关闭了剔除
Pass{
Tags{ "RenderType" = "Opaque"}
Cull off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
FallBack Off
}
3.透视