
思路:
- uv.x取模
- 使用step处理这个模(也可以用smoothstep,以造成柔和边缘)
- 乘上一个旋转矩阵
- 根据a通道,控制阴影(透明)部分不要应用这个效果(如果有背景的时候可以看出来)
紧接着,就遇到了九宫格特性与其兼容性问题。
反九宫格变换
当使用了九宫格特性,得到的uv已经是变换之后的,如果希望上面的运算处在一个平直的均匀的坐标中。那么需要对这个变换做反运算。
仔细查询spriteframe的一个属性_capInsets:
和另一个属性uvSliced:(这个属性可以也用node size和上面的capInsets计算比例得到)
显然这跟九宫格的设置是有关系的 Border Top/Bottom/Left/Right
仔细观察,这16个点的与原始图形的关系是这样的
[ 12 13 14 15 8 9 10 11 4 5 6 7 0 1 2 3 ] \begin{bmatrix}12&13&14&15\\8&9&10&11\\4&5&6&7\\0&1&2&3\end{bmatrix}⎣⎢⎢⎡1284013951141062151173⎦⎥⎥⎤
那么需要获取拉伸的信息,只需要把1,2的u坐标和8,4的v坐标传给shader即可。
另外用上面的_capInsets结合node.width/height排出一个这几条线的序列,方便shader使用。
比如上图的4条线在200宽的图像上的实际坐标位:[0,24,176,200]
其中的0,200确实是冗余的,其实只要24和200这两个数据就能算出上面的数组,但是为了shader计算方便,我就这么写了。
最终用一个这样给material传参数:
let uvSliced: { u: number, v: number }[] = this._sprite.spriteFrame.uvSliced;
let capInsets: number[] = this._sprite.spriteFrame._capInsets;
material.setProperty('u_uvs', [uvSliced[1].u, uvSliced[2].u, uvSliced[8].v, uvSliced[4].v]);
material.setProperty('u_ur', [0, capInsets[0], this.node.width - capInsets[2], this.node.width]);
material.setProperty('u_vr', [0, capInsets[1], this.node.height - capInsets[3], this.node.height]);
思路:
1.uv的x,y独立处理
2.linear函数自己可以构造一个,非常方便在原点不为零的两个线段之间做线性变换
3.把标准值[0,1]先按照九宫格的分段,映射到真实长度坐标下[0,width],[0,height]
4.真实坐标后,按真实坐标总长线性变换回标准区间[0,1]即可
float linear(float x0, float x1, float y0, float y1, float inputX) {
return (y1 - y0) * (inputX - x0) / (x1 - x0) + y0;
}
而兼容九宫格特性的相关shader代码这样写:
vec2 uvsc = vec2(v_uv0.x, v_uv0.y);
#if USE_SLICED_CONFRONT
vec4 un = vec4(0, u_uvs[0], u_uvs[1], 1.0);
vec4 vn = vec4(0, u_uvs[2], u_uvs[3], 1.0);
float rx = 0.0;
float ry = 0.0;
if (uvsc.x < u_uvs[0]) {
rx = linear(un[0], un[1], u_ur[0], u_ur[1], uvsc.x);
} else if (uvsc.x < u_uvs[1]) {
rx = linear(un[1], un[2], u_ur[1], u_ur[2], uvsc.x);
} else {
rx = linear(un[2], un[3], u_ur[2], u_ur[3], uvsc.x);
}
if (uvsc.y < u_uvs[2]) {
ry = linear(vn[0], vn[1], u_vr[0], u_vr[1], uvsc.y);
} else if (uvsc.y < u_uvs[3]) {
ry = linear(vn[1], vn[2], u_vr[1], u_vr[2], uvsc.y);
} else {
ry = linear(vn[2], vn[3], u_vr[2], u_vr[3], uvsc.y);
}
uvsc.x = rx / u_ur[3]; // linear(0.0, u_ur[3], 0.0, 1.0, rx);
uvsc.y = ry / u_vr[3]; // linear(0.0, u_vr[3], 0.0, 1.0, ry);
#endif
计算得到的uvsc在下面代替原本的v_uv0使用即可
图集兼容性问题
如果你用的这张图片是图集中的,那么显示上一定会出问题。
两个原因:
- uv是相对大图中的。
- uvSliced是相对大图中的。
因此需要变换回标准区间[0,1]
详细内容看这下一篇文章:
CocosCreator Effect (Shader) - 反图集打包(Packable)补偿