unity-shader 2D精灵图描边效果

前言

今天我们来实现一个简单的2D精灵图描边效果,效果图如下:

在这里插入图片描述

准备工作:

首先我们先打开unity新建一个场景,导入一个图片,并把该图片设置为Sprite类型,如图:

在这里插入图片描述   在这里插入图片描述

实现思路:

思路其实很简单,可以通过判断该像素周围是否有透明度为 0的值,如果有,则说明该像素位于边缘。

所以我们需要打开alpha blend,即: Blend SrcAlpha OneMinusSrcAlpha,并且加入渲染队列,

Tags{
     "Queue" = "Transparent"
}
Blend SrcAlpha OneMinusSrcAlpha

下面我们开始编写片元着色器,顶点着色器就是常规的顶点坐标变换,这里就不贴代码了,主要来看看片元着色器:

// ---------------------------【片元着色器】---------------------------
fixed4 frag (VertexOutput i) : SV_Target
{
    fixed4 col = tex2D(_MainTex, i.uv);
    // 采样周围4个点
    float2 up_uv = i.uv + float2(0,1) * _lineWidth * _MainTex_TexelSize.xy;
    float2 down_uv = i.uv + float2(0,-1) * _lineWidth * _MainTex_TexelSize.xy;
    float2 left_uv = i.uv + float2(-1,0) * _lineWidth * _MainTex_TexelSize.xy;
    float2 right_uv = i.uv + float2(1,0) * _lineWidth * _MainTex_TexelSize.xy;
    // 如果有一个点透明度为0 说明是边缘
    float w = tex2D(_MainTex,up_uv).a * tex2D(_MainTex,down_uv).a * tex2D(_MainTex,left_uv).a * tex2D(_MainTex,right_uv).a;

    if(w == 0){
       col.rgb = _lineColor;
    }
   
    return col;
}

_MainTex_TexelSize:贴图像素尺寸大小
我们可以通过该属性计算出周围(上下左右)的像素坐标,然后判断有没有透明度为0的值。
如果为0就返回边框颜色。

效果如下:

在这里插入图片描述

虽然我们实现了该功能,但是我们仔细观察会发现边缘锯齿很严重,不平滑。我们可以做下优化,利用插值来平滑过渡,

最终完整Shader代码如下:

// ---------------------------【2D 描边效果】---------------------------
// create by 长生但酒狂
Shader "lcl/shader2D/outline"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _lineWidth("lineWidth",Range(0,10)) = 1
        _lineColor("lineColor",Color)=(1,1,1,1)
    }
    // ---------------------------【子着色器】---------------------------
    SubShader
    {
        // 渲染队列采用 透明
        Tags{
            "Queue" = "Transparent"
        }
        Blend SrcAlpha OneMinusSrcAlpha
        
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            //顶点着色器输入结构体 
            struct VertexInput
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            //顶点着色器输出结构体 
            struct VertexOutput
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            // ---------------------------【顶点着色器】---------------------------
            VertexOutput vert (VertexInput v)
            {
                VertexOutput o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
            float _lineWidth;
            float4 _lineColor;

            // ---------------------------【片元着色器】---------------------------
            fixed4 frag (VertexOutput i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                // 采样周围4个点
                float2 up_uv = i.uv + float2(0,1) * _lineWidth * _MainTex_TexelSize.xy;
                float2 down_uv = i.uv + float2(0,-1) * _lineWidth * _MainTex_TexelSize.xy;
                float2 left_uv = i.uv + float2(-1,0) * _lineWidth * _MainTex_TexelSize.xy;
                float2 right_uv = i.uv + float2(1,0) * _lineWidth * _MainTex_TexelSize.xy;
                // 如果有一个点透明度为0 说明是边缘
                float w = tex2D(_MainTex,up_uv).a * tex2D(_MainTex,down_uv).a * tex2D(_MainTex,left_uv).a * tex2D(_MainTex,right_uv).a;

                // if(w == 0){
                    //    col.rgb = _lineColor;
                // }
                // 和原图做插值
                col.rgb = lerp(_lineColor,col.rgb,w);
                return col;
            }
            ENDCG
        }
    }
}

最终效果图对比:

在这里插入图片描述

最后

