Shader之旅4:outline edge 描边效果

书写本文的初衷是为了自我反省记录。如有表达不当,请批评指正
在shadertoy上看到了物体的描边效果,感觉效果不错,拿来学习一下,先贴出代码,然后分析

float d;

float lookup(vec2 p, float dx, float dy)
{
    vec2 uv = (p.xy + vec2(dx * d, dy * d)) / iResolution.xy;
    vec4 c = texture(iChannel0, uv.xy);
	
	// return as luma
    return 0.2126*c.r + 0.7152*c.g + 0.0722*c.b;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    d = 1.5; // kernel offset
    vec2 p = fragCoord.xy;
    
	// simple sobel edge detection
    float gx = 0.0;
    gx += -1.0 * lookup(p, -1.0, -1.0);
    gx += -2.0 * lookup(p, -1.0,  0.0);
    gx += -1.0 * lookup(p, -1.0,  1.0);
    gx +=  1.0 * lookup(p,  1.0, -1.0);
    gx +=  2.0 * lookup(p,  1.0,  0.0);
    gx +=  1.0 * lookup(p,  1.0,  1.0);
    
    float gy = 0.0;
    gy += -1.0 * lookup(p, -1.0, -1.0);
    gy += -2.0 * lookup(p,  0.0, -1.0);
    gy += -1.0 * lookup(p,  1.0, -1.0);
    gy +=  1.0 * lookup(p, -1.0,  1.0);
    gy +=  2.0 * lookup(p,  0.0,  1.0);
    gy +=  1.0 * lookup(p,  1.0,  1.0);
    
	// hack: use g^2 to conceal noise in the video
    float g = gx*gx + gy*gy;
    float g2 = g ;
    
    vec4 col = texture(iChannel0, p / iResolution.xy);
    col += vec4(0.0, g, g2, 1.0);
    
    fragColor = col;
}

可以看到shader中构造了gx gy两个变量,
这两个变量是用来Sobel(一种边缘检测滤波)算法来计算检测边缘。
构造两个矩阵如下

可以看到,x y互为转置矩阵,在sobel算法中,gx变量的构造过程中,
gx += -1.0 * lookup(p, -1.0, -1.0);
gx += -2.0 * lookup(p, -1.0, 0.0);
gx += -1.0 * lookup(p, -1.0, 1.0);
gx += 1.0 * lookup(p, 1.0, -1.0);
gx += 2.0 * lookup(p, 1.0, 0.0);
gx += 1.0 * lookup(p, 1.0, 1.0);
第一排数字是x矩阵的列阵,而looup方法里的dx参数的(1,-1)的正负是根据第一排数字的正负决定的,dy是固定的(-1,0,1对应列阵的line 1 line2 line 3)
这样就能根据uv.xy创建不同的gx gy
大概原理如下

对于某一点vec2(x,y),对其边缘的8个位置进行检测

// Sobel masks (参考 http://en.wikipedia.org/wiki/Sobel_operator)
    //        1 0 -1      -1 -2 -1
    //    X=2 0 -2   Y= 0  0  0
    //        1 0 -1      1  2  1

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答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函数混合描边颜色和原始颜色。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值