大家好,我是阿赵。
  之前在玩怒之铁拳4里面,看到了马赛克场景转换的效果,觉得很有趣,于是也来做一下。

一、2D版本的马赛克转场效果

  先看看视频效果:

马赛克转场

  这里我是直接写shader实现的,我这里是把shader直接挂在了面片网格上面。当然如果是需要挂在UGUI的Image上面,也是同理,只需要修改一下shader适应UI的裁剪就行,重点关注一下shader本身,这是我在ASE里面的连线:
Unity制作马赛克效果-LMLPHP

  从原理上看就很简单了,分别计算一个正常的UV和一个马赛克的UV,把它们叠加起码成为一个UV来采样图片而已。
  那么重点就是这个马赛克的UV是要怎样计算了:
Unity制作马赛克效果-LMLPHP

  关键的代码其实很简单,就是把原来的UV乘以一个mosaic值,然后再取整,最后再除以mosaic值,让在某个区域内的所有UV值变成采样同一个点的颜色而已。
Unity制作马赛克效果-LMLPHP

  这就是最核心的代码了。至于为什么我要在这个基础上,又加又乘。是因为如果只是这样计算,在增大mosaic值的时候,会看到所有马赛克是以屏幕左下角为中心变大的。而我想达到的效果,是以整个屏幕的中心点为中心放大,所以就对UV做了一个中心点的偏移。
Unity制作马赛克效果-LMLPHP

  然后转场怎么做?其实就是k一个动画,调节这个mosaic值,让画面变成马赛克,然后通过渐变显示另外一张也是用这个shader的图片,把马赛克从很大变成没有而已。

  给一下shader的源码,可以复制回ase去看看连线

