七彩极 发表于 2022-11-20 10:14

Unity3D后期Shader特效-马赛克1-基础(UV马赛克化)

=========源文件下载群========
QQ群:818511955
=========================
最近在知乎上看到很多聊unity后期特效的文章。
自己研究了一下,做出了很多效果。
今天开一个新坑,做个系列教程。
欢迎大家,观看和讨论。
=======下面进入正文=======
先看最终效果


镜头特效的作用流程是:
在摄像机上挂载一个引入Shader的C#程序。

=====C#程序中渲染特效结构====
OnRenderImage(渲染输入,渲染输出)
{
Graphics.Blit(渲染输入,渲染输出,特效材质球);
}
==============

使用Shader驱动GPU渲染输出。



首先要在摄像机上添加一个C#程序。



在启动程序中,我预先确定用来处理图像的Shader名称。



在 OnRenderImage()渲染处理是系统默认函数。
使用Graphics.Blit()引入Shader和渲染图像。



C#中预先定义好将要使用的“马赛克尺寸”变量。
使用Material.SetInt()传入材质的“_TileSize”变量



CRLuo_CameraFX_Mosaic_Base.cs 代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//摄像机特效(马赛克)C#
public class CRLuo_CameraFX_Mosaic_Base : MonoBehaviour
{
    //特效Shader变量
    private Shader shader;
    //特效材质球变量
    private Material material;


    //单块马赛克尺寸
   
    public int tileSize = 10;



    //程序启动处理部分
    void Start()
    {
      //依据材质球路径和名称获取shader
      shader = Shader.Find("CRLuo/CameraFXShader_Mosaic_Base");

      //依据Shader创建材质球
      material = new Material(shader);
    }


    //渲染处理部分
    void OnRenderImage(RenderTexture InImg, RenderTexture OutImg)
    {
      //片段是否成功获取Shader
      if (shader != null)
      {
            //给特效材质球传递马赛克块儿尺寸参数
            material.SetInt("_TileSize", tileSize);

            //渲染(输入渲染图像,输出渲染图像,使用材质球)
            //输入渲染图像自动传递给材质球的_MainTex属性。
            Graphics.Blit(InImg, OutImg, material);
      }
      else
      {
            //直接输出渲染图像
            Graphics.Blit(InImg, OutImg);
      }

    }

}


======CameraFXShader_Mosaic_Base.shader
源代码
//材质的路径与名称
Shader "CRLuo/CameraFXShader_Mosaic_Base"
{
        //材质的变量定义
    Properties
    {
      _MainTex ("需要处理的图像", 2D) = "white" {}
                _TileSize("块尺寸", Range(0, 100)) = 10
    }

        //子着色器
    SubShader
    {
                //渲染队列顺序
      Tags { "RenderType"="Opaque" }
      LOD 100

      Pass
      {

                        //引入程序使用的函数集
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

                        //获取场景模型数据
            struct appdata
            {
                                //获取模型顶点数据
                float4 vertex : POSITION;
                                //获取模型UV数据
                float2 uv : TEXCOORD0;
            };


                        //定义顶点片段着色器数据类型
            struct v2f
            {
                                //UV数据
                float2 uv : TEXCOORD0;
                                //顶点数据
                float4 vertex : SV_POSITION;
            };



                        //载入材质球主贴图
            sampler2D _MainTex;
                        //获取贴图的偏移与重复数据
                        float4 _MainTex_ST;
                        //载入马赛克尺寸(正整形数据)
                        uniform int _TileSize;



                        //顶点片段着色器程序
            v2f vert (appdata v)
            {
                                //定义输出数据
                v2f o;
                                //顶点转换为摄像机视角坐标
                o.vertex = UnityObjectToClipPos(v.vertex);
                                //应用贴图的偏移与重复
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                                //输出模型结果
                return o;
            }


                        //表面着色器程序
            fixed4 frag (v2f i) : SV_Target
            {
                                //获取马赛克块数量 = 屏幕像素个数/块的像素个数
                                float2 TileSum = _ScreenParams / _TileSize;


                                //整理UV,让一个马赛克块儿中的UV统一为一个数据
                                float2 uv_Mosaic = ceil(i.uv *TileSum)/ TileSum;

                                //把整理后的UV结果赋给渲染图像,获得马赛克画面。
                                fixed4 col_Mosaic = tex2D(_MainTex, uv_Mosaic);


                                //输出图像 = 马赛克图像;
                                fixed4 col = col_Mosaic;

                                //输出贴图结果
                return col;
            }
            ENDCG
      }
    }
}

自定义材质球名称与参数,注意与上面CS文件中使用的材质路径,名称与参数一致。



参数中我们定义了 _MainTex 主贴图(名称一定不要改),用来接收渲染图片。
使用_TileSize来定义马赛克尺寸(像素个数)。(一个数据是方形的,可以定义两个数据来保存长/宽)
并且在Pass(渲染片段)程序中正确的载入变量。



最后就是核心的,效果实现部分:



_ScreenParams是系统默认保存屏幕尺寸的二维变量
屏幕尺寸/马赛克像素尺寸=屏幕的横竖需要X,Y块马赛克
二维数据除以一维数据,相当于每个二维数据被除(X,Y)/A=(X/A,Y/A)
//获取马赛克块数量 = 屏幕像素个数/块的像素个数
float2 TileSum = _ScreenParams / _TileSize;



===========================
要想看懂接下来的内容,
我们需要知道,到底在对什么操作。


UV的0~1与贴图的0~1一一对应,如果我们使用程序重新定义一个UV坐标,然后就可以通过UV与贴图一一对应的关系,得到UV修改后的贴图。
=============================
原始i.UV的范围就是0~1,与输出图像一一对应。



使用 i.UV(原始UV数据)*块数,使得UV从0~1,放大到0~X。



然后使用,ceil()向上取整函数得到 1,2,3,4,5~X的UV分布。



结果除以块数,把UV约束回0~1范围,这时每块儿中UV数据相同。



//整理UV,让一个马赛克块儿中的UV统一为一个数据
float2 uv_Mosaic = ceil(i.uv *TileSum)/ TileSum;

随后应用UV数据给渲染图像
//把整理后的UV结果赋给渲染图像,获得马赛克画面。
fixed4 col_Mosaic = tex2D(_MainTex, uv_Mosaic);



输出图像结果
//输出图像 = 马赛克图像;
fixed4 col = col_Mosaic;

//输出贴图结果
return col;

=====本节完======
谢谢观看。

NoiseFloor 发表于 2022-11-20 10:18

强啊

Zephus 发表于 2022-11-20 10:21

写的好详细

七彩极 发表于 2022-11-20 10:26

这是我见过写的最详细的教程
页: [1]
查看完整版本: Unity3D后期Shader特效-马赛克1-基础(UV马赛克化)