本文要实现的内容是一个很常见的描边效果。

外边缘描边发光Shader
灵感来自《Real Time Rendering》的相关章节,即基于几何生成方法的描边。相关的理论内容想必大家都已经看过好多次了,这里简略说明下绘制模型时用到的两个pass,第一遍正常绘制模型;第二遍绘制则要将模型正面剔除——正面剔除的原因我会在下方给大家展示一下——接着在vs中修改顶点位置,将顶点沿着法线方向膨胀一定距离,最后在fs中将模型用纯色输出即可。

外边缘描边发光Shader

现在就可以把这个效果展示出来了。虽然写两个pass,但是可以先实现一个pass,把模型正常的绘制出来即可。
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 第一个pass用来渲染正常的模型
    Pass
    {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
 
        #include "UnityCG.cginc"
 
        struct appdata
        {
            float4 vertex : POSITION;
            float2 uv : TEXCOORD0;
        };
 
        struct v2f
        {
            float2 uv : TEXCOORD0;
            float4 vertex : SV_POSITION;
        };
 
        sampler2D _MainTex;
        float4 _MainTex_ST;
 
        v2f vert (appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            o.uv = TRANSFORM_TEX(v.uv, _MainTex);
            return o;
        }
 
        fixed4 frag (v2f i) : SV_Target
        {
            fixed4 col = tex2D(_MainTex, i.uv);
            return col;
        }
        ENDCG
实现了一次绘制,所以模型正常的出来了。

外边缘描边发光Shader

下面我们进行第二个pass。
因为这次需要使用法线的信息,所以我们可以直接使用Unity内建的appdata_base作为vs的输入,它包含了顶点的法线信息。而由于这次vs和fs之间并没有数据的传递,因此vs只需要输出位置到SV_POSITION,所以我们fs只需要输出纯色到SV_Target即可。

[AppleScript] 纯文本查看 复制代码

?

 
1
2
3
4
5
6
7
float4 vert(appdata_base v) : SV_POSITION
 {
     ...
 }
 
 fixed4 frag() : SV_Target {
     return _OutlineColor;
 }

大家要注意的是,在vs中我们不能直接使用在model空间的法线信息,所以还要将顶点的法线信息从model空间转换到clip空间才可以。?


float3 normal = mul((float3x3) UNITY_MATRIX_MVP, v.normal);

然后将顶点沿着法线方向膨胀一定距离:


?


pos.xy += _OutlineFactor * normal.xy;

这个效果已经开始展现了


外边缘描边发光Shader

然后我们来说说为什么要打开正面剔除,因为没有正面剔除会出现颜色错误的模型现象


就像这样:
外边缘描边发光Shader


完整的shader实现脚本

Shader "chenjd/OutlineShader"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_OutlineFactor("Outline Factor", Range(0, 6)) = 3
_OutlineColor("Outline Color", Color) = (0, 0, 0, 1)
}

SubShader
{
Tags{ "RenderType" = "Opaque" }
LOD 100

// 第一个pass用来渲染正常的模型
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};

struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};

sampler2D _MainTex;
float4 _MainTex_ST;

//拿到模型顶点信息
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}

fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
}

//第二个pass渲染轮廓
Pass
{
Cull Front

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

float _OutlineFactor;
fixed4 _OutlineColor;

float4 vert(appdata_base v) : SV_POSITION
{
float4 pos = UnityObjectToClipPos(v.vertex);
float3 normal = mul((float3x3) UNITY_MATRIX_MVP, v.normal);
pos.xy += _OutlineFactor * normal.xy;
return pos;
}

fixed4 frag() : SV_Target

{
return _OutlineColor;
}
ENDCG
}
}
}


相关文章:

  • 2021-07-20
  • 2022-12-23
  • 2022-01-23
  • 2021-04-20
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-12-24
  • 2021-12-29
  • 2022-12-23
  • 2021-07-07
  • 2022-12-23
  • 2021-11-01
相关资源
相似解决方案