Unity Shader学习(1)-Shader的结构

Unity Shader(着色器)与Material(材质)的关系:

常见流程为:

  • 创建一个材质
  • 创建一个shader,并将其赋给上一步的材质
  • 将材质赋给要渲染的对象
  • 在材质面板调整shader的属性,达到满意的效果

Unity中的材质需要结合一个GameObject的Mesh或者Partical System组件来工作

Shader的基本结构:

Shader
        Properties(属性)
        Subshader(子着色器)
            Pass
        Subshader(另一个子着色器)
      
        Fallback(回滚,一般用于上述着色器无法运行的情况)

一个简单的Shader:

Shader "Custom/Diffuse Texture" {
    	Properties {
	              _MainTex ("Base (RGB)", 2D) = "white" {}
   		}
    	SubShader {
	                Tags { "RenderType"="Opaque" }
	                LOD 200
	
	              	CGPROGRAM
					#pragma surface surf Lambert

					sampler2D _MainTex;

					struct Input {
								float2 uv_MainTex;
					};

					void surf (Input IN, inout SurfaceOutput o) {
								half4 c = tex2D (_MainTex, IN.uv_MainTex);
								o.Albedo = c.rgb;
								o.Alpha = c.a;
					}
					ENDCG
			} 
			FallBack "Diffuse"
}

Properties(属性)

Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
}

一般格式为 _Name("Display Name", type) = defaultValue[{options}]

参考上述例子,可以解释为:

  • _Name即每个属性的名字,简单说就是变量名,在之后整个Shader代码中将使用这个名字来获取该属性的内容;在unity中,这些属性的名字通常由一个下划线开始。

  • Display Name即显示的名称,一般是出现在材质面板上的名字。

  • Type即属性的类型,常用的属性类型为:
    Int-整数
    Float-浮点数
    Range(min,max)-一个介于最大值和最小值之间的浮点数,一般用来当作调整shader某些特性的参数(比如透明度渲染的截止值可以是从0到1的值)
    Color-颜色,由RGBA四个量来定义
    Vector-一个四维数
    2D-一张2的阶数大小(256,512之类)的贴图,这张贴图将在采样后被转为对应基于模型UV的每个像素的颜色,最终被显示出来
    Cube-即Cube map texture(立方体纹理),简单说就是6张有联系的2D贴图的组合,主要用来做反射效果(比如天空盒和动态反射),也会被转换为对应点的采样;
    Rect-一个非2阶数大小的贴图

  • DefaultValue即属性的默认值,通过输入一个符合格式的默认值来指定对应属性的初始值(某些效果可能需要某些特定的参数值来达到需要的效果,虽然这些值可以在之后进行调整,但是如果默认就指定为想要的值就会省去调整的时间)
    Int Float Range这些数字类型的属性,默认值为单独的数字
    ColorVector默认值为圆括号包围的四维向量(x,y,z,w)
    2D Cube Rect3种纹理类型,默认值可以是一个代表tint颜色的字符串,也可以是空字符串或者内置的纹理名称如”white”, ”black”,”gray”, “bump”

  • {Options}花括号的用处原本是用于指定一些纹理属性的,例如可以通过TexGenCubeReflect TexGenCubeNormal等选项来控制固定管线的纹理坐标的生成,但是在Unity5.0及以后的版本中,这些选项被移除了。

它只对2D,Rect或者Cube贴图有关,在写输入时我们最少要在贴图之后写一对什么都不含的空白的{},当我们需要打开特定选项时可以把其写在这对花括号内。如果需要同时打开多个选项,可以使用空白分隔。可能的选择有ObjectLinear,EyeLinear, SphereMap, CubeReflect, CubeNormal中的一个,这些都是OpenGL中TexGen的模式

Subshader(子着色器)

  • 每一个shader文件可以包含多个Subshader语义块,但至少应该有一个
  • 当unity需要加载这个shader时,会扫描所有的Subshader语义块,然后选择第一个能够在目标平台运行的Subshader。如果都不支持的话,就执行Fallback(回滚)
  • 原因:不同的显卡具有不同的能力,在不同的显卡上执行不同复杂度的着色器。

基本结构:

Subshader{
    //可选的
    [Tags]标签
    
    //可选的
    [Rendersteup]状态
    Pass {
    }
    //其余Passes
}

状态设置[Rendersteup]

ShaderLab提供了一系列渲染状态的设置指令来设置显卡的渲染状态,常见选项有:

  • Cull-Cull Back/Front/Off(设置剔除模式,剔除背面/正面/关闭)
  • ZTest-ZTestLessGreater/LEqual/GEqual/Equal/NotEqual/Always(设置深度测试时使用的函数)
  • ZWrite-ZWrite On/Off(深度写入开启/关闭)
  • Blend-Blend SrcFactor DstFactor(开启并设置混合模式)

标签[Tags]

子着色器的标签[Tags]是一个键值对(Key/Value Pair),键和值都是字符串类型,它们用来告诉Unity的渲染引擎:我希望怎样以及何时渲染这个对象。
标签结构

Tags{"Tagname1" = "Value1" "Tagname2" = "Value2"}

Tags { "RenderType"="Opaque" }

RenderType为标签类型,"RenderType"="Opaque" 表明该着色器应该在渲染非透明物体时被调用,与之对应的是"RenderType" = "Transparent",在渲染透明物体的时候调用。在这里Tags其实暗示了你的Shader输出的是什么,如果输出中都是非透明物体,那写在Opaque里;如果想渲染透明或者半透明的像素,那应该写在Transparent中。
具体更加详细的标签类型见《Unity Shader入门精要》P32

这里想要着重说一下的是Queue这个标签,如果你使用Unity做过一些透明和不透明物体的混合的话,很可能已经遇到过不透明物体无法呈现在透明物体之后的情况。这种情况很可能是由于Shader的渲染顺序不正确导致的。Queue指定了物体的渲染顺序,预定义的Queue有:

  • Background - 最早被调用的渲染,用来渲染天空盒或者背景
  • Geometry - 这是默认值,用来渲染非透明物体(普通情况下,场景中的绝大多数物体应该是非透明的)
  • AlphaTest - 用来渲染经过Alpha Test的像素,单独为AlphaTest设定一个Queue是出于对效率的考虑
  • Transparent - 以从后往前的顺序渲染透明物体
  • Overlay - 用来渲染叠加的效果,是渲染的最后阶段(比如镜头光晕等特效)

这些预定义的值本质上是一组定义整数,Background = 1000, Geometry = 2000, AlphaTest =2450, Transparent = 3000,最后Overlay = 4000。在我们实际设置Queue值时,不仅能使用上面的几个预定义值,我们也可以指定自己的Queue值,写成类似这样:"Queue"="Transparent+100",表示一个在Transparent之后100的Queue上进行调用。通过调整Queue值,我们可以确保某些物体一定在另一些物体之前或者之后渲染,这个技巧有时候很有用处。

LOD

LOD很简单,它是Level of Detail的缩写,在这里例子里我们指定了其为200(其实这是Unity的内建Diffuse着色器的设定值)。这个数值决定了我们能用什么样的Shader。在Unity的Quality Settings中我们可以设定允许的最大LOD,当设定的LOD小于SubShader所指定的LOD时,这个SubShader将不可用。Unity内建Shader定义了一组LOD的数值,我们在实现自己的Shader的时候可以将其作为参考来设定自己的LOD数值,这样在之后调整根据设备图形性能来调整画质时可以进行比较精确的控制。

  • VertexLit及其系列 = 100
  • Decal, Reflective VertexLit = 150
  • Diffuse = 200
  • Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit= 250
  • Bumped, Specular = 300
  • Bumped Specular = 400
  • Parallax = 500
  • Parallax Specular = 600

Pass语义块

pass {
	[Name]
	[Tags]
	[RenderSetup]
	//other code
}

首先,在Pass中定义该Pass的名字Name "MyPassName"
通过名字,可以使用UsePass命令直接使用其他Shader中的PassUsePass "MyShader/MYPASSNAME"
这样可以提高代码的复用性
注意:在Unity中会把所有的Pass名字自动转化为大写,因此在用UsePass命令调用Pass时必须使用大写

其次,可以对Pass设置渲染状态RenderSetup,SubShader的渲染状态同样适用于Pass,除了上述的渲染状态外,Pass还可以使用固定管线的着色器
Pass同样可以设置标签Tags,与SubShader的标签不同,如LightMode定义Pass在Unity的渲染流水线中的角色,RequireOptions用于指定当满足某些条件时才渲染该Pass.

Fallback
在所有SubShader语义块后面定义一个Fallback指令,它用来指定当所有的子着色器无法使用的时候,调用Fallback中最低级的Shader.

Fallback "name" 
//或者
Fallback Off

Fallback "name":指出最低级的Shader,例Fallback "VertexLit"
Fallback Off:关闭Fallback功能


参考资料
猫都能学会的Unity3D Shader入门指南(一)
UnityShader入门精要

发布了8 篇原创文章 · 获赞 16 · 访问量 5740

猜你喜欢

转载自blog.csdn.net/JJJJJJJJJerry/article/details/86666216