【Unity Shaders】Vertex Magic —— 访问顶点颜色

本系列主要参考《Unity Shaders and Effects Cookbook》一书(感谢原书做者),同时会加上一点我的理解或拓展。html

这里是本书全部的插图。这里是本书所需的代码和资源(固然你也能够从官网下载)。app

========================================== 分割线 ==========================================函数



写在前面


啦啦啦~我又回来看书啦。文章开始说一些题外话。由于写博客和在群里比较活跃(为何QQ给个人称谓是“吐槽”。。。我这么乖。。。),最近有一些朋友给我发邮件或者私信,不少人都给了我鼓励,我很开心,也有人给了我很好的学习建议,这些经历让我以为坚持写博客是正确的一条路~也谢谢全部鼓励、支持和关注个人人!我会坚持下去的。学习


好啦,言归正传~这篇开始,我决定开始一个新的章节——Vertex Magic。顾名思义,就是学习顶点相关的内容。自从看了下面Unity Gems里面的一张图,我对整个Surface Shader的工做流程有了更好的理解,在此再重温一下。spa




能够看出来共有四个阶段咱们能够参与。咱们以前学习的大可能是在上图的第二和第三个阶段,也就是编写surf和LightingXXX函数来影响表面着色和光照函数的。在卡通风格的Shader中,咱们也对最后一个更改像素颜色的机会——finalcolor命令有了一个初步的尝试。而如今,咱们将用一章的时间来学习如何利用第一个阶段——顶点函数来影响像素颜色。.net


顶点函数是在每一个顶点被传送到GPU以前被调用一次。它的做用是从模型坐标系中获得三维坐标,而后再转换到其渲染到屏幕时在屏幕坐标系中的二维位置。所以,经过顶点函数,咱们能够修改顶点的位置、颜色和UV坐标。一旦咱们完成了对顶点的修改后,就会进入到surf函数的执行。与顶点函数是逐顶点执行的方式不一样,surf函数则是逐像素执行的。3d


经过顶点函数,咱们能够创造像海上的波浪、旗帜飘动的动态效果,或者使用Shader来给顶点着色。这一篇,咱们来学习如何在一个Surface Shader中建立一个最简单的顶点函数!code



准备工做


在深刻学习顶点函数以前,咱们得首先了解如何经过顶点函数获得和存储顶点相关信息。htm


  1. 首先,咱们要准备一个已经给顶点着色过的模型,以便咱们能够在顶点函数中查看顶点颜色。为了方便,咱们使用本书自带资源(见文章开头)中第七章的模型资源——VertexColorObject.fbx。咱们把VertexColorObject.fbx导入Unity,并拖入到一个新的场景中。最后添加一个平行光。
  2. 新建一个Shader和Material,能够分别命名为SimpleVertexColor,并将Shader赋给Material,再将Material赋给模型。

你的场景应该看起来是这样的:blog





实现


下面,咱们开始编写Shader。


  1. Properties块中添加新的Properties:
    Properties 
    	{
    		_MainTint("Global Color Tint", Color) = (1,1,1,1)
    	}

  2. 接下来,告诉Unity咱们将使用本身的顶点函数:
    CGPROGRAM
    		#pragma surface surf Lambert vertex:vert

  3. Properties中新添加的属性添加对应的引用:
    float4 _MainTint;

  4. 下面是很重要的Input结构。咱们添加了一个新的变量vertColor以便surf函数能够访问vert函数中传递的数据:
    struct Input 
    		{
    			float2 uv_MainTex;
    			float4 vertColor;
    		};

  5. 下面是一个很是简单的vert函数。咱们访问模型的顶点颜色,再存储到Input结构体中:
    void vert(inout appdata_full v, out Input o)
    		{
    			o.vertColor = v.color;
    		}

  6. 最后,咱们使用从Input中获得的数据填充SurfaceOutput结构体的Albedo参数:
    void surf (Input IN, inout SurfaceOutput o) 
    		{
    			o.Albedo = IN.vertColor.rgb * _MainTint.rgb;
    		}


完整代码以下:

Shader "Custom/SimpleVertexColor" {
	Properties 
	{
		_MainTint("Global Color Tint", Color) = (1,1,1,1)
	}
	
	SubShader 
	{
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Lambert vertex:vert

		float4 _MainTint;

		struct Input 
		{
			float2 uv_MainTex;
			float4 vertColor;
		};
		
		void vert(inout appdata_full v, out Input o)
		{
			o.vertColor = v.color;
		}

		void surf (Input IN, inout SurfaceOutput o) 
		{
			o.Albedo = IN.vertColor.rgb * _MainTint.rgb;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}

效果以下:





解释



经过顶点函数,咱们能够修改顶点的位置、颜色、UV坐标等值。在本节中咱们使用了一个从Maya导入的已给顶点着色的模型,但咱们能够发现,在使用默认材质的状况下这些颜色在Unity中是不显示的。咱们须要编写Shader,提取这些颜色再在模型上显示出来。

咱们首先经过在#pragma声明中添加vertex:vert语句。这实际上告诉Unity,嘿,不要用你本身内置的顶点函数访问模型顶点信息啦,去我写的Shader里找一个名叫vert的家伙,用它去处理信息!若是Unity没有找到,它就会报一个编译错误。

vert函数里,除了咱们熟悉的Input结构体,还有一个很特别的参数—— appdata_full  。这个参数也是Unity内置的一个变量,它包含了模型顶点的全部信息,包括位置、切线、法线、两个纹理坐标和颜色信息。其余的还有appdata_baseappdata_tan,具体能够看见官网

你还能够发现,vertColor是一个float4类型的变量,这意味着咱们还能够访问它的透明通道。像下面这样:
void surf (Input IN, inout SurfaceOutput o) 
		{
			o.Albedo = IN.vertColor.rgb * _MainTint.rgb;
			o.Alpha = IN.vertColor.a;
		}