通过实现一个只有颜色属性可调节的简单材质效果更好的了解顶点与片段着色器

一、顶点着色器

顶点着色器就是处理顶点的着色器,每个顶点都会执行一次顶点着色器。我们先认识下顶点函数的结构:

  1. 顶点着色器函数的名称,在上面我们已经指定了顶点着色器的名称就是vert,所以这里我们必须要用vert作为名称。
  2. 其中float4 vertex是我们自己定义的一个四维向量,名字叫vertex(名字我们可以随便起),仅仅定义一个四维向量并不能使它拥有我们模型的顶点信息,所以这里我们需要为它指定一个语义——POSITION,POSITION就是代表着模型的顶点位置信息。此时变量vertex就表示着我们模型的顶点位置。
  3. 在顶色着色器中最主要的事情就是将顶点从模型坐标转换到裁剪坐标(说白了就是将模型显示在二维显示器上时需要做的一些矩阵转换)。不会矩阵转换怎么办,没关系,Unity已经为我们准备好现成的命令了,只需调用UnityObjectToClipPos即可,后面括号中加上我们的顶点位置变量就可以了。
  4. 然后呢,在后面片断着色器中我们需要顶点着色器中的输出结果,所以3中需要加上return来将转换后的顶点返回,float4就是用来定义我们返回的是四维向量。
  5. 经过变换后返回的顶点位置,我们也需要利用语义来标记一下,以便片断着色器可以知道哪个是从顶点着色器输出过来的顶点位置信息。所以我们在函数的后面加上: SV_POSITION。

简单地说POSITION语义是用于顶点着色器,用来指定模型的顶点位置,是在变换前的顶点的本地空间坐标。SV_POSITION语义则用于像素着色器,用来标识经过顶点着色器变换之后的顶点坐标。

关于语义,我们在补充下以下几种,根据这些语义顶点着色器就可以知道谁是模型的顶点数据谁又是模型的法线数据

struct appdata{float4 vertex : POSITION;       //顶点float4 tangent : TANGENT;       //切线float3 normal : NORMAL;         //法线float4 texcoord : TEXCOORD0;            //UV1float4 texcoord1 : TEXCOORD1;          //UV2float4 texcoord2 : TEXCOORD2;          //UV3float4 texcoord3 : TEXCOORD3;          //UV4fixed4 color : COLOR;          //顶点色};

在顶点着色器中处理顶点时,我们首先需要获取到模型的顶点数据(比如顶点位置、法线信息、顶点颜色等等),那么这些数据都是直接存储在模型中的,我们在Shader中只需要通过标识语义就可以自动获得。

此时我们的Shader还不能正常编译,因为了除了顶点着色器外,还们需要一个片断着色器。

二、片段着色器:

片断着色器也被称作像素着色器,主要是处理最终显示在屏幕上的像素结果。经过顶点着色器的处理,我们已经得到了最终显示在屏幕上的顶点矩阵,内部会自动进行插值计算,以获得当前模型的所有片断像素,然后每个像素都会执行一次片断着色器,得到最终每个像素的颜色值。

  1. 片断着色器的函数名,其中()中是空的,因为在这个简单的示例中我们并不需要额外的数据传过来,所以暂时为空。
  2. 在Cg/HLSL中使用Properties中的变量前还需要在Cg/HLSL中再重新声明一次,名称要求一致。这是死规则,我们只能按照要求来执行。float、half、fixed,这三都是浮点数的表示,只是分别对应的精度不一样,主要用此可以进行更进一步的优化。
  3. 去们直接返回_Color,也就是直接返回我们在材质面板中定义的颜色,这也是我们这个小例子想要的效果。
  4. 同样,返回的值是个四维向量,我们用float4来表示,如果想优化的话就用fixed4来表示,关于精度问题我们后面会专门讲解,这里不是重点。
  5. SV_TARGET是系统值,表示该函数返回的是用于下一个阶段输出的颜色值,也就是我们最终输出到显示器上的值。

顶点着色器与片断着色器的执行并不是1:1的,举个例子,一个三角面片,只有三个顶点,顶点着色器只需执行3次,而片断着色器由最终的像素数来决定,执行几百上千都是很正常的。所以从性能的角度来考虑,我们要尽量把计算放在顶点着色器中去执行。其次在片断着色器中也要尽量的简化算法,节省开支。

三、整合顶点与片段着色器

Shader "Unlit/MyFirstShader"
{Properties{_Color("Color", Color) = (1,1,1,1)}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment fragfixed4 _Color;float4 vert ( float4 vertex : POSITION ) : SV_POSITION{return UnityObjectToClipPos(vertex);}fixed4 frag () : SV_Target{return _Color;}ENDCG}}
}

如上图所示的代码中,我们在应用程序阶段把模型的顶点位置信息(float4 vertex:POSITION)传输到了几何阶段,然后在顶点着色器中利用UnityObjectToClipPos矩阵转换把模型顶点坐标从本地转换到齐次裁剪坐标中,并输出转换后的坐标(SV_POSITION)到光栅化阶段中,最后在光栅化的片元着色器中我们给所有像素都返回了一个颜色值_Color。

但是仔细思索我们会发现有些局限,如果我想在应用程序阶段传递多个值呢,除了顶点位置还想传递顶点色、UV坐标等信息那要怎么办呢?同时现在从几何阶段的顶点着色器输出的只有顶点坐标(SV_POSITION),如果我也想传递其它值到片元着色器中又该怎么办呢?

这个时候就该结构体(structure)出场了。

四、Struct

结构体就像是一个组或者说是一个容器,我们可以在其中存放多个变量,然后在各个阶段传递时我们就传递这个结构体就好了,这样子就把结构体中我们定义的多个变量一同传递过去了。

    struct具体语法:

  1. 结构体的声明以关键字 struct 开始,然后紧跟结构体的名字,结构体范围由{}定义,最后以分号结尾。
  2. 使用“.”引用结构体中的成员变量和成员函数。

于是,我们将原来的代码利用struct功能改进后如下:

注意,这里的fixed4 _Color;这条语句一定要定义在它用到的函数之前,否会报错。

虽然相较之前的版本会显示的代码多了不少,但是这样做更加灵活方便。

Shader "Unlit/NewUnlitShader"
{Properties{_Color("Color", Color) = (1,1,1,1)}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag   fixed4 _Color;//POSITION语义是用于顶点着色器,用来指定模型的顶点位置,是在变换前的顶点的本地空间坐标。struct appdata{float4 vertex : POSITION;//模型顶点位置坐标            };//SV_POSITION语义则用于像素着色器,用来标识经过顶点着色器变换之后的顶点坐标。struct v2f{                           float4 pos : SV_POSITION;       }; //这是顶点着色器,顶点着色器是属于几何阶段//appdata v 模型的顶点位置信息v2f vert (appdata v){v2f o;//矩阵转换把模型顶点坐标从本地转换到齐次裁剪坐标中,说白了就是将模型显示在二维显示器上时需要做的一些矩阵转换o.pos = UnityObjectToClipPos(v.vertex);//                 return o;//输出转换后的顶点坐标(SV_POSITION)到片段着色器中}//这是片段着色器,片段着色器是属于光栅化阶段//SV_Target,是系统值,表示该函数返回的是用于下一个阶段输出的颜色值,也就是我们最终输出到显示器上的值。fixed4 frag () : SV_Target{                                   //直接返回我们在材质面板中定义的颜色,给所有像素都返回了一个颜色值_Colorreturn  _Color;      }ENDCG}}
}

效果如下:

4.1 、结构体中使用到多个变量

例如最后输出模型的UV做为颜色信息来显示:

Shader "Unlit/NewUnlitShader"
{Properties{_Color("Color", Color) = (1,1,1,1)}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag   fixed4 _Color;//POSITION语义是用于顶点着色器,用来指定模型的顶点位置,是在变换前的顶点的本地空间坐标。struct appdata{float4 vertex : POSITION;//模型顶点位置坐标float2 uv : TEXCOORD0;//添加了UV变量};//SV_POSITION语义则用于像素着色器,用来标识经过顶点着色器变换之后的顶点坐标。struct v2f{                           float4 pos : SV_POSITION;float2 uv : TEXCOORD0; //添加了UV变量}; //这是顶点着色器,顶点着色器是属于几何阶段//appdata v 模型的顶点位置信息v2f vert (appdata v){v2f o;//矩阵转换把模型顶点坐标从本地转换到齐次裁剪坐标中,说白了就是将模型显示在二维显示器上时需要做的一些矩阵转换o.pos = UnityObjectToClipPos(v.vertex);//o.uv = v.uv;   //添加了UV变量          return o;//输出转换后的顶点坐标(SV_POSITION)和模型的UV到片段着色器中}//这是片段着色器,片段着色器是属于光栅化阶段//SV_Target,是系统值,表示该函数返回的是用于下一个阶段输出的颜色值,也就是我们最终输出到显示器上的值。//v2f i  顶点着色器返回的模型顶点坐标与模型UVfixed4 frag (v2f i) : SV_Target{                                   //直接返回我们在材质面板中定义的颜色,给所有像素都返回了一个颜色值_Color//return  _Color;return fixed4(i.uv,0,1);//传过来的模型的UV,作为颜色信息显示}ENDCG}}
}

这里属性中的_Color没有使用到,先不管它。

自定义函数

有的时候我们可能需要在顶点着色器或者片断着色器中写大量的代码,这样就会使得代码不够整洁,这个时候我们就可以使用自定义函数的方式,将部分代码整合进去,使其看起来直观易懂。

比如在上面的基础上,我们想实现一个利用模型自身UV来产生棋盘格的效果(不用贴图,而是程序生成的方式生成棋盘格),如下:

关键代码如下:

Shader "Unlit/NewUnlitShader"
{Properties{_Color("Color", Color) = (1,1,1,1)}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag   fixed4 _Color;//POSITION语义是用于顶点着色器,用来指定模型的顶点位置,是在变换前的顶点的本地空间坐标。struct appdata{float4 vertex : POSITION;//模型顶点位置坐标float2 uv : TEXCOORD0;//添加了UV变量};//SV_POSITION语义则用于像素着色器,用来标识经过顶点着色器变换之后的顶点坐标。struct v2f{                           float4 pos : SV_POSITION;float2 uv : TEXCOORD0; //添加了UV变量}; //这是顶点着色器,顶点着色器是属于几何阶段//vert方法是自动调用的,类似Unity的Update//appdata v 模型的顶点位置信息v2f vert (appdata v){v2f o;//矩阵转换把模型顶点坐标从本地转换到齐次裁剪坐标中,说白了就是将模型显示在二维显示器上时需要做的一些矩阵转换o.pos = UnityObjectToClipPos(v.vertex);//o.uv = v.uv;   //添加了UV变量          return o;//输出转换后的顶点坐标(SV_POSITION)和模型的UV到片段着色器中}//checker是我们自己定义的函数//利用模型自身UV来生成棋盘效果的方法//checker返回的就是一个浮点数,所以你用float或者fixed都可以,//不过你得把checker函数放在frag函数的上面,要不然会报错,原理是要先定义才能调用。fixed checker(float2 uv) {//UV取值范围是0-1,repeatUV取值范围是0-10,是float类型float2 repeatUV = uv*10;// floor(repeatUV)取值结果都变为整数类型//floor(repeatUV)/2 ,小数点后是0或者是0.5两种情况             float2 c = floor(repeatUV)/2;//floor 对输入参数向下取整//c.x+c.y的结果的小数点部分可能是0或0.5,//frac取得是小数部分,即取得是0.5或者是0//返回的checker是0.5*2=1或者是0*2=0,即checker等于1或者0float checker = frac(c.x+c.y)*2;//它返回标量或每个矢量中各分量的小数部分return checker;}                  //这是片段着色器,片段着色器是属于光栅化阶段//SV_Target,是系统值,表示该函数返回的是用于下一个阶段输出的颜色值,也就是我们最终输出到显示器上的值。//v2f i  顶点着色器返回的模型顶点坐标与模型UVfixed4 frag (v2f i) : SV_Target{                                                               fixed col = checker(i.uv);//调用函数得到返回值return col;//col应该是一维的?但却返回了fixed4,这是为什么呢?//因为shader会以这个数值构造一个fixed4类型的返回值,每个分量的值都一样。//即返回的可能是(0,0,0,0)代表黑色,(1,1,1,1)代表白色}ENDCG}}
}

checker是我们自己定义的函数,内部就是利用模型自身UV来生成棋盘效果的方法,返回类型为fixed,然后在frag函数中,我们直接调用得到返回值,并最终输出即可。

注意,这里的checker函数同样也要在其用到之前进行声明,和上面的变量声明是一样的,这一点与脚本程序代码是有所不同的,ShaderLab中要先声明再调用。

补充: 片段着色器中的纹理采样

(贴纹理的过程)例如下面给Cube贴纹理

如果纹理贴图大小跟Cube显示区域不匹配怎么办?

1. 纹理跟显示区域相等   所有顶点一 一映射

2. 纹理大于显示区域

第(1)种原则:因为不能一对一的映射 ,所以采用等比例映射的原则,可以把图片划分成二维坐标系(UV坐标)

第(2)种原则:点击图片,看图片的Filter Mode 属性(同样是UV坐标,单位长度相等)

3. 纹理小于显示区域

如果纹理大小小于显示区域出现马赛克或锯齿对应的解决方案可以把纹理的Filter Mode属性,选择Bilinear或Trilinear,可以减少锯齿。

相关知识借鉴:千锋视频资源库-Java视频教程海量下载-千锋教育

