Koo叔说Shader-调试Shader
前言
当写的Shader出现了一些不是预期的效果时,可能需要对Shader进行调试。但Shader不像其它语言能加断点debug,也不能打印输出。输出的只能是颜色值,所以可以将想要看的变量变换成颜色值,可视化的来查看问题,当然这需要一些小小的转换。
顶点数据从哪里来
一般对Shader调试,主要是调试顶点数据及变换,那么顶点数据是如何设置到Shader中的呢?在Unity中,是由Mesh Renderer component对象每帧将mesh data发送到GPU中的(这个过程通常就叫做draw call)。每次draw call都会对性能有一定的开销,因此应当尽可能的在每次Draw call中传输大量Mesh.Mesh数据是由一组三角形(Triangle)列表数据组成,每个三角形有三个顶点(vertex),每个顶点存储着多个属性(attributes),如POSITION,NORMAL,TEXCOORD0,TEXCOORD1,TANGENT,COLOR等
内置顶点属性
通常将顶点属性组织到一个struct中。例如:
struct vertexInput{
flaot4 vertex:POSITION;
float4 tangent:TANGENT;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
float4 texcoord1:TEXCOORD1;
float4 texcoord2:TEXCOORD2;
float4 texcoord3:TEXCOORD3;
float4 color:COLOR;
}
这个结构在Shader中的用法如下:
Shader "Cg shader with all built-in vertex input parameters"{
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct vertexInput{
flaot4 vertex:POSITION;
float4 tangent:TANGENT;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
float4 texcoord1:TEXCOORD1;
float4 texcoord2:TEXCOORD2;
float4 texcoord3:TEXCOORD3;
float4 color:COLOR;
};
struct vertexOutput{
float4 pos:SV_POSITION;
float4 col:TEXCOORD0;
};
vertexOutput vert(vertexInput input){
vertexOutput output;
output.pos = UnityObjectToClipPos(input.vertex);
// other possibilities to play with:
// output.col = input.vertex;
// output.col = input.tangent;
// output.col = float4(input.normal, 1.0);
// output.col = input.texcoord;
// output.col = input.texcoord1;
// output.col = input.texcoord2;
// output.col = input.texcoord3;
// output.col = input.color;
return output;
}
float4 frag(vertexOutput input):COLOR{
return input.col;
}
ENDCG
}
}
}
Unity中预定义的结构体
Unity提供了几种预定义结构分别是appdata_base,
appdata_tan,appdata_full和appdata_img,都在UnityCG.cginc中定义,具体结构如下:
struct appdata_base{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
};
struct appdata_tan{
float4 vertex:POSITION;
float4 tangent:TANGENT;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
};
struct appdata_full{
float4 vertex:POSITION;
float4 tangent:TANGENT;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
float4 texcoord1:TEXCOORD1;
float4 texcoord2:TEXCOORD2;
float4 texcoord3:TEXCOORD3;
fixed4 color:COLOR;
};
struct appdata_img{
float4 vertex:POSITION;
half2 texcoord:TEXCOORD0;
};
所以上面的shader可以重写成这样:
Shader "Cg shader with all built-in vertex input parameters" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct vertexOutput {
float4 pos : SV_POSITION;
float4 col : TEXCOORD0;
};
vertexOutput vert(appdata_full input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
output.col = input.texcoord;
return output;
}
float4 frag(vertexOutput input) : COLOR
{
return input.col;
}
ENDCG
}
}
}
颜色对应属性的解析
当尝试理解false-color图像中的信息时,最重要的是一次只关注一个颜色分量。例如:输入顶点属性texcoord,然后使用red分量表示texcoord的x坐标
output.col = float4(input.texcoord.x,0.0,0.0,1.0);
使用green分量表示texcoord的y坐标
output.col = float4(0.0,input.texcoord.y,0.0,1.0)
x,y坐标取值范围是(0,1)
颜色分量也是(0,1)正好一一对应。
当可视化法线时,由于法线取值范围是(-1,1),所以需要一个小小的转化,变到(0,1),才能用颜色分量表示
output.col = float4(input.normal+float3(1.0,1.0,1.0))/2.0,1.0);
调试练习
为了练习,下面有几个小例子,将会产生黑色的结果,你的任务是考虑每一行,为什么结果是黑色的。到最后,你应该可以可视化任何你不能完全确定的值
output.col = input.texcoord - float4(1.5,2.3,1.1,0.0);
output.col = input.texcoord.zzzz;
output.col = input.texcoord/tan(0.0);
下面的需要一些点乘和叉乘的知识
output.col = dot(input.normal,input.tangent.xyz)*input.texcoord;
output.col = dot(cross(input.normal,input.tangent.xyz),input.normal)*input.texcoord;
output.col = float4(cross(input.normal,input.normal),1.0);
output.col = float4(cross(input.normal,input.vertex.xyz),1.0);
总结
- 学习了Unity中内置的输入属性
- 如何通过设置fragment 的color,可视化这些属性.
上一篇: OSGEarth添加文字
下一篇: shader学习笔记(一)
推荐阅读