IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 【unity shader】用数字图像处理的知识写屏幕后处理效果(bloom、高斯模糊、反色、曝光、黑白图像、灰度图像) -> 正文阅读

[游戏开发]【unity shader】用数字图像处理的知识写屏幕后处理效果(bloom、高斯模糊、反色、曝光、黑白图像、灰度图像)

《关于我马上就要考研却还花时间自学数字图像处理这档事》
-写于2021年12月21日(摸鱼摸鱼)。。

计算机图形学、数字图像处理、计算机视觉(模式识别)
这三门学科各不相同,各有交叉
在这里插入图片描述
(图片转自知乎用户 孙佳明 的回答)

画图表示就长下面这样:
在这里插入图片描述
(三年美术经验,指上小学前学了一堆长大后全tm忘完了)

学了数字图像处理的我才终于意识到,原来冯女神的入门精要12章、13章讲的是数字图像处理的内容啊。怪说不得自学起来这么吃力原来我连地基都没搭就开始造房子,不塌才怪。

这里简单介绍下,unity将屏幕当成一张面片处理(别问我为什么这么处理,因为opengl是这么干的)。所以我们写的屏幕后处理效果往往更多应用的是图像处理而不是图形学的知识了。

OK,开始。
首先上模之屋把原神的胡桃和烟绯模型下载下来,用作本次示例。
接着用MMD4Mecanim这个牛逼的插件把模型转为FBX用在unity中,换上我自己写的卡通着色材质。
在这里插入图片描述

颜色处理

根据数字图像处理的内容,一幅RGB图像就是M×N×3大小的彩色像素数组,每个彩色像素点都是在特定空间位置的成彩色图像所对应的红绿蓝分量。RGB也可以视为三幅灰度图像形成的“堆叠”,当将他们送到彩色显示器的红绿蓝输入端时,会在屏幕上生成一副彩色图像。

反色(负片效果)
反色效果可以说是这次教程最简单的了,反色也可以叫补色,意思是某个颜色加上它的补色可以变成白色。而一般我们用的都是RGB色彩模型,1-图像的RGB值就可以得到图像的反色。

	    fixed4 frag (v2f i) : SV_Target
        {
            fixed4 col = tex2D(_MainTex, i.uv);
			//负片效果
			col = 1 - col;
			return col;
		}

在这里插入图片描述

灰度图
注意,一般如下面这张图在计算机图像邻域我们称之为灰度图像而不是黑白图像。黑白图像指的是完全只有黑白两种颜色的图像,但灰度图像在黑色与白色之间还有许多级的颜色深度,比如浅灰深灰这样的区分。
另外在单色图像中,灰度=亮度。但彩色图像则不能这么理解。
在这里插入图片描述
在图像识别邻域中,我们往往会将彩色图像转换为灰度图像后再做处理,因为梯度因素是最关键的,梯度意味着边缘,自然就用到灰度图像了。而颜色本身就容易受到光照等因素的影响,同类的物体颜色有很多变化。所以颜色本身难以提供关键信息。
(部分摘自知乎水哥的回答)

具体将彩色图像转换为一张灰度图像的流程其实就是将RGB三色通道转换为一个通道的过程。计算公式有很多:
1.浮点算法:Gray=R0.3+G0.59+B0.11
2.整数方法:Gray=(R
30+G59+B11)/100
3.移位方法:Gray =(R76+G151+B*28)>>8;
4.平均值法:Gray=(R+G+B)/3;
5.仅取绿色:Gray=G;
这里我就用冯乐乐入门精要里的计算公式:Gray=R * 0.2125 + G *0.7154 + B * 0.0721

fixed4 col = tex2D(_MainTex, i.uv);
//灰度图
fixed graycolor = col.r * 0.2125 + col.g *0.7154 + col.b * 0.0721;
return fixed4(graycolor,graycolor,graycolor,1.0);

黑白图
和灰度图做了区别后,我们自然知道黑白图就是只有颜色值为255和0的图像(即只有黑白)。
在这里插入图片描述
要想实现黑白图,可以设定一个阈值(我这里就设定0.5,也就是纯灰色),灰度值小于阈值的输出黑色,大于阈值的返回白色。
代码如下:

