glsl shader中实现canvas中的createRadialGradient效果

15 篇文章 0 订阅
5 篇文章 0 订阅

在网上找了好久,也没有发现有现成用shader去实现canvas radialGradient效果的.大部分都是简单的只有一个中心圆或者通过canvas绘制渐变再作为纹理图像进行贴图,没有类似像canvas有内圆与外圆,两圆心位置不一样,可以用实现类似焦点视锥效果。然后想到canvas底层用的是skia,就去看了一下skia源码(过于复杂和抽象,不建议直接阅读,可以调试进入熟悉执行顺序和目录功能结构),后面在skia官网搜索了一篇关于Two-point Conical Gradient的介绍一些算法,但是我感觉有点复杂。
渐变效果(线性渐变、径向、圆锥)都是通过求t,然后通过t计算colorstop它们的插间值:



struct RadialGredient{
   float x0;
   float y0;
   float r0;
   float x1;
   float y1;
   float r1;
   vec4 colorStops[3];
};


float sdfRect(vec2 uv,vec2 center,vec2 size){
    vec2 halfSize=size/2.;
    vec2 offset=abs(uv-center)-halfSize;

    return  length(max(offset,0.))+min(max(offset.x,offset.y),0.);
}
// 投影坐标系
vec2 ProjectionCoord(in vec2 coord, in float scale) {
  return scale * 2. * (coord - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);
}

// 坐标轴
vec4 AxisHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor) {
  vec4 color = vec4(0, 0, 0, 0);
  float dx = dFdx(coord.x) * axisWidth;
  float dy = dFdy(coord.y) * axisWidth;
  if(abs(coord.x) < dx) {
    color = yAxisColor;
  } else if(abs(coord.y) < dy) {
    color = xAxisColor;
  }
  return color;
}

// 栅格
vec4 GridHelper(in vec2 coord, in float gridWidth, in vec4 gridColor) {
  vec4 color = vec4(0, 0, 0, 0);
  float dx = dFdx(coord.x) * gridWidth;
  float dy = dFdy(coord.y) * gridWidth;
  vec2 fraction = fract(coord);
  if(fraction.x < dx || fraction.y < dy) {
    color = gridColor;
  }
  return color;
}

// 投影坐标系辅助对象
vec4 ProjectionHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor, in float gridWidth, in vec4 gridColor) {
  // 坐标轴
  vec4 axisHelper = AxisHelper(coord, axisWidth, xAxisColor, yAxisColor);
  // 栅格
  vec4 gridHelper = GridHelper(coord, gridWidth, gridColor);
  // =投影坐标系
  return bool(axisHelper.a) ? axisHelper : gridHelper;
}
vec3 interplateColor(vec4 colorStops[3],float t){

    vec3 col=colorStops[0].rgb;
    float colorStop0=colorStops[0].w;
    for(int i=1;i<3;i++){
      vec3  currentColor=colorStops[i].rgb;
      float colorStop1=colorStops[i].w;
      float ct=clamp((t-colorStop0)/(colorStop1-colorStop0),0.,1.);
      col=mix(col,currentColor,ct);   
      colorStop0=colorStop1;
    }
   return col;
}
float interplate(float start,float end,float value){
   return clamp((value-start)/(end-start),0.,1.);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
  vec2 uv = ProjectionCoord(fragCoord,5.);
  vec3 col=ProjectionHelper(uv,2.,vec4(1,0,0,1),vec4(0,1,0,1),1.,vec4(1)).rgb;
  float len=sdfRect(uv,vec2(1,1),vec2(2));
  RadialGredient gredient;
  gredient.x0=1.;
  gredient.y0=1.;
  gredient.r0=0.;
  gredient.x1=1.;
  gredient.y1=1.;
  gredient.r1=1.;
  gredient.colorStops[0]=vec4(1,0,0,0.);
  gredient.colorStops[1]=vec4(0,1,0,0.5);
  gredient.colorStops[2]=vec4(0,0,1,1.);
  if(len<0.){

      float r1=gredient.r0; // 起始圆半径
      float r2=gredient.r1; // 结圆半径
      // 起始圆
      vec2 c1=vec2(gredient.x0,gredient.y0);
      // 结束圆 
      vec2 c2=vec2(gredient.x1,gredient.y1);

      vec2 cd=c2-c1;
      vec2 pd=uv-c1;
      float dr=r2-r1;

      float mindr=-1.*r1;
      float A=cd.x*cd.x+cd.y*cd.y-dr*dr;
      float B=pd.x*cd.x+pd.y*cd.y+r1*dr;
      float C=pd.x*pd.x+pd.y*pd.y-r1*r1;
      float inva;
      float t=1. / 2. * C / B;
   
      if(A!=0.){
        inva=1.*1./A;
      }
      if(A==0.){
        if(t*dr>=mindr){
          col=interplateColor(gredient.colorStops,t);
        }
    } 
     else{
        float discr = B*B - A * C;
        if(discr>=0.){
          float sqrtdiscr=sqrt(discr);
          float t0=(B+sqrtdiscr)*inva;
          float t1=(B-sqrtdiscr)*inva;
          if(t0*dr>=mindr){
                col=interplateColor(gredient.colorStops,t0);
          } else if(t1*dr>=mindr){
                col=interplateColor(gredient.colorStops,t1);
          }
        }
      }
  }
  fragColor = vec4(col, 1);
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==



内圆半径不为0时:

 中心位置一样时,效果


内圆与外圆的中心位置不一样的效果


代码:

  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值