本文共 2815 字,大约阅读时间需要 9 分钟。
说来其实也不算什么疑难杂症,原因后面再叙,只是最近遇到个代码问题确实令我犯了好一阵疑难,在此简单一记,算作总结吧~
起因颇为简单,平时工作时无意瞟了一眼的UILabel实现,其中有段实现字体阴影的代码,原理来说其实挺清晰的,就是将原先用以渲染文本的顶点数据原样拷贝一份,然后做些位置偏移和颜色改变即可,代码大概是这副样子~
// generate original vertex dataNGUIText.Print(text, verts, uvs, cols);// apply an effect if one was requestedif (effectStyle != Effect.None){ // apply shadow vertex data ApplyShadow(verts, uvs, cols, offset, end, pos.x, -pos.y);}
浮光掠影的看了一下,突然想到一个问题:既然ApplyShadow整体复制了一份文本顶点数据,那么最终生成的顶点数据大概就是:
v1, v2, v3, ... , c1, c2, c3, ...
(其中v1, v2, v3, ... 为原始顶点数据、c1, c2, c3, ... 为复制后的顶点数据)
那么问题来了,既然这些顶点是在同一个drawcall中进行绘制的,那么为何c1, c2, c3, ...的绘制结果总是在v1, v2, v3, ... 之后呢?如果不能保证这个绘制顺序,那么自然也不能正确的实现文本阴影的渲染~
依着这个疑问,首先瞅了下相关的NGUI Shader实现,大概来看还是相对简单的,代码大抵是这样:
// shader headerSubShader{ Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } Cull Off Lighting Off ZWrite Off Offset -1, -1 Fog { Mode Off } Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata_t { float4 vertex : POSITION; half4 color : COLOR; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : POSITION; half4 color : COLOR; float2 texcoord : TEXCOORD0; }; sampler2D _MainTex; v2f vert (appdata_t v) { v2f o; o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); o.texcoord = v.texcoord; o.color = v.color; return o; } half4 frag (v2f i) : COLOR { half4 col = i.color; col.a *= tex2D(_MainTex, i.texcoord).a; return col; } ENDCG }}// other SubShaders
简单上张图~
值得注意的就是Shader关闭了ZWrite,这将导致所有的顶点都会被渲染至framebuffer,如果我们尝试打开下ZWrite,便如期而至了~
但是目前虽然确定了所有顶点都会被渲染,但是各个顶点间的渲染顺序还不能确定,普通渲染一般都会打开ZWrite,那么这时的渲染顺序其实并不重要,因为最终的绘制顺序会由ZBuffer来保证,可惜这里的ZWrite被关闭了~
继续Google了一阵,觉得还是请教下熟悉这块的同事效率更高,于是问了下引擎组的同事,几次询问讨论下来,确定了渲染顺序的问题:
即顶点的渲染顺序与其提交至GPU的顺序是一致的(至少“看上去”是的,GPU可以在保证渲染结果正确性的基础上进行优化),所以如果那上面的例子来说的话:
v1, v2, v3, ... , 会首先绘制,然后绘制c1, c2, c3, ...
WTF,如果是这样的话,文本的阴影就会一直显示在文本之上!但是同事的一个简单示例又确实说明了这种渲染顺序的正确性,没办法,事实证明还有猫腻的地方,得再细查一下~
重新回头深入看了一下之前那个想当然的ApplyShadow,终于发现了蹊跷:
////// Apply a shadow effect to the buffer./// public void ApplyShadow (BetterListverts, BetterList uvs, BetterList cols, int start, int end, float x, float y){ // implementation details ... for (int i = start; i < end; ++i) { // add copy buffer data verts.Add(verts.buffer[i]); uvs.Add(uvs.buffer[i]); cols.Add(cols.buffer[i]); // update verts.buffer[i] Vector3 v = verts.buffer[i]; v.x += x; v.y += y; verts.buffer[i] = v; // implementation details ... // update cols.buffer[i] }}
原来该方法是将阴影顶点数据加到了原顶点数据之前!还是按照之前的示例来说,正确的顶点数据应该是:
c1, c2, c3, ..., v1, v2, v3, ...
(其中v1, v2, v3, ... 为原始顶点数据、c1, c2, c3, ... 为复制后的顶点数据)
看到此,终算是全身通畅了,上也有相关的解答(好像还提到了例外的情况,不是十分熟悉),也大概了浏览了一下,不过暂时没有定位到十分相关的说明,有 了解的童鞋可以告之一下~
就这样吧~