最后欢迎来我的GitHub Star,谢谢!
里面有我平时学习unity shader过程中实现的一些特效demo。

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 这是一个描边效果的简单示例,你可以在这里找到更多关于描边效果的信息:https://docs.unity3d.com/Manual/OutlineEffect.html ``` Shader "Outlined/Silhouette Only" { Properties { _Color ("Color", Color) = (1,1,1,1) _OutlineColor ("Outline Color", Color) = (0,0,0,1) _Outline ("Outline width", Range (0.0, 0.1)) = 0.003 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; half _Outline; fixed4 _Color; fixed4 _OutlineColor; void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Metallic and smoothness come from slider variables o.Metallic = 0; o.Smoothness = 0; o.Alpha = c.a; } ENDCG } SubShader { // Render the outline on top of everything Tags { "RenderType"="Transparent" "Queue"="Geometry+10" } LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; half _Outline; fixed4 _Color; fixed4 _OutlineColor; void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = _OutlineColor.rgb; o.Alpha = c.a; } ENDCG } } ``` ### 回答2: Unity中可以通过编写Shader代码来实现描边效果。下面是一个简单的Unity描边效果shader代码示例: ```csharp Shader "Custom/Outline" { Properties { _MainTex ("Texture", 2D) = "white" {} _OutlineColor ("Outline Color", Color) = (1,1,1,1) _OutlineWidth ("Outline Width", Range(0, 0.1)) = 0.01 } SubShader { Tags { "Queue" = "Transparent" } LOD 100 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 _OutlineColor; float _OutlineWidth; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } half4 frag (v2f i) : SV_Target { float2 uv = i.uv; // 在这里计算描边效果 float4 mainTexColor = tex2D(_MainTex, uv); float4 outlineColor = _OutlineColor; float4 finalColor = mainTexColor; float2 pixelUv = i.vertex.xy / i.vertex.w; float2 oPixelUv = float2(_ScreenParams.z, _ScreenParams.w) * 0.008 * _OutlineWidth; float4 outline = tex2D(_MainTex, pixelUv + float2(-oPixelUv.x, oPixelUv.y)) + tex2D(_MainTex, pixelUv + float2(oPixelUv.x, oPixelUv.y)) + tex2D(_MainTex, pixelUv + float2(-oPixelUv.x, -oPixelUv.y)) + tex2D(_MainTex, pixelUv + float2(oPixelUv.x, -oPixelUv.y)); if (length(mainTexColor - outlineColor) < 0.1) { finalColor = outline; } return finalColor; } ENDCG } } } ``` 这段Shader代码中,我们首先定义了两个属性:_MainTex是主纹理,_OutlineColor是描边颜色,_OutlineWidth是描边的宽度。然后在顶点和片段着色器中定义了相应的结构体,并通过顶点着色器将顶点坐标和纹理坐标传递给片段着色器。在片段着色器中,我们首先根据纹理坐标获取主纹理颜色,并计算出描边的颜色。然后,我们根据屏幕参数和描边宽度计算出描边效果,并判断是否需要将描边效果应用到最终的颜色中。最后返回最终的颜色。 ### 回答3: Unity中的描边效果可以通过编写一个自定义的shader来实现。以下是一个简单的示例代码: ```csharp Shader "Custom/Outline" { Properties{ _MainTex ("Texture", 2D) = "white" {} _Color ("Color", Color) = (1, 1, 1, 1) _OutlineColor ("Outline Color", Color) = (0, 0, 0, 1) _OutlineWidth ("Outline Width", Range(0, 0.1)) = 0.01 } SubShader { Tags { "RenderType" = "Opaque" } Cull Back 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; }; float _OutlineWidth; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 _Color; sampler2D _MainTex; float4 frag (v2f i) : SV_Target { float4 col = _Color * tex2D(_MainTex, i.uv); float4 outlineCol = _OutlineColor; // 计算描边区域 fixed2 ddx = ddx(i.uv); fixed2 ddy = ddy(i.uv); fixed3 grad = normalize(fixed3(ddx.x, ddy.x, 0)); float2 o = i.uv + grad.xy * _OutlineWidth; float4 outline = tex2D(_MainTex, o); // 混合描边颜色和原始颜色 col = lerp(col, outlineCol, outline.a); return col; } ENDCG } } } ``` 以上是一个简单的Unity描边效果shader示例代码。该shader使用_MainTex作为主要纹理输入,并通过参数_Color指定基本颜色,并通过参数_OutlineColor指定描边颜色,并通过参数_OutlineWidth指定描边宽度。描边的计算是通过计算当前像素和偏移像素的颜色进行混合来实现的。混合使用了tex2D函数来获取主纹理颜色和偏移像素颜色,并使用lerp函数混合描边颜色和原始颜色。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值