在使用Unity开发游戏的过程中,可能由于Shader代码处理不当或是其它原因导致最后的游戏画面与预期效果不太一致。遇到这样的情况要如何定位Bug并调试呢?Unite 2017 Shanghai的国内技术专场就为大家解答了这个问题。今天这篇文章将由Unity技术支持工程师张陈渊,为大家分享在Unity中如何对图形相关的Bug进行定位并调试的技巧。
讲解具体内容之前,首先来看一个示例:

从上图中可以看出,画面有不明黑色块。下面的内容就为大家介绍如何定位并调试类似的Bug。
首先来剖析这个Bug的定位步骤。第一步就是查看面片的Fragment Shader处理,这也是最容易让人怀疑的一个环节。

Fragment Shader代码很简洁,只有一张主纹理采样和雾处理。屏蔽雾处理的代码段后画面并没有变好。所以基本可以依此推断是采样纹理时出现了黑色。
既然是采样纹理出现了黑色,那就可以尝试使用不同的过滤方式来看看结果。



从上图可以看出,点过滤和双线性过滤的效果是相同的,而三线性过滤的结果不太一致。由于三线性过滤会采样不同MipMap层级进行线性插值,而双线性过滤和点过滤只会采样一个MipMap层级。所以可以推断出这个图形Bug是由于MipMap导致的。
使用RenderDoc查看这张纹理的MipMap信息:

这里可以看出Mip0是正常的,而Mip1是纯黑,其它Mip层级也都是纯黑。所以这个问题到这里就调查清楚了,由于MipMap不正确导致了图像错误。

通过以上的分析可以总结出图像调试的基本方法:分析->调试->验证

  • 分析是最重要的一个环节,需要一定的图形学知识。
  • 调试阶段可以排除掉一些干扰因素。
    验证则是使用各种工具来检验分析结果。

有时候我们需要调查一些图像效果的实现原理。下面来看看另一个示例。
文本纹理
下图是一个文本显示效果,可以看出,无论文字纹理离摄像机多近,都不会产生失真,这种效果是如何实现的呢?

首先通过调试在Shader中找出关键代码,如下:


只输出纹理值

可以看出,只输出纹理值,结果与预想的一样,近处产生了失真,同时可以看出字纹理的存储方式与常规的字纹理存储有些不同,这里的
字纹理中央部分Alpha值为1,越靠近边缘的部分Alpha值越小。
通过不断的调试,最后可以得出其渲染的基本原理,可以用下图来表示。


100和90会随着像素与摄像机的距离产生变化,从而实现无论摄像机离字纹理多近,都不会出现失真的效果。
使用这种方式实现勾边效果也非常高效,只需要一个DrawCall就可完成。

调查这个例子时使用了如下方法:

  • 确认关键数据,得出大致原理
  • 运行时修改Shader,编译并重新应用(renderdoc支持)
    Shader运行时调试,断点

图形调试工具使用简介
可用于图形调试的工具非常多,以下是一些较常用的工具:

  • Unity Frame Debugger
  • Nsight
  • Visual Studio Graphics Analyzer
  • Render Doc
  • Android studio
    Apple Instruments

下面为大家介绍其中几款工具的具体用法。
RenderDoc:

Nsight:

Visual Studio Graphics Analyzer:

介绍完工具用法后,最后再来看一个图形Bug:



上图为正确的结果,下图为错误结果。从两张图比较可以看出,这是一个典型的半透明排序问题,下图之所以出错, 是因为绿色的半透明面片先绘制,而黄色的面片后绘制。

使用Unity自带的Frame Debugger工具查看渲染顺序:

从上图中可以看到这里显示的DrawCall数量为1,所以下面要确认顶点的渲染顺序。
使用RenderDoc可以看出,绿色的面片果然排在前面,所以它先绘制。

最后通过调整顶点顺序就可以解决这个问题。


总结
本文为大家分析了几个较为常见的图形Bug,并分别阐述了这些Bug从定位、借助工具进行调试到最终解决问题的过程。不同的调试工具各有优势,大家可根据实际情况灵活应用。相比于选用工具,更加重要的部分在于分析过程。希望本文的内容能对广大Unity开发者们有所帮助,别让图形成为阻碍游戏创意的瓶颈。