【Shader笔记】Unity Shader基础

参考书籍:UNITY SHADER入门精要

一、材质(Material)与 Unity Shader

  效果的实现需要材质和Unity Shader配合使用。常见流程为:

   1. 创建一个材质
   2. 创建一个Unity Shader,并赋予给上一步新建材质中
   3. 将材质赋予要需要渲染的物体对象上
   4. 在材质面板中调整Unity Shader属性,以达到预期效果

1.1 Unity中的材质

  工作原理:结合一个GameObject的Mesh或者Particle Systems组件来工作。

1.1 Unity中的Shader

  Unity Shader:Unity中的Shader,不同于渲染管线中描述的Shader。
  Unity Shader 本质:是一个文本文件。

  Unity提供了4种Unity Shader模板供选择:

Unity Shader描述
Standard Surface Shader产生一个包含了标准光照模型的表面着色器模板
Unlit Shader产生一个不包含光照(但包含雾效)的基本的顶点/片元着色器
Image Effect Shader实现各种屏幕或处理效果的基本模板
Compute Shader产生一种特殊的Shader文件(非重要,详情自行参考Unity手册)

二、什么是ShaderLab

  答:服务于Unity Shader的语言。

三、Shader基础结构

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

3.1 标题

Shader "MyShader/Shader01" {}

  上代码内容指:路径"MyShader/"下,该Shader命名为 Shader01

3.2 属性 Properties

  Properties语义块的作用:仅将属性可视呈现于Inspector材质面板中。

  下图为Properties{}基本格式:

Properties {
	Name {"display name", PropertyType} = DefaultValue
	Name {"display name", PropertyType} = DefaultValue
	...
	Name {"display name", PropertyType} = DefaultValue
	// 更多属性
}

Name:属性名,用于后面SubShader中。通常以"_"开头命名,例如:_Color_Texture等。
display name:显示名称,即显示在Inspector窗口上的属性名称。
PropertyType:Unity Shader提供的可用属性类型,需要我们选择使用,并与前面的属性、名称相符。
DefaultValue:默认数值,指第一次将Shader赋予至材质时的初始数值。

;Properties 语义块支持的属性类型

属性类型默认值的定义语法案例
Intnumber_Int ("Int", Int) = 2
Floatnumber_Float ("Float", Float) = 1.5
Range(min, max)number_Range ("Range", Range(0, 1)) = 0.5
Color(number, number, number, number)_Color ("Color" Color) = (1, 1, 1, 1)
Vector(number, number, number, number)_Vector ("Vector" Vector) = (2, 3, 4, 5)
2D“defaulttexture” {}_2D ("2D", 2D) = "" {}
Cube“defaulttexture” {}_Cube ("Cube", Cube) = "white" {}
3D“defaulttexture” {}_3D ("3D", 3D) = "black" {}

  特别的,2DCube3D的默认值是 "字符串" {}。其中字符串有以下:
   - 空,即""
   - 内置文理名称,如"white"/black/gray/bump

3.3 子着色器 SubShader

SubShader {
	[Tags] //可选标签
	[RenderSetup] //可选状态
	
	Pass {
		//实现A
	}	

	Pass {
		//实现B
	}
	... //更多的实现
}

  Tags:标签。出现多个SubShader{}的区分命名
  RenderSetup:工作状态设置

  注意:一个 Unity Shader 可以包含多个SubShader语义块。但至少有一个SubShader{}在其中。

3.3.1 工作原理

  答:”Unity加载该Shader时会扫描所有SubShader{},选择第一个能在目标平台工作的SubShader{}。若都不支持,则选择Fallback语义指定的Unity Shader。

3.3.2 使用目的

  答:受限制于不同版本的显卡中所支持的指令数目,配置合适的着色器,以保障画面的出色能力。

3.3.3 SubShader中的标签 Tags

  SubShader的标签设置

标签类型说明案例
Queue控制渲染顺序,指定该物体属于哪一个渲染队列Tags { “Queue” = “Transparent” }
RenderType着色器分类,指明使用的着色器对象Tags { “RenderType” = “Qpaque” }
DisableBatching指明是否对该SubShader使用批处理(配合Unity批处理)Tags { “DisableBatching” = “True”}
ForceNoShadowCasting控制使用该SubShader的物体是否会投射阴影Tags { “ForceNoShadowCasting” = “True”}
IgnoreProjector控制使用该SubShader的物体是否不受Projector的影响(常用于半透明物体)Tags { “IgnoreProjetctor” = “True”}
CanUseSpriteAtlas该SubShader是否没有被用于sprites(精灵)Tags { “CanUseSpritesAtlas” = “False”}
PreviewType材质面板预览该材质的类型,默认为球型。(如 “Plane”、“SkyBox”)Tags { “PreviewType” = “Plane”}

  标签的声明仅在SubShader{}中进行,不可在Pass{}进行。

  附注:Pass{}可以声明标签,但声明的标签类型与SubShader{}不同。

