欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Koo叔说Shader-调试Shader

程序员文章站 2022-06-10 21:12:25
...

前言

当写的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,可视化这些属性.
相关标签: shader