Unity自定义校色后处理
大家好,我是阿赵。
之前介绍了一些后处理的做法,包括了PostProcessing和CommandBuffer。其实如果我们只是实现一些比较单一的全屏效果,也没必要搞那么复杂,直接在OnRenderImage里面写Graphics.Blit就可以了。接下来的几篇文章都会介绍这种简单直接的后处理实现方式。
这一期先来讲最简单的校色。我还是拿这个模型作为原始画面。
实现饱和度的调整,原理其实是先取一个灰度颜色,然后再用这个灰度的颜色和当前的颜色做lerp插值。
//饱和度
float lumin = dot(col, float3(0.22f, 0.707f, 0.071f));
col = lerp(lumin, col, _Saturation);
其中 float3(0.22f, 0.707f, 0.071f)这个灰度可以根据你自己的需要去调整。然后声明一个_Saturation变量,用于控制饱和度的插值。
_Saturation默认是1,即是画面的原始颜色。当我们增大_Saturation,让_Saturation大于1时,可以看到画面的饱和度变高了。
当我们减少_Saturation值,让它接近0时,画面就变成了灰度。
原则上_Saturation应该是大于等于0的,但如果我们把_Saturation的可调范围包含负数时,负数的效果就会变成反色了。
对比度的原理和饱和度有点类似,但它不再是那灰度作为0插值时的颜色,而是用一个中间色
//对比度
float3 midPoint = float3(0.5, 0.5, 0.5);
col.rgb = lerp(midPoint, col.rgb, _Contrast);
其中的_Contrast变量是控制对比度的值。
由于后处理是作用在整个画面的,所以当我们调整_Contrast时,连着背景的颜色一起,整个画面的对比度就变低。
和饱和度一样,假如我们把_Contrast的取值范围包含负数,就可以做这种反相的效果。
要调整颜色的色调,我们要转换一下,把RGB颜色转换成HSV颜色模型。然后再对其中的Hue进行调整,调整完之后,再调整回到RGB颜色。
RGB和HSV的转换
float3 HSVToRGB(float3 c)
{
float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y);
}
float3 RGBToHSV(float3 c)
{
float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g));
float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
调整色调
//色调
float3 hsvCol = RGBToHSV(col.rgb);
hsvCol.x = hsvCol.x + _HueShift;
col.rgb = HSVToRGB(hsvCol);
调整_HueShift,可以看到画面的色调在改变。
明度调整是最简单的了,直接RGB颜色乘以一个变量就行
//明度
col.rgb *= _Light;
由于后处理针对的是整个画面,所以调整明度,会整个画面一起变化。
用Photoshop时,我们很喜欢亮度和对比度一起调整,这里的各种效果都是可以互相配合使用的。亮度和对比度一起调整,就变成这种效果。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AdjustColor : MonoBehaviour
{
private Material colorMat;
[Range(-5, 5)]
public float saturation = 1;
[Range(-5, 5)]
public float contrast = 1;
[Range(0, 1)]
public float hueShift = 0;
[Range(0, 5)]
public float lightVal = 1;
void Start()
{
}
void Update()
{
}
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
AjustColor(source, destination);
}
private bool AjustColor(RenderTexture source, RenderTexture destination)
{
if (colorMat == null)
{
colorMat = new Material(Shader.Find("Hidden/AzhaoAjustColor"));
}
if (colorMat == null || colorMat.shader == null || colorMat.shader.isSupported == false)
{
return false;
}
colorMat.SetFloat("_Saturation", saturation);
colorMat.SetFloat("_Contrast", contrast);
colorMat.SetFloat("_HueShift", hueShift);
colorMat.SetFloat("_Light", lightVal);
Graphics.Blit(source, destination, colorMat, 0);
return true;
}
}
Shader "Hidden/AzhaoAjustColor"
{
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_TexelSize;
float _Saturation;
float _Contrast;
float _HueShift;
float _Light;
float3 HSVToRGB(float3 c)
{
float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y);
}
float3 RGBToHSV(float3 c)
{
float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g));
float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
half4 fragColorAjust(v2f_img i) : SV_Target
{
half4 col = tex2D(_MainTex, i.uv);
//饱和度
float lumin = dot(col, float3(0.22f, 0.707f, 0.071f));
col = lerp(lumin, col, _Saturation);
//对比度
float3 midPoint = float3(0.5, 0.5, 0.5);
col.rgb = lerp(midPoint, col.rgb, _Contrast);
//色调
float3 hsvCol = RGBToHSV(col.rgb);
hsvCol.x = hsvCol.x + _HueShift;
col.rgb = HSVToRGB(hsvCol);
//明度
col.rgb *= _Light;
return col;
}
ENDCG
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Saturation("Saturation",Range(-5,5)) = 1
_Contrast("Contrast",Range(-5,5)) = 1
_HueShift("HueShift",Range(0,1)) = 0
_Light("Light",Range(0,5)) = 1
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
//0校色
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment fragColorAjust
ENDCG
}
}
}