Unity 3D 图形学 Shader之顶点与片段着色器(五)相关推荐

  1. Vertex and FragmentShader顶点与片段着色器

    一.顶点与片段着色器简介 Vertex and FragmentShader:最强大的Shader类型,也是本系列的重点,下文中简称V&FShader,属于可编程渲染管线.使用的是CG/HLS ...

  2. 顶点与片段着色器的例子

    视窗坐标 Shader "Custom/WindowCoordinates/Base" {SubShader {Pass {CGPROGRAM#pragma vertex vert ...

  3. Learn OpenGL(四)——片段着色器(Fragment Shader)

    片段着色器(Fragment Shader) 片段着色器是第二个也是最终我们打算创建的用于渲染三角形的着色器. 片段着色器的全部, 都是用来计算你的像素的最后颜色输出. 为了让事情比较简单, 我们的片 ...

  4. Unity 3D开发--Shader入门基础

    Shader "Unlit/xxShader" {Properties{//基础属性 并可以显示在属性板上_MainTex ("Texture", 2D) = ...

  5. 【Unity Shader】学习顶点/片元着色器

    上一篇博客重点放在了Unity Shader的基本结构,分别介绍了它包含的三个语义块,最后简单介绍了Unity Shader的形式:表面着色器.顶点/片元着色器和固定函数着色器. 趁热打铁,今天接着上 ...

  6. 顶点着色器和片段着色器的区别

    1:图片中,渲染管线大致被分为6个步骤,其中有3个步骤是蓝色背景.这3个蓝色背景的步骤,就是可编程渲染管线中用户可以自定义的部分.(一般来说,我们只自定义顶点着色器和片段着色器,几何着色器一般使用默认 ...

  7. Shader山下(八)片段着色器

    Shader可以分为两类: 表面着色器(Surface Shader) - 为你做了大部分的工作,只需要简单的技巧即可实现很多不错的效果,但也显示了部分功能. 片段着色器(Fragment Shade ...

  8. 初识OpenGL (3)片段着色器(Fragment Shader)

    step1. 片段着色器 计算像素最后的颜色输出. 片段着色器只需要一个输出变量,这个变量是一个4分量向量,它表示的是最终的输出颜色,我们应该自己将其计算出来. #version 330 core o ...

  9. 网页高性能图形编程(四)-WebGL颜色-使用着色器绘制多顶点颜色的三角图形 操作部骤-顶点着色器和片段着色器 着色器编译羽图像绘制-vertexAttribPointer 方法

    第5章-WebGL颜色 01-操作步骤介绍 颜色添加步骤 在顶点着色器中定义一个接收外部传入颜色值的属性变量a_Color和用于传输获取到的颜色值变量v_Color 在片段着色器中定义一个同一类型和名 ...

最新文章

  1. 人造神经元计算速度超过人脑
  2. Linux下Web服务器应用之基础简介
  3. mac、linux 查看端口占用程序
  4. android one x3怎么样,618旗舰手机怎么选,看完这篇文章,你就会知道
  5. 蓝桥杯:试题 历届真题 修改数组【第十届】【省赛】【研究生组】Java实现
  6. siege4安装和使用介绍
  7. ASP.NET MVC的JavaScriptResult
  8. Selenium API-WebElement 方法
  9. 从openjdk.java.net获取OpenJDK8源码并编译(amd64/aarch64/arm64)
  10. oracle12c下载安装
  11. iOS YYText的使用笔记二(YYLabel聊天表情+文字并排)
  12. 最小二乘支持向量机LSSVM
  13. 经理人必看的8大网站
  14. Opus 和 AAC 声音编码格式
  15. 主机链接vm虚拟机中的oracle实例
  16. linux系统设置密钥登录
  17. 淘淘商城---8.7
  18. 达人评测 i5-1155g7和i5-1235u选哪个 i51155g7和1235u差距
  19. 被人误解的设计思路1
  20. C#实现图书管理系统(课程设计)——第二步、登陆界面

热门文章

  1. 2018个人年度总结:我是如何从嵌入式开发到服务器开发对接天猫精灵、小爱智能音箱服务器!懵懂 | 奋斗 | 进阶 | 信心
  2. Arranging The Sheep
  3. 等差数列java用等差公式写_等差数列
  4. mysql中图片的属性名是啥_数据库属性名
  5. verilog语言实现FPGA板的交通信号灯
  6. 数字化觉醒时代:传统企业云原生技术体系建设之路(一)
  7. 【概率论基础进阶】随机事件和概率-古典概型与伯努利概型
  8. 【解决方案】连锁店巡店难?开发成本高?TSINGSEE青犀视频打造一站式连锁店视频上云/安防监控/AI智能分析解决方案
  9. linux下批量修改前缀,Linux 中批量修改后缀名
  10. 高校女生穿旗袍答辩!网友:导师说论文要是和旗袍一样漂亮就好了