在Cocos Creator中图片元素都是以矩形的形式来呈现的,并没有直接进行圆角化处理的方式。写过前端的朋友应该都用过 border-radius 来将区块圆角化,输入圆角半径即可呈现效果,布局微调的时候非常方便。为了更舒坦的进行界面微调,XianYu根据border-radius的使用习惯制作了圆角矩形Shader。
效果展示
圆角顺序与border-radius的顺序相同,输入大于等于1的数值,会以px为单位进行圆角化,输入小于1的数值时,会以宽高百分比的方式进行圆角化。
可能有的童鞋会好奇,为什么每个角对应两个值,半径一个值不就够了吗?有此疑问的童鞋请移步至MDN查看border-radius说明,border-radius属性可以单独设置水平轴和垂直轴的长度,此时边角会呈现椭圆形。
与前端的border-radius联合写法相比,此种配置方案显得繁琐。有简单方案怎么能不安排呢?
受限于微信平台对动图的要求,仅仅演示了直接设置px的方式,百分比配置方式在CocosEditor模式下以小于1的小数方式实现,当然为了能够实现1px圆角,输入1的时候并不代表100%。在Web模式下可以自由的输入字符串,但请遵循前端规范。
- 不写数字、“px”、“PX”、"%"、空格以外的其他字符。
- 百分比不以小数形式表示,用%的形式表示。
- 在画椭圆边角时,"/"两边记得带上空格。
- 末尾";"可有可无
正文
1. 实现思路
开始表演前先来MDN学习下border-radius,毕竟我的前端也就是半吊子水平。下图为MDN对border-radius给出的解释。
从上图可以看出,border-radius的实现原理就是在边角画内切椭圆(圆只是椭圆的特殊形态),而椭圆的原点可以通过设置的圆角半径计算出来,下边只需要像素是否在椭圆内部即可,将边角区域椭圆外部的像素的Alpha通道的值设为零就会呈现出圆角效果。
如何确定在椭圆外呢?一步一步来,先来画个椭圆吧!下图是长半轴为4,短半轴为2的椭圆。
椭圆有了,那么椭圆上的点该如何用代数式来表示呢?根据椭圆标准方程书写规则,上图的椭圆可以表示为:
椭圆上的点的坐标满足上述表达式,将 = 换成 > 后表达式就变成了:
上述表达式对应的区域为:
很明显,这就是我们要找的区域,在该区域的点均满足上述不等式。接下来的任务就是在Shader中找到对应的点将Alpha通道的值设为0即可。
2. 代码实现
在Shader中不管是uv坐标还是颜色值,取值范围均被压缩至 [0, 1] ,而长方形的宽高是不相等的,为了使圆角能够达到满意的效果,统一的将uv坐标的取值范围映射到精灵节点的宽高,传入的圆角半径也以该范围为基准,百分比半径在传入前通过组件脚本进行计算转换后传入Shader。
第一次接触Shader,不知道这种操作方式是否会有什么负面影响,至少目前在我编写的过程中它是可以运行的。如有不妥之处还望大佬多多指教。
CCEffect %{
techniques:
- passes:
- vert: vs
frag: fs
blendState:
targets:
- blend: true
rasterizerState:
cullMode: none
properties:
texture: { value: white }
size: { value: [0, 0], editor: { tooltip: "图片尺寸" } }
leftTop: { value: [0, 0], editor: { tooltip: "左上角,第一个数为水平方向" } }
rightTop: { value: [0, 0], editor: { tooltip: "右上角,第一个数为水平方向" } }
rightBottom: { value: [0, 0], editor: { tooltip: "右下角,第一个数为水平方向" } }
leftBottom: { value: [0, 0], editor: { tooltip: "左下角,第一个数为水平方向" } }
}%
参数区代码
CCProgram fs %{
precision highp float;
in vec2 v_uv0;
in vec4 v_color;
#if USE_TEXTURE
uniform sampler2D texture;
#endif
uniform Properties {
vec2 size;
vec2 leftTop;
vec2 rightTop;
vec2 rightBottom;
vec2 leftBottom;
};
float inEllipse(vec2 origin, float radius_a, float radius_b, vec2 coordinate) {
// 转换坐标
coordinate.x -= origin.x;
coordinate.y -= origin.y;
// 圆锥曲线
return pow(coordinate.x, 2.0) / pow(radius_a, 2.0) + pow(coordinate.y, 2.0) / pow(radius_b, 2.0);
}
void main () {
vec4 color = v_color;
color *= texture(texture, v_uv0);
// 转换坐标原点到左下角
vec2 uv = vec2(v_uv0.x, 1.0 - v_uv0.y);
// 转换坐标到实际尺寸大小
uv.xy *= size.xy;
// 处理左上角
if(uv.x < leftTop.x && uv.y > size.y - leftTop.y) {
if(inEllipse(vec2(leftTop.x, size.y - leftTop.y), leftTop.x, leftTop.y, uv) > 1.0) {
color.a = 0.0;
};
}
// 右上角
if(uv.x > size.x - rightTop.x && uv.y > size.y - rightTop.y) {
if(inEllipse(vec2(size.x - rightTop.x, size.y - rightTop.y), rightTop.x, rightTop.y, uv) > 1.0) {
color.a = 0.0;
};
}
// 右下角
if(uv.x > size.x - rightBottom.x && uv.y < rightBottom.y) {
if(inEllipse(vec2(size.x - rightBottom.x, rightBottom.y), rightBottom.x, rightBottom.y, uv) > 1.0) {
color.a = 0.0;
};
}
// 左下角
if(uv.x < leftBottom.x && uv.y < leftBottom.y) {
if(inEllipse(vec2(leftBottom.x, leftBottom.y), leftBottom.x, leftBottom.y, uv) > 1.0) {
color.a = 0.0;
};
}
color.a *= v_color.a;
gl_FragColor = color;
}
}%
片段着色器代码
顶点着色器未进行实质性的修改,就不贴代码了。
Shader写完后只是实现了绘制圆角的功能,修改一次圆角创建一个Material还不如直接让美工给带有圆角的图片。因此配合使用的SpriteRadius脚本就登场了,考虑到篇幅问题就不再贴代码了,上传送门
3. 使用方法
从gitee获取代码文件啥的就不说了,获取过程中点Star还是要提一提的
-
将下载过的资源文件放到工程中,具体放置路径看你项目的资源架构了。
-
找到需要圆角的精灵节点,将SpriteRadius脚本添加到节点上。
-
将Effect资源拖入到SpriteRadius脚本组件中
-
剩下的就是配置圆角半径了,X为水平方向圆角半径,Y为垂直方向圆角半径,Web方式用前端的CSS字符串即可使用。
4. 参考资料
-
MDN:https://developer.mozilla.org/zhCN/docs/Web/CSS/border-radius
-
Eazax-CCC 游戏开发工具包:https://gitee.com/ifaswind/eazax-ccc
站在皮皮的肩膀上编程,香
文章排版有点乱,公众号相对凑和点
源码获取:传送门
开发环境:Cocos Creator 2.4.5其他版本未测试