当前位置: 首页 > news >正文

【Threejs进阶教程-着色器篇】9.顶点着色器入门

【Threejs进阶教程-着色器篇】9.顶点着色器入门

  • 本系列教程第一篇地址,建议按顺序学习
  • 认识顶点着色器
    • varying介绍
    • 顶点着色器与片元着色器分别的作用
    • Threejs在Shader中的内置变量
    • 各种矩阵
    • gl_Position
  • 尝试使用顶点着色器
    • 增加分段数增强效果
  • 制作平面鼓包效果
    • 鼓包效果分析
    • 路障效果
    • 让路障效果变得圆滑
    • uniform 控制鼓包效果
  • 完整源码

本系列教程第一篇地址,建议按顺序学习

本系列目前已累计第九篇,这里直接省略了2到8篇,可以通过上方专栏来查阅前面的教程
【Threejs进阶教程-着色器篇】1. Shader入门(ShadertoyShader和ThreejsShader入门)

本篇使用到的模板代码,从这里自取一个shader模板代码即可
【模板代码】用于编写Threejs Demo的模板代码

认识顶点着色器

首先我们把着色器部分的代码拎出来逐一分析

<script type="x-shader/x-vertex" id="vertexShader">varying vec2 vUv;void main(){vUv = vec2(uv.x,uv.y);vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );gl_Position = projectionMatrix * mvPosition;}</script>
<script type="x-shader/x-fragment" id="fragmentShader">varying vec2 vUv;void main(){gl_FragColor = vec4(1.0,0.0,0.0,1.0);}
</script>

varying介绍

有人发现顶点着色器的第一行与片元着色器的第一行,完全一致,那么这个varying用来做什么的呢?

这里我们直接借用webgl编程指南211页的介绍
在这里插入图片描述

varying主要用于 顶点着色器到片元着色器传输数据,是个全局变量

现阶段,我们只需要认识到,varying声明的变量,要在顶点着色器和片元着色器都要存在,且初始值需要在顶点着色器中设置即可

了解了varying之后,我们发现,其实uv的值是从顶点着色器中传递给片元着色器的

顶点着色器与片元着色器分别的作用

现阶段我这里不打算讲渲染原理和管线那些,太繁琐,这里先简单的总结一句
后续在讲到后处理的时候,这部分内容会再细讲

顶点着色器决定外型
片元着色器决定色彩

Threejs在Shader中的内置变量

顶点着色器中使用到了几个threejs的内置变量,在下面的官方文档中有说明
Threejs内置变量-WebGLProgram
一般来说我们最需要关注的几个:
position,模型的顶点信息会传递到这里,常用于计算模型的外观和最终渲染的外型
uv,模型的uv信息会传递到这里,常用于传递给顶点着色器用于计算颜色
normal,模型的法线信息会传递到这里,常用于计算光照等高级计算

如果看完了前面的BufferGeometry教程,有没有发现这里很熟悉?
【ThreeJS基础教程-高级几何体篇】2.6 BufferGeometry与BufferAttribute

你们想的没错,这些就是threejs向shader系统传递的数据,如果这里不懂BufferGeometry的,要继续下去学习Shader,就需要去前面补一下BufferGeometry的相关知识了

各种矩阵

在顶点着色器模板代码中的第四行第五行,分别出现了modelViewMatrix和projectionMatrix这两个矩阵,现阶段先不用管,只需要记住顶点着色器最终计算是这样即可

现阶段顶点着色器的代码,在模板代码的最后两行,除了**vec4(position,1.0);**会稍作改变,其他时间不会发生大的变动

gl_Position

一般来说,顶点着色器也需要有个固定输出,gl_Position就是顶点着色器的最终输出结果,最终结果也是一个vec4类型的对象

这样,顶点着色器的代码我们就介绍完毕了,接下来我们要尝试修改一下顶点着色器,感受一下顶点着色器带来的效果

尝试使用顶点着色器

我们用个最简单的方式来操作顶点