// Made with Amplify Shader Editor
// Available at the Unity Asset Store - http://u3d.as/y3X 
Shader "msk"
{
	Properties
	{
		_MainTex("MainTex", 2D) = "white" {}
		_mosaic("mosaic", Range( 0 , 64)) = 1

	}
	
	SubShader
	{
		
		
		Tags { "RenderType"="Opaque" }
	LOD 100

		CGINCLUDE
		#pragma target 3.0
		ENDCG
		Blend Off
		AlphaToMask Off
		Cull Back
		ColorMask RGBA
		ZWrite On
		ZTest LEqual
		Offset 0 , 0
		
		
		
		Pass
		{
			Name "Unlit"
			Tags { "LightMode"="ForwardBase" }
			CGPROGRAM

			

			#ifndef UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX
			//only defining to not throw compilation error over Unity 5.5
			#define UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input)
			#endif
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_instancing
			#include "UnityCG.cginc"
			

			struct appdata
			{
				float4 vertex : POSITION;
				float4 color : COLOR;
				float4 ase_texcoord : TEXCOORD0;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};
			
			struct v2f
			{
				float4 vertex : SV_POSITION;
				#ifdef ASE_NEEDS_FRAG_WORLD_POSITION
				float3 worldPos : TEXCOORD0;
				#endif
				float4 ase_texcoord1 : TEXCOORD1;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
			};

			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			uniform float _mosaic;

			
			v2f vert ( appdata v )
			{
				v2f o;
				UNITY_SETUP_INSTANCE_ID(v);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
				UNITY_TRANSFER_INSTANCE_ID(v, o);

				o.ase_texcoord1.xy = v.ase_texcoord.xy;
				
				//setting value to unused interpolator channels and avoid initialization warnings
				o.ase_texcoord1.zw = 0;
				float3 vertexValue = float3(0, 0, 0);
				#if ASE_ABSOLUTE_VERTEX_POS
				vertexValue = v.vertex.xyz;
				#endif
				vertexValue = vertexValue;
				#if ASE_ABSOLUTE_VERTEX_POS
				v.vertex.xyz = vertexValue;
				#else
				v.vertex.xyz += vertexValue;
				#endif
				o.vertex = UnityObjectToClipPos(v.vertex);

				#ifdef ASE_NEEDS_FRAG_WORLD_POSITION
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				#endif
				return o;
			}
			
			fixed4 frag (v2f i ) : SV_Target
			{
				UNITY_SETUP_INSTANCE_ID(i);
				UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
				fixed4 finalColor;
				#ifdef ASE_NEEDS_FRAG_WORLD_POSITION
				float3 WorldPosition = i.worldPos;
				#endif
				float2 uv_MainTex = i.ase_texcoord1.xy * _MainTex_ST.xy + _MainTex_ST.zw;
				float2 mainUV38 = uv_MainTex;
				float mosaic31 = _mosaic;
				float mosaicStep35 = step( 0.01 , mosaic31 );
				float2 commonUV41 = ( mainUV38 * ( 1.0 - mosaicStep35 ) );
				float temp_output_13_0 = ( 256.0 / mosaic31 );
				float2 mosaicUV43 = saturate( ( ( floor( ( ( mainUV38 + ( mosaic31 / 510.0 ) ) * temp_output_13_0 ) ) / temp_output_13_0 ) * mosaicStep35 ) );
				
				
				finalColor = tex2D( _MainTex, ( commonUV41 + mosaicUV43 ) );
				return finalColor;
			}
			ENDCG
		}
	}
	CustomEditor "ASEMaterialInspector"
	
	
}
/*ASEBEGIN
Version=18500
1920;0;1920;1019;2563.494;945.7002;1.585708;True;True
Node;AmplifyShaderEditor.CommentaryNode;47;-2642.936,-59.26529;Inherit;False;577.1555;177.5091;mosaic;2;5;31;mosaic;1,1,1,1;0;0
Node;AmplifyShaderEditor.RangedFloatNode;5;-2592.936,-9.265312;Inherit;False;Property;_mosaic;mosaic;1;0;Create;True;0;0;False;0;False;1;36.14118;0;64;0;1;FLOAT;0
Node;AmplifyShaderEditor.CommentaryNode;45;-2622.422,-886.6477;Inherit;False;530.7781;357;mainUV;2;3;38;mainUV;1,1,1,1;0;0
Node;AmplifyShaderEditor.RegisterLocalVarNode;31;-2289.78,2.243812;Inherit;False;mosaic;-1;True;1;0;FLOAT;0;False;1;FLOAT;0
Node;AmplifyShaderEditor.TextureCoordinatesNode;3;-2572.422,-836.6476;Inherit;True;0;1;2;3;2;SAMPLER2D;;False;0;FLOAT2;1,1;False;1;FLOAT2;0,0;False;5;FLOAT2;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
Node;AmplifyShaderEditor.CommentaryNode;49;-1945.846,-218.7495;Inherit;False;1420.37;674.8312;CommentmosaicUV;13;40;17;14;33;43;30;26;12;37;11;10;13;34;mosaicUV;1,1,1,1;0;0
Node;AmplifyShaderEditor.RegisterLocalVarNode;38;-2315.644,-787.2075;Inherit;False;mainUV;-1;True;1;0;FLOAT2;0,0;False;1;FLOAT2;0
Node;AmplifyShaderEditor.GetLocalVarNode;33;-1895.846,-10.44408;Inherit;False;31;mosaic;1;0;OBJECT;;False;1;FLOAT;0
Node;AmplifyShaderEditor.GetLocalVarNode;34;-1862.571,187.9045;Inherit;False;31;mosaic;1;0;OBJECT;;False;1;FLOAT;0
Node;AmplifyShaderEditor.GetLocalVarNode;40;-1877.545,-151.7495;Inherit;False;38;mainUV;1;0;OBJECT;;False;1;FLOAT2;0
Node;AmplifyShaderEditor.SimpleDivideOpNode;17;-1734.671,-76.62849;Inherit;True;2;0;FLOAT;0;False;1;FLOAT;510;False;1;FLOAT;0
Node;AmplifyShaderEditor.CommentaryNode;46;-2670.603,-490.6985;Inherit;False;694.2064;304;mosaicStep;3;32;28;35;mosaicStep;1,1,1,1;0;0
Node;AmplifyShaderEditor.SimpleAddOpNode;14;-1546.357,-106.6868;Inherit;False;2;2;0;FLOAT2;0,0;False;1;FLOAT;0;False;1;FLOAT2;0
Node;AmplifyShaderEditor.SimpleDivideOpNode;13;-1668.911,176.7558;Inherit;True;2;0;FLOAT;256;False;1;FLOAT;0;False;1;FLOAT;0
Node;AmplifyShaderEditor.GetLocalVarNode;32;-2620.603,-417.2126;Inherit;False;31;mosaic;1;0;OBJECT;;False;1;FLOAT;0
Node;AmplifyShaderEditor.StepOpNode;28;-2417.488,-440.6985;Inherit;True;2;0;FLOAT;0.01;False;1;FLOAT;0.01;False;1;FLOAT;0
Node;AmplifyShaderEditor.SimpleMultiplyOpNode;10;-1384.497,-50.64218;Inherit;False;2;2;0;FLOAT2;0,0;False;1;FLOAT;0;False;1;FLOAT2;0
Node;AmplifyShaderEditor.CommentaryNode;48;-1858.511,-680.6942;Inherit;False;998.7271;391.8005;commonUV;5;25;39;24;36;41;commonUV;1,1,1,1;0;0
Node;AmplifyShaderEditor.FloorOpNode;11;-1252.421,-27.28818;Inherit;False;1;0;FLOAT2;0,0;False;1;FLOAT2;0
Node;AmplifyShaderEditor.RegisterLocalVarNode;35;-2200.396,-368.6219;Inherit;False;mosaicStep;-1;True;1;0;FLOAT;0;False;1;FLOAT;0
Node;AmplifyShaderEditor.GetLocalVarNode;37;-1439.824,247.6174;Inherit;False;35;mosaicStep;1;0;OBJECT;;False;1;FLOAT;0
Node;AmplifyShaderEditor.SimpleDivideOpNode;12;-1304.3,115.7625;Inherit;False;2;0;FLOAT2;0,0;False;1;FLOAT;0;False;1;FLOAT2;0
Node;AmplifyShaderEditor.GetLocalVarNode;36;-1808.511,-404.8937;Inherit;False;35;mosaicStep;1;0;OBJECT;;False;1;FLOAT;0
Node;AmplifyShaderEditor.SimpleMultiplyOpNode;26;-1153.535,104.2957;Inherit;True;2;2;0;FLOAT2;0,0;False;1;FLOAT;0;False;1;FLOAT2;0
Node;AmplifyShaderEditor.OneMinusNode;25;-1605.699,-462.2919;Inherit;False;1;0;FLOAT;0;False;1;FLOAT;0
Node;AmplifyShaderEditor.GetLocalVarNode;39;-1665.845,-630.6942;Inherit;False;38;mainUV;1;0;OBJECT;;False;1;FLOAT2;0
Node;AmplifyShaderEditor.SaturateNode;30;-942.7899,106.7188;Inherit;False;1;0;FLOAT2;0,0;False;1;FLOAT2;0
Node;AmplifyShaderEditor.SimpleMultiplyOpNode;24;-1432.036,-557.4781;Inherit;True;2;2;0;FLOAT2;0,0;False;1;FLOAT;0;False;1;FLOAT2;0
Node;AmplifyShaderEditor.RegisterLocalVarNode;41;-1083.784,-522.0004;Inherit;False;commonUV;-1;True;1;0;FLOAT2;0,0;False;1;FLOAT2;0
Node;AmplifyShaderEditor.RegisterLocalVarNode;43;-782.6457,90.67676;Inherit;False;mosaicUV;-1;True;1;0;FLOAT2;0,0;False;1;FLOAT2;0
Node;AmplifyShaderEditor.GetLocalVarNode;44;-517.429,-397.5422;Inherit;False;43;mosaicUV;1;0;OBJECT;;False;1;FLOAT2;0
Node;AmplifyShaderEditor.GetLocalVarNode;42;-549.429,-557.5424;Inherit;False;41;commonUV;1;0;OBJECT;;False;1;FLOAT2;0
Node;AmplifyShaderEditor.SimpleAddOpNode;29;-277.429,-557.5424;Inherit;True;2;2;0;FLOAT2;0,0;False;1;FLOAT2;0,0;False;1;FLOAT2;0
Node;AmplifyShaderEditor.TexturePropertyNode;1;-453.429,-797.5424;Inherit;True;Property;_MainTex;MainTex;0;0;Create;True;0;0;False;0;False;None;bf7546efface0f7418e69d9340cb7ffe;False;white;Auto;Texture2D;-1;0;2;SAMPLER2D;0;SAMPLERSTATE;1
Node;AmplifyShaderEditor.SamplerNode;2;-37.42896,-749.5424;Inherit;True;Property;_TextureSample0;Texture Sample 0;1;0;Create;True;0;0;False;0;False;-1;None;None;True;0;False;white;Auto;False;Object;-1;Auto;Texture2D;8;0;SAMPLER2D;;False;1;FLOAT2;0,0;False;2;FLOAT;0;False;3;FLOAT2;0,0;False;4;FLOAT2;0,0;False;5;FLOAT;1;False;6;FLOAT;0;False;7;SAMPLERSTATE;;False;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
Node;AmplifyShaderEditor.TemplateMultiPassMasterNode;20;410.571,-733.5424;Float;False;True;-1;2;ASEMaterialInspector;100;1;msk;0770190933193b94aaa3065e307002fa;True;Unlit;0;0;Unlit;2;True;0;1;False;-1;0;False;-1;0;1;False;-1;0;False;-1;True;0;False;-1;0;False;-1;False;False;False;False;False;False;True;0;False;-1;True;0;False;-1;True;True;True;True;True;0;False;-1;False;False;False;True;False;255;False;-1;255;False;-1;255;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;True;1;False;-1;True;3;False;-1;True;True;0;False;-1;0;False;-1;True;1;RenderType=Opaque=RenderType;True;2;0;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;True;1;LightMode=ForwardBase;False;0;;0;0;Standard;1;Vertex Position,InvertActionOnDeselection;1;0;1;True;False;;False;0
WireConnection;31;0;5;0
WireConnection;38;0;3;0
WireConnection;17;0;33;0
WireConnection;14;0;40;0
WireConnection;14;1;17;0
WireConnection;13;1;34;0
WireConnection;28;1;32;0
WireConnection;10;0;14;0
WireConnection;10;1;13;0
WireConnection;11;0;10;0
WireConnection;35;0;28;0
WireConnection;12;0;11;0
WireConnection;12;1;13;0
WireConnection;26;0;12;0
WireConnection;26;1;37;0
WireConnection;25;0;36;0
WireConnection;30;0;26;0
WireConnection;24;0;39;0
WireConnection;24;1;25;0
WireConnection;41;0;24;0
WireConnection;43;0;30;0
WireConnection;29;0;42;0
WireConnection;29;1;44;0
WireConnection;2;0;1;0
WireConnection;2;1;29;0
WireConnection;20;0;2;0
ASEEND*/
//CHKSM=798A82A66ABBD12CB9AD89632DE1513DF1F1324E

二、3D版本的马赛克效果

  怒之铁拳里面还有一种马赛克效果,它不是转场,而是整个画面保持在马赛克的状态,然后玩家可以在马赛克的画面中正常游戏。我思考了一下,用一个3D的场景来简单模拟一下:

钢铁侠马赛克

很明显,如果要整个画面变成马赛克,那么把刚才的材质球,通过屏幕后处理的方式来绘制就可以了。
Unity制作马赛克效果-LMLPHP

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MSKScreen : MonoBehaviour
{
    private Material colorMat;
    [Range(0.1f, 2)]
    public float val = 0.1f;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Mosaic(source, destination);
    }

    private void Mosaic(RenderTexture source, RenderTexture destination)
    {
        if (colorMat == null)
        {
            colorMat = new Material(Shader.Find("msk"));
        }
        if (colorMat == null || colorMat.shader == null || colorMat.shader.isSupported == false)
        {
            return;
        }
        colorMat.SetFloat("_mosaic", val);

        Graphics.Blit(source, destination, colorMat, 0);
        return;
    }
}
03-13 09:57