Unity Shader入门精要笔记(六):由一个简单的顶点/片元着色器谈起
来源:互联网 发布:网络交换机的安装 编辑:程序博客网 时间:2024/05/22 17:07
本系列文章由Aimar_Johnny编写,欢迎转载,转载请标明出处,谢谢。
http://blog.csdn.net/lzhq1982/article/details/74129759
本系列笔记使用的Unity版本是5.5.2,和书上的区别就是_Object2World已经变成unity_ObjectToWorld,_World2Object也变成了unity_WorldToObject,但即使用书上的例子也没问题,Unity会自行转换。当然版本差别太大就不保证了。
1、一个简单的顶点/片元着色器
有关Unity的知识这里就不说了,比如咋建场景,建材质啥的。这里我们建一个球体,建一个材质,把材质丢给球体,然后建个Unity Shader,一般都要先选个Shader模板,Unity Shader基础这篇里我提到过目前提供的模板类型,我们要用顶点/片元着色器,所以选Unlit Shader和Image Effect Shader都可以。然后我们改写成下面这样:
Shader "Custom/Simple Shader" {SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag float4 vert(float4 v : POSITION) : SV_POSITION { return mul(UNITY_MATRIX_MVP, v.vertex); } fixed4 frag() : SV_Target { return fixed4(1.0, 1.0, 1.0, 1.0); } ENDCG } }}把shader丢到材质上,然后我们看到效果如下:
这是一个最简单不过的Shader了,Unity Shader结构请看Unity Shader基础,这里我不解释了。首先我定义了该Shader的名字:"Custom/Simple Shader"。然后这里没用到Properties语义块,不是必须的,我们没定义任何属性。然后声明了SubShader和Pass,我们也没进行任何渲染设置和标签设置。接着是CGPROGRAM和ENDCG之间的CG代码,这是我们的重点。先是这两行:
#pragma vertex vert
#pragma fragment frag
它们告诉Unity,顶点着色器函数是vert,片元着色器函数是frag,当然函数名你可以完全自己定义。对应下面的函数就行。接着我们看vert部分:
float4 vert(float4 v : POSITION) : SV_POSITION {
return mul(UNITY_MATRIX_MVP, v.vertex);
}
这就是顶点着色器部分,它是逐顶点执行的。其中输入参数v包含了顶点的位置,这是由POSITION语义指定的。它的返回值是float4类型,mul(UNITY_MATRIX_MVP, v.vertex)只是把顶点变换到裁剪空间,所以输出的是裁剪空间该顶点的位置。POSITION和SV_POSITION是CG/HLSL的语义,在这里是不可以省略的,它们告诉系统需要哪种输入值,以及输出的是什么。例如这里POSITION说明输入的是顶点坐标,SV_POSITION说明输出的是裁剪空间的顶点坐标。如果没有语义,渲染器就不知道用户输入输出是什么。再看片元着色器:
fixed4 frag() : SV_Target {
return fixed4(1.0, 1.0, 1.0, 1.0);
}
本例frag函数没有任何输入。输出是fixed4类型的变量。颜色值一般用fixed4类型就够了。然后使用SV_Target语义,说明输出颜色存储到一个渲染目标(render target)中。fixed4(1.0, 1.0, 1.0, 1.0)只是返回一个白色值。颜色分量在[0, 1]之间,(0, 0, 0)表示黑,(1, 1, 1)表示白。所以最后我们得到了一个白色的球。
2、更多的模型数据
上面例子我们用POSITION语义得到了模型的顶点位置。但我们时常还需要顶点的纹理坐标,法线,切线等数据,所以一个单一的输入参数肯定不行,就需要一个结构体了。我们修改上面的代码如下:
Shader "Custom/Simple Shader" {SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag struct a2v { //POSITION语义:用模型空间顶点坐标填充vertex变量 float4 vertex : POSITION; //NORMAL语义:模型空间的法线填充normal变量 float3 normal : NORMAL; //TEXCOORD0语义:用模型的第一套纹理坐标填充texcoord变量 float4 texcoord : TEXCOORD0; }; float4 vert(a2v v) : SV_POSITION { return mul(UNITY_MATRIX_MVP, v.vertex); } fixed4 frag() : SV_Target { return fixed4(1.0, 1.0, 1.0, 1.0); } ENDCG } }}从上面代码看出,我们只是比第一个代码多定义了一个结构a2v,并把a2v结构变量作为参数传入顶点着色器,结构体里面定义了顶点着色器需要的模型数据。注释写的很明白,我就不解释了。Unity支持的顶点着色器输出的语义有POSITION(位置),TANGENT(切线),NORMAL(法线),TEXCOORD0,TEXCOORD1,TEXCOORD2,TEXCOORD3(一系列纹理坐标),COLOR(颜色),等。
那么,填充到POSITION,TANGENT,NORMAL这些语义中的数据是从哪来的呢?是由使用该材质的Mesh Render组件提供的。每帧调用Draw Call的时候,Mesh Render会把它渲染的模型数据发送给Unity Shader。模型是由三角形面片组成的,三角形面片由3个顶点构成,每个顶点就包含着上面那些数据。
3、顶点和片元之间的通信
在渲染流水线中我们知道,顶点数据经过顶点着色器的一系列变换,输出的数据要交给片元着色器进行光栅化阶段。我们上面说的模型顶点坐标,法线,纹理坐标等数据是怎么传给片元着色器的呢,我们还需要定义一个结构体,我们对上面的代码改动如下。
Shader "Custom/Simple Shader" {SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag struct a2v { float4 vertex : POSITION;float3 normal : NORMAL;float4 texcoord : TEXCOORD0; }; //使用一个结构体来定义顶点着色器的输出和片元着色器的输入 struct v2f { //SV_POSITION语义:pos里包含了顶点在裁剪空间中的位置 float4 pos : SV_POSITION; //COLOR0语义:可以用于存储颜色信息 fixed3 color : COLOR0; }; v2f vert(a2v v) { //声明输出结构v2f v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); //这是把v.normal(法线数据)转换成颜色信息存在o.color中 o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5); return o; } //v2f结构作为参数传递给片元着色器 fixed4 frag(v2f i) : SV_Target { //将插值后的i.color显示到屏幕上 return fixed4(i.color, 1.0); } ENDCG } }}
上面的代码声明了一个结构体v2f,用于顶点着色器的输出和片元着色器的输入,从而实现信息的传递。v2f中每个变量都需要语义,而且至少要包含SV_POSITION。否则渲染器得不到裁剪空间的坐标,也就无法显示在屏幕上。上例中我们把法线数据转化成颜色了,法线范围是[-1, 1],颜色范围是[0, 1],所以上面用先乘0.5,再加0.5的办法进行了转换。最后把转换后的颜色插值后显示在了屏幕上。不要忘了插值哦,忘了的童鞋就回去看Unity Shader基础。
4、使用属性
材质提供给我们一个可以方便调节Unity Shader中参数的方式,我们可以在Shader中声明属性,并在顶点/片元着色器中使用这些属性,这些属性会在材质面板中显示出来,我们可以通过材质面板调它们并且实时显示效果。比如上例,我们想在材质面板显示一个颜色拾取器,从而直接控制模型在屏幕上显示的颜色。
Shader "Custom/Simple Shader" {Properties { //声明一个Color类型的属性_Color ("Color Tint", Color) = (1, 1, 1, 1)}SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag //Cg代码中,我们需要定义一个与属性名称和类型都匹配的变量 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) { 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; //使用_Color属性控制输出颜色 c *= _Color.rgb; return fixed4(c, 1.0); } ENDCG } }}上面代码我们添加了Properties语义块,属性就声明在其中,_Color ("Color Tint", Color) = (1, 1, 1, 1):我们声明了一个变量_Color,注意它的名字要与程序中名字一致,然后对应材质面板上的名字是"Color Tint",类型是Color,这要写在一对括号里,然后等号后面是默认值,这里是白色。然后在CG中我们也要定义这个变量_Color,对应的类型是fixed4,与Properties中它的类型Color相匹配。
ShaderLab中属性的类型和CG中变量类型之间匹配关系如下:
至此我们的第一个Shader就写完了,是不是感觉很简单,下一篇我们重点总结一下Unity为我们提供的内置变量和语义,以及一些相关知识的介绍。
- Unity Shader入门精要笔记(六):由一个简单的顶点/片元着色器谈起
- 【Unity Shader】一个简单的顶点/片元着色器
- Unity最简单的顶点片元着色器
- Unity Shader入门精要 阅读笔记六
- 【Unity Shader】一个简单的着色器
- 【Unity Shader学习笔记】(二)最基本的顶点片段着色器
- Unity Shader入门精要笔记(二):Unity Shader基础
- UnityShader初级篇——最简单的顶点/片元着色器
- Unity Shader 学习笔记(十三)表面着色器的顶点动画
- UnityShader从入门到放弃(二)表面着色器和顶点、片元着色器
- 【Unity Shader学习笔记】(一)在表面着色器中控制顶点变换
- GPUImage 顶点着色器和片元着色器 详解
- 顶点着色器和片元着色器通信
- Unity Shader入门精要笔记(九):Unity 的基础光照——漫反射的实现
- opengl一个简单的顶点着色器
- unity shader 可编程管线(二) 顶点和片段着色器(Vertex Shader and Fragmet Shader)
- Unity Shader入门精要笔记(八):Unity 的基础光照——概念与理论
- 《Unity Shader入门精要》自学笔记(一)
- 使用ServletContextListener完成定时任务(固定频率执行)
- 多态陷阱---域与静态方法
- Docker——版本升级(三)
- JAVA 8函数式编程(三):柯里化与惰性求值
- MPEG-1 Audio 编码器
- Unity Shader入门精要笔记(六):由一个简单的顶点/片元着色器谈起
- Centos7 安装redis及其入门使用
- 不同字符集编码的英文字母和中文汉字的字节数
- JS-高级 DOM 技术
- 覆盖与重载(一):泛型能决定方法宗量吗?
- Java变量
- express-2-中间件
- 【Maven】Eclipse 使用Maven创建Java Web项目
- Go net/PRC源码阅读server.go