Unity3D学习笔记3——Unity Shader的初步使用
Unity3D学习笔记3——Unity Shader的初步使用
目录
1. 概述
2. 详论
2.2.1. 名称
2.2.2. 属性
2.2.3. SubShader
2.2.4. 回退(FallBack)
2.2.3.1. 标签(Tags)
2.2.3.2. 渲染状态(RenderSetup)
2.2.3.3. 通道(Pass)
2.1. 创建材质
2.2. 着色器
2.3. 渲染管线
1. 概述
在上一篇文章《Unity3D学习笔记2——绘制一个带纹理的面》中介绍了如何绘制一个带纹理材质的面,并且通过调整光照,使得材质生效(变亮)。不过,上篇文章隐藏了一个很重要的细节——Unity Shader。Shader(着色器)是渲染管线中可被用户编程的阶段,依靠着色器可以控制渲染管线的细节。现代图像渲染技术,都把Shader封装成与Material(材质)相关的组件。所以这篇文章,我们就初步学习下在Unity中使用Shader。
2. 详论
2.1. 创建材质
在上一章中,材质、以及材质相关的资源是在Unity3D编辑器中创建,在C#脚本中直接引用的。这里为了学习使用Shader,我们使用自定义的Shader,可以在C#脚本中创建材质。修改上一章代码的材质部分:
Shader shader = Shader.Find("Custom/MainShader"); Material material = new Material(shader); Texture2D texture = Resources.Load<Texture2D>("ImageDemo"); material.mainTexture = texture; MeshRenderer meshRenderer = newGameObject.AddComponent<MeshRenderer>(); meshRenderer.material = material;
可以看到,要创建一个Material,首先得创建一个Shader。我们在Project视图中右键菜单->Create->Standard Surface Shader,创建一个标准表面着色器MainShader:
双击打开这个Shader,可以看到这个Shader的具体内容。标准着色器很复杂,我们清空里面的内容,填入我们这个更简单的着色器示例:
Shader "Custom/MainShader"{ Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags{"Queue" = "Geometry"} Cull Back Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag sampler2D _MainTex; //顶点着色器输入 struct a2v { float4 position : POSITION; float3 normal: NORMAL; float2 texcoord : TEXCOORD0; }; //顶点着色器输出 struct v2f { float4 position: SV_POSITION; float2 texcoord: TEXCOORD0; }; v2f vert(a2v v) { v2f o; o.position = mul(UNITY_MATRIX_MVP, v.position); o.texcoord = v.texcoord; return o; } fixed4 frag(v2f i) : SV_Target { return tex2D(_MainTex, i.texcoord); } ENDCG } } FallBack "Diffuse"}
2.2. 着色器
Unity使用的着色器语言叫做ShaderLab,它是图形渲染中Shader(例如GLSL,HLSL以及CG)的更高级更抽象一级的封装。ShaderLab是个非常简单的说明性描述语言,通过嵌套在花括号中的语义来描述Unity Shader文件。
2.2.1. 名称
通过Shader语义指定Unity Shader的名称:
Shader "Custom/MainShader"{ }
这个名称非常重要,在Unity编辑器中需要通过这个名字来引用Shader。
2.2.2. 属性
Shader语义块的第一个语义块是Properties语义块,它连接着材质和Unity3d编辑器,设置了这个属性就能够通过材质面板调整材质,调整材质的本质就是调整Shader。Properties的定义通常描述如下:
Properties { Name ("display name",PropertyType) = DefaultValue }
Name指的是在Shader中使用的名称,display name指的是显示在材质面板的名称。PropertyType则有点容易混淆,它指的是显示在材质面板中的属性类型,借用一下《Unity Shader入门精要》的图表:
2.2.3. SubShader
每个Unity Shader都至少包含一个SubShader语义块,Unity会优先选择第一个能够在当前平台下运行的SubShader作为最终渲染效果的Shader。
这个语义块下面又会包含三个语义块:
2.2.3.1. 标签(Tags)
SubShader的标签用于用于标识何时以何种方式被渲染到渲染引擎,它由一系列键值对组成。Queue是最常用的标签,用于标识渲染物体在渲染队列中的位置:
我们这里,把这个渲染物体放到Geometry队列中,这个位置通常放置不透明物体的渲染:
Tags{"Queue" = "Geometry"}
2.2.3.2. 渲染状态(RenderSetup)
渲染状态用于设置图形硬件的各种状态,例如是否应开启 Alpha 混合或是否应使用深度测试等。在像OpenGL这样的图形接口中,通常是以函数的形式进行调用的,Unity3d将其放在Shader里面,也有一定的道理。
这里的渲染状态设置成将背面裁剪掉:
Cull Back
2.2.3.3. 通道(Pass)
在Pass语义块中,才是像OpenGL/DirectX中使用的Shader。OpenGL使用的着色器语言叫做GLSL,DirectX使用的着色器语言叫做HLSL,Unity3D则推荐使用Cg语言,这是一种类C语言,与HLSL非常相似。Cg语言代码段在Pass语义块中被包裹在CGPROGRAM和ENDCG之间:
CGPROGRAM//...ENDCG
2.2.4. 回退(FallBack)
FallBack定义了一种退化策略,由于不同机器支持的性能特性不同,如果之前的子着色器都不生效,那么就使用这个着色器,通常这个着色器是内置的:
FallBack "Diffuse"
2.3. 渲染管线
图形渲染引擎的渲染管线其实是个内涵非常丰富的概念,再次借用《Unity Shader入门精要》的插图,渲染管线的描述大致如下:
当然只看这个图是不够的,但是我们可以直接从代码层面去了解它。镶嵌在CGPROGRAM和ENDCG之间的CG代码,体现的正是渲染管线的思维。
首先,通过编译指令,分别指定顶点着色器程序和片元着色器程序:
#pragma vertex vert #pragma fragment frag
vert就是顶点着色器的函数,在这个着色器程序中指定了计算了顶点坐标和纹理坐标:
v2f vert(a2v v){ v2f o; o.position = mul(UNITY_MATRIX_MVP, v.position); o.texcoord = v.texcoord; return o; }
传入参数是一个结构体,POSITION,NORMAL,TEXCOORD0是Unity Shader中固定的语义,分别代表这位置、法向量以及纹理坐标,他们也被称为顶点属性。还记得在上一篇文章《Unity3D学习笔记2——绘制一个带纹理的面》中创建Mesh时给Mesh创建的成员变量vertices、uv和normals吧?给他们传入的数据正是在这里用到了。
//顶点着色器输入struct a2v{ float4 position : POSITION; float3 normal: NORMAL; float2 texcoord : TEXCOORD0; };
传出参数则是另外一个结构体:
//顶点着色器输出struct v2f{ float4 position: SV_POSITION; float2 texcoord: TEXCOORD0; };
SV_POSITION表示的是裁剪空间坐标,也就是在顶点着色器中计算的顶点值。这个计算内容的内涵也挺丰富的,简单来说,创建Mesh时的顶点坐标,经过一个模型变换(Model)、视图变换(View)、投影变换(Projection),最终变成了裁剪空间坐标系中的坐标,体现在着色器中,就是内置的MVP矩阵UNITY_MATRIX_MVP。
剩下的就是片元着色器函数的部分了。在这个着色器中,_MainTex也就是我们先前创建的,并且传递到材质中的纹理,通过将顶点着色器中传递过来的纹理坐标进行采样,得到具体的片元颜色:
sampler2D _MainTex;fixed4 frag(v2f i) : SV_Target { return tex2D(_MainTex, i.texcoord); }
最终显示的效果如下:
可以看到这里显示的就是图片本身的颜色,这是因为在着色器中只是采样了图片的颜色,并没有光照计算的参与。也就是在图形引擎中,任何效果的设置只是表象,任何效果的实现都会归结到着色器中。
来源https://www.cnblogs.com/charlee44/p/15087159.html