(七)unity shader基础之——————对shader进行调试的方法(假色彩图像技术、Visual Studio插件、帧调试器FrameDebugger等)

对一个shader进行调试非常困难,这也是造成shader难写的原因之一,如果发现效果不对,我们可能要花非常多的时间来找到问题所在,shader中可以选择调试的方法非常有限,甚至简单输出都不行。

下面给出unity对unity shader的调试方法,主要包含两种。

1.使用假彩色图像

假彩色图像指的是用假彩色技术生成的一种图像,与假彩色图像对应的是照片这种真彩色图像。一张假彩色图像可以用于可视化一些数据,如何用它对shader进行调试?

主要思想是我们可以把需要调试的变量映射到[0,1]之间,把他们作为颜色输出到屏幕上,然后通过屏幕上显示的像素颜色来判断这个值是否正确。这种方法得到的调试信息很模糊,能够得到的信息有限,但在很长的一段时间内,这种方法的确是唯一的可选方法。

要注意的是,由于颜色的分量范围在[0,1],因此我们需要小心处理需要调试的变量的范围,如果我们已知它的值域范围,可以先把它映射到[0,1]之间在进行输出。如果不知道一个变量的范围,只能不停地实验。一个提示是,颜色分量中任何大于1的数值将被设置为1,而任何小于0的数值会被设置为0。因此我们可以尝试使用不同的映射,直到发现颜色发生了变化。(这意味着得到了0到1的值)。

如果要调试一个一维数据,可以选择一个单独的颜色分量(比如R分量)进行输出,其他颜色分量设置为0。如果是多维数据,可以选择对他的每一个分量单独调试,或者选择多个颜色分量进行输出。

下面使用一个实例,使用假彩色图像的方式可视化一些模型数据,如法线、切线、纹理、坐标、顶点颜色以及他们之间的运算结构等。代码如下:

SubShader {
		Pass {
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			
			struct v2f {
				float4 pos : SV_POSITION;
				fixed4 color : COLOR0;
			};
			
			v2f vert(appdata_full v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Visualize normal
				o.color = fixed4(v.normal * 0.5 + fixed3(0.5, 0.5, 0.5), 1.0);
				
				// Visualize tangent
				o.color = fixed4(v.tangent.xyz * 0.5 + fixed3(0.5, 0.5, 0.5), 1.0);
				
				// Visualize binormal
				fixed3 binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w;
				o.color = fixed4(binormal * 0.5 + fixed3(0.5, 0.5, 0.5), 1.0);
				
				// Visualize the first set texcoord
				o.color = fixed4(v.texcoord.xy, 0.0, 1.0);
				
				// Visualize the second set texcoord
				o.color = fixed4(v.texcoord1.xy, 0.0, 1.0);
				
				// Visualize fractional part of the first set texcoord
				o.color = frac(v.texcoord);
				if (any(saturate(v.texcoord) - v.texcoord)) {
					o.color.b = 0.5;
				}
				o.color.a = 1.0;
				
				// Visualize fractional part of the second set texcoord
				o.color = frac(v.texcoord1);
				if (any(saturate(v.texcoord1) - v.texcoord1)) {
					o.color.b = 0.5;
				}
				o.color.a = 1.0;
				
				// Visualize vertex color
//				o.color = v.color;
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				return i.color;
			}
			
			ENDCG
		}
	}

上面的代码使用了一个unity内置的结构体——appdata_full,可以在UnityCG.cginc里查看该结构体的构成。它几乎包含所有的模型数据。

我们把计算得到的假彩色存储到了顶点着色器的输出结构体——v2f中的color变量,并在片元着色器输出了这个颜色。我们可以对其中的代码添加或取消注释,观察不同运算和数据得到的效果。

为了得到某点的颜色值,我们可以使用类似颜色拾取器的脚步得到屏幕上某点的RGBA值,从而推断出该点的调试信息。下面附上代码,把该脚步拖拽到一个摄像机上,单击运行用鼠标点击屏幕,以得到该点的颜色值。

using UnityEngine;
using System.Collections;

public class ColorPicker : MonoBehaviour {
	
	public BoxCollider pickerCollider;

	private bool m_grab;
	private Camera m_camera;
	private Texture2D m_screenRenderTexture;
	private static Texture2D m_staticRectTexture;
	private static GUIStyle m_staticRectStyle;

	private static Vector3 m_pixelPosition = Vector3.zero;
	private Color m_pickedColor = Color.white;

