Unity Shader基础 - Shader入门精要学习(2)

Unity Shader基础

1 Unity Shader概述

1.1 Unity中配合使用材质和Shader的常见流程

  1. 创建一个材质
  2. 创建一个Unity Shader
  3. 把材质赋给需要渲染的对象
  4. 在材质面板中调整Unity Shader的属性,以得到满意的效果

Unity Shader定义了渲染所需的各种代码、属性和指令,而材质则允许我们调节这些属性,并将其赋值给相应的模型

1.2 Unity提供的Shader模板

  • Standard Surfacel Shader:产生一个包含标准光照模型的表面着色器模板
  • Unlit Shader:产生一个不包含光照的基本的顶点/片元着色器
  • Image Effect Shader:为实现各种屏幕后处理效果提供一个基本模板
  • Compute Shader:产生一种特殊的Shader文件,利用CPU的并行性来进行一些与渲染流水线无关的计算

通常使用Unlit Shader来生成一个基本的顶点/片元着色器模板

Shader需要与材质结合才能发挥作用,在材质下方选择需要执行的Unity Shader,材质中才会出现Unity Shader的各种属性

(颜色、纹理、浮点数、滑动条等),当把材质赋给场景中一个对象时,就可以看到调整属性所发生的视觉变化

2 ShaderLab

在Unity中,所有的Shader都是使用ShaderLab来编写的,ShaderLab是Unity提供的编写Unity Shader的一种说明性语言

// 一个Unity Shader的基础结构
Shader "ShaderName"{
    Properties{
    	// 属性
    }
    SubShader{
    	// 显卡A使用的子着色器
    }
    SubShader{
    	// 显卡B使用的子着色器
    }
    Fallback "VertexLit"
}

3 Unity Shader的结构

3.1 为Shader取名

在Unity Shader文件的第一行通过Shader语义来指定该Unity Shader的名字,由字符串定义

通过在字符串中加“/”可以控制Unity Shader在材质面板中出现的位置

Shader "Costom/MyShader"{}   // 在材质面板中的位置Shader->Costom->MyShader中出现

3.2 材质和Shader的桥梁:Properties

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

声明这些属性是为了在材质面板中能够更方便德调整各种材质属性

想要在Shader中访问它们,就需要使用每个属性的名字(Name),属性名通常由一个下划线开始,显示的名称(display name)则是出现在材质面板上的名字,同时,需要为每个属性指定它的类型(PropertyType)

Property语义块支持的属性类型

属性类型默认的定义语法例子
Intnumber_Int(“Int”, Int) = 2
Floatnumber_Float(“Float”, Float) = 1.5
Range(min, max)number_Range(“Range”, Range(0.0, 5.0)) = 3.0
Color(number, number, number, number)_Color(“Color”, Color) = (1, 1, 1, 1)
Vector(number, number, number, number)_Vector(“Vector”, Vector) = (2, 3, 6, 1)
2D“defaulttexture”{}_2D(“2D”, 2D) = “” {}
Cube“defaulttexture”{}_Cube(“Cube”, Cube) = “white” {}
3D“defaulttexture”{}_3D(“3D”, 3D) = “black” {}

对于2D、Cube、3D这种纹理类型,默认值的定义稍微复杂,默认值是通过一个字符串后跟一个花括号来指定的,其中,字符串要么是空的,要么是内置的纹理名称,如"white",“black”,“gray"或者"bump”

