Unity Shader入门精讲

基础知识

Unity 渲染需要两个组件 Mesh Filter 和 Mesh Renderer

Mesh Filter:存储一个Mesh(网格,模型的网格,就是模型的由哪些三角面组成,组成一个什么样子的模型,三角面的一些顶点信息)

Mesh Renderer: 用来渲染一个模型外观,按照Mesh的顶点,颜色以及Material(材质)控制模型渲染的样子

Material(材质):简单理解为贴图(Texture) + Shader组成(贴图可以没有也可以是单纯的颜色,但是Shader必须要有)

Shader:着色器,一种GPU编程语言,可由opengl或者dx来进行解析,来控制渲染

C#代码 -> CPU(准备好顶点数据等) -> Shader(Shader嵌入GPU的可编程流水线) -> 调用DX或者OpenGl -> GPU

Direct3D是微软推出的底层图形API,只能在Windows平台使用,显卡厂商在对应的驱动程序上实现DX接口,开发者便可以使用DX进行图形学开发
OpenGl 是专门的图形库,具有较好的移植性,主要在移动端使用广泛

DirectX 使用HLSL语言编写Shader
OpenGl 使用GLSL语言编写Shader

CG语言:Microsoft和 NVIDIA公司联手推出的GPU编程高级着色器语言。全称(C for Graphic)

Cg 是一个可以被 OpenGL 和 Direct3D 广泛支持的图形处理器编程语言。Cg 语言和OpenGL DirectX并不是同一层次的语言
而是OpenGL和DirectX的上层,即Cg 程序是运行在 OpenGL 和 DirectX 标准顶点和像素着色的基础上的

其二,Cg语言是Microsoft和NVIDIA相互协作在标准硬件光照语言的语法和语义上达成了一致,标题就是“Microsoft and NVIDIA’s Collaboration to Develop Cg and HLSL”,所以HLSL和Cg其实是同一种语言。很多时候,你会发现用 HLSL 写的代码可以直接当中 Cg 代码使用。也就是说,Cg 基于知识联盟(Microsoft 和 NVIDIA),且拥有跨平台性,选择 Cg 语言是大势所趋。有心的读者,可以注意市面上当前的 GPU 编程方面的书籍,大部分是基于 CG 语言的。
(附:Microsoft 和 NVIDIA 联手推出 Cg,应该是一种经济和技术上的双赢,通过这种方式联手打击 GLSL)

此外,Cg,即 C for Graphics,用于图形的 C 语言,这其实说明了当时设计人员的一个初衷,就是“让基于图形硬件的编程变得和 C 语言编程一样方便,自由”。正如 C++ 和 Java 的语法是基于 C 的,Cg 语言本身也是基于 C 语言的。如果您使用过 C、C++、Java 其中任意一个,那么 Cg 的语法也是比较容易掌握的。Cg 语言极力保留了 C 语言的大部分语义,力图让开发人员从硬件细节中解脱出来,Cg 同时拥有高级语言的好处,如代码的易重用性,可读性提高等。使用 Cg 还可以实现动画驱动、通用计算(排序、查找)等功能

CG编写shader 因为他是跨平台的 可以用OpenGL解析也可以使用DX解析

Unity中的Shader,是Unity封装了一层,即Shader Lab,其SubShader中的Pass使用CG语言来实现的,使得Unity开发的软件可以跨平台

Unity中的Shader分类:

  • Surface Shader
    Unity3d推崇的Shader类型,使用Unity预制的光照模型来进行光照运算(CG/HLSL语法)
  • Vertex and Fragment Shader
    最强大的Shader类型,属于可编程渲染管线。(CG/HLSL语法)
  • Fixed function shader
    属于固定渲染管线 Shader。在老显卡无法显示时则Fallback
编写Shader

Unity Shader使用的是ShaderLab语法,这是Unity自家创造的说明性语言,除了里面内嵌着色器代码(Cg/HLSL)外,最重要的是可以方便程序员去设置着色器的属性和状态,如Properties语句块中定义了着色器所需要的各种属性(颜色、纹理等),这些属性将会出现在属性面板中,在Editor里输入数据,而不用像传统的Shader中需要编写冗长的代码来设置着色器的输入和状态。设计上类似于CgFX和Direct3D Effect(.FX)语言,它们都定义了一个着色器所需要的输入数据和状态,而不仅仅是着色器代码。让Unity开发者绝大多数时候只需要和Unity Shader打交道便可以编写出色的游戏效果

演示Shader基本语法:

Shader"myshader/myshader01"{ 
	// Properties里写属性 (固定写法)
	Properties{
		// 第一个 _color 代表属性的名字 是自定义的
		// ("_color",Color) 里面的字符串是在Inspector面板显示的名字 是自定义的
		// ("_color",Color) 后面的Color代表定义的属性 是固定的
		// = (1,1,1,1) 这个()里面设置默认值 R G B A 

		// 颜色类型
		_Color("_Color",Color) = (1,1,1,1)
		// 四维向量类型 X Y Z W
		_Vector("_Vector",Vector) = (1,2,3,4)
		// 整数类型
		_Int("_Int",Int) = 100
		// 小数类型  默认值不加F  shader里面没有double类型
		_Float("_Float",Float) = 12.3
		// 范围类型 从哪个范围到哪个范围 这里设置的范围是1 - 100 默认值设置为了55 可以取到1和100
		_Range("_Range",Range(1,100)) = 55
		// 指定图片 类型  如果{}里指定图片了会显示图片的颜色 如果没有指定图片会显示为设置的纯色 这里为white
		_2D("_Texture",2D) = "white"{}
		// 矩形纹理属性
		_Rect("RectTex", Rect) = "white" {}
		// 立方体纹理 立方体贴图
		_Cube("_Cube",Cube) = "white"{}
		// 3D 纹理
		_3D("_Texure",3D) = "White"{}

	}
		// SubShader 可以有很多个  里面编写的是渲染的代码 利用属性编写一些 控制 渲染出来的效果
		// 为什么创建多个SubShader  因为不同的SubShader可以实现不同的效果 在不同的显卡上运行时需要
		// 比如不同的显卡对应不同的SubShader 好一点的对应 一个SubShader 差一点的对应另外一个SubShader
		// 1. 显卡运行效果时,从第一个SubShader开始,如果里面的效果都可以实现,那么就使用第一个SubShader
		// 2. 如果这个SubShader满足不了这个显卡的需求,它会自动跳到下一个,若所有的SubShader均不能执行,则
		// 3. 调用 Fallback"VertexLit" 默认 (Legacy Shaders中)
		
		// 一般第一个SubShader是显示效果最好的  第二个比第一个次一点 以此类推 这样会增加shader的适应能力
		SubShader{
		// SubShader 里面必须有一个 Pass块 也可以有多个
		// 一个Pass块代表一个方法 
		Pass{
		// 在这里编写Shader代码
			CGPROGRAM
			// 使用CG语言编写shader代码

			// 使用属性需要先定义 
			// 语法: 类型 名字   这里的名字需要和属性里面的一致
			// 不需要附默认值 会按 Properties 里面的来
			// 结尾需要带  ;  号
			float4 _Color;   
			// float4 可以使用 float half fixed 代替
			// 还有 float3   float2  float  分别代表里面有几个值 
			// 主要看是要怎么用  比如 float4 可以存储值 也可以存储向量 也可以使用float3存储向量
	        // 这里的float 的使用主要是看想存储几个值 和 怎么用
	        // 在任何使用float的地方都可以使用 half 和 fixed 代替 比如 half3 half2 fixed3 fixed2
			// float 和 half 的区别:精度范围(可在优化时调整)
			//  值   二进制位         范围
			// float    32   -2147483648 到 2147483647
			// half		16          -6万 到 +6万
			// fixed    11          -2万  到 +2万
			// 一般颜色都使用fixed存储 位置使用float存储 half使用的较少

			float4 _Vector;
			float _Int;
			float _Float;
			float _Range;
			sampler2D _2D;
			samplerCube _Cube;
			sampler3D _3D;


		ENDCG
	}
	}
	// 上面的SubShader都不执行的时候 执行Fallback
	// Fallback用来指定一个已经存在了的Shader 
	Fallback"VertexLit"
}

类型使用技巧扩展:

float 32位浮点数
half 16位浮点数
int 32位整形数
fixed 12位定点数
bool 布尔数据
float2×4 matrix; //表示2×4阶矩阵,包含8个float类型数据

1.精度够用就好
2.颜色和单位向量,使用fixed
3.其他情况,尽量使用half(即范围在[-6万,+6万]、精确的小数点3.3位);否则才使用用float

定义顶点函数和片元函数:

Shader"myshader/myshader02"{
	// 如果不需要任何属性的话 是可以不设置属性的

	SubShader{
	Pass{
	CGPROGRAM

	// 定义两个系统函数 由系统自己调用的函数
	// 顶点函数 声明了顶点函数的函数名
#pragma vertex vert   // 顶点函数定义语法 #pragma vertex 是固定的 vert为函数名 是自己设置的
	// 顶点函数的基本作用是: 完成顶点坐标到剪裁空间的转换(从游戏环境转换到视野相机屏幕上)
	
	// 定义和使用顶点函数 (float4 v:POSITION) 系统传递过来参数给v 后面加:POSITION 的意思是把顶点坐标传给v
	float4 vert(float4 v:POSITION):SV_POSITION 
	// 通过语义告诉系统,我这个参数是干嘛的,比如POSSITION是告诉系统我需要顶点坐标
	// SV_POSITION这个语义用来解释说明返回值,意思是返回值是剪裁空间下的顶点坐标
	// 语义写法是固定的 不同的语义代表不同的效果
	    {
		// mul()是一个方法可以完成一个 模型空间坐标到剪裁空间的转换   一个矩阵和一个postition的乘法运算
		// 第一个参数是一个矩阵 这里使用的是 矩阵宏(固定写法 代表的是一串数字)  第二个是顶点坐标
		// 这时V里面的W就有了值 但是在坐标上没有实际意义 就是用来辅助坐标转换的
		float4 pos =mul(UNITY_MATRIX_MVP,v);
		return pos;
			
	    }

	// 片元函数 这里是声明了,片元函数的函数名
#pragma fragment frag // 片元函数定义语法 #pragma fragment 是固定的 frag为函数名 是自己设置的
	// 片元函数的基本作用:返回模型对应屏幕上的每一个像素的颜色值
	
		// 定义和使用片元函数
		// 这里返回一个颜色值 所以这个方法返回 fixed4类型  现在这个参数不需要 以后可能会写参数进去
		fixed4 frag():SV_Target // :SV_Target 这个语义代表模型对应到屏幕上的颜色
		{
			// 返回的颜色 R G B A 但是需要跟上类型 这里是fixed4类型
			return fixed4(1,1,1,1);
		}


	ENDCG
	}
	}
		Fallback "VertexLit"
}

正弦相位水波:

Shader "Water/waterShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;

				// 相位
				float dist = distance(v.vertex.xyz, float3(0, 0, 0));
				//float h = sin(dist + _Time.z);
				float h = sin(dist * 2 + _Time.z) / 5;
				
				o.vertex = mul(unity_ObjectToWorld, v.vertex);
				o.vertex.y = h;
				o.vertex = mul(unity_WorldToObject, o.vertex);

                o.vertex = UnityObjectToClipPos(o.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);

                return col;
            }
            ENDCG
        }
    }
}

模型物体表面发射波光,光效等:

Shader "Water/ShipShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {} // 物体的纹理
		_SubTex("Texture", 2D) = "white" {} // 需要反射的波光的纹理 背景为黑色 只显示闪耀波光
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
			sampler2D _SubTex;

            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {

				float2 uv_Offset = float2(0.0, 0.0);
				uv_Offset.x = _Time.y * 0.5;  // t
				uv_Offset.y = _Time.y * 0.5;

                // sample the texture
                //纹理越界问题 不需要考虑
				// 纹理已经设置成 Wrap Mode -> Repeat
				fixed4 colLight = tex2D(_SubTex, i.uv + uv_Offset); // 随时间随机采样对应位置的像素颜色
                fixed4 col = tex2D(_MainTex, i.uv);
				//fixed col = fixed4(1.0, 0.0, 0.0, 1.0);
				col += colLight;
                return col;
            }
            ENDCG
        }
    }
}

GrabPass截屏通道:

Shader "Unlit/GrabPassShader"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
	}
		SubShader
	{
		Tags { "RenderType" = "Opaque" "Queue" = "Overlay"} //最后被绘制
		LOD 100

		GrabPass {}
		//GrabPass {} // 截图通道, 后面使用_GrabTexture访问截屏纹理

		Pass
		{
			name "GRABPASS"
			
			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;
			};

			sampler2D _MainTex;
			sampler2D _GrabTexture;

			float4 _MainTex_ST;
			float4 _GrabTexture_ST;

			v2f vert(appdata v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _GrabTexture);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				// sample the texture
				fixed4 col = tex2D(_GrabTexture, i.uv);
			return col;
			}
		ENDCG
		}
	}
}

UsePass:

Shader "Unlit/UsePass"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
	}
	
	SubShader
	{
		Tags { "RenderType" = "Opaque" }
		LOD 100


		UsePass "Unlit/GrabPassShader/GRABPASS"
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值