	void Awake() {
		// Get the Camera component
		m_camera = GetComponent();
		if (m_camera == null) {
			Debug.LogError("You need to dray this script to a camera!");
			return;
		}

		// Attach a BoxCollider to this camera
		// In order to receive mouse events
		if (pickerCollider == null) {
			pickerCollider = gameObject.AddComponent();
			// Make sure the collider is in the camera's frustum
			pickerCollider.center = Vector3.zero;
			pickerCollider.center += m_camera.transform.worldToLocalMatrix.MultiplyVector(m_camera.transform.forward) * (m_camera.nearClipPlane + 0.2f);
			pickerCollider.size = new Vector3(Screen.width, Screen.height, 0.1f);
		}
	}

	// Draw the color we picked
	public static void GUIDrawRect( Rect position, Color color )
	{
		if( m_staticRectTexture == null )
		{
			m_staticRectTexture = new Texture2D(1, 1);
		}
		
		if( m_staticRectStyle == null )
		{
			m_staticRectStyle = new GUIStyle();
		}
		
		m_staticRectTexture.SetPixel(0, 0, color);
		m_staticRectTexture.Apply();
		
		m_staticRectStyle.normal.background = m_staticRectTexture;
		
		GUI.Box(position, GUIContent.none, m_staticRectStyle);
	}

	// OnPostRender is called after a camera has finished rendering the scene.
	// This message is sent to all scripts attached to the camera.
	// Use it to grab the screen
	// Note: grabing is a expensive operation
	void OnPostRender() {
		if (m_grab) {
			m_screenRenderTexture = new Texture2D(Screen.width, Screen.height);
			m_screenRenderTexture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
			m_screenRenderTexture.Apply();
			m_pickedColor = m_screenRenderTexture.GetPixel(Mathf.FloorToInt(m_pixelPosition.x), Mathf.FloorToInt(m_pixelPosition.y));
			m_grab = false;
		}
	}
	
	void OnMouseDown() {
		m_grab = true;
		// Record the mouse position to pick pixel
		m_pixelPosition = Input.mousePosition;
	}

	void OnGUI() {
		GUI.Box(new Rect(0, 0, 120, 200), "Color Picker");
		GUIDrawRect(new Rect(20, 30, 80, 80), m_pickedColor);
		GUI.Label(new Rect(10, 120, 100, 20), "R: " + System.Math.Round((double)m_pickedColor.r, 4) + "\t(" + Mathf.FloorToInt(m_pickedColor.r * 255)+ ")");
		GUI.Label(new Rect(10, 140, 100, 20), "G: " + System.Math.Round((double)m_pickedColor.g, 4) + "\t(" + Mathf.FloorToInt(m_pickedColor.g * 255)+ ")");
		GUI.Label(new Rect(10, 160, 100, 20), "B: " + System.Math.Round((double)m_pickedColor.b, 4) + "\t(" + Mathf.FloorToInt(m_pickedColor.b * 255)+ ")");
		GUI.Label(new Rect(10, 180, 100, 20), "A: " + System.Math.Round((double)m_pickedColor.a, 4) + "\t(" + Mathf.FloorToInt(m_pickedColor.a * 255)+ ")");
	}
}

效果图:

(七)unity shader基础之——————对shader进行调试的方法(假色彩图像技术、Visual Studio插件、帧调试器FrameDebugger等)_第1张图片

2.利用Visual Studio调试shader

 Visual Studio作为Windows系统下的开发利器,在Visual Studio 2012版本中也提供了对unity shader的调试功能——Graphics Debugger。

通过Graphics Debugger,我们不仅可以查看每个像素的最终颜色、位置信息,还可以对顶点着色器和片元着色器进行单步调试。具体安装和使用方式可以看unity官网文档中使用Visual Studio对DirectX 11的shader进行调试一文。

当然本方法也有一些限制。需要保证unity运行在DirectX 11平台上,而且Graphics Debugger本身存在一些bug。但这已经是Windows用户的福音了。

3.最新利器:帧调试器

我们可以使用它来看到游戏图像的某一帧是如何一步步渲染出来的。

帧调试器可以用于查看渲染该帧时进行的各种渲染事件,这些事件包含了Draw Call系列,也包括了类似清空帧缓存等操作。单机某个事件在右侧窗口中就会显示出该事件的细节,例如几何图形的细节以及使用了哪个shader等。同时在Game视图也可以看到它的效果。

unity5提供的帧调试器实际上并没有实现一个真正的帧拾取功能,而是仅仅使用停止渲染的方法来查看渲染事件的结果。例如我们想看第四个draw call的结果,那么帧调试器就会在第4个draw call调用完毕后停止渲染,这种方法虽然简单,但得到的信息很有限。如果想获取更多信息,还需要使用外部功能,例如Visual Studio插件,或者Intel GPA、NVIDIA NSight、AMD GPU PerfStudio、RenderDoc等工具。

你可能感兴趣的:(unity,Shader,游戏开发,shader,调试技术,unity,shader,游戏开发,shader,调试,假色彩图像技术,Graphics,Debugger)