c 做网站的六大对象/如何进行搜索引擎优化 简答案
前言
在上一篇文章中,麒麟子给大家分享了如何在 Cocos Creator 3.8 中的自定义管线中,添加属于自己的后期效果 Shader。 但基于 BlitScreen 的方案,我们只能编写最简单后效 Shader,如果我们想要支持更多复杂的 Shader,比如模糊、景深等等效果,就需要配合代码才能实现。
今天麒麟子就用高斯模糊来演示如何编写一个多 Pass 的后效 Shader。
准备 Shader
高斯模糊的涉及到的内容比较多,什么正态分布、高斯函数、高斯卷积核。
如果对数学没兴趣,上面这些统统不用管。
简单来说,高斯模糊就是把图片上的每一个像素都用下面的流程处理一遍。
直白来说,就是一个简单的加权求和:采样目标像素的同时,再采样一些周围的像素;并且每一个像素给一个权重(权重和为1.0)。最终像素值 = 所有(像素 x 权重)的和。
如果想要效果好,就多迭代几次,迭代次数越多,画面越好,但性能开销越高。
我们创建一个 Cocos Shader 文件,命名为 “gaussian-blur.effect” 然后编写下面的内容。
CCEffect %{techniques:- passes:- vert: blur-hor-vsfrag: blur-fspass: blur-xdepthStencilState:depthTest: falsedepthWrite: false- vert: blur-vert-vsfrag: blur-fspass: blur-ydepthStencilState:depthTest: falsedepthWrite: false
}%CCProgram blur-hor-vs %{//...
}%
CCProgram blur-hor-vs %{//...
}%CCProgram blur-fs %{//...
}%//为了方便文章阅读
//完整 Shader 被放到了文末
可以看到,整个 Cocos Shader 只有两个 Pass,一个用来水平模糊,一个用来竖直模糊。
为了不影响阅读,完整 Shader 放在了文末。
注意:Cocos Creator 3.8.0 版本如果新增了后效 Shader,需要重启编辑器才能识别。后面版本中会优化这个流程。
编写属性组件
Cocos Creator 3.8 中,只需要在节点上添加对应的后期效果组件,就可以启动对应的后期效果。
自定义后期管线提供了一个 postProcess.PostProcessingSetting
组件类,我们可以通过继承它类来实现后效参数的可视化界面配置。
通过它自定义出来的后期效果 ,是完全可以达到和内置的后期效果一样的使用体验的。
新建一个 TS 脚本文件,起名为 “GaussianBlur.ts”,然后输入以下代码。
代码片段1:
import { _decorator, gfx, postProcess, Material, EffectAsset, renderer, rendering, Vec4 } from 'cc';
const { Format } = gfxconst { ccclass, property, menu, executeInEditMode } = _decorator;@ccclass('GaussianBlur')
@menu('PostProcess/GaussianBlur')
@executeInEditMode
export class GaussianBlur extends postProcess.PostProcessSetting{@property(EffectAsset)_effectAsset: EffectAsset | undefined@property(EffectAsset)get effect () {return this._effectAsset;}set effect (v) {this._effectAsset = v;if(this._effectAsset == null){this._material = null;}else{if(this._material == null){this._material = new Material();}this._material.reset({effectAsset:this._effectAsset});}this.updateMaterial();}@propertyiterations = 3;@propertyget blurRadius(){return this._blurParams.x;}set blurRadius(v){this._blurParams.x = v;this.updateMaterial();}private _material:Material;public get material():Material{return this._material;}@propertyprivate _blurParams:Vec4 = new Vec4(1.0,0.0,0.0,0.0);public get blurParams():Vec4{return this._blurParams;}updateMaterial(){if(!this._material){return;}this._material.setProperty('blurParams', this.blurParams);}protected start(): void {if(this._effectAsset){this._material = new Material();this._material.initialize({effectAsset:this._effectAsset});this._material.setProperty('blurParams', this.blurParams);}}
}
接下来,我们就可以通过“添加组件“按钮,把 PostProcess/GansussianBlur
添加到后处理结点上了。
可以看到,在 Inspector 面板上,它接受 3 个参数。
effect
:用于指定用于这个后期效果的 Shader 文件iterations
:用于指定需要迭代多少次,迭代次数越多越模糊blurRadius
:采样临近像素时的偏移,偏移量越大越模糊
这个时候是没有任何效果的,因为还没有实现对应的渲染代码。
编写渲染代码
Cocos Creator 3.8 中的后处理管线是基于新版自定义管线的,而新版的自定义管线是基于 RenderGraph 架构的。
你可以简单地认为,我们想要实现的单个后处理效果,对应的就是渲染流程图上的一个节点。
后处理管线提供了 postProcess.SettingPass
给我们,用来编写自己的后处理渲染效果。(也可以叫,自定义后处理节点的资源和行为)。
接下来,我们来做真正的渲染实现。 我们需要继承自定义管线中的 postProcess.SettingPass
类,并实现并要的代码。
get setting
:获取配置信息,对应的就是上面实现的界面组件checkEnable
:用于判断此后效是否开启name
:后效的名字,一般保持和类名一致即可outputNames
:最终输出的 RT 数组。(临时用的 RT 不用放在这里)render
:用于执行渲染流程
完整编码如下。
代码片段2:
export class GaussianBlurPass extends postProcess.SettingPass {get setting () { return this.getSetting(GaussianBlur); }checkEnable (camera: renderer.scene.Camera) {let enable = super.checkEnable(camera);if (postProcess.disablePostProcessForDebugView()) {enable = false;}return enable && this.setting.material != null;}name = 'GaussianBlurPass';outputNames = ['GaussianBlurMap'];public render (camera: renderer.scene.Camera, ppl: rendering.Pipeline): void {const setting = this.setting;if(!setting.material){return;}let passContext = this.context;passContext.material = setting.material;const cameraID = this.getCameraUniqueID(camera);const cameraName = `Camera${cameraID}`;const passViewport = passContext.passViewport;passContext.clearBlack();const format = Format.RGBA8;let input = this.lastPass!.slotName(camera, 0);for(let i = 0; i < setting.iterations; ++i){passContext.updatePassViewPort().addRenderPass(`blur-x`, `blur-x${cameraID}`).setPassInput(input, 'outputResultMap').addRasterView('GaussianBlurMap_TMP', format).blitScreen(0).version();passContext.updatePassViewPort().addRenderPass(`blur-y`, `blur-y${cameraID}`).setPassInput('GaussianBlurMap_TMP', 'outputResultMap').addRasterView(this.slotName(camera), format).blitScreen(1).version();input = this.slotName(camera);}}
}
接下来,我们主要看看 render
处理了哪些事情。
准备工作
每一个 SettingPass
就是一个绘制节点。而节点的数据,我们存在了 context
中。
render
函数会每帧执行,所以需要调用 context.clearBack()
来清理背景。
然后,我们要将材质设置给 context
。
模糊的处理,需要使用上一个处理流程结束后的画面内容。因此,我们使用 this.lastPass.slotName(camera,0);
来获取。
一切准备就绪后,就进入到了绘制环节。
绘制
这里,我们使用了 iterations 属性来控制总共要迭代的次数。迭代一次,绘制流程就会走一遍。
我们来看看,绘制流程中每一步操作的用途。
-
updatePassViewPort:这个函数用来指定相对分辨率大小,这个根据算法需求来指定就行。如果要保持和后台缓冲区一样大,传入 1.0 即可。
-
addRenderPass:这个函数用来告诉管线,需要执行一次绘制流程。
- layout:对应的是 Cocos Shader 中的 Pass 名称
- passName:助记名称,便于调试查看
-
setPassInput:如果有用到自定义管线中的 RT 资源(比如上一次执行的结果),则需要在这里指定,方便自定义管线对资源进行管理。
- inputName: 自定义管线资源分配的资源名称
- shaderName: 对应 Cocos Shader 中的 uniform Sampler2D 名称。
-
addRasterView:可以简单理解为,输出结果
- name:输出的 RT 名称,便于后续流程复用
- format:输出的 RT 格式,比如,RGBA8888、RGBA16F 等等
-
blitScreen:执行绘制
- passIdx:Cocos Shader 中的 Pass 索引(这个在后面的版本中会优化一下,到时候,后处理流程可以不用传这个值)。
-
version:无实际意义,可以忽略。
添加到管线
如果只是写好了上面的代码,不进行添加,也是不会生效的。
我们在文件末尾加上下面代码。
代码片段3:
let builder = rendering.getCustomPipeline('Custom') as postProcess.PostProcessBuilder;
if (builder) {builder.insertPass(new GaussianBlurPass(),postProcess.BlitScreenPass);
}
首先,我们获取到了 Custom 管线,然后把我们新写的效果添加进去。
回到编辑器中,调节参数,就可以看到我们新写的模糊效果生效咯。
后处理管线源码浅析
为了方便大家理解后处理的渲染流程,麒麟子简单说明一些关键的源码。
代码集
后效相关的类,大多都在 postProcess
下,建议先从 “cc” 引入 postProcess
,再使用。
RT 管理
开启后处理管线后,开启了后处理管线效果的摄像机,会自动生成 RT,并设置它的 targetTexture
。
后效管线基于 RenderGraph 管线架构, Cocos 引擎中使用的 RenderGraph 是基于数据驱动的,会每帧收集需要的 RT 资源,并做统一管理。 因此不需要自己再手工新建 RT。
后效执行顺序
后处理效果的执行,不是按界面添加的顺序来的,而是按照处于数组中的顺序来的。我们通过源码可以看到它的内部顺序如下:
// pipeline relatedthis.addPass(new HBAOPass());this.addPass(new ToneMappingPass());// user post-processingthis.addPass(new TAAPass());this.addPass(new FxaaPass());this.addPass(new ColorGradingPass());this.addPass(new BlitScreenPass());this.addPass(new BloomPass());// final outputthis.addPass(new FSRPass()); // fsr should be finalthis.addPass(forwardFinal);
后处理效果在渲染时,管线会遍历这个数组,依次执行可用的后处理效果。
for (let i = 0; i < passes.length; i++) {const pass = passes[i];if (!pass.checkEnable(camera)) {continue;}if (i === (passes.length - 1)) {passContext.isFinalPass = true;}pass.lastPass = lastPass;pass.render(camera, ppl);lastPass = pass;
}
**源码位置:**engine/cocos/rendering/post-process/post-process-builder.ts
后效注意事项
自定义后效添加
在将自己定义的后效 Shader 添加到管线中时,需要注意几个问题:
- 必须添加在
ForwardFinal
之前,否则没有效果。所以builder.addPass
不建议使用 - 使用
builder.insterPass
添加新的后效时,如果新后效与旧的后效重名,会先移除旧的后效 builder.insterPass
会将新的后效插入到第二个参数类型指定的后效后面,通常情况下,建议使用postProcess.BlitScreenPass
。
效果
- TAA+FXAA+FSR 同时开启,才能达到很好的抗锯齿效果,因为 TAA 主要负责动态(帧间)抗锯齿,但会让画面略微变糊,FXAA 主要负责边缘抗锯齿,而 FSR 可以让糊了的画面变清晰。
- ColorGrading 是最划算的后期效果,能够快速提升项目画面颜值
- 不要过渡依赖 HBAO,因为中低端机跑不动。场景优先使用光照图烘焙出 AO。这样即使HBAO 关闭的情况下,效果依然不会差。
- Bloom 的阈值调小,强度调低,可实现全屏泛光,柔和画面。 Bloom 的阈值调大,强度调高,可实现亮部像素曝光效果。
内存
- 虽然 RenderGraph会自动管理和复用 RT,但是 后效依然会对内存有所消耗,可自行测试当所需后效开启后,上涨了多少内存。
- 内存开销受分辨率决定,请以实机测试为准。
- 可以通过 shadingScale 来降低分辨率,从而减少内存和减少渲染开销,提升性能的目的。这个值可以根据当前实际分辨率与设定的机型可用的最大后效分辨率来决定。
性能
-
后效会多次读写帧缓存,对 GPU 带宽和像素填充率,纹理填充率要求高。 在中低端机型上很可能造成性能锐减。
-
做好高、中、低端机型管理 ,在不同档次的机型上,开启对应的后效组合,确保性能和效果的平衡。
-
许多单 Pass 的后效,可以合成一个。 比如 ColorGrading,FXAA,Vignette,FinalPass 可以合成一个。这样可以减少 BlitScreen 次数,提升性能。
-
HBAO 能够极大地提升空间关系,但也是性能开销大户,谨慎使用。
写在最后
使用后处理效果能极大地提升画面质感,但也需要注意后处理效果带来的额外内存开销和填充率开销。
后效对低端机型很不友好,请做好在低端机上根据性能测试结果做好分档。
另外,后效的种类繁多,且根据具体的项目需求,又可以做特殊优化。因此引擎只能内置常见的后效供大家使用,更多后效果需求,还得开发者们自己来。
希望今天的分享能够对大家有帮助,谢谢大家!
附1:完整源码
新建一个 TS 脚本,将代码片段1、代码片段2、代码片段3 复制到这个脚本中即可。
附2:完整 Shader
新建一个 Cocos Shader 文件,将下面 Shader 代码复制到文件中即可。
CCEffect %{techniques:- passes:- vert: blur-hor-vsfrag: blur-fspass: blur-xdepthStencilState:depthTest: falsedepthWrite: false- vert: blur-vert-vsfrag: blur-fspass: blur-ydepthStencilState:depthTest: falsedepthWrite: false
}%CCProgram blur-hor-vs %{precision highp float;#include <legacy/input-standard>#include <builtin/uniforms/cc-global>#include <common/common-define>uniform MyConstants {vec4 blurParams;};out vec2 v_uv;out vec2 v_uv1;out vec2 v_uv2;out vec2 v_uv3;out vec2 v_uv4;void main () {StandardVertInput In;CCVertInput(In);CC_HANDLE_GET_CLIP_FLIP(In.position.xy);gl_Position = In.position;gl_Position.y = gl_Position.y;v_uv = a_texCoord;vec2 texelSize = cc_nativeSize.zw;float blurOffsetX = blurParams.x * texelSize.x;v_uv1 = v_uv + vec2(blurOffsetX * 1.0, 0.0);v_uv2 = v_uv - vec2(blurOffsetX * 1.0, 0.0);v_uv3 = v_uv + vec2(blurOffsetX * 2.0, 0.0);v_uv4 = v_uv - vec2(blurOffsetX * 2.0, 0.0);}
}%CCProgram blur-vert-vs %{precision highp float;#include <legacy/input-standard>#include <builtin/uniforms/cc-global>#include <common/common-define>uniform MyConstants {vec4 blurParams;};out vec2 v_uv;out vec2 v_uv1;out vec2 v_uv2;out vec2 v_uv3;out vec2 v_uv4;void main () {StandardVertInput In;CCVertInput(In);CC_HANDLE_GET_CLIP_FLIP(In.position.xy);gl_Position = In.position;gl_Position.y = gl_Position.y;v_uv = a_texCoord;vec2 texelSize = cc_nativeSize.zw;float blurOffsetY = blurParams.x * texelSize.y;v_uv1 = v_uv + vec2(0.0, blurOffsetY * 1.0);v_uv2 = v_uv - vec2(0.0, blurOffsetY * 1.0);v_uv3 = v_uv + vec2(0.0, blurOffsetY * 2.0);v_uv4 = v_uv - vec2(0.0, blurOffsetY * 2.0);}
}%CCProgram blur-fs %{precision highp float;#include <builtin/uniforms/cc-global>in vec2 v_uv;in vec2 v_uv1;in vec2 v_uv2;in vec2 v_uv3;in vec2 v_uv4;#pragma rate outputResultMap passuniform sampler2D outputResultMap;layout(location = 0) out vec4 fragColor;void main () {vec3 weights = vec3(0.4026,0.2442,0.0545);vec3 sum = texture(outputResultMap, v_uv).rgb * weights.x;sum += texture(outputResultMap, v_uv1).rgb * weights.y;sum += texture(outputResultMap, v_uv2).rgb * weights.y;sum += texture(outputResultMap, v_uv3).rgb * weights.z;sum += texture(outputResultMap, v_uv4).rgb * weights.z;fragColor = vec4(sum, 1.0);}
}%
关于麒麟子
深耕游戏引擎与游戏开发15年,每一滴干货都源自商业项目实践。
用技术资源赋能行业商机,提供实用解决方案、项目分析、技术指导与干货教程!
欢迎私信!
相关文章:

Cocos Creator 3.8 后期效果 Shader 编写(2/2) 进阶篇
前言 在上一篇文章中,麒麟子给大家分享了如何在 Cocos Creator 3.8 中的自定义管线中,添加属于自己的后期效果 Shader。 但基于 BlitScreen 的方案,我们只能编写最简单后效 Shader,如果我们想要支持更多复杂的 Shader,…...

【JS自用模板】自动点击选课的操作模板
以激动点击课程为案例复习一下基本前端,容易涉及的问题包括如何提取object类的数字,setTimeout为什么不起作用? 具体思路是,此处会立刻选中符合条件的页面元素打开,然后1小时后会刷新页面,相应地播放页面也…...

TENNECO EDI 项目——X12与XML之间的转换
近期为了帮助广大用户更好地使用 EDI 系统,我们根据以往的项目实施经验,将成熟的 EDI 项目进行开源。用户安装好知行之桥EDI系统之后,只需要下载我们整理好的示例代码,并放置在知行之桥指定的工作区中,即可开始使用。 …...

C++项目:在线五子棋对战(网页版)
项目介绍 本项⽬主要实现⼀个⽹⻚版的五⼦棋对战游戏,其主要⽀持以下核⼼功能: • 用户管理:实现用户注册,用户登录、获取用户信息、用户天梯分数记录、用户比赛场次记录等。 • 匹配对战:实现两个玩家在网页端根据天梯分数匹配游戏对⼿&…...

flutter遇到的小问题记录
flutter-getx的Get.bottomSheet组件改变高度 Get.bottomSheet( isScrollControlled: true,) isScrollControlled: true 就是控制高度 (无语) 截取视频第一针 返回的是本地url 或者Uint8List的数据 String? videoStr await VideoThumbnail.thumbnailFile(video: videoPath,…...

Golang bitset 基本使用
安装: go get github.com/bits-and-blooms/bitset下面代码把fmtx换成fmt就行 //------------基本操作------------//构建一个64bit长度的bitsetb : bitset.New(64)//放入一个数b.Set(10)fmtx.Println("add-10:", b.DumpAsBits()) // 0000000…...

sql 分组讨论,二级分组(非2个字段分组),使用 窗口函数和普通分组实现
1. 二级分组需求 先按照一个字段分组,在按照 第二个字段分组。之后,如果 这个 二级分组中的数据,是 > 1条的。就筛选出来。 比如: 先按照 站点分组,再按照 设备分组, 即:如果站点上配置了…...

业务中如何过滤敏感词
在我们访问网站的时候,如果发现我们发布的内容有色情暴力的东西等等,会屏蔽掉,这种行为就是过滤敏感词。 从技术层面实现起来,其实比较简单,因为我们输入的内容就是一个大型的字符串,我们要调用某些api来判…...

用服务器搭建网站需要做什么
网站建设是一个广义的术语,涵盖了许多不同的技能和学科中所使用的生产和维护的网站。不同领域的网页设计,网页图形设计,界面设计,创作,其中包括标准化的代码和专有软件,用户体验设计和搜索引擎优化。许多人…...

clickhouse 删除操作
OLAP 数据库设计的宗旨在于分析适合一次插入多次查询的业务场景,市面上成熟的 AP 数据库在更新和删除操作上支持的均不是很好,当然 clickhouse 也不例外。但是不友好不代表不支持,本文主要介绍在 clickhouse 中如何实现数据的删除,…...

C 语言中,「.」与「->」有什么区别?
使用“.”的话,只需要声明一个结构体。格式是结构体类型名结构体名。然后通过结构体名加上“.”再加上域名,就可以引用结构体的域了。因为结构体的内存是自动分配的,就像使用int a;一样。而使用“->”的话,需要声明一个结构体的…...

github pages 用法详解 发布自己的网站
github pages 基础用法 URL 规则 假设你的 github 帐号为 mygithub,需要发布的仓库名为 myrepo,那么 pages 的 URL 为: https://mygithub.github.io/myrepo 添加内容 用任意编辑器写好(或者生成)标准的网页内容&a…...

坤简炫酷的JQuery轮播图插件
介绍: 找到了一个炫酷的JQuery轮播图插件,只需要配置三四行代码就可以实现很多二维三维炫酷的切换效果。 视频效果及教程: https://www.bilibili.com/video/BV1Fu4y1d776/ 代码: https://github.com/w-x-x-w/AwesomeWeb 使用…...

C# 条件编译
C# 条件编译 C# 条件编译:根据不同的需求,编译生成不同的程序版本,条件编译是一种编译预处理命令,它是在编译代码之前对源代码进行处理。它可以根据条件,决定是否编译某段代码 条件编译的三种形式: 第一种…...

IntelliJ IDEA如何重新弹出git身份验证窗口
1、点击File菜单—>点击Settings—>点击Appearance & Behavior—>点击System Settings—>点击Passwords—>选中Do not save, forget passwords after restart—>点击Apply—>点击OK,如下所示: 2、重启IntelliJ IDEA—>通过g…...

【雕爷学编程】Arduino动手做(200)---WS2812B幻彩LED灯带4
37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&#x…...

【雕爷学编程】Arduino动手做(201)---DFRobot 行空板03
37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&#x…...

Spring中Bean的“一生”(生命周期)
文章目录 一、图解二、文字解析总结 一、图解 >注:处于同一行的执行顺序是从左往右 二、文字解析 SpringBean的生命周期总体分为四个阶段:实例化>属性注入>初始化>销毁 Step1 实例化Bean:根据配置文件中Bean的定义,…...

安卓:LitePal操作数据库
目录 一、LitePal介绍 常用方法: 1、插入数据: 2、更新数据: 3、删除数据: 4、查询数据: 二、LitePal的基本用法: 1、集成LitePal: 2、创建LitePal配置文件: 3、创建模型类…...

【JavaEE初阶】了解JVM
文章目录 一. JVM内存区域划分二. JVM类加载机制2.1 类加载整体流程2.2 类加载的时机2.3 双亲委派模型(经典) 三. JVM垃圾回收机制(GC)3.1 GC实际工作过程3.1.1 找到垃圾/判定垃圾1. 引用计数(不是java的做法,Python/PHP)2. 可达性分析(Java的做法) 3.1.2 清理垃圾1. 标记清除2…...

基于vue2.0和elementUi的vue农历日期组件vue-jlunar-datepicker(插件)
vue-jlunar-datepicker(插件) vue实现农历日历插件——vue-jlunar-datepicker插件 这个插件本身是基于vue2.0和elementUi框架来实现的。 安装 vue-jlunar-datepicker 插件 npm install vue-jlunar-datepicker --save // 如果安装过程中,出现…...

Python爬虫——selenium_元素定位
元素定位:自动化要做的就是模拟鼠标和键盘来操作这些元素,点击,输入等等。操作这些元素前首先要找到它们,WebDriver提供很多定位元素的方法 from selenium import webdriver# 创建浏览器对象 path files/chromedriver.exe brows…...

短视频内容平台(如TikTok、Instagram Reel、YouTube Shorts)的系统设计
现在,短视频内容已成为新趋势,每个人都在从TikTok、Instagram、YouTube等平台上消费这些内容。让我们看看如何为TikTok创建一个系统。 这样的应用程序看起来很小,但在后台有很多事情正在进行。以下是相关的挑战: •由于该应用程序…...

【git】Git 回退到指定版本:
文章目录 方法一: 使用 git reset 命令方法二:使用 git revert 命令方法三:使用 git checkout 命令常见的错误及其解决办法如下: 方法一: 使用 git reset 命令 命令可以将当前分支的 HEAD 指针指向指定的提交,从而回退代码到指定版…...

kibana+nginx配置密码 ubuntu
JAVA进阶之路-nginx设置密码 Kibana——通过Nginx代理Kibana并实现登陆认证 需要配置一下nginx文件 nginx配置文件详解 密码生成安装软件 apt install apache2-utils...

Git仓关联多个远程仓路径
前言 Git仓如果需要将代码push到多个仓,常用的做法是添加多个远程仓路径,然后分别push。这样虽然可以实现目的,但是需要多次执行push指令,很麻烦。 本文介绍关联多个远程仓路径且执行一次push指令的方法:git remote …...

使用ffmpeg将m4a及wav等文件转换为MP3格式
要使用ffmpeg将m4a及wav等文件转换为MP3格式,您可以按照以下步骤进行操作: 安装 ffmpeg 确保您已经安装了ffmpeg软件。如果没有安装,请访问ffmpeg的官方网站https://ffmpeg.org/ 并按照说明进行安装。 Win10 / Win11 可以通过 winget 命令…...

【CI/CD】Git Flow 分支模型
Git Flow 分支模型 1.前言 Git Flow 模型(本文所阐述的分支模型)构思于 2010 年,也就是 Git 诞生后不久,距今已有 10 多年。在这 10 多年中,Git Flow 在许多软件团队中大受欢迎。 在这 10 多年里,Git 本身…...

SpringBoot Thymeleaf模板引擎
Thymeleaf 模板引擎 前端交给我们的页面,是html页面。如果是我们以前开发,我们需要把他们转成jsp页面,jsp好处就是当我们查出一些数据转发到JSP页面以后,我们可以用jsp轻松实现数据的显示,及交互等。 jsp支持非常强大…...

prometheus部署
一、前言 Prometheus 是一个开源的系统监控和警报工具,用于收集、存储和查询时间序列数据。它旨在提供高效的多维数据收集和查询功能,帮助用户监控其应用程序和基础设施的性能,并在出现问题时触发警报,总来得说prometheus是用来收…...