반응형
스크립트 코드
ColorSuite.cs
using UnityEngine; |
using System.Collections; |
|
[ExecuteInEditMode] |
[ImageEffectTransformsToLDR] |
[RequireComponent(typeof(Camera))] |
[AddComponentMenu("Image Effects/Color Adjustments/Color Suite")] |
public class ColorSuite : MonoBehaviour |
{ |
#region Public Properties |
|
// White balance. |
[SerializeField] float _colorTemp = 0.0f; |
[SerializeField] float _colorTint = 0.0f; |
|
public float colorTemp { |
get { return _colorTemp; } |
set { _colorTemp = value; } |
} |
public float colorTint { |
get { return _colorTint; } |
set { _colorTint = value; } |
} |
|
// Tone mapping. |
[SerializeField] bool _toneMapping = false; |
[SerializeField] float _exposure = 1.0f; |
|
public bool toneMapping { |
get { return _toneMapping; } |
set { _toneMapping = value; } |
} |
public float exposure { |
get { return _exposure; } |
set { _exposure = value; } |
} |
|
// Color saturation. |
[SerializeField] float _saturation = 1.0f; |
|
public float saturation { |
get { return _saturation; } |
set { _saturation = value; } |
} |
|
// Curves. |
[SerializeField] AnimationCurve _rCurve = AnimationCurve.Linear(0, 0, 1, 1); |
[SerializeField] AnimationCurve _gCurve = AnimationCurve.Linear(0, 0, 1, 1); |
[SerializeField] AnimationCurve _bCurve = AnimationCurve.Linear(0, 0, 1, 1); |
[SerializeField] AnimationCurve _cCurve = AnimationCurve.Linear(0, 0, 1, 1); |
|
public AnimationCurve redCurve { |
get { return _rCurve; } |
set { _rCurve = value; UpdateLUT(); } |
} |
public AnimationCurve greenCurve { |
get { return _gCurve; } |
set { _gCurve = value; UpdateLUT(); } |
} |
public AnimationCurve blueCurve { |
get { return _bCurve; } |
set { _bCurve = value; UpdateLUT(); } |
} |
public AnimationCurve rgbCurve { |
get { return _cCurve; } |
set { _cCurve = value; UpdateLUT(); } |
} |
|
// Dithering. |
public enum DitherMode { Off, Ordered, Triangular } |
[SerializeField] DitherMode _ditherMode = DitherMode.Off; |
|
public DitherMode ditherMode { |
get { return _ditherMode; } |
set { _ditherMode = value; } |
} |
|
#endregion |
|
#region Internal Properties |
|
// Reference to the shader. |
[SerializeField] Shader shader; |
|
// Temporary objects. |
Material _material; |
Texture2D _lutTexture; |
|
#endregion |
|
#region Local Functions |
|
// RGBM encoding. |
static Color EncodeRGBM(float r, float g, float b) |
{ |
var a = Mathf.Max(Mathf.Max(r, g), Mathf.Max(b, 1e-6f)); |
a = Mathf.Ceil(a * 255) / 255; |
return new Color(r / a, g / a, b / a, a); |
} |
|
// An analytical model of chromaticity of the standard illuminant, by Judd et al. |
// http://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D |
// Slightly modifed to adjust it with the D65 white point (x=0.31271, y=0.32902). |
static float StandardIlluminantY(float x) |
{ |
return 2.87f * x - 3.0f * x * x - 0.27509507f; |
} |
|
// CIE xy chromaticity to CAT02 LMS. |
// http://en.wikipedia.org/wiki/LMS_color_space#CAT02 |
static Vector3 CIExyToLMS(float x, float y) |
{ |
var Y = 1.0f; |
var X = Y * x / y; |
var Z = Y * (1.0f - x - y) / y; |
|
var L = 0.7328f * X + 0.4296f * Y - 0.1624f * Z; |
var M = -0.7036f * X + 1.6975f * Y + 0.0061f * Z; |
var S = 0.0030f * X + 0.0136f * Y + 0.9834f * Z; |
|
return new Vector3(L, M, S); |
} |
|
#endregion |
|
#region Private Methods |
|
// Set up the temporary assets. |
void Setup() |
{ |
if (_material == null) |
{ |
_material = new Material(shader); |
_material.hideFlags = HideFlags.DontSave; |
} |
|
if (_lutTexture == null) |
{ |
_lutTexture = new Texture2D(512, 1, TextureFormat.ARGB32, false, true); |
_lutTexture.hideFlags = HideFlags.DontSave; |
_lutTexture.wrapMode = TextureWrapMode.Clamp; |
UpdateLUT(); |
} |
} |
|
// Update the LUT texture. |
void UpdateLUT() |
{ |
for (var x = 0; x < _lutTexture.width; x++) |
{ |
var u = 1.0f / (_lutTexture.width - 1) * x; |
var r = _cCurve.Evaluate(_rCurve.Evaluate(u)); |
var g = _cCurve.Evaluate(_gCurve.Evaluate(u)); |
var b = _cCurve.Evaluate(_bCurve.Evaluate(u)); |
_lutTexture.SetPixel(x, 0, EncodeRGBM(r, g, b)); |
} |
_lutTexture.Apply(); |
} |
|
// Calculate the color balance coefficients. |
Vector3 CalculateColorBalance() |
{ |
// Get the CIE xy chromaticity of the reference white point. |
// Note: 0.31271 = x value on the D65 white point |
var x = 0.31271f - _colorTemp * (_colorTemp < 0.0f ? 0.1f : 0.05f); |
var y = StandardIlluminantY(x) + _colorTint * 0.05f; |
|
// Calculate the coefficients in the LMS space. |
var w1 = new Vector3(0.949237f, 1.03542f, 1.08728f); // D65 white point |
var w2 = CIExyToLMS(x, y); |
return new Vector3(w1.x / w2.x, w1.y / w2.y, w1.z / w2.z); |
} |
|
#endregion |
|
#region Monobehaviour Functions |
|
void Start() |
{ |
Setup(); |
} |
|
void OnValidate() |
{ |
Setup(); |
UpdateLUT(); |
} |
|
void Reset() |
{ |
Setup(); |
UpdateLUT(); |
} |
|
void OnRenderImage(RenderTexture source, RenderTexture destination) |
{ |
var linear = QualitySettings.activeColorSpace == ColorSpace.Linear; |
|
Setup(); |
|
if (linear) |
_material.EnableKeyword("COLORSPACE_LINEAR"); |
else |
_material.DisableKeyword("COLORSPACE_LINEAR"); |
|
if (_colorTemp != 0.0f || _colorTint != 0.0f) |
{ |
_material.EnableKeyword("BALANCING_ON"); |
_material.SetVector("_Balance", CalculateColorBalance()); |
} |
else |
_material.DisableKeyword("BALANCING_ON"); |
|
if (_toneMapping && linear) |
{ |
_material.EnableKeyword("TONEMAPPING_ON"); |
_material.SetFloat("_Exposure", _exposure); |
} |
else |
_material.DisableKeyword("TONEMAPPING_ON"); |
|
_material.SetTexture("_Curves", _lutTexture); |
_material.SetFloat("_Saturation", _saturation); |
|
if (_ditherMode == DitherMode.Ordered) |
{ |
_material.EnableKeyword("DITHER_ORDERED"); |
_material.DisableKeyword("DITHER_TRIANGULAR"); |
} |
else if (_ditherMode == DitherMode.Triangular) |
{ |
_material.DisableKeyword("DITHER_ORDERED"); |
_material.EnableKeyword("DITHER_TRIANGULAR"); |
} |
else |
{ |
_material.DisableKeyword("DITHER_ORDERED"); |
_material.DisableKeyword("DITHER_TRIANGULAR"); |
} |
|
Graphics.Blit(source, destination, _material); |
} |
|
#endregion |
// 셰이더 코드
Shader "Hidden/ColorSuite" |
{ |
Properties |
{ |
_MainTex ("-", 2D) = ""{} |
_Curves ("-", 2D) = ""{} |
_Exposure ("-", Float) = 1.0 |
_Saturation ("-", Float) = 1.0 |
_Balance ("-", Vector) = (1, 1, 1, 0) |
} |
|
CGINCLUDE |
|
// Multi-compilation options. |
#pragma multi_compile COLORSPACE_SRGB COLORSPACE_LINEAR |
#pragma multi_compile BALANCING_OFF BALANCING_ON |
#pragma multi_compile TONEMAPPING_OFF TONEMAPPING_ON |
#pragma multi_compile DITHER_OFF DITHER_ORDERED DITHER_TRIANGULAR |
|
#include "UnityCG.cginc" |
|
sampler2D _MainTex; |
float2 _MainTex_TexelSize; |
sampler2D _Curves; |
float _Exposure; |
float _Saturation; |
float4 _Balance; |
|
#if COLORSPACE_LINEAR |
|
// Color space conversion between sRGB and linear space. |
// http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html |
|
float3 srgb_to_linear(float3 c) |
{ |
return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); |
} |
|
float3 linear_to_srgb(float3 c) |
{ |
return max(1.055 * pow(c, 0.416666667) - 0.055, 0.0); |
} |
|
#endif |
|
#if BALANCING_ON |
|
// Color space conversion between linear RGB and LMS |
// based on the CIECAM02 model (CAT02). |
// http://en.wikipedia.org/wiki/LMS_color_space#CAT02 |
|
float3 lrgb_to_lms(float3 c) |
{ |
float3x3 m = { |
3.90405e-1f, 5.49941e-1f, 8.92632e-3f, |
7.08416e-2f, 9.63172e-1f, 1.35775e-3f, |
2.31082e-2f, 1.28021e-1f, 9.36245e-1f |
}; |
return mul(m, c); |
} |
|
float3 lms_to_lrgb(float3 c) |
{ |
float3x3 m = { |
2.85847e+0f, -1.62879e+0f, -2.48910e-2f, |
-2.10182e-1f, 1.15820e+0f, 3.24281e-4f, |
-4.18120e-2f, -1.18169e-1f, 1.06867e+0f |
}; |
return mul(m, c); |
} |
|
// Color balance function. |
// - The gamma compression/expansion equation used in this function |
// differs from the standard sRGB-Linear conversion. |
|
float3 apply_balance(float3 c) |
{ |
#if !COLORSPACE_LINEAR |
// Do the gamma expansion before applying the color balance. |
c = pow(c, 2.2); |
#endif |
|
// Apply the color balance in the LMS color space. |
c = lms_to_lrgb(lrgb_to_lms(c) * _Balance); |
|
// It may return a minus value, which should be cropped out. |
c = max(c, 0.0); |
|
#if !COLORSPACE_LINEAR |
// Gamma compression. |
c = pow(c, 1.0 / 2.2); |
#endif |
|
return c; |
} |
|
#endif |
|
#if TONEMAPPING_ON |
|
// John Hable's filmic tone mapping operator. |
// http://filmicgames.com/archives/6 |
|
float3 hable_op(float3 c) |
{ |
float A = 0.15; |
float B = 0.50; |
float C = 0.10; |
float D = 0.20; |
float E = 0.02; |
float F = 0.30; |
return ((c * (c * A + B * C) + D * E) / (c * (c * A + B) + D * F)) - E / F; |
} |
|
float3 tone_mapping(float3 c) |
{ |
c *= _Exposure * 4; |
c = hable_op(c) / hable_op(11.2); |
return pow(c, 1 / 2.2); |
} |
|
#endif |
|
// Color saturation. |
|
float luma(float3 c) |
{ |
return 0.212 * c.r + 0.701 * c.g + 0.087 * c.b; |
} |
|
float3 apply_saturation(float3 c) |
{ |
return lerp((float3)luma(c), c, _Saturation); |
} |
|
// RGB curves. |
|
float3 apply_curves(float3 c) |
{ |
float4 r = tex2D(_Curves, float2(c.r, 0)); |
float4 g = tex2D(_Curves, float2(c.g, 0)); |
float4 b = tex2D(_Curves, float2(c.b, 0)); |
return float3(r.r * r.a, g.g * g.a, b.b * b.a); |
} |
|
#if DITHER_ORDERED |
|
// Interleaved gradient function from CoD AW. |
// http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare |
|
float interleaved_gradient(float2 uv) |
{ |
float3 magic = float3(0.06711056, 0.00583715, 52.9829189); |
return frac(magic.z * frac(dot(uv, magic.xy))); |
} |
|
float3 dither(float2 uv) |
{ |
return (float3)(interleaved_gradient(uv / _MainTex_TexelSize) / 255); |
} |
|
#endif |
|
#if DITHER_TRIANGULAR |
|
// Triangular PDF. |
|
float nrand(float2 uv) |
{ |
return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453); |
} |
|
float3 dither(float2 uv) |
{ |
float r = nrand(uv) + nrand(uv + (float2)1.1) - 0.5; |
return (float3)(r / 255); |
} |
|
#endif |
|
float4 frag(v2f_img i) : SV_Target |
{ |
float4 source = tex2D(_MainTex, i.uv); |
float3 rgb = source.rgb; |
|
#if BALANCING_ON |
rgb = apply_balance(rgb); |
#endif |
|
#if COLORSPACE_LINEAR |
#if TONEMAPPING_ON |
// Apply the tone mapping. |
rgb = tone_mapping(rgb); |
#else |
// Convert the color into the sRGB color space. |
rgb = linear_to_srgb(rgb); |
#endif |
#endif |
|
// Color saturation. |
rgb = apply_saturation(rgb); |
|
// RGB curves. |
rgb = apply_curves(rgb); |
|
#if !DITHER_OFF |
rgb += dither(i.uv); |
#endif |
|
#if COLORSPACE_LINEAR |
// Take the color back into the linear color space. |
rgb = srgb_to_linear(rgb); |
#endif |
|
return float4(rgb, source.a); |
} |
|
ENDCG |
|
Subshader |
{ |
Pass |
{ |
ZTest Always Cull Off ZWrite Off |
Fog { Mode off } |
CGPROGRAM |
#pragma target 3.0 |
#pragma glsl |
#pragma vertex vert_img |
#pragma fragment frag |
ENDCG |
} |
반응형
'Technical Report > Unity Scripts' 카테고리의 다른 글
Texture2D.Encode (0) | 2017.08.17 |
---|---|
Unity FPSCounter (0) | 2017.07.26 |
Unity Attribute API (0) | 2017.07.26 |
Unity Shader Property API (0) | 2017.07.25 |
Unity Attribetes (0) | 2016.07.18 |