<script type="x-shader/x-vertex" id="vertexShader">varying vec2 vUv;void main(){vUv = vec2(uv.x,uv.y);vec3 aPosition = position;//这里我们直接操作顶点的z轴,偏移的激进一点aPosition.z = sin(aPosition.x * aPosition.y) * 10.0;vec4 mvPosition = modelViewMatrix * vec4( aPosition , 1.0 );gl_Position = projectionMatrix * mvPosition;}</script>

在这里插入图片描述
然后,我们运行起来之后,发现,我们的平面,扭曲了,这是因为我们的z轴发生了改变

增加分段数增强效果

这里我们修改一下addMesh()

    function addMesh() {//增加到100分段let geometry = new THREE.PlaneGeometry(10,10,3,3);let material = new THREE.ShaderMaterial({uniforms,vertexShader:document.getElementById('vertexShader').textContent,fragmentShader:document.getElementById('fragmentShader').textContent,transparent:true})let mesh = new THREE.Mesh(geometry,material);scene.add(mesh);}

在这里插入图片描述
我们可以发现,增加了分段和顶点之后,我们的这个平面变化巨大,已经不再是最初的PlaneGeometry了

这样,我们就完成了一次顶点着色器的尝试

制作平面鼓包效果

首先我们把平面横过来,然后需要做一个数据变换,分段数增加到100,然后设定材质的线框模式开,不然我们等一下不好看到效果
然后修改回最初的顶点着色器代码

addMesh

    function addMesh() {//注意这里必须旋转几何体,旋转了几何体,我们的数据才是正确的//mesh.rotation是在矩阵层面修改了旋转方向,最终会传递到modelViewMatrix中//顶点着色器的所有教程,除非特殊说明,否则全部使用旋转几何体let geometry = new THREE.PlaneGeometry(10,10,100,100).rotateX(-Math.PI/2);let material = new THREE.ShaderMaterial({uniforms,vertexShader:document.getElementById('vertexShader').textContent,fragmentShader:document.getElementById('fragmentShader').textContent,transparent:true,wireframe:true})let mesh = new THREE.Mesh(geometry,material);scene.add(mesh);}

顶点着色器

<script type="x-shader/x-vertex" id="vertexShader">varying vec2 vUv;void main(){vUv = vec2(uv.x,uv.y);vec3 aPosition = position;vec4 mvPosition = modelViewMatrix * vec4( aPosition , 1.0 );gl_Position = projectionMatrix * mvPosition;}
</script>

在这里插入图片描述

鼓包效果分析

既然要制作鼓包效果,那么,我们需要一个鼓包顶点,然后鼓包顶点处的高度最高,然后依次递减,所以我们这里直接从顶点着色器来定义这个鼓包点

<script type="x-shader/x-vertex" id="vertexShader">varying vec2 vUv;void main(){vUv = vec2(uv.x,uv.y);vec3 aPosition = position;//0 为int类型,0.0为float类型, 如果写0,threejs会报错// 可以写成 .0 来替代0.0以及任何 0.X 的数字, 但是个人不是很喜欢这种写法,看着太混乱vec3 swelling = vec3(0.0);//计算鼓包点到顶点的距离float dis = distance(swelling,aPosition);dis = clamp(dis,0.0,5.0);aPosition.y = 5.0 - dis;vec4 mvPosition = modelViewMatrix * vec4( aPosition , 1.0 );gl_Position = projectionMatrix * mvPosition;}
</script>

路障效果

这里,我们先声明了鼓包点在中心点,然后,我们计算鼓包点到四周的距离,但是要做一下限制,如果大于5.0的值,则直接赋值为5.0,紧接着直接把这个计算出来的dis值丢给aPosition.y,我们得到了一个漏斗型
在这里插入图片描述
既然我们计算的dis的最大值为5,那么,我们把大小做一下交换即可,用5.0 - dis,即可把漏斗形改成路障型
在这里插入图片描述

让路障效果变得圆滑

这里我们使用指数函数来优化

我们现在知道了最高点是5,最低点为0,那么,我们就可以计算它的高度比例,然后把线性的比例换成指数型比例
在这里插入图片描述
保持最高点和最低点不变,然后我们直接带入图像上面的数学公式,即可得到我们的鼓包效果

<script type="x-shader/x-vertex" id="vertexShader">varying vec2 vUv;void main(){vUv = vec2(uv.x,uv.y);vec3 aPosition = position;//0 为int类型,0.0为float类型, 如果写0,threejs会报错// 可以写成 .0 来替代0.0以及任何 0.X 的数字, 但是个人不是很喜欢这种写法,看着太混乱vec3 swelling = vec3(0.0);//计算鼓包点到顶点的距离float dis = distance(swelling,aPosition);dis = clamp(dis,0.0,5.0);dis = pow( dis / 5.0, 2.0 ) * 5.0;aPosition.y = 5.0 - dis;vec4 mvPosition = modelViewMatrix * vec4( aPosition , 1.0 );gl_Position = projectionMatrix * mvPosition;}
</script>

在这里插入图片描述

uniform 控制鼓包效果

uniform亦可用于顶点着色器

我们在代码中多次使用到5.0,这个实际上是鼓包的最大高度,这里我们抽出来这个常数作为鼓包最大高度,指数函数用的2次幂,这个参数可以抽出一个参数为鼓包圆滑率,我们写到uniform和lil.gui来调试
当然,我们的鼓包中心点,也可以单独拎出来放到uniform中

顶点着色器中编写uniform与片元着色器基本一致

修改后的顶点着色器

<script type="x-shader/x-vertex" id="vertexShader">varying vec2 vUv;uniform float maxSwelling;uniform vec3 swellingCenter;uniform float swellingPower;void main(){vUv = vec2(uv.x,uv.y);vec3 aPosition = position;//计算鼓包点到顶点的距离float dis = distance(swellingCenter,aPosition);dis = clamp(dis,0.0,maxSwelling);dis = pow( dis / maxSwelling, swellingPower ) * maxSwelling;aPosition.y = maxSwelling - dis;vec4 mvPosition = modelViewMatrix * vec4( aPosition , 1.0 );gl_Position = projectionMatrix * mvPosition;}
</script>

修改后的addMesh()和uniforms

let uniforms = {maxSwelling:{value:5.0},swellingCenter:{value:new THREE.Vector3()},swellingPower:{value:2.0}}function addMesh() {//注意这里必须旋转几何体,旋转了几何体,我们的数据才是正确的//mesh.rotation是在矩阵层面修改了旋转方向,最终会传递到modelViewMatrix中//顶点着色器的所有教程,除非特殊说明,否则全部使用旋转几何体let geometry = new THREE.PlaneGeometry(10,10,100,100).rotateX(-Math.PI/2);let material = new THREE.ShaderMaterial({uniforms,vertexShader:document.getElementById('vertexShader').textContent,fragmentShader:document.getElementById('fragmentShader').textContent,transparent:true,wireframe:true})let mesh = new THREE.Mesh(geometry,material);scene.add(mesh);//注意自己引入lil.guilet gui = new GUI();gui.add(uniforms.maxSwelling,'value',0,10).step(0.01).name('最大鼓包高度');gui.add(uniforms.swellingPower,'value',0,10).step(0.01).name('鼓包曲线');let folder = gui.addFolder('鼓包中心');folder.add(uniforms.swellingCenter.value,'x',-5,5);folder.add(uniforms.swellingCenter.value,'y',-5,5);folder.add(uniforms.swellingCenter.value,'z',-5,5);}

在这里插入图片描述

完整源码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>body{width:100vw;height: 100vh;overflow: hidden;margin: 0;padding: 0;border: 0;}</style>
</head>
<body><script type="importmap">{"imports": {"three": "../three/build/three.module.js","three/addons/": "../three/examples/jsm/"}}</script><script type="x-shader/x-vertex" id="vertexShader">varying vec2 vUv;uniform float maxSwelling;uniform vec3 swellingCenter;uniform float swellingPower;void main(){vUv = vec2(uv.x,uv.y);vec3 aPosition = position;//计算鼓包点到顶点的距离float dis = distance(swellingCenter,aPosition);dis = clamp(dis,0.0,maxSwelling);dis = pow( dis / maxSwelling, swellingPower ) * maxSwelling;aPosition.y = maxSwelling - dis;vec4 mvPosition = modelViewMatrix * vec4( aPosition , 1.0 );gl_Position = projectionMatrix * mvPosition;}
</script>
<script type="x-shader/x-fragment" id="fragmentShader">varying vec2 vUv;void main(){gl_FragColor = vec4(1.0,0.0,0.0,1.0);}
</script><script type="module">import * as THREE from "../three/build/three.module.js";import {OrbitControls} from "../three/examples/jsm/controls/OrbitControls.js";import {GUI} from "../three/examples/jsm/libs/lil-gui.module.min.js";window.addEventListener('load',e=>{init();addMesh();render();})let scene,renderer,camera;let orbit;function init(){scene = new THREE.Scene();renderer = new THREE.WebGLRenderer({alpha:true,antialias:true});renderer.setSize(window.innerWidth,window.innerHeight);document.body.appendChild(renderer.domElement);camera = new THREE.PerspectiveCamera(50,window.innerWidth/window.innerHeight,0.1,2000);camera.add(new THREE.PointLight());camera.position.set(15,15,15);scene.add(camera);orbit = new OrbitControls(camera,renderer.domElement);orbit.enableDamping = true;scene.add(new THREE.GridHelper(10,10));}let uniforms = {maxSwelling:{value:5.0},swellingCenter:{value:new THREE.Vector3()},swellingPower:{value:2.0}}function addMesh() {//注意这里必须旋转几何体,旋转了几何体,我们的数据才是正确的//mesh.rotation是在矩阵层面修改了旋转方向,最终会传递到modelViewMatrix中//顶点着色器的所有教程,除非特殊说明,否则全部使用旋转几何体let geometry = new THREE.PlaneGeometry(10,10,100,100).rotateX(-Math.PI/2);let material = new THREE.ShaderMaterial({uniforms,vertexShader:document.getElementById('vertexShader').textContent,fragmentShader:document.getElementById('fragmentShader').textContent,transparent:true,wireframe:true})let mesh = new THREE.Mesh(geometry,material);scene.add(mesh);let gui = new GUI();gui.add(uniforms.maxSwelling,'value',0,10).step(0.01).name('最大鼓包高度');gui.add(uniforms.swellingPower,'value',0,10).step(0.01).name('鼓包曲线');let folder = gui.addFolder('鼓包中心');folder.add(uniforms.swellingCenter.value,'x',-5,5);folder.add(uniforms.swellingCenter.value,'y',-5,5);folder.add(uniforms.swellingCenter.value,'z',-5,5);}function render() {renderer.render(scene,camera);orbit.update();requestAnimationFrame(render);}</script>
</body>
</html>

相关文章:

【Threejs进阶教程-着色器篇】9.顶点着色器入门

【Threejs进阶教程-着色器篇】9.顶点着色器入门 本系列教程第一篇地址&#xff0c;建议按顺序学习认识顶点着色器varying介绍顶点着色器与片元着色器分别的作用Threejs在Shader中的内置变量各种矩阵gl_Position 尝试使用顶点着色器增加分段数增强效果 制作平面鼓包效果鼓包效果…...

质量留住用户:如何通过测试自动化提供更高质量的用户体验

在当今竞争异常激烈的市场中&#xff0c;用户手头有无数种选择&#xff0c;但有一条真理至关重要&#xff1a; 质量留住用户。 产品的质量&#xff0c;尤其是用户体验 (UX)&#xff0c;直接决定了客户是留在您的品牌还是转而选择竞争对手。随着业务的发展&#xff0c;出色的用户…...

【CSP CCF记录】201803-1第13次认证 跳一跳

题目 样例输入 1 1 2 2 2 1 1 2 2 0 样例输出 22 思路 没有技术含量的一道题&#xff0c;解题的关键是理解游戏规则。用state标记跳跃状态&#xff0c;以下是对游戏规则的分析&#xff1a; 1. state1&#xff0c;跳到方块上但没跳到中心&#xff0c;得1分 2. state2&#xf…...

详解Qt 中使用虚拟键盘(软键盘qtvirtualkeyboard)

文章目录 详解 Qt 中使用虚拟键盘&#xff08;软键盘&#xff1a;QtVirtualKeyboard&#xff09;1. 虚拟键盘简介1.1 虚拟键盘的应用场景 2. 安装和配置2.1 安装 QtVirtualKeyboard2.2 配置环境变量 3. 使用虚拟键盘3.1 示例代码main.cppwidget.hwidget.cpp 4. 总结 详解 Qt 中…...

cocoscreater3.8.4生成图集并使用

1.安装texturepacker&#xff0c;去官网下载https://www.codeandweb.com/texturepacker 2.将图片拖动进来&#xff0c;即可自动生成精灵表&#xff0c;这里输出选用cocos2d-x&#xff0c;打包用免费版的“基本”就行&#xff0c;高级模式是收费的&#xff0c;然后点击“发布精…...

IDEA如何快速地重写方法,如equals、toString等

前言 大家好&#xff0c;我是小徐啊。我们在使用IDEA的时候&#xff0c;有时候是需要重写equals和toString等方法的。这在IDEA中已经很方便的给我们准备好了快速的操作了。今天就来讲解一下。 如何重写 首先&#xff0c;打开要重写方法的文件&#xff0c;让鼠标定位到这个文…...

网络安全——SpringBoot配置文件明文加密

一、前言 在日常开发中&#xff0c;项目中会有很多配置文件。比如SpringBoot项目核心的数据库配置、Redis账号密码配置都在properties、yml配置文件 中。 如果这些信息以明文的方式存储&#xff0c;你的电脑被拿去修理&#xff0c;就会容易泄露&#xff0c;一旦被其他人获取到…...

LightRAG开源了…结合本地ollama实现股票数据接口Akshare智能问答

LightRAG是由香港大学研究团队推出的一种检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;系统。该系统通过整合图结构索引和双层检索机制&#xff0c;显著提升了大型语言模型在信息检索中的准确性和效率。LightRAG 不仅能够捕捉实体间的复杂依赖关系…...

【PCB设计】AD16教程:分配位号

1、前提条件 确保已经基本画完原理图 2、点击【Tools-Annotate Schematics】 3、依次点击【Reset All】、【Update Changes Lise】、【Close】 最后位号就被自动分配好了...

ElasticSearch7.x入门教程之索引概念和基础操作(三)

文章目录 前言一、索引基本概念二、索引基本使用elasticsearch-head插件Kibana使用 总结 前言 要想熟悉使用ES的索引&#xff0c;则必须理解索引相关的概念&#xff0c;尤其是在工作当中。 在此记录&#xff0c;方便开展工作。 一、索引基本概念 尽量以通俗的话语。 1、集群…...

Python后端flask框架接收zip压缩包方法

一、用base64编码发送&#xff0c;以及接收 import base64 import io import zipfile from flask import request, jsonifydef unzip_and_find_png(zip_data):# 使用 BytesIO 在内存中处理 zip 数据with zipfile.ZipFile(io.BytesIO(zip_data), r) as zip_ref:extracted_paths…...

机器学习中数据集Upsampling和Downsampling是什么意思?中英文介绍

对GPT4o大模型的Prompt如下&#xff1a; Datasets marked with ↓ are downsampled from their original datasets, datasets marked with ↑ are upsampled.这里的上采样和下采样是什么意思 内容援引自&#xff1a;paper &#xff08;https://allenai.org/papers/tulu-3-repor…...

浏览器控制台中使用ajax下载文件(没有postman等情况下)

有时候&#xff0c;可能电脑里面没有postman&#xff08;比如内网&#xff09;&#xff0c;然后又需要导出一些文件&#xff0c;前端又没有提供相应的功能&#xff08;比如循环调用导出等&#xff09;&#xff0c;这时候我们就可以通过在控制台写代码的方式来实现了。这个还是在…...

完全二叉树的基本操作(顺序存储)

#include<iostream> #include<math.h> using namespace std;#define MaxSize 100 struct TreeNode {int value;bool isEmpty;//判断该节点是否为空 }t[MaxSize];/** *定义一个长度位MaxSize的数组&#xff0c;按照从上到下&#xff0c; *从左到右的方式依次存储完全…...

【HTTP】http与https

http与https的关系 应用层协议&#xff1a; http&#xff08;HyperText Transfer Protocol&#xff09;超文本传输协议&#xff1b; https&#xff08;Hypertext Transfer Protocol Secure&#xff09;超文本传输安全协议&#xff1b; 传输层协议&#xff1a;TCP&#xff08;Tr…...

【Git多人开发与协作之团队的环境搭建】

Git多人开发与协作之团队的环境搭建 新的改变1. Git 的用途2. 分支的概念与类型3. HEAD 和分支指针如何查看 HEAD 指向的位置&#xff1a; 4. 常见的 Git 操作5. 常见问题与解决方法总结GitHub 项目获取实操在新电脑上运行 Git1. 安装 Git2. 配置用户名和邮箱3.配置 Git 和 SSH…...

java基础概念36:正则表达式1

一、正则表达式的作用 作用一&#xff1a;校验字符串是否满足规则&#xff1b;作用二&#xff1a;在一段文本中查找满足要求的内容。——爬虫 二、正则表达式 2-1、字符类 示例&#xff1a; public static void main(String[] args) {System.out.println("a".matc…...

java实现小程序接口返回Base64图片

文章目录 引言I java 接口返回Base64图片接口设计获取验证码图片-base64字符串获取验证码图片-二进制流arraybufferII 小程序端代码过期代码: 显示文件流图片(arraybuffer)知识扩展:微信小程序下载后端返回的文件流引言 场景: 图形验证码 背景: 接口返回arraybuffer的格式…...

网络编程并发服务器的应用

作业2&#xff1a;完成局域网CS模型&#xff0c;局域网内一个服务器&#xff0c;多个客户端连接一个服务器&#xff0c;完成局域网聊天&#xff08;select函数&#xff0c;poll函数&#xff0c;完成TCP并发服务器&#xff09;。 poll函数应用&#xff1a; 服务器部分代码&…...

数据结构——停车场管理问题

目录 1、问题描述2、逐步分析1&#xff09;涉及操作2&#xff09;代码实现 3、代码整合 1、问题描述 1、题目 设停车场内只有一个可停放n辆汽车的狭长通道&#xff0c;且只有一个大门可供汽车进出。汽车在停车场内按车辆到达时间的先后顺序&#xff0c;依次由北向南排列&#x…...

道品智能科技移动式水肥一体机:农业灌溉施肥的革新之选

在现代农业的发展进程中&#xff0c;科技的力量正日益凸显。其中&#xff0c;移动式水肥一体机以其独特的可移动性、智能化以及实现水肥一体化的卓越性能&#xff0c;成为了农业领域的一颗璀璨新星。它不仅改变了传统的农业灌溉施肥方式&#xff0c;更为农业生产带来了高效、精…...

AI实习--常用的Linux命令

一、基础命令 1. 切换到根目录。 cd ~ 2. 返回上一级目录。 cd .. 3. 查看当前目录下包括哪些文件和文件夹。 ls 4. 查看当前路径。 pwd 5. 将文件或文件夹剪切到目标目录下。 mv 文件所在路径 目标路径 6. 查看文本文件内容。 cat 文本文件名 7. 创建文件或文件夹…...

Python学习指南 + 谷歌浏览器如何安装插件

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; Python 目录 前言 Python 官方文档的使用 谷歌浏览器中如何安装插件 前言 在学习Python时&#xff0c;我们可能会出现这样的困惑&#x…...

研0找实习【学nlp】15---我的后续,总结(暂时性完结)

当下进展成果&#xff1a; nlptransformerpytorchhuggingfacebert简历环境配置表情识别文本分类 断更了快1个月&#xff0c;2个礼拜找实习&#xff0c;1个礼拜伤心&#xff0c;1个礼拜想我要干什么…… 承认自己的才疏学浅&#xff0c;了解了leetcode&#xff0c;和老师商量了…...

kylin麒麟银河桌面版操作系统安装部署

本文主要描述kylin麒麟银河桌面版操作系统的安装&#xff0c;该操作系统的安装源文件可以从kylin麒麟银河官方网站上下载&#xff0c;商业版本需要申请试用&#xff0c;开源版本可以直接下载使用。 如上所示&#xff0c;x86芯片处理器架构的请下载INTEL版本&#xff0c;华为海思…...

MyBatis插件原理及应用

&#x1f3ae; 作者主页&#xff1a;点击 &#x1f381; 完整专栏和代码&#xff1a;点击 &#x1f3e1; 博客主页&#xff1a;点击 文章目录 介绍<plugins>标签解析拦截器链的工作原理插件的应用场景MyBatis插件应用的四个组件InterceptorChain和Interceptor MyBatis框架…...

[M最短路] lc743. 网络延迟时间(spfa最短路+单源最短路)

文章目录 1. 题目来源2. 题目解析 1. 题目来源 链接&#xff1a;743. 网络延迟时间 相关链接&#xff1a; [图最短路模板] 五大最短路常用模板) 2. 题目解析 怎么讲呢&#xff0c;挺抽象的…很久没写最短路算法了。反正也是写出来了&#xff0c;但脱离了模板&#xff0c;把…...

MySQL 中的锁

MySQL 中的锁&#xff1a;全面解析与应用指南 在 MySQL 数据库的复杂世界里&#xff0c;锁是确保数据一致性、完整性以及并发控制的关键机制。无论是简单的小型应用还是复杂的企业级系统&#xff0c;深入理解 MySQL 中的锁对于优化数据库性能、避免数据冲突和错误都具有至关重要…...

【动手学电机驱动】STM32-FOC(8)MCSDK Profiler 电机参数辨识

STM32-FOC&#xff08;1&#xff09;STM32 电机控制的软件开发环境 STM32-FOC&#xff08;2&#xff09;STM32 导入和创建项目 STM32-FOC&#xff08;3&#xff09;STM32 三路互补 PWM 输出 STM32-FOC&#xff08;4&#xff09;IHM03 电机控制套件介绍 STM32-FOC&#xff08;5&…...

【C++11】尽显锋芒

(续) 一、可变参数模板 C11支持可变参数模板&#xff0c;也就是说支持可变数量参数的函数模板和类模板&#xff0c;可变数目的参数被称 为参数包&#xff0c;存在两种参数包&#xff1a;模板参数包&#xff0c;表示零或多个模板参数&#xff1b;函数参数包&#xff1a;表示零…...