经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 软件/图像 » unity » 查看文章
【Unity3D】高斯模糊特效
来源:cnblogs  作者:little_fat_sheep  时间:2023/8/11 8:36:25  对本文有异议

1 高斯模糊原理

? 边缘检测特效中使用了卷积运算进行了边缘检测,本文实现的高斯模糊特效同样使用了卷积运算,关于卷积核和卷积运算的概念,读者可以参考边缘检测特效

? 本文完整资源见→Unity3D高斯模糊特效

? 我们将用于模糊处理的卷积核称为模糊算子,它一般满足以下条件:

  • 卷积核中权值上下对称、左右对称;
  • 卷积核中每个权值大于或等于 0,小于 1;
  • 卷积核中所有权值之和为 1。

? 我们将所有权值都为 1 / n(n 为权值个数)的卷积核称为平均模糊算子;将权值随位置变化且符合高斯分布(或正太分布)的卷积核称为高斯模糊算子(或高斯核),它较好地模拟了邻域中每个像素对当前处理像素的影响程度(距离越近,影响越大)。高斯方程如下:

img

? σ 是标准差,其值越大,高斯分布函数的图像越矮胖,一般取值为 1,x、y 为当前位置到卷积核中心的整数距离。要构建一个高斯核,我们只需要计算高斯核中各个位置对应的高斯值。为了保证模糊处理后的图像不会变暗,我们需要对高斯核中的元素进行归一化,即将所有元素都除以它们的权值和,从而保证归一化后的权值和为 1。因此,高斯函数中 e 前面的系数不会对高斯核产生任何影响,在计算高斯核的过程中可以省去。

? 高斯核的维数越高,模糊程度越大。使用一个 n * n 的卷积核,需要进行 n * n * w * h 次纹理采样(w、h 分别为图像的宽高),为节省性能,我们将二维高斯核拆分为 2 个一维高斯核,采样次数只需要 2 * n * w * h 次。进一步观察到,2 个高斯核中包含了很多重复权重。对于一个大小为 5 的一维高斯核,实际只需记录 3 个权值即可。

img

2 代码实现

? GaussianBlur.cs

  1. using UnityEngine;
  2. [ExecuteInEditMode] // 编辑态可以查看脚本运行效果
  3. [RequireComponent(typeof(Camera))] // 需要相机组件
  4. public class GaussianBlur : MonoBehaviour {
  5. [Range(0, 4)]
  6. public int iterations = 3; // 高斯模糊迭代次数
  7. [Range(0.2f, 3.0f)]
  8. public float blurSpread = 0.6f; // 每次迭代纹理坐标偏移的速度
  9. [Range(1, 8)]
  10. public int downSample = 2; // 降采样比率
  11. private Material material = null; // 材质
  12. private void Start() {
  13. material = new Material(Shader.Find("MyShader/GaussianBlur"));
  14. material.hideFlags = HideFlags.DontSave;
  15. }
  16. void OnRenderImage(RenderTexture src, RenderTexture dest) {
  17. if (material != null) {
  18. int rtW = src.width / downSample; // 降采样的纹理宽度
  19. int rtH = src.height / downSample; // 降采样的纹理高度
  20. RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
  21. buffer0.filterMode = FilterMode.Bilinear; // 滤波模式设置为双线性
  22. Graphics.Blit(src, buffer0);
  23. for (int i = 0; i < iterations; i++) {
  24. material.SetFloat("_BlurSize", 1.0f + i * blurSpread); // 设置模糊尺寸(纹理坐标的偏移量)
  25. RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
  26. Graphics.Blit(buffer0, buffer1, material, 0); // 渲染垂直的Pass
  27. RenderTexture.ReleaseTemporary(buffer0);
  28. buffer0 = buffer1;
  29. buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
  30. Graphics.Blit(buffer0, buffer1, material, 1); // 渲染水平的Pass
  31. RenderTexture.ReleaseTemporary(buffer0);
  32. buffer0 = buffer1;
  33. }
  34. Graphics.Blit(buffer0, dest);
  35. RenderTexture.ReleaseTemporary(buffer0);
  36. } else {
  37. Graphics.Blit(src, dest);
  38. }
  39. }
  40. }

? GaussianBlur.shader

  1. Shader "MyShader/GaussianBlur" { // 高斯模糊
  2. Properties {
  3. _MainTex ("Base (RGB)", 2D) = "white" {} // 主纹理
  4. _BlurSize ("Blur Size", Float) = 1.0 // 模糊尺寸(纹理坐标的偏移量)
  5. }
  6. SubShader {
  7. CGINCLUDE
  8. #include "UnityCG.cginc"
  9. sampler2D _MainTex; // 主纹理
  10. half4 _MainTex_TexelSize; // _MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)
  11. float _BlurSize; // 模糊尺寸(纹理坐标的偏移量)
  12. struct v2f {
  13. float4 pos : SV_POSITION; // 模型空间顶点坐标
  14. half2 uv[5]: TEXCOORD0; // 5个邻域的纹理坐标
  15. };
  16. v2f vertBlurVertical(appdata_img v) { // 垂直模糊顶点着色器
  17. v2f o;
  18. o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
  19. half2 uv = v.texcoord;
  20. o.uv[0] = uv;
  21. o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
  22. o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
  23. o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
  24. o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
  25. return o;
  26. }
  27. v2f vertBlurHorizontal(appdata_img v) { // 水平模糊顶点着色器
  28. v2f o;
  29. o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
  30. half2 uv = v.texcoord;
  31. o.uv[0] = uv;
  32. o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
  33. o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
  34. o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
  35. o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
  36. return o;
  37. }
  38. fixed4 fragBlur(v2f i) : SV_Target {
  39. float weight[3] = {0.4026, 0.2442, 0.0545}; // 大小为5的一维高斯核,实际只需记录3个权值
  40. fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
  41. for (int j = 1; j < 3; j++) {
  42. sum += tex2D(_MainTex, i.uv[j * 2 - 1]).rgb * weight[j]; // 中心右侧或下侧的纹理*权值
  43. sum += tex2D(_MainTex, i.uv[j * 2]).rgb * weight[j]; // 中心左侧或上侧的纹理*权值
  44. }
  45. return fixed4(sum, 1.0);
  46. }
  47. ENDCG
  48. ZTest Always Cull Off ZWrite Off
  49. Pass {
  50. NAME "GAUSSIAN_BLUR_VERTICAL"
  51. CGPROGRAM
  52. #pragma vertex vertBlurVertical
  53. #pragma fragment fragBlur
  54. ENDCG
  55. }
  56. Pass {
  57. NAME "GAUSSIAN_BLUR_HORIZONTAL"
  58. CGPROGRAM
  59. #pragma vertex vertBlurHorizontal
  60. #pragma fragment fragBlur
  61. ENDCG
  62. }
  63. }
  64. FallBack "Diffuse"
  65. }

3 运行效果

? 1)原图

img

? 2)模糊处理

? 调整模糊迭代次数 iterations 由 0 ~ 4 变化,效果如下:

img

? 声明:本文转自【Unity3D】高斯模糊特效

原文链接:https://www.cnblogs.com/zhyan8/p/17615598.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号