自言自语
学习时根据乐乐女神书中教学走了一遍。记忆不是很深刻理解也不是很好 3D美术在TA路上爬行真的很艰难
复习的时候再来一遍 标记各个我个人需要关注的点
还是先上效果 图文并茂加深记忆
效果
设置面板
检测效果图 与原图混合
检测效果图不与原图混合
自己尝试加入一个变量想扩展边缘宽度 失败 结果意外形成了另外两种效果
C#部分
基类就不贴出来了 就是抄的 自己没绕明白各种IF 绕明白后再注释贴出来
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class EdgeDetectedSobelWang : PostProcessingWangBase
{
[Range(0, 1f)]
public float edgeOnly = 1f;
public float edgeContrast = 1f;
public Color backgroundColor = Color.white;
public Color edgeColor = Color.black;
public Shader mshader;
private Material mmaterial;
public Material Omaterial
{
get
{
mmaterial = CheckShaderAndMaterial(mshader, mmaterial);
return mmaterial;
}
}
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if(Omaterial!=null)
{
//对shader中开放的参数进行动态设置
Omaterial.SetFloat("_edgeOnly", edgeOnly);
Omaterial.SetFloat("_edgeContrast", edgeContrast);
Omaterial.SetColor("_backgroundColor", backgroundColor);
Omaterial.SetColor("_edgeColor", edgeColor);
//再利用OnRenderImage.Graphics.Blit 将使用该shader的材质球作用于原图像再存与目标图像中
Graphics.Blit(source, destination, Omaterial);
}
else
{
Graphics.Blit(source, destination);
}
}
}
Shader部分
Shader "TNShaderPractise/ShaderPractise_EdgeDetectionSobel_031"
{
Properties
{
//主贴图经测试不能不再属性块中声明,否则获取的缓存贴图无法存入其中
_MainTex ("Albedo (RGB)", 2D) = "white" {}
///其他属性可以不再属性块中声明 只要在CGPROGRAM块中定义了即可访问到
//_edgeOnly ("EdgeOnly", Range(0,1)) = 1
//_backgroundColor ("BackGroundColor", Color) = (1,1,1,1)
//_edgeColor ("EdgeColor", Color) = (0,0,0,1)
}
SubShader
{
//屏幕后期 需要在最后渲染 渲染顺序定义为 Transparent
Tags { "RenderType"="Opaque" "Queue" ="Transparent" }
Pass
{
//同样后期效果 需要关闭深度写入 开启深度测试 并关闭Cull
ZWrite Off ZTest Always Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
//这里需要使用到 TexelSize 来进行图素采样计算
float2 _MainTex_TexelSize;
float _edgeOnly;
fixed4 _backgroundColor;
fixed4 _edgeColor;
float _edgeContrast;
struct a2v
{
float4 vertex : POSITION;
float4 uv : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
//声明一个uv数组
float2 uv[9] : TEXCOORD1;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
float2 temuv = v.uv;
//注意不要写错顺序了
//图素采样用乘法 即可 其采样范围 由算子NXN决定 Sobel算子此处采用3X3的算子
//因此采样坐标为以图片像素点为中心点 即坐标0,0 间隔单位为1的九宫格坐标分布 如下排列
// Sobel 3X3
//-1,1 0,1 1,1
//-1,0 0,0 1,0
//-1,-1 0,-1 1,-1
//因为OPENGL的算法坐标是从左下角开始计算则 o.uv[0] 对应的图素位置应该是从 -1,-1 开始
o.uv[0] = temuv + _MainTex_TexelSize.xy*half2(-1,-1);
o.uv[1] = temuv + _MainTex_TexelSize.xy*half2(0,-1);
o.uv[2] = temuv + _MainTex_TexelSize.xy*half2(1,-1);
o.uv[3] = temuv + _MainTex_TexelSize.xy*half2(-1,0);
o.uv[4] = temuv + _MainTex_TexelSize.xy*half2(0,0); // 等同于 o.uv[4]=v.uv; 即原始像素所在坐标点
o.uv[5] = temuv + _MainTex_TexelSize.xy*half2(1,0);
o.uv[6] = temuv + _MainTex_TexelSize.xy*half2(-1,1);
o.uv[7] = temuv + _MainTex_TexelSize.xy*half2(0,1);
o.uv[8] = temuv + _MainTex_TexelSize.xy*half2(1,1);
return o;
}
//去色算法传入一个颜色值 返回一个 去色过后的颜色. 忽略掉Alpha
fixed3 luminance (fixed4 color)
{
//去色系数 0.2125 r 0.7145 g 0.0721b 这个可以记下来
fixed temp =0.2125*color.r+0.7145*color.g+0.0721*color.b;
return fixed3 (temp,temp,temp);
}
//声明一个sobel的卷积核去计算梯度和边界 (这点冯乐乐女神书中关于sobel算子卷积计算理解有点问题 在其勘误贴中有和网友讨论得出过正确结论 但是我没看懂)
float Sobel (v2f i)
{
//需要把sobel算子 翻转一下。。。。 (冯乐乐原本理解的 卷积计算需要翻转下卷积核来进行计算)
// 原本sobel卷积核 X Y方向的分布排列如下
//X
// -1, -2, -1,
// 0, 0, 0,
// 1, 2, 1
//Y
// -1, 0, 1,
// -2, 0, 2,
// -1, 0, 1
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};
//虽然冯乐乐女神理解错了, 但最后计算却巧合的对了.
//声明一个存放去色像素的变量
half3 texcol;
//声明梯度分量X Y
half edgeX=0;
half edgeY=0;
//用for循环减少代码数量 循环9次计算累加 完成卷积计算
for (int it=0;it<9;it++)
{
//得到一个去色后的图素
texcol = luminance(tex2D(_MainTex,i.uv[it]));
//该图素与对应X Y 方向的Sobel算子进行卷积操作 即 相乘之后累加
edgeX += texcol * Gx[it]*_edgeContrast; //乘以个系数 增加描边的权重和对比度 想增加描边宽度的 没成功 思考失败了再找找方案
edgeY += texcol * Gy[it]*_edgeContrast;
}
// 将其绝对值进行相加得到该图素的梯度值 G = |Gx| + |Gy| 本来是要平方相加开方,但这样运算量大 改为直接用绝对值相加
float edge = 1-(abs(edgeX)+abs(edgeY)); // 不知道这里为啥要用1去减。书上写这样得到的edge越小 越有可能是边界
return edge;
}
fixed4 frag (v2f i ):SV_Target
{
float edge = Sobel(i);
//让描边和图像颜色有关联 即正片叠底计算
_edgeColor *= tex2D(_MainTex,i.uv[4]);
fixed4 withTexture = lerp (_edgeColor,tex2D(_MainTex,i.uv[4]),edge); // 【4】正好是算子中心点即原本贴图像素的坐标点
fixed4 onlyEdge = lerp (_edgeColor,_backgroundColor,edge);
fixed4 finalCol = lerp(withTexture,onlyEdge,_edgeOnly);
return finalCol;
}
ENDCG
}
}
FallBack Off
}
关于卷积计算的讨论
乐乐女神书中对于卷积计算的操作是要先翻转卷积核 这个当时没理解为啥要翻转 当然我也不明白卷积计算具体该怎么理解
后来查看了勘误,发现了乐乐女神认为自己当时书中的理解是有误的 来源于她和一个网友的讨论 讨论卷积计算翻转不翻转的问题。神仙聊天,我也看不懂,至少目前看不懂。
因此标记在此,日后继续去悟!!
https://github.com/candycat1992/Unity_Shaders_Book/issues/24