一、前言
前端开发肯定都会遇到这样的问题,
明明是类似的图片,设计有可能一次性会切很多张图,如图
他们都有类似的属性
1、纯色,或者有规律的渐变
2、有可能带圆角
3、如果这个图片用于按钮,那么它很可能会有纯色滤镜的需求,比如:在按钮禁用的时候,这个按钮都要变为灰色状态
就因为这些属性,催生了UI设计无数张类似的切图,
drawcall、图片资源的管理与维护受到了很大的挑战。
阅读本文需要有opengl shader 的基础知识,
如果你使用creator开发,建议先阅读官方的文档:cocoscreator shader入门
下面我们通过操作片段着色器,一次性简单的解决这些问题
一、渐变图片
渐变的核心思想很简单,从一个颜色渐变到另一个颜色,最简单的渐变,其渐变的程度,可以根据横坐标来,也可以根据纵坐标来,假设横坐标控制渐变量,随着其慢慢的变大,越来越接近目标颜色,那么我们只操作片段着色器,其算法是这样的:
// o 为图片原本的纹理信息
// gradientStartColor 为初始原色
// v_uv0 指目前操作的当前纹理上的坐标
o = gradientStartColor + (o-gradientStartColor) *v_uv0.x;
// 最终换算得来的是该点最终要渲染在纹理中的信息
// 如果渐变是反转,那么就需要这么处理
o = o - (o-white) *v_uv0.x;
二、灰度/xx 图
灰度图更简单,然后将纹理上所有的点的颜色强制变为一个灰色,当然灰色只是个例子,可以是任何颜色
o = colorFilter;// 将纹理原本要渲染的颜色,变为目标颜色,
一般前端通过业务逻辑给一个图片设置的颜色,是 图片颜色与设置颜色 两者相加得来的,所以,单纯通过业务去设置灰色是达不到我们的效果的,我们需要直接操作着色器。
三、圆角
1、我们在一张图片上切圆角,可以看作是一个圆的1/4,由此实现思路是,在一张矩阵的四个角,划四个圆,如果左上角,就忽略左上角的1/4圆区域外的纹理,右上角就忽略右上角1/4圆区域外的纹理,以此类推4个角,
float distanceX = abs(point1.x - v_uv0.x);
float distanceY = abs(point1.y - v_uv0.y);
return sqrt(distanceX*distanceX+distanceY*distanceY) > radiationRadius;
// 勾股定理计算距离,如果距离大于该圆的半径,说明该点在圆之外,需要忽略
//shouldAlpha 为return后的值, 忽略该点,只需要把alpha设置为0
if (shouldAlpha){ o.a = 0.0;}
2、你可能会发现,上面的算法,只会在正方形的图片上有良好的效果,
换成了长宽不一的矩阵,四个角就不是圆角了,效果打了折扣
这是因为,着色器长宽,都是1,所以如果我们画个圆,那么在矩阵上就会被拉伸成椭圆,
那么我们就反过来,以上图为例,如果要宽>高,那么我在纹理矩阵上面,则划一个长轴在y轴的椭圆,那么拉伸一下,就变成圆了。
//椭圆公式
// x、y为椭圆上任意一点的坐标,a为长轴长、b为短轴长
x*x/(a*a) + y*y/(b*b) = 1
根据上面的公式,可得算法
float distanceX = abs(point1.x - v_uv0.x);
float distanceY = abs(point1.y - v_uv0.y);
// textureSize 为该纹理的物理尺寸
float w = textureSize[0];
float h = textureSize[1];
// convertX:如果distanceY 作为椭圆上一点的高的划,那么响应的该点的宽为多少
float convertX = sqrt((radiationRadius*radiationRadius -distanceY*distanceY) *h*h/w/w);
// 如果换算出来的宽长度小于当前的点的宽长度,那么就说明在圆角为,舍弃该点
return convertX < distanceX;
好了,这样就成功在一个正方形划了个椭圆,如果纹理是个矩阵,那么它拉伸下,就成为了圆。