前言
前一篇Unity Shader 入门笔记 (一)中,我们学习和了解了Shader程序是什么以及Unity Shader代码的主要结构和相关含义,本篇文章中我们将进一步学习和了解Shader程序如何编写。
1. 写一个简单的Shader程序
- // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
-
- Shader "My Shader/SimpleShader01"
- {
- SubShader
- {
- Pass{
- CGPROGRAM
- //声明顶点着色函数 vert是顶点着色函数名字
- #pragma vertex vert
- //声明片元着色函数 frag是片元着色函数名字
- #pragma fragment frag
-
- // POSITION / SV_POSITION 是 CG/HLSL 的语义
- float4 vert (float4 v : POSITION) : SV_POSITION
- {
- //注意:MVP变换就是将模型空间转化到裁剪空间
- return UnityObjectToClipPos(v);
- }
-
- //SV_Target 是HLSL中的系统语义,
- //它等同于告诉渲染器,将用户输出的颜色存储到一个渲染目标上
- //这里输出到默认的帧缓存上
- fixed4 frag () : SV_Target
- {
- return fixed4(1.0, 1.0, 1.0, 1.0);
- }
- ENDCG
- }
- }
- }
1.1. 代码运行效果
1.2. 代码解释
我们在上面的代码中使用了POSITION,那么填充POSITION、NORMAL、TEXCOORD这些语义中的数据从哪里来?
在Unity中,由使用该材质的MeshRender组件提供。在每帧调用DrawCall的时候,MeshRender组件会将它负责渲染的模型数据发送给UnityShader。
一个模型通常包含了一组三角面片,每个三角面片由3个顶点构成,而每个顶点又包含了一些数据,例如顶点位置、法线、切线、纹理坐标、顶点颜色等。
通过上面的方法我们就可以在顶点着色器上访问这些数据了。
从上面获取到的数据是物体模型空间下的顶点数据,所以我们要将其转化到裁剪空间中以便实现正确渲染,如果不对其转换就会得到如下图的异常,这并不满足想要的效果。
2. 顶点着色器和片元着色器通信
上面讲述了顶点着色器的数据是如何来的,并对模型顶点空间的值进行了转换,现在了解顶点着色器和片元着色器是如何通信,还是老规矩上代码:
- // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
-
- Shader "My Shader/SimpleShader02"
- {
- Properties{
- _Color ("Color Tint", Color) = (1.0, 1.0, 1.0, 1.0)
- }
-
- SubShader
- {
- Pass{
- CGPROGRAM
- //声明顶点着色函数 vert是顶点着色函数名字
- #pragma vertex vert
- //声明片元着色函数 frag是片元着色函数名字
- #pragma fragment frag
-
- //在CG代码中,需要定义一个属性名称都和类型匹配的变量
- fixed4 _Color;
-
- //顶点着色器的输入
- struct a2v {
- float4 vertex : POSITION; //用模型空间顶点填充vertex变量
- float3 normal : NORMAL; //用模型空间法线填充normal变量
- float4 texcoord : TEXCOORD0; //用模型空间第一套纹理坐标填充texcoord变量
- };
-
- //顶点着色器的输出 --> 输出到片元着色器
- struct v2f{
- float4 pos : SV_POSITION;
- fixed3 color : COLOR0;
- };
-
- // POSITION / SV_POSITION 是 CG/HLSL 的语义
- v2f vert (a2v v)
- {
- v2f o;
- //注意:MVP变换就是将模型空间转化到裁剪空间
- o.pos = UnityObjectToClipPos(v.vertex);
- //顶点法线的分量范围在[-1,1]之间
- o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
- return o;
- }
-
- // SV_Target 是HLSL中的系统语义,
- //它等同于告诉渲染器,将用户输出的颜色存储到一个渲染目标上
- //这里输出到默认的帧缓存上
- fixed4 frag (v2f i) : SV_Target
- {
- fixed3 c = i.color;
- c *= _Color.rgb;
- return fixed4(c,1.0);
- }
- ENDCG
- }
- }
- }
2.1. 代码运行效果
2.2. 代码解释
2.2.1. 如何通信
在上面代码中,我们声明了一个结构体v2f,v2f用于在顶点着色器和片元着色器之间传递信息。顶点着色器的输出结构中,必须包含一个变量,它的语义是SV_POSITION。否则渲染器就无法得到裁剪空间中的顶点坐标,也就不能把顶点渲染到屏幕上。
顶点着色器是逐顶点调用的,而片元着色器是着片元调用的。片元着色器中的输入实际上是把顶点着色器的输出进行插值后得到的结果。
2.2.2. Properties语义块
在上面的代码中我们使用了Properties语义块,在上述代码中声明了一个属性_Color,它的类型是Color。为了在CG代码中可以访问它,我们还需要在CG代码中提前定义一个新的变量,这个变量的名称和类型必须与Properties语义块中的属性定义相匹配。
以下是ShaderLab属性类型和CG变量类型的匹配关系:
ShaderLab属性类型 | CG变量类型 |
Color,Vector | float4,half4,fixed4 |
Range,Float | float,half,fixed |
2D | sampler2D |
Cube | samplerCube |
3D | sampler3D |
2.2.3. 关于uniform
uniform fixed4 _Color;
uniform 关键词是CG中修饰变量和参数的一种修饰词,它仅仅用于提供一些关于该变量的初始值是如何指定和存储相关信息。
3. 总结
在这篇文章中我们学习了Shader版本的"Hello World",了解了关于Unity Shader中顶点着色器和片元着色器如何通信和渲染,下一篇文章中我们将继续学习和了解Unity的语义和内置文件,并了解Unity Shader中一些常用的Debug工具。
最后,非常感谢您能看到这里,如果该文章对你有所帮助和启发,也欢迎您能点赞?支持,如果有任何疑问和优化建议也非常欢迎各位大佬能够在评论区友好交流,一起学习进步。
前言
前一篇Unity Shader 入门笔记 (一)中,我们学习和了解了Shader程序是什么以及Unity Shader代码的主要结构和相关含义,本篇文章中我们将进一步学习和了解Shader程序如何编写。
1. 写一个简单的Shader程序
- // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
-
- Shader "My Shader/SimpleShader01"
- {
- SubShader
- {
- Pass{
- CGPROGRAM
- //声明顶点着色函数 vert是顶点着色函数名字
- #pragma vertex vert
- //声明片元着色函数 frag是片元着色函数名字
- #pragma fragment frag
-
- // POSITION / SV_POSITION 是 CG/HLSL 的语义
- float4 vert (float4 v : POSITION) : SV_POSITION
- {
- //注意:MVP变换就是将模型空间转化到裁剪空间
- return UnityObjectToClipPos(v);
- }
-
- //SV_Target 是HLSL中的系统语义,
- //它等同于告诉渲染器,将用户输出的颜色存储到一个渲染目标上
- //这里输出到默认的帧缓存上
- fixed4 frag () : SV_Target
- {
- return fixed4(1.0, 1.0, 1.0, 1.0);
- }
- ENDCG
- }
- }
- }
1.1. 代码运行效果
1.2. 代码解释
我们在上面的代码中使用了POSITION,那么填充POSITION、NORMAL、TEXCOORD这些语义中的数据从哪里来?
在Unity中,由使用该材质的MeshRender组件提供。在每帧调用DrawCall的时候,MeshRender组件会将它负责渲染的模型数据发送给UnityShader。
一个模型通常包含了一组三角面片,每个三角面片由3个顶点构成,而每个顶点又包含了一些数据,例如顶点位置、法线、切线、纹理坐标、顶点颜色等。
通过上面的方法我们就可以在顶点着色器上访问这些数据了。
从上面获取到的数据是物体模型空间下的顶点数据,所以我们要将其转化到裁剪空间中以便实现正确渲染,如果不对其转换就会得到如下图的异常,这并不满足想要的效果。
2. 顶点着色器和片元着色器通信
上面讲述了顶点着色器的数据是如何来的,并对模型顶点空间的值进行了转换,现在了解顶点着色器和片元着色器是如何通信,还是老规矩上代码:
- // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
-
- Shader "My Shader/SimpleShader02"
- {
- Properties{
- _Color ("Color Tint", Color) = (1.0, 1.0, 1.0, 1.0)
- }
-
- SubShader
- {
- Pass{
- CGPROGRAM
- //声明顶点着色函数 vert是顶点着色函数名字
- #pragma vertex vert
- //声明片元着色函数 frag是片元着色函数名字
- #pragma fragment frag
-
- //在CG代码中,需要定义一个属性名称都和类型匹配的变量
- fixed4 _Color;
-
- //顶点着色器的输入
- struct a2v {
- float4 vertex : POSITION; //用模型空间顶点填充vertex变量
- float3 normal : NORMAL; //用模型空间法线填充normal变量
- float4 texcoord : TEXCOORD0; //用模型空间第一套纹理坐标填充texcoord变量
- };
-
- //顶点着色器的输出 --> 输出到片元着色器
- struct v2f{
- float4 pos : SV_POSITION;
- fixed3 color : COLOR0;
- };
-
- // POSITION / SV_POSITION 是 CG/HLSL 的语义
- v2f vert (a2v v)
- {
- v2f o;
- //注意:MVP变换就是将模型空间转化到裁剪空间
- o.pos = UnityObjectToClipPos(v.vertex);
- //顶点法线的分量范围在[-1,1]之间
- o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
- return o;
- }
-
- // SV_Target 是HLSL中的系统语义,
- //它等同于告诉渲染器,将用户输出的颜色存储到一个渲染目标上
- //这里输出到默认的帧缓存上
- fixed4 frag (v2f i) : SV_Target
- {
- fixed3 c = i.color;
- c *= _Color.rgb;
- return fixed4(c,1.0);
- }
- ENDCG
- }
- }
- }
2.1. 代码运行效果
2.2. 代码解释
2.2.1. 如何通信
在上面代码中,我们声明了一个结构体v2f,v2f用于在顶点着色器和片元着色器之间传递信息。顶点着色器的输出结构中,必须包含一个变量,它的语义是SV_POSITION。否则渲染器就无法得到裁剪空间中的顶点坐标,也就不能把顶点渲染到屏幕上。
顶点着色器是逐顶点调用的,而片元着色器是着片元调用的。片元着色器中的输入实际上是把顶点着色器的输出进行插值后得到的结果。
2.2.2. Properties语义块
在上面的代码中我们使用了Properties语义块,在上述代码中声明了一个属性_Color,它的类型是Color。为了在CG代码中可以访问它,我们还需要在CG代码中提前定义一个新的变量,这个变量的名称和类型必须与Properties语义块中的属性定义相匹配。
以下是ShaderLab属性类型和CG变量类型的匹配关系:
ShaderLab属性类型 | CG变量类型 |
Color,Vector | float4,half4,fixed4 |
Range,Float | float,half,fixed |
2D | sampler2D |
Cube | samplerCube |
3D | sampler3D |
2.2.3. 关于uniform
uniform fixed4 _Color;
uniform 关键词是CG中修饰变量和参数的一种修饰词,它仅仅用于提供一些关于该变量的初始值是如何指定和存储相关信息。
3. 总结
在这篇文章中我们学习了Shader版本的"Hello World",了解了关于Unity Shader中顶点着色器和片元着色器如何通信和渲染,下一篇文章中我们将继续学习和了解Unity的语义和内置文件,并了解Unity Shader中一些常用的Debug工具。
最后,非常感谢您能看到这里,如果该文章对你有所帮助和启发,也欢迎您能点赞?支持,如果有任何疑问和优化建议也非常欢迎各位大佬能够在评论区友好交流,一起学习进步。
评论记录:
回复评论: