glsl shader中实现canvas中的createRadialGradient效果

news2024/11/13 11:15:31

在网上找了好久,也没有发现有现成用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时:

 中心位置一样时,效果


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


代码:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1957277.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【开发问题记录】启动某个微服务时无法连接到seata(seata启动或配置异常)

问题记录 一、问题描述1.1 问题复现1.1.1 将Linux中的部分微服务启动1.1.2 在本地启动当时出错的服务 1.2 解决思路1.2.1 Nacos中seata相关的信息1.2.2 Linux中seata相关的信息 二、问题解决2.1 seata的配置错误2.1.1 Nacos中seata的配置问题2.1.2 命名空间问题的发现 2.2 网络…

Python数值计算(12)

本篇说说Neville方法。Neville方法的基础是&#xff0c;插值多项式可以递归的生成&#xff0c;有时进行插值的目的是为了计算某个点的值&#xff0c;这个时候并不需要将拟合曲线完全求出&#xff0c;而是可以通过递归的方式进行计算&#xff0c;具体操作如下&#xff1a; 例如…

代码实践思考:ROS1和ROS2

扩展 能否借助人工智能工具将ROS1批量转为ROS2如何更高效的进行ROS学习 ROS1大量案例直接批处理用智能工具转为ROS2案例 不同版本之间的工具软件直接用智能方式进行代码升级 例如OpenCV之类&#xff0c;一些函数变化&#xff0c;直接用智能工具进行批量代码优化 机器人画圆-…

滞回比较器工作原理,应用,TINA仿真

滞回比较器 滞回比较器的主要应用是增加滞回控制&#xff0c;让其对微小的变化不那么敏感&#xff0c;增强抗干扰能力&#xff0c;避免由噪声引起的不稳定状态&#xff0c;通常用于噪声环境下的阈值检测以及信号调理。根据不同需求&#xff0c;滞回比较器还可以设计成开漏极输出…

超级智能体创造营:启动!我的情侣匹配度测试助手

零、写在前面 听说由百度文心智能体平台主办&#xff0c;万众瞩目的 超级智能体创造营 第三期启动啦&#xff01; 在本期的创造营中&#xff0c;博主开发了一款有趣的超级智能体&#xff1a;情侣匹配度测试助手。 为什么要开发它呢&#xff0c;快来听博主娓娓道来吧~ 一、超…

【通俗理解】自由意志与预测——混沌理论与预测的不可能性

【通俗理解】自由意志与预测——混沌理论与预测的不可能性 自由意志与预测的类比 你可以把自由意志比作一只难以捉摸的蝴蝶&#xff0c;它的每一次振翅都可能引发不可预测的风暴。而预测则像是一个试图捕捉这只蝴蝶的网&#xff0c;但往往因为蝴蝶的微小变动而错失目标。 自由意…

五大AI测试开源框架及使用方法介绍

AI测试框架是一套系统的测试工具和方法&#xff0c;包括测试的规范和基础代码&#xff0c;涵盖了一系列的测试思想和方法。这些框架可以帮助开发者和测试人员对AI模型进行测试&#xff0c;确保AI模型在真实世界的应用中能够达到预期的效果。接下来的内容&#xff0c;我们将介绍…

PHP苹果 V X iPhone微商i o s多分开V X语音转发密友朋友圈一键跟圈软件

苹果VX神器&#xff01;iPhone微商必备&#xff1a;ios多开、VX语音转发、密友朋友圈一键跟圈软件大揭秘&#xff01; 一、iOS多开新境界&#xff0c;工作生活两不误&#xff01; 你是不是也烦恼过&#xff0c;想要在工作号和生活号之间自由切换&#xff0c;却因为iPhone的限制…

【vue3】pinia配置(任意组件通信,集中状态管理容器)

选择式写法 一、安装pinia依赖 终端输入&#xff1a;npm install pinia 二、配置pinia 第一步&#xff1a;创建仓库&#xff0c;在src目录下创建文件夹modules和文件index.ts. 第二步&#xff1a;配置index.ts // 创建pinia大仓库 import { createPinia } from "pin…

H5+JS视频列表

源码在最后面 好像有些浏览器不适配&#xff08;可能是设置了禁用自动播放的功能&#xff09; 实现功能 视频自动无声播放鼠标置于某个视频板块之上时自动无声播放点击视频跳转视频链接 效果图&#xff08;部分 视频加载可能有点慢&#xff09; 部分功能实现原理 1.无声播放…

学习C语言第十六天(调试、)

调试 第一题 C 第二题 A 第三题 C 第四题 C 第五题 D release不可以调试 第六题 越界访问访问到了i的地址 编程题 模拟strcpy void my_strcpy(char*arr1,char*arr2) {while (*arr2){*arr1 *arr2;} } int main() {char arr1[50…

【足球走地软件】走地数据分析预测【大模型篇】走地预测软件实战分享

了解什么是走地数据&#xff1f; 走地数据分析&#xff0c;在足球赛事的上下文中&#xff0c;是一种针对正在进行中的比赛进行实时数据分析的方法。这种方法主要用于预测比赛中的某些结果或趋势&#xff0c;如总进球数、比分变化、球队表现等。 在足球走地数据分析中&#xf…

仕考网:公务员可以报考军队文职吗?

公务员可以报考军队文职考试&#xff0c;但是需要满足前提条件。 对于已经与国家、地方的用人单位建立劳动关系的社会人才&#xff0c;在获得当前用人单位的许可后才可以申请报考。 在面试过程中&#xff0c;考生必须出示一份由其用人单位出具的且加盖公章的同意报考证明。一…

C++: set容器

一、关联式容器 我们已经了解了STL中的部分容器&#xff0c;比如vector、list、deque等&#xff0c;这些容器被称为序列式容器&#xff0c;其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。 关联式容器也是用来存储数据的&#xff0c;与序列式容器不同&#xf…

Windows中启用Linux命令功能WSL,并安装Linux子系统

文章目录 一、WSL简介 二、启用WSL 三、参考文章 一、WSL简介 WSL&#xff08;Windows Subsystem for Linux&#xff0c;适用于 Linux 的 Windows 子系统&#xff09;允许用户直接在 Windows 上运行Linux 环境&#xff08;包括大多数命令行工具、实用程序和应用程序&#x…

汽车软件开发中的功能安全挑战与应对策略:基于Jira平台构建端到端的可追溯性,实现精细化需求管理

2024年7月18-19日&#xff0c;龙智携汽车软件开发及管理解决方案创新亮相2024 ATC汽车软件与安全技术周。龙智技术支持部负责人&Atlassian认证专家叶燕秀、龙智功能安全高级工程师景玉鑫在活动主会场联合发表了精彩演讲&#xff0c;分享推动汽车软件开发与功能安全的创新实…

硬核产品经理

链接&#xff1a;硬核产品经理 (qq.com)

DNS劫持实验

实验背景 利用ettercap进行DNS欺骗&#xff0c;攻击者冒充域名服务器&#xff0c;也就是把查询的IP地址设为攻击者 的IP地址&#xff0c;这样用户上网就只能看到攻击者设计的网页。 实验设备 一个网络 net:cloud0 一台模拟黑客主机 kali 一台靶机 windows 主机 实验拓扑 …

消息队列-rabbitmq(生产者.消费者. 消息.可靠性)

生产者者的可靠性 为了保证我们生产者在发送消息的时候消息不丢失&#xff0c;我们需要保证发送者的可靠性 1.生产者重试 假如发送消息的时候消息丢失 &#xff0c;我们可以使用发送者 重试机制&#xff0c;尝试重新发送消息 实现该机制非常简单&#xff0c;只需要在yml文…

The Llama 3 Herd of Models.Llama 3 模型第1,2,3部分全文

现代人工智能(AI)系统是由基础模型驱动的。本文提出了一套新的基础模型,称为Llama 3。它是一组语言模型,支持多语言、编码、推理和工具使用。我们最大的模型是一个密集的Transformer,具有405B个参数和多达128K个tokens的上下文窗口。本文对Llama 3进行了广泛的实证评价。我们…