第二天 开始Unity Shader的学习之旅之熟悉顶点着色器和片元着色器

发布于:2025-03-27 ⋅ 阅读:(29) ⋅ 点赞:(0)

Shader初学者的学习笔记

第二天 开始Unity Shader的学习之旅之熟悉顶点着色器和片元着色器



前言

今天对入门精要第五章进行一个小小的总结和归纳,主要是学习顶点着色器和片元着色器的结构.


一、顶点/片元着色器的基本结构

Shader "Unity Shaders Book/Chapter 5/ Simple Shader"{
	SubShader														②
	{
		Pass
		{
			CGPROGRAM												③
			#pragma vertex vert										④
			#pragma fragment frag
			
			float4 vert (float4 : POSITION) : SV_POSITION			⑤
			{
				return mul (UNITY_MATRIX_MVP, v):
			}
			
			fixed4 frag() : SV_Target								⑥
			{
				return fixed4 (1.0, 1.0, 1.0, 1.0);
			}
			
			ENDCG
		}
	}
}

① Shader “Unity Shaders Book/Chapter 5/ Simple Shader”

该语句定义了Unity Shader的语义 – “Unity Shaders Book/Chapter 5/ Simple Shader”,这个名字有利于我们快速选择到自定义的Shader.

② SubShader

你会发现,我们没有声明任何的材质属性,因为Properties不是必须的, 同时在这个SubShader中没有Tags, RenderSetup等,因此SubShader会使用默认的渲染设置和标签设置.
并且我们在SubShader中定义的Pass也没有设置Tags和RenderSetup.

③ CGPROGRAM和ENDCG

我们把cg代码片段放在CGPROGRAM和ENDCG中;

④ 指明顶点着色器和片元着色器函数名称

#pragma vertex name1
#pragma fragment name2

name1, name2是我们自己指定的函数名,可以告诉Unity,name1包含了顶点着色器的代码,name2包含了片元着色器的代码,这个函数的名字不一定要使用vertex和frag,但是我们一般用使用这两个来定义函数,因为他们很直观.

⑤ 顶点着色器

float4 vert (float4 : POSITION) : SV_POSITION
{
	return mul (UNITY_MATRIX_MVP, v):
}

需要再提一次,顶点着色器是逐顶点执行的, vert函数的输入是float4类型的顶点位置,这是通过POSITION语义指定的,vert函数的输出也是float4类型的,即顶点在裁剪空间的位置,由SV_POSITION的语义指定的;
POSITION 和 SV_POSITION都是Cg/HLSL的语义,他们是不可忽略的,他们会告诉Unity输入和输出,例如:POSITION告诉Unity,将模型的顶点坐标填充到参数v中,SV_POSITION告诉Unity,顶点着色器的输出是裁剪空间的坐标,所以使用这些语义现在输入输出参数是非常有必要的.
再看顶点着色器的代码,这一句代码的含义就是将顶点坐标从模型空间转换到裁剪空间.
补充:UNITY_MATRIX_MVP矩阵是Unity内置的模型观察投影矩阵.

⑥ 片元着色器

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

在片元着色器中,frag函数没有任何输入,他的输出是一个fixed4类型的变量,并且使用了SV_Target语义进行限定,SV_Target也是HSLSL中的一个系统语义,他等同于告诉渲染器,把用户的输出颜色存储到渲染目标中,这里将输出到默认的帧缓存中.
片元着色器的代码也很简单,返回一个白色的fixed4变量.

二、模型数据从哪来?

我们如果想要得到模型上的每个顶点的纹理坐标和法线方向,那么我们就不能只是将float4作为顶点着色器的输入了,我们就需要自己定义一个结构体,如下:

Shader "Unity Shaders Book/Chapter 5/ Simple Shader"
{
	Properties
	{
		_Color ("Color Tint", Color) = (1.0, 1.0, 1.0, 1.0)
	}
	SubShader
	{
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			fixed4 _Color
			
			struct a2v{
				float4 vertex : POSITION;float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			}
			
			struct v2f{
				float4 pos : SV_Position;								
				fixed3 color : COLOR0;
			}
			
			v2f vert (a2v v) : SV_POSITION							
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				o.color = v.normal * 0.5 + fixed3 (0.5, 0.5, 0.5);
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target
			{
				fixed3 c = i.color;
				c *= _Color.rgb;
				return fixed4(c, 1.0);
				
			}
			
			ENDCG
		}
	}
}

① a2v

在上面的代码中,我声明了新的结构体a2v,这里面包含很多数据:模型空间的顶点坐标,法线方向,以及模型的第一套纹理坐标,这些都是通过特定的语义进行修饰的,unity支持的语义有:POSITION, TANGENT, NORMAL, TEXCOORD0, TEXCOORD1, TEXCOORD2, TEXCOORD3, COLOR等
注意:语义是不可以被忽略的

是不是很疑惑为什么要起名字为a2v,因为a是应用,v表示顶点着色器,a2v的意思就是从应用阶段到顶点着色器.

② 语义中的数据从何而来

这些语义的数据是从哪来的呢?他们其实是由Mesh Render组件剔红的,在每帧调用Draw Call的时候, Mesh Render组件会把它负责渲染的模型数据发送给Unity Shader.
我们知道,一个模型通常包含一组三角面片,每个三角面片由三个顶点组成,每个顶点又包含一些数据:顶点位置,法线,切线,纹理坐标,顶点颜色等,通过上面的方法,我们就可以在顶点着色器中访问顶点的模型数据了.

③ v2f

我们在顶点着色器进行了一些变换之后,希望把模型的法线,纹理坐标等传递给片元着色器,那么这个传递过程是怎样的呢?
我们首先定义了一个v2f的结构体,用于在顶点着色器和片元着色器之前传递信息,为了将模型渲染到屏幕上,我们必须在顶点着色器的输出中定义一个变量,他的语义是SV_POSITION,COLOR0一般用于存储颜色,也可由用户自行定义.

④ 如何使用属性

在上面的代码中,我们声明了属性:_Color,为了可以在cg中访问它,我们需要在Cg代码片段中提前定义一个名称和类型与Properties中属性相匹配的变量.
下面表格记录了属性的类型和变量类型的匹配关系

ShaderLab属性的类型 Cg变量的类型
Color, Vector float4, half4, fixed4
Range, float float, half, fixed
2D sampler2D
Cube samplerCube
3D sampler3D

补充

uniform fixed4 _Color;

在上述Cg变量前,有uniform变量进行修饰,他仅仅用于提供一些关于该变量的初始值是如何指定和存储的相关信息,在Unity Shader中是可以省略的.


总结

今天我对第五章的顶点着色器和片元着色器的知识进行了总结,为以后复杂的Unity Shader的学习打下了坚实的基础!!!