fixed4 frag (v2f i) : SV_Target
	{
    	fixed4 col = tex2D(_MainTex, i.uv);
				
		//黑白
		fixed graycolor = col.r * 0.2125 + col.g *0.7154 + col.b * 0.0721;
		//fixed4 finalColor = graycolor < 0.5 ? fixed4(0,0,0,1) : fixed4(1,1,1,1) ;
		fixed splitColor = saturate(sign(graycolor - 0.5));
		fixed4 finalColor = fixed4(splitColor,splitColor,splitColor,1);
		return finalColor;
	}

曝光处理
在这里插入图片描述
(图截自天津理工大学杨淑莹的数字图像处理课程)
在这里插入图片描述

fixed4 frag (v2f i) : SV_Target
{
		fixed4 col = tex2D(_MainTex, i.uv);
		//曝光
		if(col.r < 0.5){col.r = 1 - col.r;}
		if(col.g < 0.5){col.g = 1 - col.g;}
		if(col.b < 0.5){col.b = 1 - col.b;}
		return col;
}

(这个就不多赘述了,只是为了简单实现效果,真要写这么多if写在shader里我怕是要被同事追着打)

模糊

说到模糊效果,这个就要引入一下图像处理中的卷积概念。
我们把要处理的平面图像看做一个矩阵, 图像的每个像素分别对应着矩阵的元素, 假设屏幕的分辨率是 640*480, 那么对应的矩阵行数= 640, 列数= 480。
在数字图像处理中, 有一种处理方法叫滤波(分为线性滤波和非线性滤波),用于滤波计算的是滤波器(也叫卷积核), 滤波器通常是个方阵(即长宽相等的方形矩阵)。
进行滤波就是对于大矩阵中的每个像素, 计算它周围像素和滤波器矩阵对应位置元素的乘积, 然后把结果相加到一起, 最终得到的值就作为该像素的新值, 这样就完成了一次滤波。
尽可能简单地说,你用一个小方阵,从左到右从上到下的顺序遍历整个图像,每一次遍历都用方阵里的数值和方阵所覆盖图像范围内对应的像素值做计算。得到最后的值就是方阵中心位置对应的图像像素进行滤波后的值。
如果还是不太清楚,看看下面的图:

在这里插入图片描述
原图像某像素点值为1,经过卷积核加权运算后:-4 * 2 + 4 * 0 + (其他都是0*0我就不写了) = -8。以此得出经过某种滤波后该像素点值为-8。

在shader里构造一个滤波器
开头我们就提到了,unity底层是把屏幕当成一张面片处理,那我们就屏幕后处理材质来实现某些特效。
如下,在顶点着色器里构造了一个3x3矩阵。

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

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

            sampler2D _MainTex;
            float4 _MainTex_TexelSize;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv[0] = v.uv + _MainTex_TexelSize.xy * float2(-1,-1);
                o.uv[1] = v.uv + _MainTex_TexelSize.xy * float2( 0,-1);
                o.uv[2] = v.uv + _MainTex_TexelSize.xy * float2( 1,-1);
                o.uv[3] = v.uv + _MainTex_TexelSize.xy * float2(-1, 0);
                o.uv[4] = v.uv + _MainTex_TexelSize.xy * float2( 0, 0);
                o.uv[5] = v.uv + _MainTex_TexelSize.xy * float2( 1, 0);
                o.uv[6] = v.uv + _MainTex_TexelSize.xy * float2(-1, 1);
                o.uv[7] = v.uv + _MainTex_TexelSize.xy * float2( 0, 1);
                o.uv[8] = v.uv + _MainTex_TexelSize.xy * float2( 1, 1);
                return o;
            }

float2(-1,-1)对应的是滤波器左下角,float2(0,0)对应的是滤波器中心,以此类推。

均值模糊
一个3x3均值模糊卷积核如下:
(也就是矩阵一共有N个元素,则每个元素的权重值为1/N。)
请添加图片描述
(赶时间用画图画的,不要在意。。)
每一个元素对应的像素值乘以方阵的权重值,最后相加得到中心像素位置的值:

			fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = 0;
				for(int k = 0; k < 9 ;k++)
				{ col += tex2D(_MainTex,i.uv[k]); }
                return col * 0.1111;
            }

结果:
在这里插入图片描述
再加一个float类型的_Range变量控制采样范围。

