Unity Shader入门(二)

本文档详细介绍了Unity Shader的入门知识,包括UnityShader概述、ShaderLab语法、不同Shader类型(如SurfaceShader、Vertex/FragmentShader和FixedFunctionShader)及其应用场景,帮助初学者理解并掌握Unity Shader的基本结构和编写技巧。
摘要由CSDN通过智能技术生成

Unity Shader入门(二)
本人是一个初学Shader的小白,写此博客为了记录学习Shader过程中的一些概念以及问题,主要参考书籍《Unity Shader入门精要》如果有哪些不对的地方欢迎各位大佬指导!!!

Unity Shader基础

1、Unity Shader概述

总体来说,在Unity中我们需要配合使用材质和Unity Shader才能达到需要的效果。Unity Shader定义了渲染所需的各种代码(如顶点着色器和片元着色器)、属性(如使用哪些纹理)和指令(渲染和标签设置等),而材质则允许我们调节这些属性,并将其最终赋给相应的模型。
材质的创建流程以及选择使用哪个Shader这里就不做解释了。
就目前我自己了解熟悉的,Unity一共提供了4种Unity Shader模板供我们选择:
Standard Surface Shader:会产生一个包含了标准光照模型的表面着色器模板
Unlit Shader:会产生一个不包含光照(但包含雾效)的基本的顶点/片元着色器
Image Effect Shader:(不熟悉)
Compute Shader:(不熟悉)

2、 ShaderLab

在Unity中,所有的Unity Shader都是使用ShaderLab来编写的。
一个Unity Shader的基础结构如下所示:+

Shader "ShaderName"{
      Properties{
         //属性
         }
         SubShader{
            //显卡A使用的子着色器
            }
         SubShader{
            //显卡B使用的子着色器
            }
         Fallback "VertexLit"
         }

3、Unity Shader的结构

  1. Shader名字:
Shader "Custom/MyShader"
  1. Shader属性(property):

声明这些属性是为了在材质面板中能给方便地调整各种材质属性。

Properties{
   Name("display name",PropertyType)=DefaultValue
   Name("display name",PropertyType)=DefaultValue
   }

下面代码给出了一个展示所有属性类型的例子:

Shader "Custom/ShaderLab"
{
   Properties
   {
   	   //Number and Sliders
	   _Int("Int",Int)=2
	   _FLoat("Float",Float)=1.5
	   _Range("Range",Range(0.0,5.0))=3.0
	   //Color and Vectors
	   _Color("Color",Color)=(1,1,1,1)
	   _Vector("Vector",Vector)=(2,3,6,1)
	   //Textures
	   _2D("2D",2D)=""{}
	   _Cube("Cube",Cube)="white"{}
	   _3D("3D",3D)="black"{}
   }
   FallBack "Diffuse"
}

如下图给出了上述代码在材质面板的显示结果!在这里插入图片描述

  1. SubShader
    每一个Unity Shader文件可以包含多个SubShader语义块,但最少要有一个。当Unity需要加载这个Unity Shader时,Unity会扫描所有的SubShader语义块,然后选择第一个能给在目标平台上运行的SubShader,如果都不支持的话,Unity就会使用Fallback语义指定的Unity Shader。这样做的原因在于不同的显卡具有不同的能力。
    SubShader语义块中包含的定义如下:
 SubShader
   {
   	   //可选的
	   [Tags]

	   //可选的
	   [RenderSetup]
	   Pass
	   {
	   
	   }
	   //other Passes
   }

SubShader定义了一系列Pass以及可选的状态([RenderSetup])和标签([Tags])设置。每个Pass定义了一个完整的渲染流程,但如果Pass的数目过多,往往会造成渲染性能的下降。

状态设置:
ShaderLab提供了一系列渲染状态的设置指令,这些指令可以设置显卡的各种状态,这些渲染状态将会运用到Pass语句块内,如果不想这样,可以在每个Pass语句块内设置渲染状态。

SubShader的标签:
SubShader的标签是一个键值对(Key/Value Pair),它的键和值都是字符串类型。这些键值对是SubShader和渲染引擎之间的沟通桥梁,它们用来告诉Unity的渲染引擎:我们希望怎样以及何时渲染这个对象。例如可以控制物体渲染顺序,对着色器进行分类等。
标签的结构如下:

Tags{"TagName1"="Value1" "TagName2"="Value2"}

注意区分SubShader的标签和Pass语句快的标签不可以通用

Pass语义块:
Pass语义块包含的语义如下:

Pass
	   {
	      [Name]
		  [Tags]
		  [RenderSetup]
		  //otherCode	   
	   }

首先,我们可以在Pass中定义该Pass的名称,例如:

Name "MyPassName"

通过这个名称,我们可以使用ShaderLab的UsePass命令直接使用Unity Shader中的Pass。例如:

UsePass "MyShader/MyPASSNAME"

这样可以提高代码的复用性。需要注意的是,由于Unity内部会把所有Pass的名称转换成大写字母表示,因此,在使用UsePass命令必须使用大写形式的名字。

