Unity毛玻璃渲染模糊渲染Shader数学入门

高斯模糊 / 噪点 / 毛玻璃 / 雪花屏 等模糊采样的原理

Shader代码

C#调用代码在最后,最终效果图如下<blur>

cpp 复制代码
Shader "ZUI/UI_Blur"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        _BlurSize ("Blur Strength", Range(0, 20)) = 1  // 模糊强度,0=无模糊
        _BlurIteration ("Blur Iteration", Range(1,5)) = 2 // 模糊迭代次数,值越高越细腻
        
        _StencilComp ("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        _StencilOp ("Stencil Operation", Float) = 0
        _StencilWriteMask ("Stencil Write Mask", Float) = 255
        _StencilReadMask ("Stencil Read Mask", Float) = 255

        _ColorMask ("Color Mask", Float) = 15
    }

    SubShader
    {
        Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
            };

            sampler2D _MainTex;
            fixed4 _Color;
            float _BlurSize;
            int _BlurIteration;
            float4 _MainTex_ST;
            float4 _MainTex_TexelSize;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.vertex = UnityObjectToClipPos(IN.vertex);
                OUT.texcoord = TRANSFORM_TEX(IN.texcoord, _MainTex);
                OUT.color = IN.color * _Color;
                return OUT;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
                float2 uv = IN.texcoord;
                fixed4 col = fixed4(0,0,0,0);
                float2 blurStep = _MainTex_TexelSize * _BlurSize;
                int iteration = _BlurIteration;
                int sampleCount = 0;

                for(int x = -iteration; x <= iteration; x++)
                {
                    for(int y = -iteration; y <= iteration; y++)
                    {
                        float2 offset = float2(x, y) * blurStep;
                        col += tex2D(_MainTex, uv + offset) * IN.color;
                        sampleCount++;
                    }
                }
                col /= sampleCount;
                return col;
            }
            ENDCG
        }
    }
}

模糊处理的数学原理

模糊采样的理论核心属于 数字图像处理(Digital Image Processing) 中的 图像平滑(Image Smoothing) 分支,其本质是邻域滤波(Neighborhood Filtering) 技术,理论体系成熟

不好意思,我是没办法把这个公式和下面的实际图片对应起来

(已写代码实现,分别三个,上面只提供了一个 blur的代码)

blur

noise

grass

数学没学好,不知道 1/MN 是何意味

**参考:《计算机图形学原理及实践》(Computer Graphics: Principles and Practice)**作者:James D. Foley 等地位:计算机图形学的权威著作,被称为「图形学红宝书」。

相关内容:第 15 章「图像合成与处理」,讲解了如何将图像处理的滤波理论移植到计算机图形学的 Shader 实现中,明确了「纹理采样 + 邻域加权」是实时模糊的核心工程方案。

==========================

至少做过,或思考过一个实际公式,第一个突破口比较重要

1/MN 就是二维参数的意思

经常写代码的同学应该知道,我们只需要把 S,T 统一成一个参数: Step

1/MN 也大概率是 1/(M + N ) 也应该不是乘法(实际上真的是乘法)

