55

Unity Shader之色彩变换-腾讯游戏学院

 5 years ago
source link: http://gad.qq.com/article/detail/287447
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
先来看一个特效:
5bb72902f0fba.gif
这个效果是几年前我在Shadertoy看到的一位大神写的Shader特效,里面没有用到任何额外的纹理贴图,纯用Shader编写出来的。我把他翻译到了Unity中,想研究的同学可以点击连接获取完成的Unity工程 Github

在开始本文具体案例之前下面先做些基础的科普,便于还没入坑的同学对Shader编程有个全局的了解。

为什么出现了shader编程?这个要从渲染管线开始说起:

渲染管线

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

timg?image&quality=80&size=b9999_10000&sec=1538740917065&di=8b20082258708981df5c7264ef447ad2&imgtype=jpg&src=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D2741969728%2C500724010%26fm%3D214%26gp%3D0.jpg

具体的可编程的部分就是上图中两个橙色的节点:Vertex Shader(顶点着色)和 Fragment Shader(片元着色)。

了解了Shader在渲染管线中的工作位置,那么我们在看看Unity中如何进行Shader开发呢?

Unity3D Shader

Unity的Shader有四种:

  1. Fixed function shader 
    属于固定渲染管线Shader, 基本用于高级Shader在老显卡无法显示时的备用Shader。

  2. Vertex and Fragment Shader 
    最强大的Shader类型,属于可编程渲染管线。使用的是CG/HLSL语言。也就是我上面说过的两种。

  3. Surface Shader 
    Unity3d推荐的Shader类型。它是一个代码生成器,帮我们将重复的代码省去了,使得编写Shader更为容易。使用的也是CG/HLSL语言。

  4. Compute Shader 
    可直接将GPU作为并行处理器加以利用,GPU将不仅具有3D渲染能力,也具有其他的运算能力。


基本概念粗略的过完,下面我们来做一个比较常用的Shader特效:
游戏开发经常会用到一些颜色上的变化来标示当前的状态。例如:
中毒全是泛绿:
5bb80e14ea56f.jpg
植物大战僵尸中冰冻缓速僵尸色彩变为 蓝色 的效果:
5bb80d384bd50.jpg

尤其是当年FC时代的游戏,因为内存的不足,用不同色调来标示不同的角色这方面用到了极致:
5bb812506aba9.jpg
5bb8122fec644.png
具体这些效果如何用Shader实现呢?
在撸代码之前先要介绍几个概念:

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


RGB(Red,Green,Blue) 

就是绿组成的三维坐标系:

5bb7359f1622c.png


HSV(Hue色相,Saturation饱和度,Value色调)

色相,饱和度,色调组成的倒立的圆锥体:

5bb735aad98b1.png

HSL(Hue色相,Saturation饱和度,Lightness亮度)

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

5bb7361fa6d9e.png


HSV和HSL的对比图:

5bb7363ac21d0.png


色彩相关的概念介绍完毕,下面在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
		}
	}
}
然后新建一个Material:
5bb7380fd0ce6.png
将这个Material的Shader设置为刚新建的“HSLShader”,然后在Inspector中就会看到Hue,Saturation,Lightness三个属性:
5bb738bceebb6.png

新建一个Scene来测试我们的Shader,场景中放置个Sprite:
5bb73a6cb204c.png5bb73a732831f.png
下面我们来调节材质Inspector中的Hue、Saturation、Lightness来看看对应的效果:

Hue 从0~360°的变化图:
5bb73d9434c22.gif
Saturation 为-1(黑白)和1(艳丽)时的效果图:
5bb73eec39eee.png5bb73ef24358e.png
Lightness 为0.2时的效果图:
5bb74326266fe.png

好了,看到这相信大家对色彩变换应该有了直观的印象。对具体的实现感兴趣的同学可以点击链接下载

本文参与“Unity Shader”征文活动

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK