Unity Shader入门精要笔记(二):Unity Shader基础

本系列文章由Aimar_Johnny编写,欢迎转载,转载请标明出处,谢谢。

http://blog.csdn.net/lzhq1982/article/details/73442306


1、Unity Shader概述

1)在Unity中我们需要配合使用材质(material)和Unity Shader才能达到需要的效果。材质是载体,Unity Shader是文本文件,我们在Unity Shader中写好渲染的代码(属性,顶点着色器,片元着色器等),加载到材质上,在材质上可以调节shader的属性,然后将材质赋给模型展示渲染效果。

2)Unity Shader不是传统意义上的的Shader,Unity Shader实际上指的是一个ShaderLab文件--以.shader为文件后缀的一种文件。但Unity Shader可以做的远多于一个传统意义的Shader。

a、传统Shader中,我们只可以编写特定类型的Shader,比如顶点着色器,或片元着色器等。Unity Shader中我们可以在一个文件中编写他们。

b、传统Shader中,我们不能做一些例如开启混合、深度测试等渲染测试,这是在另外的代码中设置的。Unity Shader中我们加几个指令就可以了。

c、传统Shader中,需要编写大量代码设置着色器的输入和输出。Unity Shader中,我们只需要声明一些属性,并可以在材质中改变这些属性。对于模型数据(顶点位置,纹理坐标,法线,切线等),Unity Shader中我们可以直接访问,传统Shader需要自行编码传给shader。

d、Unity Shader的高度封装,使其失去了更多的编写自由性,不过同时也使我们只需要和Unity Shader打交道,不用关心渲染引擎底层实现。

3)Unity 5.5版本中,提供了5种Unity Shader模板:Standard Surface Shader,Standard Surface Shader(Instanced),Unlit Shader,Image Effect Shader, Compute Shader。

a、Standard Surface Shader是基于物理渲染(PBS)的包含标准光照模型的表面着色器模板,在5.x版本中,我们新建一个材质,它会默认使用内置的Standard Shader,是使用基于物理渲染(PBS)的技术,区别于传统光照Shader,它对光照渲染有着更加强大的支持,当然如果想达到强大的效果,除了主纹理和法线纹理,你可能需要加入金属纹理,粗糙度纹理,遮挡纹理,细节纹理,并配置一堆属性,再加上光照探针,反射探针啥的,想清楚再用。

b、Standard Surface Shader(Instanced),据说是5.4版本以后加的,使用了Gpu Instancing技术,这个技术在大量使用相同材质和网格的情况下可以大幅度降低Draw Call,而这个模板在生成阴影pass中使用了该技术。

c、Unlit Shader:不包含光照但包含雾效的顶点片元着色器。

d、Image Effect Shader:只是一个简单的图片展示效果,为实现屏幕后处理效果提供了基本模板。

f、Compute Shader:产生特殊的shader文件,旨在利用GPU的并行性来进行一些与常规渲染流水线无关的计算。(原书照搬,不在研究范围)。

a和b是表面着色器,c和d是顶点片元着色器,请对应选择模板。(因为喜欢用顶点片元着色器,我喜欢用d)


2、ShaderLab

上面提到过Unity Shader不是传统意义的shader,编写传统意义的shader需要和很多文件和配置打交道,Unity为我们提供了一层抽象:Unity Shader,而专门为Unity Shader服务的语言就是ShaderLab。


3、Unity Shader结构

1)名字

每个Unity Shader第一行都需要定义一个名字,格式例子:Shader "Custom/MyShader","/"是路径,其对应位置为Shader->Custom->MyShader。

2)属性(Properties)

Properties {

Name ("display name", PropertyType) = DefaultValue

Name ("display name", PropertyType) = DefaultValue

....

}

Properties中包含了一系列属性,会在材质面板中展示,开发者可以方便调试它们,同时shader会访问它们,类似于untiy的public变量。如上所示,每条属性由Name(名字,代码中要用),"display name"(面板显示名字),PropertyType(属性类型),DefaultValue(默认值)组成。Unity Shader属性类型如下:


3)SubShader

一个Shader文件可以包含多个SubShader,但最少要一个。Unity加载该shader时,会扫描并选择一个能在目标平台运行的SubShader,如果都不支持,会使用Fallback指定的Unity Shader。

SubShader通常定义如下:

SubShader {
	//可选
	[Tags]
	//可选
	[RenderSetup]

	Pass {
	}
	//其他Pass
	...
}

a、Tags:标签,由一组键值对组成,键和值都是字符串,它们告诉Unity渲染引擎,我希望怎样及何时渲染该SubShader。结构如下:

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

新手注意,每个标签间隔不要加逗号或分号,只是空格,最后也没符号,新手这里容易写错。

SubShader支持的标签类型如下(书上截图,原谅我懒):


注意,这里的标签是在SubShader里的,不是Pass里的,它们不同。

b、RenderSetup

状态设置,ShaderLab提供了一系列渲染状态的设置指令,可以设置在SubShader里,影响其下所有pass,也可以放某个pass里,只影响该pass。常见的渲染设置如下(接着截图):


c、Pass

SubShader中可以有多个pass,每个Pass定义了一次完整的渲染流程,但Pass过多会造成渲染性能下降,所以我们尽量用最少数目的Pass。

Pass包含的语义如下:

Pass {
    [Name]
    [Tags]
    [RenderSetup]
    //正式代码
    ....
}

Name:该pass名字,可选,书写例子如下:

Name "MyPassName"

通过该名字,我们可以在其他的Unity Shader中用UsePass命令直接使用该Pass,非常方便,例如

UsePass "MyShader/MYPASSNAME"

需要注意的是Unity内部会把所有Pass名称转换成大写字母,所以用UsePass时必须使用大写形式的名字。

上面SubShader的RenderSetup(状态设置)同样适用于Pass。

Pass的标签不同于SubShader的标签,下面是Pass中使用的标签:


还有一些特殊的Pass

UsePass:前面提到了,可以用Pass的名字复用访问。

GrabPass:该Pass负责抓取屏幕并存到一张纹理中,后面的Pass可以对该纹理继续处理。

d、FallBack

放在最后,当所有SubShader都不合适,就用FallBack提供的Shader。语义如下:

FallBack "name" 或 FallBack Off

不要以为它没用,当你学到阴影那里就知道它的用处了,FallBack的内置shader往往包含一个通用的阴影投射Pass,所以你不用自己实现。


4、Unity Shader种类

1)Surface Shader(表面着色器)

示例:

Shader "Custom/NewSurfaceShader" {
	Properties {
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }		
		CGPROGRAM
		#pragma surface surf Lambert
		sampler2D _MainTex;
		struct Input {
			float2 uv_MainTex;
		};
		fixed4 _Color;
		void surf (Input IN, inout SurfaceOutputStandard o) {
			fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb;
			o.Alpha = c.a;
		}
		ENDCG
	}
	FallBack "Diffuse"
}

Unity自己创造的一种着色器代码类型,少量的代码可以实现很大的功能,但其实它扩展开了就是顶点/片元着色器,只不过Unity替你封装好了很多代码,这样反而渲染代价比较大,你可以创建一个Surface Shader模板,看一下代码仅仅几行,然后可以在Inspector上点击show generated code,如下图:

然后你会看到它生成了一个庞大到让你想发狂的代码,立马就不想学了,哈哈,不过其实它就是生成了一个顶点/片元着色器的代码,就那几个模块,建议学完顶点/片元着色器再回头看看。不过写Surface Shader倒是很容易的,尤其是复杂的光照部分不用你管了,后面会有单独章节介绍。

2)Vertex/Fragment Shader(顶点/片元着色器)

示例:

Shader "Custom/VertexFragmentShader" {
	Properties {
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader {
		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;
			};

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv = v.uv;
				return o;
			}
			
			sampler2D _MainTex;

			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_MainTex, i.uv);
				return col;
			}
			ENDCG
		}
	}
}

我们主要学习的着色器,可以使用Cg/HLSL语言来编写,相对表面着色器复杂一些,但灵活性更高。

3)Fixed Function Shader(固定函数着色器)

示例:

Shader "Custom/FixedFunctionShader" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
	}
	SubShader {
		Pass {
			Material {
				Diffuse [_Color]
			}
			Lighting On
		}
	}
}

相对于上面两个可编程管线,固定管线技术已经落伍了,当然旧设备不支持可编程管线的要用固定函数着色器,而现在绝大多数GPU都支持可编程技术,而且Unity5.x目前所有固定函数着色器都会被编译成对应的顶点/片元着色器。

综上所述,虽然表面上Unity有三种着色器类型,但经过转化其实就只有一种:顶点/片元着色器,这也是我们后面着重学习的对象。


5、CG/HLSL,GLSL

相对于早期编写着色器的汇编语言,后来开发出了更高级的着色语言,常见的有DirectX的HLSL(High Level Shading Language),OpenGL的GLSL(OpenGL Shading Language),NVIDIA的CG(C for Graphic)。这些语言会被编译成汇编语言,也叫中间语言(Intermediate Language, IL)。中间语言再交给显卡驱动翻译成机器语言,即GPU可以理解的语言。

我们看到上面的Surface Shader和Vertex/Fragment Shader的代码示例,发现核心代码都放在CGPROGRAM和ENDCG之间,其实它们是在ShaderLab内部嵌套的CG/HLSL语言,因为CG和DX9风格的HLSL从写法上几乎是一样的,所以在Unity里CG和HLSL是等价的。我们也可以用GLSL来写,但发布平台会少一些,GLSL要嵌套在GLSLPROGRAM和ENDGLSL之间。

通常,Unity会自动把这些CG片段编译到所有相关平台上(D3D9,D3D11,OpenGL ES等),我们可以通过点击shader面板上的Compile and show code旁边的下拉按钮来看它当前编译的平台,你也可以更改要编译的平台。如下图:


但当发布游戏时,游戏数据文件中只包含目标平台需要的编译代码,其他平台的代码会被移除。

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值