1.ui/default代码研究
首先,我想到的是,既然是对图集纹理进行采样,而且又不能统一更改材质的纹理uv值,我们通常写的shader都是直接根据模型uv值对主纹理进行采样,那会不会是shader中对maintexture进行了什么神奇的处理,让图片采样只根据指定的uv值进行采样呢?
我去官网下载了shader代码,找到了ui/default的具体实现:
fixed4 _color;
fixed4 _texturesampleadd;
float4 _cliprect;
v2f vert(appdata_t v)
{
v2f out;
unity_setup_instance_id(v);
unity_initialize_vertex_output_stereo(out);
out.worldposition = v.vertex;
out.vertex = unityobjecttoclippos(out.worldposition);
out.texcoord = v.texcoord;
out.color = v.color * _color;
return out;
}
sampler2d _maintex;
fixed4 frag(v2f in) : sv_target
{
half4 color = (tex2d(_maintex, in.texcoord) + _texturesampleadd) * in.color;
#ifdef unity_ui_clip_rect
color.a *= unityget2dclipping(in.worldposition.xy, _cliprect);
#endif
#ifdef unity_ui_alphaclip
clip (color.a - 0.001);
#endif
return color;
}
看了上面的代码,我们可以基本确定,没有在shader中做什么特别神奇的maintexture处理。但是我们还是可以发现一些不同的地方,这里上面的变量_color,_texturesampleadd,_cliprect并没有暴露在面板上,可以看出来这三个变量是通过某些脚本传递给shader的。
我们知道,伴随着defalut材质的一般使用的是image组件、text组件。这两个组件会绘制顶点与三角形,然后使用指定的材质进行渲染。所以会不会是image组件或text组件中使用了什么算法,计算过图片uv值,并把上面三个变量填充好传给shader的呢?
2.image组件代码研究
因为unity的ui代码已经开源了,所以我们很幸运的可以看到image的源码是怎么实现的,因为image组件代码很多,所以这里就只贴出比较主要的绘制顶点的函数:
/// <summary>
/// update the ui renderer mesh.
/// </summary>
protected override void onpopulatemesh(vertexhelper tofill)
{
if (activesprite == null)
{
base.onpopulatemesh(tofill);
return;
}
switch (type)
{
case type.simple:
if (!usespritemesh)
generatesimplesprite(tofill, m_preserveaspect);
else
generatesprite(tofill, m_preserveaspect);
break;
case type.sliced:
generateslicedsprite(tofill);
break;
case type.tiled:
generatetiledsprite(tofill);
break;
case type.filled:
generatefilledsprite(tofill, m_preserveaspect);
break;
}
}
我们可以看到,这个函数是用来刷新ui渲染的,unity对图片的四种类型分别进行了处理,这里我们就只看一下最简单的simple模式的代码:
/// <summary>
/// generate vertices for a simple image.
/// </summary>
void generatesimplesprite(vertexhelper vh, bool lpreserveaspect)
{
vector4 v = getdrawingdimensions(lpreserveaspect);
var uv = (activesprite != null) ? sprites.datautility.getouteruv(activesprite) : vector4.zero;
var color32 = color;
vh.clear();
vh.addvert(new vector3(v.x, v.y), color32, new vector2(uv.x, uv.y));
vh.addvert(new vector3(v.x, v.w), color32, new vector2(uv.x, uv.w));
vh.addvert(new vector3(v.z, v.w), color32, new vector2(uv.z, uv.w));
vh.addvert(new vector3(v.z, v.y), color32, new vector2(uv.z, uv.y));
vh.addtriangle(0, 1, 2);
vh.addtriangle(2, 3, 0);
}
/// image's dimensions used for drawing. x = left, y = bottom, z = right, w = top.
private vector4 getdrawingdimensions(bool shouldpreserveaspect)
{
var padding = activesprite == null ? vector4.zero : sprites.datautility.getpadding(activesprite);
var size = activesprite == null ? vector2.zero : new vector2(activesprite.rect.width, activesprite.rect.height);
rect r = getpixeladjustedrect();
// debug.log(string.format("r:{2}, size:{0}, padding:{1}", size, padding, r));
int spritew = mathf.roundtoint(size.x);
int spriteh = mathf.roundtoint(size.y);
var v = new vector4(
padding.x / spritew,
padding.y / spriteh,
(spritew - padding.z) / spritew,
(spriteh - padding.w) / spriteh);
if (shouldpreserveaspect && size.sqrmagnitude > 0.0f)
{
preservespriteaspectratio(ref r, size);
}
v = new vector4(
r.x + r.width * v.x,
r.y + r.height * v.y,
r.x + r.width * v.z,
r.y + r.height * v.w
);
return v;
}
public void addvert(vector3 position, color32 color, vector2 uv0);
就是在这里了,首先拿到绘制的尺寸v,也就是四个顶点的位置,然后根据activesprite拿到纹理的uv值。我们可以看到addvert函数中,第三个值是绘制的顶点中填充的uv0也就是这个得到的uv值,而shader中也会根据这个uv值对maintexture进行采样。
3.小实验
我们已经知道计算顶点与uv值的操作是在image中进行的,其实unity有一个组件可以自己控制采样的uv值,就是rawimage组件,相比image组件,rawimage组件更为精简,因为没有处理image中的四种图片样式。
其实image组件中帮我们做的操作其实就相当于(是相当于,其实计算比这复杂的多)在rawimage中设置了不同的uv偏移值。这样就可以做到,每个组件使用的uv值不同,而不是改变统一使用材质上的uv值。
修改rawimage中的uv值
总结
我们最开始的想法是修改材质中的uv值,但是这样是不行的,因为改变了材质uv值后所有物体都会跟着改变。unity使用了一个巧妙的办法,也就是在建模(绘制顶点/三角形)的时候,就把得到的图集中纹理的uv采样值填充到mesh的uv中。所以材质使用的都是同一个材质,也都是对maintexture进行采样,只不过每个图片的mesh中存储的uv值都是不同的。
更多unity2018的功能介绍请到paws3d爪爪学院查找。
Unity的UI究竟为什么可以合批
程序员文章站
2022-04-09 08:32:22
1.UI/Default代码研究首先,我想到的是,既然是对图集纹理进行采样,而且又不能统一更改材质的纹理UV值,我们通常写的shader都是直接根据模型UV值对主纹理进行采样,那会不会是shader中对MainTexture进行了什么神奇的处理,让图片采样只根据指定的UV值进行采样呢?我去官网下载了 ......