概:本篇主要内容是如何在Unity中实现用图片作为基础元素去对相机最后拍到的内容做后处理渲染。(讲也不是很明白,建议直接移步效果预览那部分看看效果就明白了)
前期学习参照:这个效果的实现原理很大一部分参照于知乎罗老师的字符后处理渲染那篇文章,这里贴个链接Unity3D后期Shader特效-马赛克13-文字图像(灰度转ID|图像块坐标偏移)
啊,如上就是最终实现的一个效果,如果有兴趣就继续往下看,我会简介如何实现这样的东西,如果感觉很拉跨就跑路(如果有好的建议不妨给一手评论欸嘿嘿)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class Pixlate : MonoBehaviour
{
public enum EDirType
{
彩铅风格,
像素风格,
字符风格,
图片填充
}
RenderTexture re1;
RenderTexture re2;
public EDirType 滤镜类型 = EDirType.彩铅风格;
public Filter 滤镜;
static private List<Filter> col;
public Material[] effectMaterial;
private void Start()
{
col = new List<Filter>();
col.Add(new Color_Pencil_Filter());
col.Add(new PixelFilter());
col.Add(new Char_Filter());
col.Add(new Picture_Filter());
}
private void Update()
{
switch(滤镜类型)
{
case EDirType.彩铅风格:
if (滤镜 == col[0]) break;
滤镜 = col[0];
break;
case EDirType.像素风格:
if (滤镜 == col[1]) break;
滤镜 = col[1];
break;
case EDirType.字符风格:
if (滤镜 == col[2]) break;
滤镜 = col[2];
break;
case EDirType.图片填充:
if (滤镜 == col[3]) break;
滤镜 = col[3];
break;
default:
break;
}
}
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if(滤镜 == null)
{
return;
}
effectMaterial = 滤镜.material;
if(re1 == null)
{
re1 = new RenderTexture(source);
}
if (re2 == null)
{
re2 = new RenderTexture(source);
}
Graphics.Blit(source, re1);
foreach (Material m in effectMaterial)
{
Graphics.Blit(re1, re2, m);
Graphics.Blit(re2, re1);
}
//滤镜.Random_Parameter();
Graphics.Blit(re1, destination);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public abstract class Filter
{
public string[] shaderFile {
set; get; } //shader路径
public Shader[] shader {
set; get; } //shader
public string filterName {
set; get; } //滤镜名字
public Material[] material {
set; get; } //材质球
public void Init()
{
InitFile();
shader = new Shader[shaderFile.Length];
material = new Material[shaderFile.Length];
for (int i = 0;i<shaderFile.Length;i++)
{
shader[i] = Shader.Find(shaderFile[i]);
material[i] = new Material(shader[i]);
}
Random_Parameter();
}
public abstract void InitFile();
public abstract void Random_Parameter();
}
private Texture2D duplicateTexture(Texture2D source)
{
//2d纹理解除保护
RenderTexture renderTex = RenderTexture.GetTemporary(
source.width,
source.height,
0,
RenderTextureFormat.Default,
RenderTextureReadWrite.Linear);
Graphics.Blit(source, renderTex);
RenderTexture previous = RenderTexture.active;
RenderTexture.active = renderTex;
Texture2D readableText = new Texture2D(source.width, source.height);
readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
readableText.Apply();
RenderTexture.active = previous;
RenderTexture.ReleaseTemporary(renderTex);
return readableText;
}//2d纹理解除保护
for (int i = 0; i < TexList.Count; i++)
{
//计算灰度并对应存储
double gray = 0;
for(int j = 0;j<TexList[i].width;j++)
{
for (int k = 0; k < TexList[i].height; k++)
{
gray += TexList[i].GetPixel(j, k).r * 0.299;
gray += TexList[i].GetPixel(j, k).g * 0.587;
gray += TexList[i].GetPixel(j, k).b * 0.114;
}
}
gray /= (TexList[i].width * TexList[i].height);
GrayList.Add(gray);
}//计算灰度并对应存储
double d = 0;
Texture2D t;
for (int i = 0;i< TexList.Count;i++)
{
for(int j = 0;j< TexList.Count-i-1;j++)
{
if(GrayList[j]> GrayList[j+1])
{
d = GrayList[j];
GrayList[j] = GrayList[j + 1];
GrayList[j + 1] = d;
t = TexList[j];
TexList[j] = TexList[j + 1];
TexList[j + 1] = t;
}
}
}//按照灰度排序,亮的在后
private void Texture_Clone(Texture2D texture, Texture2D tex,int minwidth,int num)
{
//参数1:最终克隆到的texture中
//参数2:要缩放并克隆的目标
//参数3:缩放的边长(正方)
//参数4:第几个克隆的对象,对应位置
float xb = tex.width / minwidth;
float yb = tex.height / minwidth;
for (int i = 0;i<minwidth;i++)
{
for(int j = 0;j<minwidth;j++)
{
texture.SetPixel(i+ num * minwidth, j, tex.GetPixel((int)( i * xb), (int)(j * yb)));
}
}
texture.Apply();
}//将指定的texture2d内容按照比例缩放克隆到texture指定位置
public override void Random_Parameter()
{
//随机传参方法
//传入一个纹理
material[0].SetTexture("_PictureTex", texture);
//传入一个字符密度参数
material[0].SetFloat("_TileSize", _TileSize);
//传入图片数量参数
material[0].SetFloat("_PictureCount", TexList.Count);
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Picture_Filter : Filter
{
//渲染精细度,数字越小越精细
public float _TileSize = 2;
//Texture2D _tex = (Texture2D)Resources.Load("Lighthouse");
//要用的纹理数组
private List<Texture2D> TexList = new List<Texture2D>();
//对应纹理数组的灰度数组
private List<double> GrayList = new List<double>();
//最终拼接好的纹理
private Texture2D texture;
//所有纹理中的纹理最短边
private int minTex = 1000000;
public Picture_Filter()
{
filterName = "图片填充";
Init();
}//构造方法
private Texture2D duplicateTexture(Texture2D source)
{
//2d纹理解除保护
RenderTexture renderTex = RenderTexture.GetTemporary(
source.width,
source.height,
0,
RenderTextureFormat.Default,
RenderTextureReadWrite.Linear);
Graphics.Blit(source, renderTex);
RenderTexture previous = RenderTexture.active;
RenderTexture.active = renderTex;
Texture2D readableText = new Texture2D(source.width, source.height);
readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
readableText.Apply();
RenderTexture.active = previous;
RenderTexture.ReleaseTemporary(renderTex);
return readableText;
}//2d纹理解除保护
private void InitTexList()
{
for(int i = 1;i<=30;i++)
{
TexList.Add(Resources.Load<Texture2D>("Texture/Picture_Filter/123 (" + i+")"));
}
for (int i = 0; i < TexList.Count; i++)
{
//解开纹理保护
TexList[i] = duplicateTexture(TexList[i]);
}//解开纹理保护
for (int i = 0; i < TexList.Count; i++)
{
//计算灰度并对应存储
double gray = 0;
for(int j = 0;j<TexList[i].width;j++)
{
for (int k = 0; k < TexList[i].height; k++)
{
gray += TexList[i].GetPixel(j, k).r * 0.299;
gray += TexList[i].GetPixel(j, k).g * 0.587;
gray += TexList[i].GetPixel(j, k).b * 0.114;
}
}
gray /= (TexList[i].width * TexList[i].height);
GrayList.Add(gray);
}//计算灰度并对应存储
double d = 0;
Texture2D t;
for (int i = 0;i< TexList.Count;i++)
{
for(int j = 0;j< TexList.Count-i-1;j++)
{
if(GrayList[j]> GrayList[j+1])
{
d = GrayList[j];
GrayList[j] = GrayList[j + 1];
GrayList[j + 1] = d;
t = TexList[j];
TexList[j] = TexList[j + 1];
TexList[j + 1] = t;
}
}
}//按照灰度排序,亮的在后
}//初始化纹理数组
private void Texture_Clone(Texture2D texture, Texture2D tex,int minwidth,int num)
{
//参数1:最终克隆到的texture中
//参数2:要缩放并克隆的目标
//参数3:缩放的边长(正方)
//参数4:第几个克隆的对象,对应位置
float xb = tex.width / minwidth;
float yb = tex.height / minwidth;
for (int i = 0;i<minwidth;i++)
{
for(int j = 0;j<minwidth;j++)
{
texture.SetPixel(i+ num * minwidth, j, tex.GetPixel((int)( i * xb), (int)(j * yb)));
}
}
texture.Apply();
}//将指定的texture2d内容按照比例缩放克隆到texture指定位置
public override void InitFile()
{
InitTexList();//初始化纹理数组
for(int i = 0;i<TexList.Count;i++)
{
if (minTex > TexList[i].height)
{
minTex = TexList[i].height;
}
if (minTex > TexList[i].width)
{
minTex = TexList[i].width;
}
}//取最短边
texture = new Texture2D(minTex * TexList.Count, minTex);//实例化最终要用的纹理
for (int i = 0; i < TexList.Count; i++)
{
Texture_Clone(texture, TexList[i], minTex, i);
}//为最终要用的纹理填入参数
//为渲染器初始化
shaderFile = new string[1];
shaderFile[0] = "Custom/Myshader/Picture_Shader";
}//初始化方法
public override void Random_Parameter()
{
//随机传参方法
//传入一个纹理
material[0].SetTexture("_PictureTex", texture);
//传入一个字符密度参数
material[0].SetFloat("_TileSize", _TileSize);
//传入图片数量参数
material[0].SetFloat("_PictureCount", TexList.Count);
}
}
Properties
{
_MainTex ("Texture", 2D) = "white" {
}
_PictureTex ("PictureTex", 2D) = "white" {
}
_TileSize("TileSize", Range(0,100)) = 1
_PictureCount("PictureCount",Range(0,100)) = 1
}
Shader "Custom/Myshader/Picture_Shader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {
}
_PictureTex ("PictureTex", 2D) = "white" {
}
_TileSize("TileSize", Range(0,100)) = 1
_PictureCount("PictureCount",Range(0,100)) = 1
}
SubShader
{
Cull Off ZWrite Off ZTest Always
pass
{
CGPROGRAM
#pragma vertex _Vert
#pragma fragment Pixel
#include "UnityCG.cginc"
struct VertexInput
{
float4 Pos:POSITION;
float2 uv:TEXCOORD0;
};
struct VertexOutput
{
float4 Pos:SV_POSITION;
float2 uv:TEXCOORD0;
};
//参数定义
sampler2D _MainTex;
sampler2D _PictureTex;
float _TileSize;
int _PictureCount;
VertexOutput _Vert(VertexInput v)
{
VertexOutput r;
//将顶点转换到剪裁空间
r.Pos = UnityObjectToClipPos(v.Pos);
r.uv = v.uv;
return r;
}
fixed4 Pixel(VertexOutput v):SV_Target
{
//用目标像素尺寸和屏幕默认尺寸计算出参数TileSum
float2 TileSum = _ScreenParams / _TileSize;
//用参数TileSum对uv进行区块划分
float2 uv_Mosaic = ceil(v.uv *TileSum)/ TileSum;
//对纹理进行取样显现
fixed4 col = tex2D(_MainTex, uv_Mosaic);
//计算当前点的灰度
fixed gray = saturate(dot(col.xyz,fixed3(0.299, 0.587, 0.114)));
int num = ceil(gray *_PictureCount); //当前点应该是用第几个图片对应的纹理
float2 uv_picture =(v.uv *TileSum-ceil(v.uv *TileSum));
uv_picture.x /= _PictureCount;
uv_picture.x -= (float)num/_PictureCount;
fixed4 r = tex2D(_PictureTex, uv_picture);
return r;
}
ENDCG
}
}
FallBack "Diffuse"
}