因为 foreach(x, + foreach (y 就是乘法逻辑

最终采样逻辑(Grid),因为Step固定):

额外说下毛玻璃Noise 的图例

(Shader 代码没有给出,图片效果为如上文章前半有给出)

主要比blur 的 step

更多还是死记硬背吧,

参考:

这大哥,在讲解美术toon 调整的时候,眼睛会发光。。。。(完全,非数学)

Toon Shader - 怎么得到更好的效果_哔哩哔哩_bilibili

https://blog.csdn.net/wolf96/article/details/45419239

C#调用shader 代码

cs 复制代码
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

/// <summary>
/// 父节点统一控制所有子物体Image的模糊效果(Shader材质版)
/// 挂载:任意父节点GameObject
/// 功能:递归查找所有子Image,统一调节模糊强度、开关模糊
/// </summary>
public class ImageBlurGroup : MonoBehaviour
{
    public bool useValidate;
    [Header("==== 模糊控制参数 ====")]
    [Range(0, 10)] public float blurStrength = 2f; // 模糊强度,0=关闭模糊
    [Header("UI模糊材质球(拖拽赋值)")]
    public Material blurMaterial; // 拖拽刚才创建的Mat_UIBlur进来

    private List<Image> allTargetImages = new List<Image>(); // 所有待控制的Image集合
    private Dictionary<Image, Material> originMaterialDic = new Dictionary<Image, Material>(); // 记录每个Image的原始材质,用于关闭模糊时还原

    private void Awake()
    {
        // 初始化:递归查找父节点下所有的Image组件
        FindAllChildImages(transform);
    }

    private void Start()
    {
        // 初始设置一次模糊强度
        UpdateAllImageBlur();
    }
    //加了这个方法很方便,可以Runtime调;但好像Unity放在后台就会一直跑CPU,内存会一直爆炸
    private void OnValidate()
    {
        if(useValidate==false) return;
        // 在编辑器模式下,调节参数时实时生效(无需运行游戏)
        if (allTargetImages.Count > 0)
        {
            UpdateAllImageBlur();
        }
    }

    /// <summary>
    /// 递归查找指定节点下的所有Image组件(核心方法)
    /// </summary>
    private void FindAllChildImages(Transform parentTrans)
    {
        // 获取当前节点的Image组件
        Image img = parentTrans.GetComponent<Image>();
        if (img != null)
        {
            allTargetImages.Add(img);
            // 记录每个Image的原始材质,用于后续还原
            originMaterialDic[img] = img.material;
        }

        // 递归遍历所有子节点
        for (int i = 0; i < parentTrans.childCount; i++)
        {
            Transform childTrans = parentTrans.GetChild(i);
            FindAllChildImages(childTrans);
        }
    }

    /// <summary>
    /// 统一更新所有Image的模糊效果(核心方法)
    /// </summary>
    public void UpdateAllImageBlur()
    {
        if (blurMaterial == null)
        {
            Debug.LogWarning("请给模糊控制器赋值【模糊材质球】!");
            return;
        }

        foreach (var image in allTargetImages)
        {
            if (image == null) continue;

            // 模糊强度为0 → 关闭模糊,还原原始材质
            if (blurStrength <= 0)
            {
                if (originMaterialDic.ContainsKey(image))
                {
                    image.material = originMaterialDic[image];
                }
            }
            // 模糊强度>0 → 开启模糊,赋值模糊材质+设置强度
            else
            {
                image.material = blurMaterial;
                image.material.SetFloat("_BlurSize", blurStrength);
            }
        }
    }

    /// <summary>
    /// 外部调用:开启模糊(可传参设置强度)
    /// </summary>
    public void OpenBlur(float strength = 2f)
    {
        blurStrength = strength;
        UpdateAllImageBlur();
    }

    /// <summary>
    /// 外部调用:关闭模糊
    /// </summary>
    public void CloseBlur()
    {
        blurStrength = 0;
        UpdateAllImageBlur();
    }
}
相关推荐
微光守望者3 小时前
Unity小知识【1】:刚体(Rigidbody)与碰撞器(Collider)的区别,你真的清楚吗?
unity·游戏引擎
imbackneverdie7 小时前
如何通过读文献寻找科研思路?
人工智能·ai·自然语言处理·aigc·ai写作·ai读文献
June bug8 小时前
【配环境】unity项目开发环境
unity·游戏引擎
JQLvopkk8 小时前
C#调用Unity实现设备仿真开发浅述
开发语言·unity·c#
TOPGUS9 小时前
谷歌将移除部分搜索功能:面对AI时代的一次功能精简策略
前端·人工智能·搜索引擎·aigc·seo·数字营销
秦奈10 小时前
Unity复习学习笔记(九):UGUI
笔记·学习·unity
avi911110 小时前
简单的Gradio实现一个统计界面+日志输出
python·aigc·gradio
aitoolhub11 小时前
自媒体视觉物料高效创作新路径:稿定设计如何用AI重构内容生产逻辑
大数据·人工智能·aigc·媒体
垂葛酒肝汤11 小时前
unity的背包滑动组件中道具的提示框被裁剪的问题
unity·游戏引擎