1. 一个最简单的顶点/片元着色器

现在,我们正式开始学习如何编写Unity Shader,更准确的说是,学习如何编写顶点/片元着色器

2.顶点/片元着色器的基本结构

我们在以前已经讲过了Unity Shader的基本结构。它包含了Shader、Properties、SubShader、Fallback等语义块。顶点/片元着色器的结构与之大体类似,它的结构如下:

Shader "MyShaderName"{
//属性
}SubShader{//针对显卡A的SubShaderPass{//设置渲染状态和标签//开始Cg代码片段CGPROGRAM//改代码片段的编译指令,例如:#pragma vertex vert#pragma fragment frag//Cg代码写在这里ENDCG//其他设置}//其它需要的PassSubShader{//针对显卡B的Shader}//上述SubShader都失败后用于回调的UnityShaderFallback "VertexLit"
}

其中,最重要的部分是Pass语义块,我们绝大部分代码都是写在这个语义块里的。下面我们就来创建一个最简单的顶点/片元着色器。
(1)新建一个场景,如下图所示:

可以看到,场景中已经包含了一个摄像机、一个平行光。而且场景的背景不是纯色,而是一个天空盒子(Skybox)。这是因为在Unity5.x中,默认的天空盒子不为空,而是Unity内置的一个天空盒子。为了得到更加原始的效果,我们选择去掉这个天空盒子。做法是,在Unity的菜单中,选择Window->Lighting->Skybox,把该项置空。
(2)新建一个UnityShader
(3)新建一个材质,把新建的UnityShader赋给它
(4)新建一个球体,把刚才的材质赋给它
(5)打开新建的Shader,删除里面的代码,把下面的代码粘进去

保存并返回Unity查看结果。
最后我们得到的结果如图所示:

这就是我们遇见的第一个真正意义上的顶点/片元着色器,我们有必要来详细的解释一下它。
首先,代码的第一行通过Shader语义定义了这个UnityShader的名字。需要注意的是,在上面的代码里,我们并没有用到Properties语义块。Properties语义并不是必须的,我们可以选择不声明任何材质属性。
然后我们声明了SubShader和Pass语义块。在本例中,我们不需要进行任何渲染设置和标签设置,因此SubShader将使用默认的渲染设置和标签设置。在SubShader语义块中,我们定义了一个Pass,在这个Pass中,我们同样没有进行任何自定义的渲染设置和标签设置。
接着就是由CGPROGRAM和ENDCG所包围的CG代码片段。这是我们的重点。首先我们遇到了两条重要的编译指令:

#pragma vertex vert
#pragma fragment frag

它们将告诉Unity,哪个函数包含了顶点着色器的代码,哪个函数包含了片元着色器的代码。更通用的编译指令表如下:

#pragma vertex name
#pragma fragment name

其中name就是我们指定的函数名,这两个函数的名字不一定是vert和frag,它们可以是任意自定义的合发函数名,但我们一般用vert和frag来定义这两个函数,因为它们很直观。
接下来我们来看看vert函数的定义:

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

这是本例使用的着色器代码,它是逐顶点执行的。vert函数的输入v包含了这个顶点的位置,这是通过POSITION语义指定的。它的返回值是一个float4类型的变量,它是该顶点在裁剪空间中的位置,POSITION和SV_POSITION都是Cg/HLSL中的语义(semantics),它们是不可省略的,这些语义将告诉系统用户需要哪些输入值,以及用户的输出是什么。例如这里,POSITION将告诉Unity,把模型的顶点坐标填充到输入参数v中,SV_POSITION将告诉Unity,顶点着色器的输出是裁剪空间中的顶点坐标。如果没有这些语义来限定输入和输出参数的话,渲染器就完全不知道用户的输入输出是什么,因此就会得到错误的结果。在后面,我们将总结这些语义。在本例中,顶点着色器只包含了一行代码,这一步就是把顶点坐标从模型空间转换到裁剪空间中。UNITY_MATRIX_MVP矩阵是Unity内置的模型·观察·投影矩阵。
然后我们再来看一下frag函数:

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

在本例中,frag函数没有任何输入。它的输出是一个fixed4类型变量,并且使用了SV_Target语义进行了限定。SV_Target也是HLSL中的一个系统语义,它等同于告诉渲染器,把用户的输出颜色存储到一个渲染目标(render target)中,这里将输出到默认的帧缓存中。片元着色器的代码很简单,返回了一个表示白色的fixed4类型的变量。片元着色器输出的颜色的每个分量范围在[0,1],其中红(0,0,0)表示黑色,而(1,1,1)表示白色。
至此,我们已经对第一个顶点/片元着色器进行了详细的解释。但是,现在得到的效果实在是太简单了,如何丰富它呢?下面我们将一步步为它添加更多的内容,以得到一个更加具有实践意义的顶点/片元着色器。

3. 模型数据从哪里来

在上面的例子中,在顶点着色器中我们使用POSITION语义得到了模型顶点的位置。那么,如果我们想要得到更多模型的数据要怎么办?
现在,我们想要得到模型上每个顶点的纹理坐标和法线方向。这个需求是很常见的,我们需要使用纹理坐标来访问纹理,而法线可用于计算光照。因此,我们需要为顶点着色器定义一个新的输入参数,这个参数不再是一个简单的数据类型,而是一个结构体。修改后的代码如下:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "Custom/NewSurfaceShader"
{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag//使用一个结构体来定义顶点着色器的输入struct a2v{//POSITION的语义告诉Unity,用模型空间的顶点坐标填充vertex变量float4 vertex:POSITION;//NORMAL语义告诉Unity,用模型空间的法线向量填充normal变量float4 texcoord:TEXCOORD0;};float4 vert(a2v v):SV_POSITION{//使用v.vertex来访问模型空间的顶点坐标return UnityObjectToClipPos(v.vertex);}fixed frag():SV_Target{return fixed4(1.0,1.0,1.0,1.0);}ENDCG}}
}

在上面的代码中,我们声明了一个新的结构体a2v,它包含了顶点着色器需要的模型数据。在a2v的定义中,我们用到了更多Unity支持的语义,如NORMAL和TEXCOORD0,当它们作为顶点着色器的输入时都是有特定含义的,因为Unity会根据这些语义来填充这个结构体。对于顶点着色器的输入,Unity支持的语义有:POSITION,TANGENT,NORMAL,TEXCOORD0,TEXCOORD1,TEXCOOR2,TEXCOORD3,COLOR等。
为了创建一个自定义的结构体,我们必须使用如下的格式来定义它:

struct StructName{Type Name:Semantic;Type Name:Semantic;......
}

其中,语义是不可以被省略的。很快,我们将给出这些语义的含义和用法。
然后,我们修改了vert函数的输入参数类型,把它设置为我们新定义的结构体a2v。通过这种自定义结构体的方式,我们可以在顶点着色器中访问模型数据。
读者:a2v的名字是什么意思呢?
我们:a表示应用(application),v表示顶点着色器(vertex shader),a2v的意思就是把数据从应用阶段传递到顶点着色器中。
那么填充到POSITION,TANGENT,NORMAL这些语义中的数据究竟是从哪里来的呢?在Unity中,它们是由使用该材质的Mesh Render组件提供的。在每帧调用Draw Call的时候,Mesh Render组件就会把它负责渲染的模型数据发送给Unity Shader。我们知道,一个模型通常包含了一组三角面片,每个三角面片由3个顶点构成,而每个顶点又包含了一些数据,如顶点位置,法线、切线、纹理坐标、顶点颜色等。通过上面方法,我们可以在顶点着色器中访问顶点的这些模型数据。

4. 顶点着色器和片元着色器如何通信

在实践中,我们往往希望从顶点着色器中输出一些数据,例如把模型的法线、纹理坐标等传递给片元着色器。这就涉及到顶点着色器和片元着色器之间的通信。
为此,我们需要再定义一个新的结构体。修改后的代码如下:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "Custom/NewSurfaceShader"
{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment fragstruct a2v{float4 vertex:POSITION;float3 normal:NORMAL;float4 texcoord:TEXCOORD0;};//使用一个结构体定义顶点着色器的输出struct v2f{//SV_POSITION语义告诉Unity.pos里包含了顶点在裁剪空间中的位置信息float4 pos:SV_POSITION;//COLOR0语义可以用于存储颜色信息fixed3 color:COLOR0;};v2f vert(a2v v){//声明输出结构v2f o;o.pos = UnityObjectToClipPos(v.vertex);//v.normal包含了顶点的法线方向,其分量范围在[-1.0,1.0]//下面的代码把分量范围映射到了[0.0,1.0]//存储到o.color中传递给片元着色器o.color=v.normal*0.5+fixed3(0.5,0.5,0.5);return o;}fixed4 frag(v2f i):SV_Target{//将插值后的i.color显示到屏幕上return fixed4(i.color,1.0);}ENDCG}}
}

5. 如何使用属性

材质提供给我们一个可以方便调节Unity Shader中参数的方式,通过这些参数,我们可以随时调节材质的效果。而这些参数就要写在Properties语义块中。
现在,我们有了新的需求。我们想要在材质面板显示一个颜色拾取器,从而可以直接控制模型在屏幕上显示的颜色。为此我们需要继续修改上面的代码。

在上面的代码中,我们首先添加了Properties语义块,并在其中声明了一个属性_Color,它的类型是_Color,初始值是(1.0,1.0,1.0,1.0),对应白色。为了在Cg代码中可以访问它,我们还需要在Cg代码段中提前定义一个新的变量,这个变量的名称和类型必须与Properties语义块中的属性定义相匹配。
ShaderLab中属性的类型和Cg中变量的类型之间的匹配关系如下表所示:

有时,读者可能会发现在Cg变量前会有一个uniform关键字,例如:

uniform fixed _Color;

uniform关键字是Cg中修饰变量和参数的一种修饰词,它仅仅用于提供一些关于该变量的初始值是如何指定和存储的相关信息(这和其他一些图像编程接口中的uniform关键词的作用不太一样)。在UnityShader中,uniform关键词是可以省略的。

转载于:https://www.cnblogs.com/xiegaosen/p/10831824.html

第四章 开始Unity Shader学习之旅(1)相关推荐

  1. 《Unity Shader入门精要》学习笔记第5章 开始Unity Shader学习之旅

    本文章用于帮助自己学习,因此只记录一些个人认为比较重要或者还不够熟悉的内容. 原作者:http://blog.csdn.net/candycat1992/article/ 第五章 开始Unity Sh ...

  2. Unity Shader 学习笔记(33) 全局光照(GI)、反射探针、线性空间和伽马空间、高动态范围(HDR)

    Unity Shader 学习笔记(33) 全局光照(GI).反射探针.线性空间和伽马空间.高动态范围(HDR) 参考书籍:<Unity Shader 入门精要> [<Real-Ti ...

  3. Unity Shader学习:体积光/体积阴影

    Unity Shader学习:体积光/体积阴影 在前向渲染下实现平行光的体积光影效果,需要全屏深度图,延迟渲染会更划算. 思路:通过ray marching的步进点位置计算该点是否在阴影中,采样阴影贴 ...

  4. Unity Shader 学习笔记(3)URP渲染管线带阴影PBR-Shader模板(ASE优化版本)

    此 Shader 已经不是最新版本,最新版本见本专栏的第四篇文章: Unity Shader 学习笔记(4) 材质面板截图: 功能实现(URP渲染管线下): PBR材质.投射和接收阴影. 代码展示: ...

  5. Unity Shader学习:动态雾

    Unity Shader学习:动态雾 先将相机近裁面四个角向量传给shader,再通过观察空间下的深度值和相机位置算出像素在世界坐标系的位置,通过世界空间高度值来设定雾的范围和浓度,然后通过噪声和uv ...

  6. Unity Shader学习-高光反射

    Unity Shader学习-高光反射 高光反射计算公式 高光反射 = 光源的色彩和强度 * 材质的高光反射系数 * pow(max(0,视角方向 · 反射方向),_Gloss) 视角方向 = ref ...

  7. Unity Shader学习:SSAO屏幕环境光遮蔽

    Unity Shader学习:SSAO屏幕环境光遮蔽 主要思路:1.随机采样像素法线半球周围的像素,平均对比与该像素深度是否处在暗处.2.双边滤波去噪点.3.后期AO图与原图混合. 原文链接:http ...

  8. Unity Shader学习:动态模糊(shutter angle方式)

    Unity Shader学习:动态模糊 动态模糊一般有帧混合和motion vector两种,这里主要介绍motion vector的方法. Keijiro源码:https://github.com/ ...

  9. Unity Shader学习:水墨效果

    Unity Shader学习:水墨效果 偶然在网上看到9级铁甲蛹大神的水墨风格后处理觉得挺有意思,参照着实现一下,还是涉及到之前油画效果的算法,叫什么滤波暂时不清楚,应该用来处理手绘效果挺多的. 水墨 ...

最新文章

  1. zabbix的安装(一)监控os资源:内存,cpu,io,负载,带宽
  2. 每日一笑 | 程序员的招租公告
  3. KubeVela 高可扩展的云原生应用平台与核心引擎
  4. 拯救不靠谱:他是怎样将技术外包做到纠纷率3%?
  5. fetch结合(async函数来使用)
  6. 方差-偏差平衡(Bias-Variance Balance)与模型选择
  7. [19保研]厦门大学软件学院暑期夏令营招生简章
  8. vagrant给vmbox创建虚拟机及docker安装mysql和redis
  9. 关于STM32的jlink仿真器突然不能工作的解决方法
  10. 电脑屏幕亮度随背景颜色变化而变化
  11. Android使用keytool-importkeypair生成一个系统签名,只要打包的时候使用该签名,便可以获得系统所有权限
  12. python123程序设计题说句心里话a_C程序设计基础(2019年春)-中国大学mooc-试题题目及答案...
  13. 利用七牛云如何上传图片制作外链?
  14. ant编译错误:不再支持源选项 1.5,请使用 1.6 或更高版本。
  15. [bzoj3711]Druzyny
  16. 生活 RH阴性血 AB型
  17. Codeforces - Good Bye 2020
  18. Linux系统性能监控
  19. 向量几何在游戏编程中的使用2
  20. 马云与史玉柱经典语录

热门文章

  1. 《肥鸟笔记--基础数据结构》一、栈
  2. LATEST DETECTED DEADLOCK
  3. python从零开始爬东方财富网
  4. Mol Cell Proteomics. |马臻| psims-一个用于编写HUPO-PSI标准下的mzML和mzIdentML的python库...
  5. VMware Workstation 12Pro安装步骤
  6. M1卡片相关(读卡,写卡,加密算法)
  7. linux查看端口被哪个程序占用
  8. ERROR:Xst:899
  9. Trance音乐风格的分类
  10. 开源日志采集器如何选择?