o.uv[0] = v.uv + _MainTex_TexelSize.xy * float2(-1,-1) *_Range;
o.uv[1] = v.uv + _MainTex_TexelSize.xy * float2( 0,-1) *_Range;
o.uv[2] = v.uv + _MainTex_TexelSize.xy * float2( 1,-1) *_Range;
o.uv[3] = v.uv + _MainTex_TexelSize.xy * float2(-1, 0) *_Range;
o.uv[4] = v.uv + _MainTex_TexelSize.xy * float2( 0, 0) *_Range;
o.uv[5] = v.uv + _MainTex_TexelSize.xy * float2( 1, 0) *_Range;
o.uv[6] = v.uv + _MainTex_TexelSize.xy * float2(-1, 1) *_Range;
o.uv[7] = v.uv + _MainTex_TexelSize.xy * float2( 0, 1) *_Range;
o.uv[8] = v.uv + _MainTex_TexelSize.xy * float2( 1, 1) *_Range;

_Range = 2时:
在这里插入图片描述
高斯模糊
高斯模糊的效果和均值模糊差不多,高斯模糊的卷积核叫高斯核,它的权重值是由高斯方程计算出来的。
在这里插入图片描述
(图摘自百科)
高斯模糊相比较均值模糊的优势在于它能很好地模拟邻域内每个像素对当前处理像素的影响程度——距离越近的像素影响越大,越远的像素影响越小。
当然,实时渲染当中我们就没必要把高斯方程计算出来,那样太消耗资源了,我们可以直接把高斯核的计算结果拿来用。
如下图,假定σ=1.5,模糊半径为1,得到一个3x3的矩阵。
在这里插入图片描述
(图片转自https://blog.csdn.net/farmwang/article/details/74452750)

将片元着色器代码修改如下:

fixed4 fragBlur (v2f i) : SV_Target
            {
				//高斯模糊
				fixed4 sum = tex2D(_MainTex,i.uv[0]) * 0.147761;

				sum += 0.0947416 * (tex2D(_MainTex,i.uv[1]) + tex2D(_MainTex,i.uv[3])
				+ tex2D(_MainTex,i.uv[6]) + tex2D(_MainTex,i.uv[8]));
				
				sum += 0.118318 * (tex2D(_MainTex,i.uv[2]) + tex2D(_MainTex,i.uv[4])
				+ tex2D(_MainTex,i.uv[5]) + tex2D(_MainTex,i.uv[7]));
            
				return sum;
			}

结果如下(_Range = 1)
在这里插入图片描述

Bloom
bloom是一种用于模拟视觉看向强光所观察到的一种强光从明亮区域的边缘渗入到周围的效果。
可能我表达不够清楚,直接上图(上图为一般效果,下图为打开bloom)
在这里插入图片描述在这里插入图片描述
(图片转自霜狼_may大佬的文章:https://www.bilibili.com/read/cv1491209/)

bloom个人认为一定要会,只要加上这玩意你就能以较低的成本换来画面上较大的提升。
bloom效果步骤分为以下:
1、提取画面中过亮的部分(比如强光源)
2、将过亮的部分做模糊处理,成新的图像
3、将得到的新图像与原图像“啪”地粘在一起,完成!

提取较亮的部分:

			fixed GrayColor(fixed3 color)
			{
				//fixed gray = color.r * 0.299 + color.g * 0.587 + color.b * 0.114;
				fixed gray = color.r * 0.2125 + color.g * 0.7154 + color.b * 0.0721;
				return gray;
			}
			
			fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
				fixed gray = GrayColor(col.rgb);
				fixed3 finalColor = fixed3(gray,gray,gray);
				fixed c = clamp( gray - _Threshlod , 0.0,1.0);
				//fixed3 finalColor =tex2D(_MainTex, i.uv).rgb * c;
				return fixed4(col.rgb * c,1.0);
            }

灰度图的好处在这里就体现出来了,我们将彩色图像转换为灰度图像,可以直接避开彩色的干扰来提取图像中较亮的部分(也就是灰度值接近白色的部分),设定一个阈值,让图像灰度值减去它,大于0的部分就是我们要的。

接着将提取出来的图像做模糊处理,用上述的两种模糊都可以。

最后,我们让这两张图贴贴

			sampler2D _Tex;
			sampler2D _MainTex;

			v2f vert(a2v v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
				return o;
			}

			fixed4 frag(v2f i) : SV_TARGET0
			{
				fixed4 a = tex2D(_MainTex,i.uv);
				fixed4 b = tex2D(_Tex,i.uv);

				return a + b;
			}

ok,完成:
请添加图片描述

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-01-12 00:22:24  更:2022-01-12 00:24:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 18:26:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码