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 } } }