Unity&Shader基础篇—轮廓增强

时间:2023-02-06 22:00:43

1、介绍:如图所示,

Unity&Shader基础篇—轮廓增强

两个胶囊体都是采用同样的透明度混合方程进行混合的,并且颜色和透明度都一样。而左边的对透明度进行了“特殊”的处理,就得到了和右边不一样的效果:胶囊体的边沿轮廓增强。

配合采用贴图的效果如图所示:

Unity&Shader基础篇—轮廓增强

两者都是采用了一样的透明度贴图以及透明度混合方法,左边的是采用了轮廓增强的效果图。

2、原理:首先,计算出模型表面的轮廓,这不废话吗,其实还真不是废话,因为并不是所有的模型我们都能预先知道它的轮廓,当然经过特殊处理的除外。怎么样去判断模型的网格点是属于轮廓边缘上呢。这里我们定义模型的网格点的法线方向为N

如上图所示的蓝色线,摄像机的观察方向为V

Unity&Shader基础篇—轮廓增强

。当N和V方向正交的时候,也即V.N=0,我们就判断这个点为轮廓点。实际上很少出现这种情况,因此,我们将点积V.N接近于0的点都可以看做是靠近轮廓。因此我们的透明度计算方程可以这么写:a=min(1,a/|V.N|)

3、实现效果的Shader代码:

1>:不加贴图的Shader代码:

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

Shader "Unlit/Cg silhouette enhancement"
{
Properties{
_Color("Color", Color) = (1, 1, 1, 0.5)
// user-specified RGBA color including opacity
}
SubShader{
Tags{ "Queue" = "Transparent" }
// draw after all opaque geometry has been drawn
Pass{
ZWrite Off // don't occlude other objects
Blend SrcAlpha OneMinusSrcAlpha // standard alpha blending
//==float4 result = fragment_output.aaaa * fragment_output + (float4(1.0, 1.0, 1.0, 1.0) - fragment_output.aaaa) * pixel_color;

CGPROGRAM

#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

uniform float4 _Color; // define shader property for shaders

struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float3 normal : TEXCOORD;
float3 viewDir : TEXCOORD1;
};

vertexOutput vert(vertexInput input)
{
vertexOutput output;

float4x4 modelMatrix = unity_ObjectToWorld;//模型矩阵
float4x4 modelMatrixInverse = unity_WorldToObject;//模型的逆矩阵

output.normal = normalize(
mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
output.viewDir = normalize(_WorldSpaceCameraPos
- mul(modelMatrix, input.vertex).xyz);

output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
return output;
}

float4 frag(vertexOutput input) : COLOR
{
//为什么在顶点着色器程序中已经将这两个向量归一化了,在此为什么还要归一化?
//1>首先,在顶点程序中归一化是因为要在任何它们之间的方向上进行或多或少的插值
//2>在此处又进行一次插值是因为,上面的插值过程会将归一化的值扭曲
float3 normalDirection = normalize(input.normal);
float3 viewDirection = normalize(input.viewDir);

float newOpacity = min(1.0, _Color.a
/ abs(dot(viewDirection, normalDirection)));
return float4(_Color.rgb, newOpacity);
}

ENDCG
}
}
}

2>:贴图渲染的代码:

将片段Shader的代码替换成下面的代码,

float4 frag(vertexOutput input) : COLOR
{
//为什么在顶点着色器程序中已经将这两个向量归一化了,在此为什么还要归一化?
//1>首先,在顶点程序中归一化是因为要在任何它们之间的方向上进行或多或少的插值
//2>在此处又进行一次插值是因为,上面的插值过程会将归一化的值扭曲
float3 normalDirection = normalize(input.normal);
float3 viewDirection = normalize(input.viewDir);
float4 tex = tex2D(_MainTex, input.tex.xy);
float3 col = tex;

float newOpacity = min(1.0, tex.a / abs(dot(viewDirection, normalDirection)));
return float4(col, newOpacity);
}

ENDCG
}

4、总结:轮廓增强在很多效果的应用都非常实用的,比如主角边缘发光效果、渲染一些云母生物,如下图所示,总之是一个非常实用的小技巧。

Unity&Shader基础篇—轮廓增强

原文参考资料来自

https://en.wikibooks.org/wiki/Cg_Programming/Unity/Silhouette_Enhancement