Shader "Custom/ShaderLabProperties"
{
    Properties
    {
        // Numbers and Sliders
        _Int("Int", Int) = 2
        _Float("Float", Float) = 1.5
        _Range("Range", Range(0.0, 5.0)) = 3.0

        // Colors and Sliders
        _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"
}

3.3 重量级成员:SubShader

当Unity需要加载这个Unity Shader时,Unity会扫描所有的SubShader语义块,然后选择第一个能够在目标平台上运行的SubShader。如果都不支持的话,Unity就会使用Fallback语义块指定Unity Shader

每一个Unity Shader文件可以包含多个SubShader语义块,但最少要有一个

原因:不同显卡的能力不同,高级的显卡能支持更多的指令数,可以计算复杂度较高的着色器

SubShader{
	// 可选的
	[Tags]
	
	// 可选的
	[RenderSetup]
	
	Pass{
	
	}
	// Other Passes
}

SubShader中定义了一系列Pass以及可选的状态([RenderSetup])和标签([Tags])

3.3.1 状态设置RenderSetup

ShaderLab提供了一系列渲染状态的指令,这些指令可以设置显卡的各种状态,例如混合/开启深度测试等

常见的渲染状态设置选项

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

当在SubShader块中设置了上述渲染状态时,将会应用到所有的Pass,如果不想这样,可以在Pass语义块中单独进行上面的设置

3.3.2 SubShader的标签Tags

是一个键值对,键和值都是字符串类型,这些键值对时SubShader和渲染引擎之间的沟通桥梁。它用来告诉Unity的渲染引擎:SubShader希望怎样以及何时渲染这个对象

Tags {"TagName1" = "Value1" "TagName2" = "Value2"}
标签类型说明例子
Queue控制渲染顺序,指定一个物体属于哪个渲染队列,通过这种方式可以保证所有的透明物体可以在所有不透明物体后面被渲染,我们也可以自定义使用的渲染队列来控制物体的渲染顺序Tags {“Queue” = “Transparent”}
RenderType对着色器进行分类,例如这是一个不透明的着色器,或是一个透明的着色器等。这可被用于着色器替换(Shader Repalcement)等功能Tags {“RenderType” = “Opaque”}
DisableBatching一些SubShader使用Unity的批处理功能可能会出现问题,例如使用了模型空间下的坐标进行顶点动画。这时可以通过该标签来直接指明是否对该SubShader进行渲染Tags {“DisableBatching” = “True”}
ForceNoShadowCasting控制使用该SubShader的物体是否会投射阴影Tags {“ForceNoShadowCasting” = “True”}
IgnoreProjector如果该标签值为”True“,那么使用该SubShader的物体将不会受Projector(投影机)的影响,通常用于半透明物体Tags {“IgnoreProjector” = “True”}
CanUseSpriteAtlas当该SubShader是用于精灵Sprite时,则将该标签设为FalseTags {“CanUseSpriteAtlas” = “True”}
PreviewType指明材质面板将如何预览该材料,默认情况下,材质将显示为一个球形,我们可以通过把该标签的值设置为”Plane“”SkyBox“来改变预览类型Tags {“PreviewType” = “Plane”}

注意:上述标签可以在SubShader中声明,而不可以在Pass语义块中声明,Pass块虽然也可以定义标签,但这些标签是不同于SubShader的标签类型

3.3.3 Pass语义块
Pass{
	[Name]
	[Tags]
	[RenderSetup]
	// Other Code
}
  1. 定义Pass的名称:
Name "MyPassName"
  1. 设置渲染状态

与SubShader的状态设置一样,除此之外,还可以使用固定管线的着色器命令(见后)

  1. Pass标签

Pass同样可以设置标签,但与SubShader的标签不同,这些标签也用于告诉渲染引擎如何渲染物体

标签类型说明例子
LightMode定义该Pass在Unity的渲染流水线中的角色Tags {“LightMode” = “ForwardBase”}
RequireOptions用于指定当满足某些条件时才渲染该Pass,它的值是一个由空格分隔的字符串。目前Unity支持的有:SoftVegetation,后面版本中会增加更多Tags {“RequiredOptions” = “SoftVegetation”}
  1. 定义一些特殊Pass
  • UsePass:通过这个名称可以使用ShaderLab中的UsePass命令来直接使用其他UnityShader中的Pass
UsePass "MyShader/MYPASSNAME"   

可以提高代码复用性,注意:Unity内部会把所有的Pass的名称转换成大写字母的表示,所以使用UsePass时必须使用大写

  • GrabPass:该Pass负责抓取屏幕并将结果存储在一张纹理中,以用于后续的Pass处理

3.4 留一条后路:Fallback

用于告诉Shader:如果上面所有SubShader能在该显卡上运行,那么就使用这个最低级的Shader吧

Fallback "name"
Fallback off

举例:

Fallback "VertexLit"

事实上Fallback还会影响阴影的投射,在渲染阴影纹理时,Unity会在每个Unity Shader中寻找一个阴影投射的Pass,通常情况下我们不需要自己专门实现一个Pass,Fallback使用的内置Shader中包含一个通用的Pass。因此,为每个UnityShader正确设置Fallback时非常重要的

4 Unity Shder的形式

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

4.1 表面着色器

Unity自己创造的着色器类型,代码量很少但是Unity在背后做了很多工作,渲染代价比较大。本质上与顶点/片元着色器相同,Unity内部会将该着色器转换为对应顶点/片元着色器,表面着色器是对顶点/片元着色器更高一层的抽象

Shader "Custom/Simple Surface Shader"{
	SubShader{
		Tags { "RenderType" = "Opaque" }
		CGPROGRAM
		#progma surface surf Lambert
        struct Input{
        	float color : COLOR;
        };
        void surf {Input IN, inout SurfaceOutput o}{
        	o.Albedo = 1;
        }
		ENDCG
	}
	Fallback "Diffuse"
}
  • 表面着色器定义在CGPROGRAM和ENDCG之间,不关心使用多少个Pass、每个Pass如何渲染等问题,Unity会在背后做好这些事情。我们只需要告诉表面着色器使用哪些纹理填充颜色,使用哪个法线纹理去填充法线,使用Lambert光照模型等

  • CGPROGRAM和ENDCG之间的代码使用的是CG/HLSL编写的,需要将CG/HLSL嵌入ShaderLab中。但是这里的CG/HLSL和原生的CG/HLSL由细微的区别,经过Unity封装过

4.2 顶点/表面着色器

顶点着色器也是用CG/HLSL来编写,定义在CGPROGRAM和ENDCG

Shader "Custom/Simple Surface Shader"{
	SubShader{
		Pass{
			CGPROGRAM
            #progma vertex vert
            #progma 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
		}
	}
}

顶点/片元着色器的代码需定义在Pass语义内,我们需要自定义每个Pass需要使用的Shader代码,虽然代码量更大,但是灵活性更高

4.3 固定函数着色器

较旧的设备不支持可编程管线着色器,这时就需要固定函数着色器来完成渲染,往往只可以实现一些简单的效果

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

固定函数着色器的代码被定义在Pass内,相当于Pass的一些渲染设置,需要完全使用ShaderLab语法

5 一些相关问题

5.1 Unity Shader != Shader

Unity中Shader实际是ShderLab文件,以.Shader作为后缀的一种文件

传统ShaderUnity Sha
仅可以编写特定类型的Shader,如顶点着色器、片元着色器等可以在同一个文件里同时包含需要的顶点着色器和片元着色器
无法设置一些渲染设置,如是否开启混合、深度测试等可以通过一行特定的指令完成
需要编写冗长的代码来设置着色器的输入和输出,需要小心的处理输入和输出的位置对应关系只需在特定语句块中声明一些属性,就可以依赖材质改变这些属性

由于Unity Shader的高度封装性,可以编写的Shade类型和语法被限制了,对应一些特定类型的Shder如曲面细分着色器和几何着色器等相关功能就不支持

5.2 Unity Shader和CG/HLSL的区别

ShaderLab中可以在CGPROGRAM和ENDCG之间嵌入CG/HLSL语言

5.3 可以使用CH/HLSL来写Shder吗

可以,但是可以发布的目标平台就只有Mac OS X,OpenGL ES 2.0 或者Linux,不支持仅支持DirectX的平台

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity Shader是一种用于渲染图形程序,它可以控制对象的表面颜色、纹理、透明度、反射等属性,从而实现特殊的视觉效果。对于游戏开发者来说,掌握Shader编写技巧是非常重要的。 以下是关于Unity Shader入门精要: 1. ShaderLab语言 ShaderLab是Unity中用于编写Shader的语言,它是一种基于标记的语言,类似于HTML。ShaderLab可以用于定义Shader的属性、子着色器、渲染状态等信息。 2. CG语言 CG语言是Unity中用于编写Shader的主要语言,它是一种类似于C语言的语言,可以进行数学运算、向量计算、流程控制等操作。CG语言可以在ShaderLab中嵌入,用于实现Shader的具体逻辑。 3. Unity渲染管线 Unity渲染管线包括顶点着色器、片元着色器、几何着色器等组件,每个组件都有不同的作用。顶点着色器用于对对象的顶点进行变换,片元着色器用于计算每个像素的颜色,几何着色器用于处理几何图形的变形和细节等。 4. 模板和纹理 在Shader中,我们可以使用纹理来给对象添加图案或者贴图,也可以使用模板来控制对象的透明度、反射等属性。纹理可以通过内置函数tex2D()来获取,模板可以通过内置函数clip()来实现裁剪。 5. Shader的实现 Shader的实现需要注意以下几点: - 在ShaderLab中定义Shader的属性、子着色器、渲染状态等信息。 - 在CG语言中实现Shader的具体逻辑,包括顶点着色器、片元着色器等内容。 - 使用纹理和模板来实现特定的视觉效果。 - 在对象上应用Shader,通过调整Shader的属性来达到不同的效果。 以上是关于Unity Shader入门精要,希望对你有所帮助。如果你想更深入地了解Shader的编写技巧,可以参考官方文档或者相关教程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值