一锥债、屏幕后處理原理
我的另外一篇文章有詳細(xì)的介紹:
UnityShader-屏幕后處理(調(diào)節(jié)亮度陡蝇,飽和度,對比度)
二赞弥、卷積與卷積核
卷積核通常是一個(gè)四方形網(wǎng)格結(jié)構(gòu)(例如2x2毅整,3x3的方形區(qū)域),該方形區(qū)域每個(gè)方格都有一個(gè)權(quán)重值绽左。當(dāng)對圖像中的某個(gè)像素進(jìn)行卷積時(shí)悼嫉,我們會(huì)把卷積核的中心位置放在該像素上,翻轉(zhuǎn)核之后再依次計(jì)算核中每個(gè)元素和其覆蓋的圖形像素值的乘積并求和拼窥,得到的結(jié)果就是該位置的像素值戏蔑。
卷積操作指的是使用一個(gè)卷積核對圖像中的每個(gè)像素進(jìn)行一系列的操作。
卷積核.png
三鲁纠、邊緣檢測原理
(1)怎么確定形成了一條邊呢总棵?
如果相鄰的像素之間存在差別明顯的顏色、亮度改含、紋理等屬性情龄,我們就會(huì)認(rèn)為他們之間應(yīng)該是有一條邊界的,這種相鄰像素之間的差值可以用梯度來表示。
(2)常見的的幾種邊緣檢測的算子
常見的邊緣檢測算子.png
在本例子中骤视,我們使用Sobel算子鞍爱。
(3)梯度計(jì)算公式
通常包括兩個(gè)方向的卷積核,一個(gè)是水平方向专酗,一個(gè)是豎直方向睹逃,我們需要對每個(gè)像素進(jìn)行一個(gè)卷積計(jì)算,得到兩個(gè)方向的梯度值Gx和Gy祷肯。整體公式:(4)代碼實(shí)現(xiàn)
C#代碼:
using UnityEngine;
using System.Collections;
public class EdgeDetection : PostEffectsBase {
public Shader edgeDetectShader;
private Material edgeDetectMaterial = null;
public Material material {
get {
edgeDetectMaterial = CheckShaderAndCreateMaterial(edgeDetectShader, edgeDetectMaterial);
return edgeDetectMaterial;
}
}
[Range(0.0f, 1.0f)]
public float edgesOnly = 0.0f;
public Color edgeColor = Color.black;
public Color backgroundColor = Color.white;
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
material.SetFloat("_EdgeOnly", edgesOnly);
material.SetColor("_EdgeColor", edgeColor);
material.SetColor("_BackgroundColor", backgroundColor);
Graphics.Blit(src, dest, material);
} else {
Graphics.Blit(src, dest);
}
}
}
shader代碼:
Shader "Unity Shaders Book/Chapter 12/Edge Detection" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_EdgeOnly ("Edge Only", Float) = 1.0
_EdgeColor ("Edge Color", Color) = (0, 0, 0, 1)
_BackgroundColor ("Background Color", Color) = (1, 1, 1, 1)
}
SubShader {
Pass {
//設(shè)置渲染狀態(tài)
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment fragSobel
sampler2D _MainTex;
//_MainTex_TexelSize:xxx_TexelSize是unity為我們提供的訪問xxx紋理對應(yīng)每個(gè)紋素大小的變量。
//例如佑笋,一張512x512大小的紋理翼闹,該值大約為0.001953的(即1/512)
uniform half4 _MainTex_TexelSize;
fixed _EdgeOnly;
fixed4 _EdgeColor;
fixed4 _BackgroundColor;
struct v2f {
float4 pos : SV_POSITION;
half2 uv[9] : TEXCOORD0;
};
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
//通過_MainTex_TexelSize.xy值,計(jì)算需要計(jì)算的矩陣的UV值
o.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1, -1);
o.uv[1] = uv + _MainTex_TexelSize.xy * half2(0, -1);
o.uv[2] = uv + _MainTex_TexelSize.xy * half2(1, -1);
o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1, 0);
o.uv[4] = uv + _MainTex_TexelSize.xy * half2(0, 0);
o.uv[5] = uv + _MainTex_TexelSize.xy * half2(1, 0);
o.uv[6] = uv + _MainTex_TexelSize.xy * half2(-1, 1);
o.uv[7] = uv + _MainTex_TexelSize.xy * half2(0, 1);
o.uv[8] = uv + _MainTex_TexelSize.xy * half2(1, 1);
return o;
}
//計(jì)算亮度值
fixed luminance(fixed4 color) {
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
//通過Sobe算子算出這個(gè)像素點(diǎn)與周圍的梯度值
half Sobel(v2f i) {
//Sobe算子矩陣允青,分為X與Y兩個(gè)方向
const half Gx[9] = {-1, 0, 1,
-2, 0, 2,
-1, 0, 1};
const half Gy[9] = {-1, -2, -1,
0, 0, 0,
1, 2, 1};
half texColor;
half edgeX = 0;
half edgeY = 0;
//累加周圍采樣點(diǎn)的梯度值
for (int it = 0; it < 9; it++) {
texColor = luminance(tex2D(_MainTex, i.uv[it]));
edgeX += texColor * Gx[it];
edgeY += texColor * Gy[it];
}
//用絕對值代替開根號(hào)
half edge = 1 - abs(edgeX) - abs(edgeY);
return edge;
}
fixed4 fragSobel(v2f i) : SV_Target {
half edge = Sobel(i);
//根據(jù)梯度值橄碾,將原顏色與背景顏色卵沉、描邊顏色進(jìn)行插值運(yùn)算颠锉,得到最終的顏色值
fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv[4]), edge);
fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);
return lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly);
}
ENDCG
}
}
FallBack Off
}