3.3.4 状态设置 RenderSetup

常见的渲染状态设置选项

状态名称设置指令解释
CullCull Back/Front/Off设置剔除模式:剔除 背面/正面/关闭
ZTestZTest Less/Greater/LEqual/Equal/NotEqual/Always设置深度测试使用的函数
ZWriteZWrite On/Off深度写入 开启/关闭
BlendBlend SrcFactor DstFactor混合模式 开启并设置

  特别注意:SubShader{}中设置上述状态时,会将应用到所有Pass{}。如果仅仅希望不同的Pass{}使用的状态不一,可在各Pass{}语义块中单独进行设置。

3.4 Pass语义块

Pass {
	[Name]
	[Tage]
	[RenderSetup]
	// Other code
}

定义该Pass的名称

Name "MyPassName"

使用指定Pass

UsePass "MyShader/MyPassName"

  通过使用ShaderLab中UsePass命令直接使用其他Unity Shader中的Pass(即 提高代码的复用性)。其中UsePass "MyShader/MyPassName"为 使用"MyShader中的名称为MyPassName"的Pass。

  特别注意:Unity会把所有Pass名称转为大写字母表示,使用UsePass命令时必须使用大写名字。

  
标签
  不同于SubShader{}中标签。Pass{}中的标签用于告诉Unity渲染引擎我们希望如何来渲染物体。

标签类型说明案例
LightMode定义 Pass 在Unity的渲染流水线中的角色Tags { “LightMode” = “ForwardBase” }
RequireOptions指定满足条件时渲染该PassTags { “RequireOptions” = “SoftVegetation”}

  
其他特殊Pass

  • UsePass:使用其他Shader中的Pass
  • GrabPass:抓取屏幕并将结果存储至一张纹理中,以用于后续Pass处理

3.5 Fallback

Fallback "name"   //返回指定SubShader的pass
Fallback Off      //不作任何操作

紧跟于各个SubShader{}语义块后面,用于告诉Unity,当上面所有的SubShader在这块显卡上都不能下运行时,就使用字符串命名的最低级Shader吧。

例如:Fallback "VertexLit",我的显卡都不能用上面的Shader,所以用这个最低级的Shader—“VertexLit”。

3.6 其他语义

  CustomEidtor语义:扩展编辑界面
  Category语义:对Unity Shader中的命令进行分组

四、Unity Shader 的形式

  SubShader{}:表面着色器的做法
  Pass{}:顶点/片元着色器和固定函数着色器的做法

  真正意义上的Shader代码:

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

4.1 表面着色器 Surface Shader

  理解:表面着色器是Unity对顶点/片元着色器的更高一层的抽象。其存在是Unity为我们处理了很多光照细节的繁琐操作。

简单的表面着色器示例代码

Shader “Custom/Simple Surface Shader” {
	SubShader {
		Tags { "RenderType" = "Opaque" }

		CGPROGRAM
		#pragma surface surf Lambert
		
		struct Input {
			float4 color : COLOR;
		};
		
		void Surf (Input input, inout SurfaceOutput out) {
			out.Albedo = 1;
		}
		ENDCG
	}
	Fallback "Diffuse"
}
附注:了解 CGPROGRAM-CG

  由代码内容所示,表面着色器被定义于SubShader{}中的CGPROGRAMENDCG中。

  原因:开发中,不需要表面着色器关心使用多少个Pass()与如何渲染等问题,只需告诉其,“使用这些纹理去填充颜色”、“使用这个法线纹理去填充法线”、"使用Lambert光照模型,不需要其他"等。

4.2 顶点/片元着色器 VertexFragment Shader

简单的顶点/片元着色器示例代码

Shader "Custom/Simple VertexFragment Shader" {
	SubShader {
		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		
		float4 vert(float4 v : POSITION) : SV_POSITION {
			return mul(UNITY_MATRIX_MVP, v);
		}

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

  注意:与表面着色器类似,顶点/片元着色器的代码也需要定义至CGPROGEAM-CG中。但却是写在Pass()中。

  原因:开发中需要定义各Pass()中需求使用的Shader代码,可能存在需求编写更多的代码,带来的好处是灵活性更高,更重要的是可以控制渲染的实现细节。

4.3 固定函数着色器 Fixed Function Shader

  适用对象:旧设备、不支持可编程管线着色器的设备。(该着色器几乎被弃用)

简单的固定函数着色器代码示例

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

  注意:固定函数着色器需要完全使用ShaderLab语法来编写。

4.4 如何选择

  • 有明确需求必须要使用 固定函数着色器。(运行于少见,旧时代的设备)
  • 与各种光源打交道,使用 表面着色器。(需小心注意其在移动平台的表现)
  • 需要使用的光照数目非常少,使用 顶点/片元着色器。
  • 有很多自定义的渲染效果,使用 顶点/片元着色器。
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值