今天介绍的是实际工作中最常用到的着色器:渐变色着色器。

渐变色着色器是一个从一种颜色平滑的过渡到另一种颜色的效果,渐变色着色器的作用主要是增强图形的视觉吸引力。

线性渐变

Skia 里的线性渐变色着色器是最简单的渐变色着色器,它用于在 2D 图形中创建从一点到另一点平滑过渡的颜色效果。它通过在起点和终点之间进行线性插值,实现了两种或多种颜色之间的平滑渐变。

现在来看一下如何使用线性渐变色着色器来绘制窗口背景,如下代码所示:

// #include "include/effects/SkGradientShader.h"void drawLinearGradientColor(SkCanvas *canvas)
{SkPaint paint;paint.setAntiAlias(true);SkPoint pts[2]{SkPoint::Make(0, 0), SkPoint::Make(w, h)};SkColor colors[6]{ 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00,0xFF0000FF,0xFF00FF00,0xFFFF0000 };sk_sp<SkShader> shader = SkGradientShader::MakeLinear(pts, colors, nullptr, 6, SkTileMode::kClamp);paint.setShader(shader);canvas->drawPaint(paint);//用线性渐变色绘制圆形//auto x = w / 2;//auto y = h / 2;//auto r = std::min(x - 60, y - 60);//canvas->drawCircle(x, y, r, paint);//用线性渐变色绘制路径//paint.setStroke(true);//paint.setStrokeWidth(16);//paint.setStrokeJoin(SkPaint::kRound_Join);//SkPath path;//path.moveTo(60, 120);//path.lineTo(180, 60);//path.lineTo(w - 60, 120);//path.lineTo(w - 160, h - 160);//path.lineTo(180, h - 60);//path.close();//canvas->drawPath(path, paint);
}

在这段代码中使用 SkGradientShader 类型的静态方法 MakeLinear 创建了一个线性渐变着色器。

MakeLinear 方法的第一个参数是一个只有两个 SkPoint 元素的数组,数组内存储的两个点为线性渐变的起点终点

第二个参数是一个颜色数组,这个数组中存储的颜色将被分布在线性渐变的起点和终点之间。

第三个参数为一个 SkPoint 数组,这个数组用于控制各个颜色的位置,如果不传递这个参数(nullptr),那么颜色将被均匀分布。

第四个参数为第二个数组参数和第三个数组参数的元素数量。

第五个参数 SkTileMode::kClamp 表示如果着色器在起点和终点之外绘制,则超出的区域使用 colors 首尾两端的颜色进行绘制。

除此之外 SkTileMode 还有一些其他的枚举项,这里就不一一介绍了。

MakeLinear方法返回一个 SkShader 类型的对象,这个对象可以被设置到 SkPaint 对象中。

SkPaint设置了shader之后,它设置的Color就不再生效,SkPaint优先使用shader

程序运行后的结果如下图所示:

由于渐变色是在 SkPaint 对象设置的,可以用来绘制任何几何图形,

注释的代码就是使用这个渐变色绘制圆形和路径的示例代码,运行结果如下图所示:

线性渐变2.png

 

素材:WebGradients 是一个提供了180个线性渐变色的免费网站。 

径向渐变

径向渐变允许颜色从中心点向四周以圆形扩散,绘制径向渐变颜色与绘制线性渐变颜色非常相似,代码如下所示:

void drawRadialGradientColor(SkCanvas *canvas)
{SkPaint paint;paint.setAntiAlias(true);SkColor colors[6]{ 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00,0xFF0000FF,0xFF00FF00,0xFFFF0000 };auto x = w / 2;auto y = h / 2;auto r = std::min(x - 10, y - 10);auto shader = SkGradientShader::MakeRadial(SkPoint::Make(x, y), r, colors, nullptr, 6, SkTileMode::kClamp);paint.setShader(shader);canvas->drawPaint(paint);
}

使用SkGradientShader类型的MakeRadial方法创建径向渐变,

这个方法的第一个参数是径向渐变的圆心,第二个参数是径向渐变的半径,其他参数与线性渐变SkGradientShader::MakeLinear方法相同。

程序运行后的结果如下图所示:

径向渐变.png

从上图中可以看出,窗口边缘的颜色为红色,这是因为设置了SkTileMode::kClamp,所以超出的区域使用colors数组首尾两端的颜色进行绘制。

锥形渐变

锥型渐变允许颜色从一个中心点向四周以锥形的方式扩散渐变,这种渐变色常用于绘制光晕或放射效果。如下代码所示:

void drawConicalGradientColor(SkCanvas* canvas)
{SkPaint paint;auto x = w / 2;auto y = h / 2;SkColor colors[6]{ 0xFF00FFFF, 0xFFFFFF66, 0xFFFF00FF, 0xFF66FFFF, 0xFFFFFF00, 0xFFFF66FF };auto shader = SkGradientShader::MakeTwoPointConical(SkPoint::Make(x, y), y, SkPoint::Make(x, 60.0f), 20.0f,colors, nullptr, 6, SkTileMode::kClamp);paint.setShader(shader);canvas->drawPaint(paint);
}

SkGradientShader::MakeTwoPointConical方法负责创建一个锥型渐变。这个方法接收两个圆作为参数。

第一个参数为第一个圆的圆心,第二个参数为第一个圆的半径。

第二个参数为第二个圆的圆心,第三个参数为第二个圆的半径,其他参数与制作线性渐变颜色相同。

程序运行结果如下图所示:

锥型渐变.png

旋转渐变

旋转渐变把一系列颜色平均分布到360°的角度范围内(默认情况下以0°开始,以360°结束),如下代码所示:

void drawSweepGradientColor(SkCanvas* canvas)
{SkPaint paint;auto x = w / 2;auto y = h / 2;SkColor colors[7]{ 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00,0xFF0000FF,0xFF00FF00,0xFFFF0000,0xFF00FFFF };auto shader = SkGradientShader::MakeSweep(x, y, colors, nullptr, 7, 0, nullptr);paint.setShader(shader);canvas->drawPaint(paint);
}

SkGradientShader::MakeSweep方法负责创建旋转渐变。

此方法前两个参数为旋转渐变的圆心,其他参数与制作线性渐变色相同。

为了让开始颜色和结束颜色更平滑的过度,我把开始颜色和结束颜色设置成了同一个颜色。

 

程序运行结果如下图所示:

旋转渐变.png

综合示例:绘制圆柱体

Skia里的几何图形、路径、颜色和渐变色着色器,运用这些知识绘制一个圆柱体,如下代码所示:

void drawCylinder(SkCanvas* canvas) {int bodyW{ 160 }, top{60};int x1{ w / 2 - bodyW / 2 }, x2{ w / 2 + bodyW / 2 };SkPath path;SkRect rect;rect.setXYWH(x1, h - 80, bodyW, 40);path.arcTo(rect, 0, 180,true);path.lineTo(x1, top+20);path.lineTo(x2, top+20);path.close();SkPaint paint;paint.setAntiAlias(true);SkPoint pts[2]{ SkPoint::Make(x1, top), SkPoint::Make(x2, top) };SkColor colors[2]{ 0xFF287191, 0xFF85B5CB };sk_sp<SkShader> shader = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);paint.setShader(shader);canvas->drawPath(path, paint);rect.setXYWH(x1, top, bodyW, 40);colors[0] = 0xffBDE4F8;colors[1] = 0xffA7CFE6;pts[0] = SkPoint::Make(x1, top + 40);pts[1] = SkPoint::Make(x2, top);shader = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);paint.setShader(shader);canvas->drawOval(rect, paint);    
}

在这段代码中,分两个步骤来绘制这个圆柱体:

  1. 使用路径绘制圆柱体的 Body

圆柱体的Body是由一个圆弧和一部分矩形组成的,绘制这个路径时,使用了一个从左到右的线性渐变色,左侧颜色较深,右侧颜色较浅。

这个路径绘制完成之后,得到的结果如下图所示:

圆柱体0.png

  1. 2,使用椭圆绘制圆柱体的顶面

圆柱体的顶面是一个椭圆,椭圆的横向中轴线就是圆柱体的Body的顶边。

同样也是使用一个线性渐变色绘制这个椭圆,椭圆的左下角颜色较浅,右上角颜色较深。

椭圆绘制完成之后,圆柱体的Body的顶边就被这个椭圆盖住了。

最终的绘制结果如下图所示:

圆柱体.png

在这个示例中,我们使用 2D 图形绘图知识绘制了一个 3D 图形。

要知道无论是 2D 图形引擎还是 3D 图形引擎,最终都是在处理像素,交付给客户的也都是二维平面像素数据(你的显示器只能承载二维平面像素数据)。3D 图形只不过看起来是立体的而已。

所以,2D 图形引擎可以画 3D 图形,3D 图形引擎也可以画 2D 图形,只不过这两个图形引擎的内部实现原理大相径庭。

综合示例:绘制圆锥体

稍稍改动一下圆柱体的绘制代码,就可以绘制圆锥体了,如下代码所示:

void drawCone(SkCanvas* canvas) {int bodyW{ 160 }, top{ 60 };int x1{ w / 2 - bodyW / 2 }, x2{ w / 2 + bodyW / 2 };SkPath path;SkRect rect;rect.setXYWH(x1, h - 80, bodyW, 40);path.arcTo(rect, 0, 180, true);path.lineTo(x1+bodyW/2, top + 20);path.close();SkPaint paint;paint.setAntiAlias(true);SkColor colors[2]{ 0xFF00FF00, 0xFF000000 };auto shader = SkGradientShader::MakeSweep(w/2, top + 20, colors, nullptr,2,SkTileMode::kClamp, 50.0, 130.0, 0,nullptr);paint.setShader(shader);canvas->drawPath(path, paint);
}

 

这段代码去除了圆柱体的顶面和圆柱体 Body 的顶边,在绘制路径时,使用了旋转渐变颜色。

旋转渐变的起始角度为 50 度,结束角度为 130 度,顺时针从绿色渐变到黑色。

这两个角度之差大于圆锥顶角值,超出圆锥(扇形)表面的颜色将不会被绘制到窗口中。

程序运行后,绘制结果如下图所示:

圆锥.png

 

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025.07.09华为机考真题解析-第二题200分

📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 02. 地铁线路故障预警系统 问题描述 LYA 负责管理一个城市的地铁网络系统。地铁网络由 n n n

数学建模:非线性规划:凸规划问题

一、定义凸集定义​​&#xff1a;设Ω是n维欧氏空间的一点集&#xff0c;若任意两点x₁∈Ω&#xff0c;x₂∈Ω&#xff0c;其连线上的所有点αx₁(1-α)x₂∈Ω&#xff0c;(0≤α≤1)&#xff0c;则称Ω为凸集。​​凸函数定义​​&#xff1a;给定函数f(x)(x∈D⊂Rⁿ)&…

ISIS | 广播网络中的 ISIS 伪节点 LSP

注&#xff1a;本文为 “ISIS | 伪节点 LSP” 相关合辑。 英文引文&#xff0c;机翻未校。 中文引文&#xff0c;略作重排。 如有内容异常&#xff0c;请看原文。 ISIS in Broadcast Network and Pseudonode LSP 广播网络中 的 ISIS 伪节点 LSP ISIS in broadcast network is…

ARIA UWB安全雷达主要产品型号与核心功能全解析

ARIA UWB雷达拥有LT系列与AHM系列两大产品线。LT103 XBT、LT102 V2、LT103 OEM等代表型号具备高精度定位、低功耗和强穿透能力&#xff0c;适用于工业自动化与物联网。AHM3D、AHM2D、AHM3DSC则专注三维检测和智能计算&#xff0c;广泛服务于医疗健康、安防监控等场景。Hydrogen…

NLP自然语言处理04 transformer架构模拟实现

总体架构输入部分代码实现:导包# -*-coding:utf-8-*- import matplotlib.pyplot as plt import numpy as np import torch import torch.nn as nn # -*-coding:utf-8-*- import copy import torch.nn.functional as F import math位置编码器部分词嵌入WordEmbedding# todo 作用…

记录一本书: Python机器学习:基于PyTorch和Scikit-Learn

记录一本书&#xff1a; Python机器学习&#xff1a;基于PyTorch和Scikit-Learn 作者&#xff1a;&#xff08;美&#xff09;塞巴斯蒂安拉施卡(Sebastian Raschka)&#xff08;美&#xff09;刘玉溪&#xff08;海登&#xff09;(Yuxi(Hayden)Liu) &#xff08;美&#xff09;…

Datomic数据库简介(TBC)

Datomic 数据库详细介绍 Datomic 是一个由 Rich Hickey&#xff08;Clojure 语言创始人&#xff09;设计的 不可变、时间感知、分布式数据库&#xff0c;专为现代应用程序设计&#xff0c;强调 数据不变性&#xff08;immutability&#xff09;、查询灵活性和可审计性。它结合…

xformers 完整安装教程【pip conda】(解决 conda 版本 pytorch 自适应安装 xformers)

我个人喜欢用 mamba&#xff08;conda&#xff09;创建环境&#xff0c;然后用 mamba 安装 pytorch CUDA&#xff08;如果需要使用 CUDA 编译&#xff09;&#xff0c;还有一些比如 gcc/g 等与 python 无关的一些工具。 但是最近我在扩充环境的时候&#xff0c;发现需要额外安…

VM虚拟机全版本网盘+免费本地网络穿透端口映射实时同步动态家庭IP教程

VM虚拟机全版本秘钥&#xff0c;文章末尾。 首先网络穿透的意义是让公网可以直接访问家庭电脑&#xff0c;这样本地电脑的硬件性能得以完全发挥&#xff0c;特别是在云服务器贵性能又没家庭电脑好&#xff0c;专线宽带又贵&#xff0c;第三方网络穿透贵的场景下。一般第三方网…

C++ - 仿 RabbitMQ 实现消息队列--项目介绍与环境搭建

目录 项目介绍 开发环境 技术选型 环境搭建 安装 wget(一般情况下默认会自带) 更换国内软件源 安装 lrzsz 传输工具 安装编译器 安装项目构建工具 make 安装调试器 安装 git 安装 cmake 安装 Protobuf 安装 Muduo 安装 SQLite3 安装 Gtest 项目介绍 首先说一下…

《目标检测模块实践手册:从原理到落地的尝试与分享》第一期

大家好&#xff0c;欢迎来到《目标检测模块实践手册》系列的第一篇。从今天开始&#xff0c;我想以一种 “实践记录者” 的身份&#xff0c;和大家聊聊在目标检测任务中那些形形色色的模块。这些内容没有权威结论&#xff0c;更多的是我在实际操作中的一些尝试、发现和踩过的坑…

C++11笑传之引用

C11前言列表初始化{}进行初始化initializer_list右值引用和移动语义左值与右值左值引用与右值引用引用延长生命周期右值引用和移动语义的使用场景左值引用移动构造和移动赋值右值引用在容器插入的提效引用折叠万能折叠完美转发前言 C11是C继98后的更新&#xff0c;其更新了许多…

瀚高数据库提交数据后,是否需要COMMIT(APP)

文章目录环境症状问题原因解决方案报错编码环境 系统平台&#xff1a; 版本&#xff1a;5.6.5,4.5 症状 瀚高数据库提交数据后&#xff0c;是否需要commit&#xff0c;瀚高数据库是否有配置项。 问题原因 瀚高数据库默认自动COMMIT&#xff08;提交数据&#xff09;&#…

深大计算机游戏开发实验三

主要步骤主角飞船的创建和移动边界设置以及护盾设置创建敌机自动生成敌机图层设置弹丸设置武器创建不同发射模式管理竞态条件击败敌机掉落升级道具不同敌机的生成分值显示实现退出游戏界面之后进入游戏的最高记录重置游戏界面失败后重新加载最记录不会重置任何时候在游戏界面按…

详解缓存淘汰策略:LRU

文章目录缓存淘汰策略LRU核心结构核心操作流程局限性源码走读AddGet缓存淘汰策略 缓存淘汰策略的存在是为了解决 缓存容量有限性 和 高缓存命中率 之间的矛盾。其核心目标是在有限的缓存空间内&#xff0c;尽可能提高缓存命中率 缓存容量有限性&#xff1a;缓存&#xff08;例…

什么是 Bootloader?怎么把它移植到 STM32 上?

一、Bootloader 是啥&#xff1f;它都干了些啥&#xff1f;想象一下你的 MCU&#xff08;比如 STM32&#xff09;是一个小机器人&#xff0c;上电之后第一件事&#xff0c;它不会立马开始“干正事”&#xff08;运行你的主程序&#xff09;&#xff0c;而是先去运行一个“开场引…

无人机避障——感知篇(Ego_Planner_v2中的滚动窗口实现动态实时感知建图grid_map ROS节点理解与参数调整影响)

处理器&#xff1a;Orin nx 双目视觉传感器&#xff1a;ZED2 实时感知建图方法&#xff1a;Vins Fusion Raycast &#xff08;VIO与射线投影法感知定位加建图方法&#xff09; 项目地址&#xff1a;https://github.com/ZJU-FAST-Lab/EGO-Planner-v2 【注意】&#xff1a;建…

26-计组-寻址方式

指令寻址与PC自增一、指令寻址方式定义&#xff1a;寻找下一条将要执行的指令地址的过程。 核心部件&#xff1a;程序计数器&#xff08;PC&#xff09;&#xff0c;用于指示待执行指令的地址。 执行流程&#xff1a;CPU根据PC值从主存取指令。取指后&#xff0c;PC自动自增&am…

生成式对抗网络(GAN)模型原理概述

生成对抗网络&#xff08;Generative Adversarial Network, GAN&#xff09;是一种通过对抗训练生成数据的深度学习模型&#xff0c;由生成器&#xff08;Generator&#xff09;和判别器&#xff08;Discriminator&#xff09;两部分组成&#xff0c;其核心思想源于博弈论中的零…

Vue和Element的使用

文章目录1.vue 脚手架创建步骤2.vue项目开发流程3.vue路由4.Element1.vue 脚手架创建步骤 创建一个文件夹 vue双击进入文件夹,在路径上输入cmd输入vue ui, 目的:调出图形化用户界面点击创建 9. 10.在vscode中打开 主要目录介绍 src目录介绍 vue项目启动 图形化界面中没有npm…