Unity Shader之色彩变换

渲染管线
一般分为固定渲染管线和可编程渲染管线。固定管线因为功能固定,无法在程序上对物体细节的表现给予更多更自由的控制,无法达到更多更炫酷的效果。为了解决这个问题,可编程的渲染管线就诞生了,见下图:

具体的可编程的部分就是上图中两个橙色的节点:Vertex Shader(顶点着色)和 Fragment Shader(片元着色)。
了解了Shader在渲染管线中的工作位置,那么我们在看看Unity中如何进行Shader开发呢?
Unity3D Shader
Unity的Shader有四种:
Fixed function shader
属于固定渲染管线Shader, 基本用于高级Shader在老显卡无法显示时的备用Shader。Vertex and Fragment Shader
最强大的Shader类型,属于可编程渲染管线。使用的是CG/HLSL语言。也就是我上面说过的两种。Surface Shader
Unity3d推荐的Shader类型。它是一个代码生成器,帮我们将重复的代码省去了,使得编写Shader更为容易。使用的也是CG/HLSL语言。Compute Shader
可直接将GPU作为并行处理器加以利用,GPU将不仅具有3D渲染能力,也具有其他的运算能力。




色彩是人的眼睛对于不同频率的光线的不同感受,色彩既是客观存在的(不同频率的光)又是主观感知的,有认识差异。计算机领域应用最多的是RGB,尤其是程序中处理。但是美术处理和工业应用领域应用更多的是HSV或HSL(举个大家最熟悉的例子:打开电视里面的色彩设置就会看到色相,饱和度,明亮度的设置)。
RGB(Red,Green,Blue)
就是红绿蓝组成的三维坐标系:

HSV(Hue色相,Saturation饱和度,Value色调)
由色相,饱和度,色调组成的倒立的圆锥体:

HSL(Hue色相,Saturation饱和度,Lightness亮度)
HSL 类似于 HSV。对于一些人,HSL 更好的反映了“饱和度”和“亮度”作为两个独立参数的直觉观念,但是对于另一些人,它的饱和度定义是错误的,因为非常柔和的几乎白色的颜色在 HSL 可以被定义为是完全饱和的。对于 HSV 还是 HSL 更适合于人类用户界面是有争议的(这个不在本文的讨论范围内,就不在深入了)。

HSV和HSL的对比图:

色彩相关的概念介绍完毕,下面在Unity中新建一个Shader
Shader "Custom/HSLShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_DH("Hue",Range(0,360)) = 0
_DS("Saturation",Range(-1,1)) = 0
_DL("Lightness",Range(-1,1)) = 0
}
SubShader
{
// No culling or depth
// Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float _DH;
float _DS;
float _DL;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float r = col.r;
float g = col.g;
float b = col.b;
float a = col.a;
float h;
float s;
float l;
float maxv = max(max(r,g),b);
float minv = min(min(r,g),b);
if (maxv == minv){
h = 0.0;
} else if (maxv == r && g >= b){
h = 60.0*(g-b)/(maxv-minv)+0.0;
} else if (maxv == r && g < b ){
h = 60.0*(g-b)/(maxv-minv)+360.0;
} else if (maxv == g){
h = 60.0*(b-r)/(maxv-minv)+120.0;
} else if (maxv == b){
h = 60.0*(r-g)/(maxv-minv)+240.0;
}
l = 0.5*(maxv+minv);
if (l == 0.0 || maxv == minv){
s = 0.0;
} else if (0.0 <= l && l <= 0.5){
s = (maxv-minv)/(2.0*l);
} else if (l > 0.5){
s = (maxv-minv)/(2.0-2.0*l);
}
h = h + _DH;
s = min(1.0,max(0.0,s+_DS));
l = l + _DL;
// final color
float q;
if (l < 0.5){
q = l*(1.0+s);
}else if (l >= 0.5){
q = l+s-l*s;
}
float p = 2.0*l-q;
float hk = h/360.0;
float t[3];
t[0] = hk+1.0/3.0;
t[1] = hk;
t[2] = hk-1.0/3.0;
for(int i=0;i<3;i++){
if (t[i] < 0.0){
t[i] += 1.0;
}else if (t[i] > 1.0){
t[i] -= 1.0;
}
}
float c[3];
for (int i=0;i<3;i++){
if (t[i] < 1.0/6.0){
c[i] = p+((q-p)*6.0*t[i]);
}else if (1.0/6.0 <= t[i] && t[i] < 0.5){
c[i] = q;
}else if (0.5 <= t[i] && t[i] < 2.0/3.0){
c[i] = p+((q-p)*6.0*(2.0/3.0-t[i]));
}else{
c[i] = p;
}
}
fixed4 finalColor = fixed4(c[0],c[1],c[2],a);
finalColor += fixed4(_DL,_DL,_DL,0.0);
return finalColor;
}
ENDCG
}
}
}








