Cg Programming In Unity Debugging of Shaders(Wiki翻译自用)


A false-color satellite image本教程讨论顶点输入参数。假设您已经熟悉“Minimal Shader”和“RGB Cube”部分。本教程还介绍在Unity中调试shader的主要技术:伪彩色图形,即通过片元Color的组成之一设置为可视化的值。然后在生成的图像中该分量颜色的强度可以让您得出有关shader值的结论。这可能是一种非常原始的调试技术,因为它是非常早期的调试技术。不幸的是,Unity中并没有其他的选择。

顶点数据从何而来

在“RGB Cube”一节中,您已经看到了片元着色器怎么通过顶点着色器的输出参数的输出结构获取数据。这里的问题是:顶点着色器的数据从哪里获取?在Unity内,答案是游戏对象的Mesh Renderer组件在每一帧中将游戏对象的mesh数据发送给GPU。(这通常被称为“draw call”,注意,每个“draw call”都有一些性能开销,因此,与通过多次draw call发送几个较小的网格相比,一次draw call发送一个较大的网格给GPU的效率要高得多)。这个数据通常由一长串三角形组成,每个三角形由三个顶点定义,每个顶点具有某些属性,包括位置。这些属性通过顶点着色器的输入参数在顶点着色器中使用。通过语义,在Cg中实现不同属性到不同顶点输入的映射,即每个顶点输入参数必须指定某种语义,例如:POSITION, NORMAL, TEXCOORD0, TEXCOORD1, TANGENT, COLOR等等。(在旧版本的Untiy中,内置的顶点输入参数还必须具有特定的名称,即本示例中使用的名称)。

内置顶点输入参数以及如何构造它们

将所有输入顶点参数包含在单个结构体中通常很方便,例如:

 struct vertexInput {
         float4 vertex : POSITION; // 模型空间中的坐标
         float4 tangent : TANGENT;  //表面切线
         float3 normal : NORMAL; // 模型空间的表面发法线向量,通常归一化为单位长度
         float4 texcoord : TEXCOORD0;  // 第0套纹理坐标(又称“UV”,介于0和1之间)
         float4 texcoord1 : TEXCOORD1; // 第1套纹理坐标
         float4 texcoord2 : TEXCOORD2; // 第2套纹理坐标
         float4 texcoord3 : TEXCOORD3; // 第3套纹理坐标
         fixed4 color : COLOR; // 顶点颜色,通常是恒定的
      };

通过以下方式使用此结构体

Shader "Cg shader with all built-in vertex input parameters" { 
   SubShader { 
      Pass { 
         CGPROGRAM 
 
         #pragma vertex vert  
         #pragma fragment frag 
 
         struct vertexInput {
            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 vertexOutput {
            float4 pos : SV_POSITION;
            float4 col : TEXCOORD0;
         };
 
         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;
 
            output.pos = UnityObjectToClipPos(input.vertex);
            output.col = input.texcoord; // set the output color

            // 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  
      }
   }
}

在“RGB Cube”一节中,我们已经看到了如何通过设置片元的颜色为顶点坐标来可视化顶点坐标。在此示例中,片元颜色设置为纹理坐标,所以我们可以看到Unity提供的纹理坐标类型。
请注意,切线方向的前三个分量代表切线方向,缩放比例和第四个分量是以特定方式设置的,这主要是用于视差映射(参见“Projection of Bumpy Surfaces”一节)。

预定义的输入结构

通常,仅指定您实际需要的顶点输入参数就可以获得更高的性能,例如,position, normal, 和一组纹理坐标,有时也有切线向量。Unity提供了常用的预定义的输入结构appdata_base, appdata_tan, appdata_full, appdata_img。它们被定义在UnityCG.cginc文件中(在目录Unity > Editor > Data > CGIncludes中):

 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;
      // and additional texture coordinates only on XBOX360
   };

   struct appdata_img {
      float4 vertex : POSITION;
      half2 texcoord : TEXCOORD0;
   };

UnityCG.cginc文件包含在语句#include "UnityCG.cginc"中。因此我们可以重写上面的着色器:

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 = UnityObjectToClipPos(input.vertex);
            output.col = input.texcoord;

            return output;
         }
 
         float4 frag(vertexOutput input) : COLOR 
         {
            return input.col; 
         }
 
         ENDCG  
      }
   }
}

