许多游戏使用非真实感渲染(NPR)的方法渲染游戏画面,例如卡通和水彩风格等;
卡通渲染
渲染轮廓线
这里使用过程式几何轮廓线渲染:使用两个Pass,第一个Pass使用轮廓线颜色渲染整个背面的面片,并在视角空间下把模型顶点沿着法线方向向外扩张一段距离,让背部轮廓线清晰可见;为了防止对于一些内凹的模型,背面面片遮挡正面面片的情况,在扩张背面顶点之前,先对顶点法线的z分量处理,使它们等于一个定值,然后把法线归一化后再对顶点扩张,使扩展后的背面更加扁平化,降低了遮挡正面面片的可能性。
优点是快速有效,适用于绝大多数表面平滑的模型,但不适合立方体这样平整的模型;
viewNormal.z=-0.5;
viewNormal=normalize(viewnormal);
viewPos=viewPos+viewNormal*_Outline;
高光
以往的高光模型不再可用,这里计算normal和halfDir的点乘结果,和一个阈值比较,为了防止高光边界出现锯齿,可以使用以下代码:
float spec=dot(worldNormal,worldHalfDir);
spec=lerp(0,1,smoothstep(-w,w,spec-threshold));
w是一个很小的值,当spec-threshold小于-w时,高光系数返回0,大于w时返回1,否则在0到1之间插值,这样可以使高光区域的边界处得到一个从0到1平滑变化的spec值;
这里使用领域像素之间的近似导数值,使用fwidth函数实现;
实现
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 14/Toon Shading" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {
}
_Ramp ("Ramp Texture", 2D) = "white" {
} //控制漫反射色调的渐变纹理
_Outline ("Outline", Range(0, 1)) = 0.1 //控制轮廓线宽度
_OutlineColor ("Outline Color", Color) = (0, 0, 0, 1) //轮廓线颜色
_Specular ("Specular", Color) = (1, 1, 1, 1) //高光反射颜色
_SpecularScale ("Specular Scale", Range(0, 0.1)) = 0.01 //计算高光反射时使用的阈值
}
SubShader {
Tags {
"RenderType"="Opaque" "Queue"="Geometry"}
Pass {
NAME "OUTLINE"
//只渲染背面
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Outline;
fixed4 _OutlineColor;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert (a2v v) {
v2f o;
//变化到视角空间是为了让描边在观察空间达到最好的效果
float4 pos = mul(UNITY_MATRIX_MV, v.vertex);
float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV