Unity1Bit抖色后处理Shader
1.效果
后处理使用前
后处理使用后
此处应该有个视频
2.原理
2.1 什么是1bit像素画
1bit像素画只有2种颜色,最常见的是黑+白。1个颜色画阳形(通常是你要画的主题),1个颜色画阴形(通常代表画面中的空白)。
虽然只有2种颜色,但是我们可以混合黑白,产生灰色。这是像素画技巧抖动的原理。(实际上利用的人眼的视觉补偿机制,人眼和大脑会将相邻的黑白像素点混合变成灰色)
来自:像素画高级教程:1bit像素画画法|白色|灰色_网易订阅 (163.com)
本教程使用的是第二个分段画法
2.2 实现思路
根据1bit像素画的定义,我们只需要先获取到屏幕的图像,然后转化成灰度图,再根据颜色的灰度值来替换成对应的像素图就行了
(明)灰度公式:0.2125R +0.7154G +0.0721*B; (该公式仅适用于unity,在其他平台可能会有颜色上的偏差)
2.3.视频教程
3.代码
3.1 1btpost.shader
Shader "Unlit/1btpost"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_SplitTex1 ("Texture", 2D) = "white" {}
_SplitTex2 ("Texture", 2D) = "white" {}
_SplitTex3 ("Texture", 2D) = "white" {}
_SplitTex4 ("Texture", 2D) = "white" {}
_SplitTex5 ("Texture", 2D) = "white" {}
_Split1To2 ("Split1To2",float) = 0.2
_Split2To3 ("Split2To3",float) = 0.4
_Split3To4 ("Split3To4",float) = 0.6
_Split4To5 ("Split4To5",float) = 0.8
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float2 uv1 : TEXCOORD1;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _SplitTex1;
float4 _SplitTex1_ST;
sampler2D _SplitTex2;
float4 _SplitTex2_ST;
sampler2D _SplitTex3;
float4 _SplitTex3_ST;
sampler2D _SplitTex4;
float4 _SplitTex4_ST;
sampler2D _SplitTex5;
float4 _SplitTex5_ST;
float _Split1To2;
float _Split2To3;
float _Split3To4;
float _Split4To5;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.uv1 = TRANSFORM_TEX(v.uv, _SplitTex1);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
fixed4 col1 = tex2D(_SplitTex1, i.uv1);
fixed4 col2 = tex2D(_SplitTex2, i.uv1);
fixed4 col3 = tex2D(_SplitTex3, i.uv1);
fixed4 col4 = tex2D(_SplitTex4, i.uv1);
fixed4 col5 = tex2D(_SplitTex5, i.uv1);
//明亮度计算公式
float luminance = 0.2125*col.r+0.7154*col.g+0.0721*col.b;
if(luminance<=_Split1To2)
{
//col = float4(luminance,luminance,luminance,col.a);
col = col1;
}
if(luminance<=_Split2To3&&luminance>_Split1To2)
{
//col = float4(luminance,luminance,luminance,col.a);
col = col2;
}
if(luminance<=_Split3To4&&luminance>_Split2To3)
{
//col = float4(luminance,luminance,luminance,col.a);
col = col3;
}
if(luminance<=_Split4To5&&luminance>_Split3To4)
{
//col = float4(luminance,luminance,luminance,col.a);
col = col4;
}
if(luminance>_Split4To5)
{
//col = float4(luminance,luminance,luminance,col.a);
col = col5;
}
// col = float4(luminance,luminance,luminance,col.a);
return col;
}
ENDCG
}
}
}
对以下两个脚本更详尽的讲解,请访问我的这篇文章:Unity:实现反相后处理效果
3.2 PostEffectsBase.cs
//
// PostEffectsBase.cs
// qi_SteamVR_0.1
//
// Created by YX on 2022/4/8.
//
//
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
/// <summary>
/// 后处理基类,需要放置在camera上
/// </summary>
public class PostEffectsBase : MonoBehaviour
{
#region Field
#endregion
#region Property
#endregion
#region UnityOriginalEvent
void Start()
{
CheckResources();
}
void Update()
{
}
#endregion
#region Function
/// <summary>
/// 检查东西当前平台是否支持图像处理
/// </summary>
/// <returns>可以返回ture,不可以返回false</returns>
protected bool CheckSupport()
{
//检查当前平台是否支持图像后处理,现在基本所有的平台都支持,所以可以不判断
if (SystemInfo.supportsImageEffects == false)
{
Debug.LogWarning("This platform does not support image effects.");
return false;
}
return true;
}
/// <summary>
/// 检查环境
/// </summary>
protected void CheckResources()
{
//当当前平台不支持图像后处理时,将该脚本关闭
if (CheckSupport() == false)
{
enabled = false;
}
}
/// <summary>
/// 使用shader创建材质
/// </summary>
/// <param name="shader">使用的sheder</param>
/// <param name="material">最终创建的材质</param>
/// <returns></returns>
protected Material CheckShaderAndCreateMaterial(Shader shader, Material material)
{
if (shader == null)
{
return null;
}
if (shader.isSupported && material && material.shader == shader)
return material;
if (!shader.isSupported)
{
return null;
}
else
{
material = new Material(shader);
//设置meaterial为不可见,且在转换场景时不会被删除
material.hideFlags = HideFlags.DontSave;
if (material)
return material;
else
return null;
}
}
#endregion
}
3.3 ShakeColor.cs
//
// Invert.cs
// qi_SteamVR_0.1
//
// Created by YX on 2022/4/8.
//
//
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 实现反相shader后处理
/// </summary>
public class ShakeColor : PostEffectsBase
{
#region Field
public Shader briSatConShader;
private Material briSatConMaterial;
public Vector2 uv;
public Texture _SplitTex1;
public Texture _SplitTex2;
public Texture _SplitTex3;
public Texture _SplitTex4;
public Texture _SplitTex5;
[Range(0,1)]
public float _Split1To2 = 0.2f;
[Range(0, 1)]
public float _Split2To3 = 0.4f;
[Range(0, 1)]
public float _Split3To4 = 0.6f;
[Range(0, 1)]
public float _Split4To5 = 0.8f;
#endregion
#region Property
/// <summary>
/// 后处理shader生成的材质
/// </summary>
public Material material
{
get
{
briSatConMaterial = CheckShaderAndCreateMaterial(briSatConShader, briSatConMaterial);
return briSatConMaterial;
}
}
#endregion
#region UnityOriginalEvent
private void Update()
{
if (_Split1To2>=_Split2To3)
{
_Split1To2 = _Split2To3 - 0.001f;
}
if (_Split2To3 >= _Split3To4)
{
_Split2To3 = _Split3To4 - 0.001f;
}
if (_Split3To4 >= _Split4To5)
{
_Split3To4 = _Split4To5 - 0.001f;
}
}
//这个方法会在所有渲染完成后调用
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material != null)
{
//使用着色器将源纹理复制到目标渲染纹理。
material.SetTexture("_SplitTex1", _SplitTex1);
material.SetTexture("_SplitTex2", _SplitTex2);
material.SetTexture("_SplitTex3", _SplitTex3);
material.SetTexture("_SplitTex4", _SplitTex4);
material.SetTexture("_SplitTex5", _SplitTex5);
material.SetTextureScale(Shader.PropertyToID("_SplitTex1"), uv);
material.SetFloat("_Split1To2", _Split1To2);
material.SetFloat("_Split2To3", _Split2To3);
material.SetFloat("_Split3To4", _Split3To4);
material.SetFloat("_Split4To5", _Split4To5);
Graphics.Blit(src, dest, material);
}
else
{
Graphics.Blit(src, dest);
}
}
#endregion
#region Function
#endregion
}