如何描述伪彩色图像

当尝试了解伪彩色图像中的信息时,只关注于一种颜色的分量时非常重要的。例如,如果将一个sphere的具有TEXCOORD0语义的顶点输入参数texcoord 写入片元着色器,那么片元的红色部分将可视化参数texcoord的x坐标,即输出的颜色时最大纯度的红色还是最大纯度的黄色或是最大纯度的洋红色都无所谓,在所有的情况下red分量都是1。另一方面,对于red分量来说这个颜色是蓝色还是绿色还是任何强度的青色都不重要,因为red分量在所有情况下均为0。如果您从未学会只专注于一种颜色成分,那么这可能是相当具有挑战性的;因此,您可能考虑一次只查看一种颜色成分。例如,使用这行代码在顶点着色器中设置输出参数:

output.col = float4(input.texcoord.x, 0.0, 0.0, 1.0);

这会将输出参数的红色分量设置为texcoord的x分量,但是将设置绿色和蓝色分量为0(alpha分量设置为1,但在这个shader中这不重要)。
如果您专注于红色分量或仅可视化红色分量,你可以看到它围绕球体Sphere从0增加到1在360°后下降到0。实际上,类似于行星表面的经度坐标。(就球坐标而言,它对应方位角)。
如果texcoord的x分量对应经度,那么可以预料y分量应该对应纬度(或者球坐标中的倾斜度)。但是,请注意纹理坐标始终都在0到1之间。因此,该值在底部(南极)为0,在顶部(北极)为1。您可以使用以下方式将y分量可视化为绿色:

output.col = float4(0.0, input.texcoord.y, 0.0, 1.0);

纹理坐标可以得到非常好的可视化因为他们就像颜色分量一样在0到1之间。归一化向量的坐标也同样很好,因为他们始终在-1到1之间。要将这个范围映射到0到1之间,需要将每个分量+1,再除以2,例如:

 output.col = float4((input.normal + float3(1.0, 1.0, 1.0)) / 2.0, 1.0);

注意,normal是三维向量。相反的方向对应坐标-1,一个分量的全部强度对应坐标1。
如果要可视化的值不在0到1或-1到1的范围,你必须将其映射到0到1的范围内,该范围是颜色分量的范围。如果你不知道将得到什么值,你只需要进行试验。这将帮助你如果你指定的颜色超出了0到1的范围,他们会自动限定在这个范围内。即,将小于0的值设置为0,大于1的值设置为1。因此,当颜色分量是0或者1的时候,您至少知道该值是小于还是大于您假定的值,然后你可以反复修改直到颜色分量在0到1之间。

Debugging练习

为了练习调试shader,本节包含了一些语句:当顶点着色器中的col分配时产生黑色来替代他们。你的任务时指出每一行的结果为什么时黑色。为此,您应该尝试可视化所有您不确定的值并将小于0或者大于1的值映射到其他范围来使这些值可见,并且您至少知道它们在哪个范围内。

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);

上面三个颜色的三个分量的值均为0,所以结果都是黑色。
下面几行需要您具备一些关于点积和叉积的知识:

output.col = dot(input.normal, input.tangent.xyz) *input.texcoord;
//法线和切线相互垂直,所以其点积结果为0
 output.col = dot(cross(input.normal, input.tangent.xyz), input.normal) * input.texcoord;
 //法线与切线相互垂直,其叉积结果与两向量分别垂直(右手系),所以结果与法线点积结果仍然为0
 output.col = float4(cross(input.normal, input.normal), 1.0);
 //向量叉乘向量本身,结果为0
 output.col = float4(cross(input.normal, input.vertex.xyz), 1.0); 
               // only for a sphere!
               //在球的表面,顶点坐标向量即为法线方向,所以又是向量叉乘向量本身,结果为0,此结果只适用于球,因为其他物体顶点坐标向量不是法线方向。

radians函数为什么总返回黑色?那有什么用?

output.col = radians(input.texcoord);
//randians函数是角度转弧度函数,texcoord的取值在0到1之间,那么转换为弧度数是0到0.0175之间,颜色对应值取0到0.0175时颜色总为黑色。

总结

至此,本教程结束。您现在已经学会:

  • Unity中内置的顶点输入参数的列表。
  • 如何通过设置片段输出颜色的组件来可视化这些参数(或任何其他值)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值