其次,我们可以对Pass设置渲染状态。SubShader的状态设置同样使用于Pass。还有一些其他的渲染状态可用于Pass语句块。

Pass可以设置标签,但它的标签不同于SubShader的标签。但这些标签也是告诉渲染引擎我们希望怎样来渲染该物体的。

除了上面普通的Pass定义外,Unity Shader还支持一些特殊的Pass,以便进行代码的复用或实现复杂的效果。
GrapPass:该Pass负责抓取屏幕并将结果存储在一张纹理中,以用于后续的Pass处理。

  1. Fallback
    紧跟在各个SubShader语义块后面的,可以是一个Fallback指令,它用于告诉Unity,如果上面所有的SubShader在这块显卡上不能运行,那么就使用最低级的Shader。
    它的语义如下:
 Fallback "name"
 //或者
 Fallback off

4、Unity Shader的形式:

在Unity中,我们可以使用下面3种形式来编写Unity Shader。而不管使用哪种形式,真正意义上的Shader代码都需要包含在ShaderLab语义块中,如下所示:

Shader "MyShader"
{
	Properties
	{
		//所需的各种属性
	}
	SubShader
	{
		//真正意义上的Shader代码会出现在这里
		//表面着色器(Surface Shader)或者
		//顶点/片元着色器(Vertex/Fragment Shader)或者
		//固定函数着色器(Fixed Function Shader)
	}
	SubShader
	{
		//和上一个SubShader类似
	}
}

Unity的宠儿:表面着色器
表面着色器是Unity自己创造的一种着色器代码类型。它需要的代码量很少,Unity在背后做了很多工作,但渲染的代价比较大。它在本质上和下面要讲到顶点/片元着色器是一样的。也就是说,当给Unity提供一个表面着色器的时候,它在背后仍旧把它转换成对应的顶点/片元着色器。我们可以理解为表面着色器是Unity对顶点/片元着色器的更高一层的抽象。它存在的价值在于,Unity为我们处理很多的光照细节。
一个非常简单的表面着色器示例代码如下:

Shader "Custom/Simple Surface Shader"
{
	SubShader{
	   Tags{"RenderType"="Opaque"}
	   CGPROGRAM
	   #pragma surface surf Lambert
	   struct Input{
	   	   float4 color : COLOR;
	   };
	   void surf(Input IN,inout SurfaceOutput o)
	   {
	   	   o.Albedo=1;
	   }
	   ENDCG

	}
	Fallback "Diffuse"
}

从上述程序中可以看出,表面着色器被定义在SubShader语义块(而非Pass语义块)中的CGPROGRAM和ENDCG之间。原因是,表面着色器不需要开发者关心使用多少个Pass,每个Pass如何渲染等问题,Unity会在背后为我们做好这些事情。
其中CGPROGRAM和ENDCG之间的代码使用Cg/HLSL编写的,也就是说,我们需要把Cg/HLSL嵌套在ShaderLab语言中。值得注意的是,这里的Cg/HLSL是Unity经封装后提供的。

总聪明的孩子:顶点/片元着色器
在Unity中我们可以使用Cg/HLSL语言来编写顶点/片元着色器。他们更加复杂,但灵活性也更高。
一个非常简单的顶点/片元着色器代码如下:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'


Shader "Custom/Simple VertexFragment Shader"
{
SubShader
{
   Pass
   {
   	   CGPROGRAM
	   #pragma vertex vert
	   #pragma fragment frag

	   float4 vert(float4 v:POSITION):SV_POSITION{
	   	   return UnityObjectToClipPos (v);
	   }

	   fixed4 frag():SV_Target{
	   	   return fixed4(1.0,0.0,0.0,1.0);
	   }

	   ENDCG
   }
   }

}

和表面着色器类似,顶点/片元着色器的代码也需要定义在CGPROGRAM和ENDCG之间,但不同的是,顶点/片元着色器是写在Pass语句块内的,而非SubShader内。

被抛弃的角落:固定的函数着色器
上面的两种Unity Shader形式都使用了可编程渲染管线。而对于一些较旧的设备,就需要使用固定函数着色器来完成渲染。这些函数往往只可以完成一些较简单的效果。
一个非常简单的固定函数着色器示例代码如下:

Shader "Tutorial/Basic"
{
	Properties
	{
		_Color("Main Color",Color)=(1.0,0.5,0.5,1)
	}
	SubShader
	{
		Pass
		{
		   Material
		   {
		   	   Diffuse[_Color]
		   }
		   Lighting On
		
		}
	}
}

可以看出,固定函数着色器的代码被定义在Pass语义块中,这些代码相当于Pass中的一些渲染设置。
对于固定函数着色器来说,我们需要完全使用ShaderLab的语法(即使用ShaderLab的渲染设置指令)来编写。
因为现在绝大多数GPU支持可编程的渲染管线,这种固定管线的编程方式已经逐渐被抛弃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值