自由学习记录(63)

编码全称:AV1(Alliance for Open Media Video 1)。

算力消耗大:目前(截至 2025 年中)软件解码 AV1 的 CPU 开销非常高,如果没有专门的硬件解码单元,播放高清视频时会很吃 CPU,容易出现卡顿、发热或掉帧。

  • 在许多视频/图像场景里,压缩率常常表示为“源文件大小 ÷ 压缩后文件大小”,比率越大代表压缩得越厉害;但更直观常用的是“码率”(bitrate),比如视频的码率是 4 Mbps,或者图片的 JPEG 质量参数 90%。

  • 一般情况下,码率越高(即单位时间/单位像素所分配的数据越多),可还原的画面细节就越丰富,相对的画质也会更好。压缩率越大则对应码率越低,画面细节和锐度就更容易丢失,出现模糊、块状或色带等伪影。

如果不在意流量消耗,就让播放器调度尽可能高的 AV1 码率(比如 4K 甚至 8K 的高码流)来保证最丰富的细节

ShaderForge介绍_哔哩哔哩_bilibili

明天了解一下吧

和Graph差不多的,但是built-in

在 Photoshop 中,“线性减淡(Linear Dodge)”与“线性减淡(添加)(Linear Dodge (Add))”实际上是同一个 Blend Mode,Photoshop 官方简称:“Linear Dodge (Add)”。它的核心思路就是把两张图像做加法,然后对结果做饱和剪裁(clamp)

滤色(Screen)融合模式通常被称为“反相相乘”(Inverse Multiply)。数学表达式是:

C = 1 - (1 - A) * (1 - B)

更柔和的叠加效果:与 Add/Linear Dodge “一旦相加就饱和到白”相比,Screen 会在低亮度区累加时更明显,在高亮度区饱和时更平滑。

高光到达白色的过程是渐进、滑顺的,不会像简单的加法

  • Linear Dodge 一旦 A+B > 1,就直接剪裁为 1,区域会瞬间变白。

  • Screen 则是在 A+B 越来越大时,按照 A+B−A*B 的方式渐进逼近 1,在值接近 1 之前都会有细微过渡。

暗部细节保存更好

内发光和外发光,内发光不能超出模型的边界

用了之后任何功能都需要自己连出来,不连就没有,右边一下子就少了很多接口,变灰不让接上了

unity中的fixed用宏定义了half,换了一种表示方法

在 Unity Shader 中,half 并不直接代表“固定 16 位浮点”,而是“编译器抽象”→等同于 fixed

  • 在移动 GPU 上,使用 halffixed 可以减少寄存器压力提高并行度

  • 在 Pixel Shader 中使用 fixed 做颜色计算可以节省资源。

  • GPU 内部的向量化 SIMD 单元更适合处理中低精度数据。

→ 所以手动标记可以让你显式告诉编译器:这个变量不需要高精度,编译器可以帮你优化。

  • 如果你在高精度 float 上做了大量运算,再赋值给一个低精度 fixed可能造成明显的精度丢失(颜色值断层、光照闪烁等)。

  • 所以手动标记数据类型,能让你显式暴露这种意图,并对精度损失行为负责

half用给插值出来的值,fixed用来装颜色的,,这是优雅的写法了(pc上统一float32,不影响)

“在手机上 fixed 是小于 half 的”
“颜色需要的精度更少”

为什么 Unity/Shader 写颜色用 fixed

fixedlowp ≈ 用于颜色值

fixed 最早其实不是 Unity 发明的,而是从旧版 Cg/HLSL 中延续而来的术语,它的名字原义是:

fixed 表示 固定精度范围 的数值(比如 -2 到 2),比 half 更低精度,但更小更快。

不同的平台,取模可能要用规定的函数,*也是一样的,乘要用规定的函数

static

  • 在 Shader 里,static 表示“静态变量”,也就是该变量只在当前编译单元(通常是当前 .cginc/.hlsl 文件)中可见,外部无法访问或修改它。

  • 典型场景

    • 你在同一个 Shader 文件里定义了一段公用函数,而这个函数里面需要一个只读的临时常量或缓存,你可以用 static 声明它,确保不被其他包(pass、include)意外重写

    • static 并不是“静态内存”那种概念(不像 C/C++ 全局变量会驻留在固定内存地址)。在 GPU 编译时,static 只是一个作用域限制:告诉编译器“外面别来碰我”。

uniform

  • 跨阶段共享:一个 uniform(如 CBUFFER 里的某个结构体、或者 samplers/纹理)能同时被顶点着色器(VS)和片段着色器(PS)访问。

  • 外部驱动:它真正的值来源于 CPU 端代码(C#、C++)或渲染框架主循环在每次 Draw Call 时引擎更新

在 Shader 里只能读取它们,不能在 Shader 里写回赋值

const

编译期展开:一旦你写了 const float PI = 3.14159;,编译器会在预处理阶段将所有 PI 的引用替换为 3.14159f,并且不会占用寄存器或 uniform 缓存。

对性能友好:因为它根本不占用寄存器或常量寄存器,所有对它的引用都被直接打入字节码里,相当于纯粹的“文本替换”。

只读:与 uniform 一样,在 Shader 代码中不能给 const 变量重新赋值;但区别在于,它根本不是由外部赋值,而是在编译时就固定了

GPU 在遇到 if else 时会把两个分支都执行一遍

因为现代 GPU(无论是桌面级的 NVIDIA/AMD,还是移动端的 ARM Mali/Qualcomm Adreno)内部都是基于SIMD(或 SIMT)并行架构

简单来说,GPU 会把若干个线程(Thread)组织成一个“线程束”(在 NVIDIA 中叫 Warp、在 AMD 中叫 Wavefront),比如一个 Warp 通常是 32 条线程。然后这 32 条线程在硬件上会同步执行同一条指令

什么是 Mipmap?

  • 假设你有一张原始纹理贴图(比如 1024×1024 像素)。Mipmap 会在这张贴图之上再依次生成几个尺寸越来越小的版本,比如:

    1024×1024 → 512×512 → 256×256 → 128×128 → 64×64 → … → 一直到 1×1

  • 每一级的图像都是对上一层用双线性/三线性滤波之类的方法预先缩小或预过滤得到的。所以当你把同一张纹理应用到远处(或尺寸很小)的物体时,GPU 不必去采样原始的 1024×1024,而是直接采样一个更低分辨率的级别(比如 128×128 或 64×64),这样能够减少锯齿、闪烁和纹理闪变现象,同时提高采样效率。

https://en.wikibooks.org/wiki/Cg_Programming/Unity/Minimal_Shader

https://enjoyphysics.cn/%E6%96%87%E4%BB%B6/soft/Hlsl/GPU-Programming-AndCgLanguage-Primer.pdf

  • 使用 HTTPS 协议,不需要专门配置 SSH Key,即使没配置 SSH 也能正常克隆/拉取代码。

  • 在公司网络或学校网络里,HTTPS 通常不被屏蔽,更容易成功下载。

GitHub Desktop 是 GitHub 官方推出的一款图形化桌面客户端(Windows 和 macOS 均支持),可以让不熟悉命令行的用户用可视化界面来管理本地 Git 仓库。

当你点击“Open with GitHub Desktop”时,如果本地安装并打开了 GitHub Desktop 应用,Launcher 会自动在 GitHub Desktop 里创建一个“克隆”任务,让你选择存放路径,然后把仓库克隆到你指定的本地文件夹。

“Download ZIP” 按钮

  • 优点

    • 极其简单,连命令行都不需要,适合只想“拿来直接看代码/文档”的场景。

    • 不用担心本地有没有安装 Git;只要会解压就行。

  • 缺点

    • 你无法继承该仓库的版本控制历史,也不能向远端推送更新、创建分支,甚至都拿不到原来的 .gitignore.gitattributes 等元信息。

    • 如果后续该项目在 GitHub 上更新了,你需要手动重新下载最新的 ZIP,无法用 git pull 一键拉取更新。

  • 如果你:

    1. 主要在 GitHub 上做开源、个人项目;

    2. 希望操作直观、界面干净;

    3. 不想花时间学命令行或太多 Git 细节;
      用 GitHub Desktop 就够了。

  • 如果你:

    1. 想用 GUI 方式做深入的 Git 操作(如多级分支管理、隐式多分支合并、交互式 rebase、子模块、stash 等);

    2. 项目不一定只在 GitHub,还可能在 GitLab、自建私库;

    3. 喜欢一次性把常用 Git 操作都放在一个软件里(不用每次都切命令行);
      用 GitExtensions 会更专业,也更灵活。

shader中的函数性质

GLSL(OpenGL Shading Language)**的顶点着色器(Vertex Shader)代码,通常用在桌面或者移动端的原生 OpenGL 项目里

  • Unity 里我们平时写的 Surface Shader、Vertex/Fragment Shader,文件后缀一般是 .shader(里面会混合 ShaderLab 语法和 CGPROGRAM/ENDCG 段落)或者 .cginc.hlsl.compute(Compute Shader),它们的语法看起来像 HLSL/CG。

  • 你这里看到的文件名是 vert.glsl(以及右边大概率还有 frag.glsl),这明确表明它是 OpenGL/GLSL 的程序。GLSL 在语法上用的关键词是 attributevaryinggl_Position,这在 Unity 的 CG/HLSL 中并不出现。

  • attribute 关键字表示顶点输入属性(顶点位置、UV、法线等),这是 GLSL 里的写法;varying 用来把顶点着色器算出的数据(例如顶点法线、坐标插值)传给片元着色器;gl_Position 则是 OpenGL 里必须写的输出变量,决定当前顶点在屏幕上的裁剪空间位置。

这些在 Unity 的 ShaderLab/CG 里都不使用。Unity 要输出给固定流水线的是 o.pos 或者 SV_POSITION(在 HLSL/DirectX 中)。所以从关键字 attributevaryinggl_Position 就能看出这是原生 GLSL,不是 Unity 的。

// 顶点属性:从 CPU 端传过来的每个顶点数据
attribute vec3 VaPos;     // 顶点的世界/模型空间位置 (x, y, z)
attribute vec2 VaUV;      // 原始贴图 UV 坐标 (u, v)
attribute vec3 VaNormal;  // 顶点法线// 统一变量(Uniform),从 CPU 端传过来,在绘制一批顶点时统一不变
uniform mat4 MVP;   // Model-View-Projection 矩阵,用于把顶点从模型空间 → 世界空间 → 视图空间 → 裁剪空间
uniform int rows;   // 多重纹理拼接时的行数
uniform int cols;   // 多重纹理拼接时的列数
uniform int index;  // 当前要使用哪张子纹理(编号)// 这是要传给片元着色器的插值数据(每个顶点算一次,片元阶段会自动插值)
varying vec3 VNormal; 
varying vec2 VUV;void main() {// 1. 把原始顶点法线直接传给片元着色器VNormal = VaNormal;// 2. 计算新的 UV —— 例如把一张大贴图分割成 rows×cols 几块子图,再根据 index 选其中一块vec2 temp = vec2(VaUV.x / float(rows), VaUV.y / float(cols));vec2 ranks = vec2(0.0, 0.0);ranks.x = mod(float(index), float(rows));   // 第几列ranks.y = floor(float(index) / float(cols)); // 第几行temp.x += ranks.x * 1.0 / float(cols);temp.y += ranks.y * 1.0 / float(rows);VUV = temp;  // 这样最终传到片元阶段的 VUV 就是“在大贴图里选取子图的坐标”// 3. 计算最终 gl_Position(OpenGL 固定变量),告诉 GPU 该顶点在裁剪空间里的坐标gl_Position = MVP * vec4(VaPos, 1.0);
}

(把一张大贴图拆为 rows×cols 个小纹理,然后根据 index 去采样)

shader forge

Shader Forge

应用阶段的流水线化”指的就是 把渲染管线里原本只在 CPU 上按帧串行做的那块“应用”工序(场景遍历、剔除、动画、排序、命令构建),拆分成多个子阶段或分成多条命令缓冲,并且跟后面的 GPU 渲染阶段错开时间、同时执行。这样才能让 CPU+GPU 两端都保持高吞吐、低空闲,从而推高帧率和抗卡顿能力。

Shader Graph 里,UV 坐标并 被系统强制“钳制”到 [0,1] 区间——它只是一个浮点向量,可以读到任意值。只要你的采样模式是 Repeat/Tiled(而不是 Clamp),UV 超出 0–1 时就会按小数部分去“重复”取样(也就是把纹理无缝平铺)。

“Light Attenuation”(光照衰减) 节点,它负责根据当前光源类型和距离,给你算出一个 0~1 之间的衰减系数,用来模拟灯光在空间里“随距离衰减”的效果。

“固定函数”着色器(Fixed-Function Shader)其实并不是一种你要手写的 shader 脚本,而是指在早期图形 API(如 OpenGL 2.x、Direct3D 9 以前)中,GPU 内置的一整套“流水线功能”,你只需要用一组固定的命令和参数就能完成顶点变换、光照计算、纹理映射等,而不用自己写顶点/片元程序。

替换着色器(Replacement Shader)

  • 当你用 Camera.SetReplacementShader(shader, "RenderType") 时,Unity 会在所有渲染物体上查找他们 SubShader 里带 "RenderType"="..." 的 Pass,并用你提供的替换着色器去渲染这一类物体。

  • 典型例子:后期效果里做“基于不透明物体的深度/法线图”时,就用 Replacement Shader 只渲染 Opaque 物体。

xyzw四维,w维度判断是什么类型的光源,如果是方向光,那就xyz是方向向量,如果是点光源,则是具体的世界位置

尽量写小数的0.0,(一些奇葩机型可能自动转换出错)

在 Unity 编辑器里,只要让鼠标焦点在 Scene 视图窗口上,按下:

Shift + Space

就可以切换该窗口的最大化/恢复(全屏化)状态。

一般从光照节点开始分析一个新的shader

scene绕着某个物体旋转自己当前的视角----Alt+左键拖拽

Graphics API/渲染接口也就是底层跟显卡打交道、驱动硬件加速的标准。

UnityUnrealGodot(结合对应平台 API:DX12、Vulkan、Metal)

GPU 驱动只实现「API 规范」,并不区分高层语言

  • 显卡厂商(NVIDIA/AMD/Intel/Apple 等)在驱动里只负责实现底层的 Graphics API(如 DirectX、Vulkan、OpenGL、Metal)的二进制接口——这些接口在本质上是 C/C++ 的 ABI(应用二进制接口)。

  • 驱动并不会同时编译出一堆 “C++ 版”、“C# 版”、“Java 版” 的驱动库。它导出的是一套底层的函数和数据结构,供任何能调用 C 接口的语言来用。

各种编程语言通过「绑定」来调用这些 C 接口

  • C/C++:可以 直接 #include <d3d12.h> 或者 #include <vulkan/vulkan.h>,在代码里无缝调用驱动导出的函数。

  • C#:通常使用 P/Invoke([DllImport])或者像 SharpDX、VulkanSharp 这样的第三方封装库,把底层 DLL 导出的 C 接口“翻译”成 C# 的类和方法。

  • Java/Python/Go:同样可以通过 JNI、ctypes、cgo 等方式,把驱动的 C 接口映射到各自语言里。比如 LWJGL(Java),PyOpenGL(Python)都是用这种思路。

游戏引擎和框架通常做更高一层封装

  • Unity、Unreal、Godot 等引擎,底层已经把对 DirectX/Vulkan/Metal 的调用都写好 C++ 代码了,然后再在引擎层给脚本语言(C#, Blueprint, GDScript)提供更易用的接口。

  • 比如在 Unity 里你调用 Graphics.DrawMesh(),它背后跑的是 C++、再到 DirectX/Vulkan 驱动,整个过程对 C# 脚本透明。

  • Unity 引擎核心 是用 C++ 写的,它直接通过诸如 DirectX、Vulkan、Metal 这些“C 语言风格”的 Graphics API 去驱动 GPU。

  • 你在 Unity 里写的 C# 脚本(例如 Graphics.DrawMesh() 或者修改材质属性)并不是直接 P/Invoke 调用这些 Graphics API,而是调用了 Unity C++ 引擎提供的“脚本绑定”(bindings)。这些绑定通常是通过 Unity 自己的宿主进程(UnityPlayer.dll)把 C# 的调用转成内部的 C++ 函数,一次跨语言切换后,后续都在 C++ → 驱动 API 里完成。

这样设计的好处有两个:

  1. 性能: 把所有高频的、与硬件交互密集的部分都留在 C++ 层;C# ↔ C++ 的调用只在脚本逻辑层面(每帧仅仅几百次)发生,而不是每绘制一个三角形、每次设置一个 GPU 状态就 P/Invoke。

  2. 稳定性与可维护性: 驱动和 Graphics API 的改动都在 C++ 层处理,对 C# 脚本层透明,不用让每个 C# 项目都去适配不同平台的 P/Invoke 签名。

为什么 Unity 不让 C# 直接 P/Invoke?

  • 减少跨语言调用次数:Unity 把常见的渲染操作都集合成了一套“C++ 原生接口”,C# 只调用这些接口一次进入本地层,之后所有渲染都在 C++ 到驱动的环节。

  • API 统一与封装:无论你是用 C#、ShaderLab 还是通过 Visual Effect Graph/Shader Graph,最终都走同一个 C++ 实现,不用重复写绑定。

  • 安全与稳定:让脚本层不直接接触底层 DLL,也防止用户误用不安全的 P/Invoke 导致崩溃或安全漏洞。

其实在 Unity 里,C# → C++ 的“中转”调用并不是在每个三角形、每次状态切换、每个像素着色时都发生的——那样的开销肯定会大得不可接受。它真正的调用频率,大致是:

  1. 每帧几十到几百次 的 “脚本层面”渲染命令(DrawMesh、SetGlobalFloat、EnableKeyword……)

  2. Unity 内部把这些命令 累积到一套命令缓冲(Command Buffer)

  3. 在 C++ 层一次性向 GPU 提交整个缓冲区(提交 DrawCall 批次,而不是单个三角形)

你在 C# 里写

Graphics.DrawMesh(mesh, matrix, material, layer);

每次这句脚本执行时,会做一次 C#→C++ 的入口调用。但它并不立刻跑到底层的 GPU 提交,而是 把参数存到 C++ 的渲染队列里

  • 当一帧结束、所有绘制逻辑跑完后,Unity C++ 层会把这些“渲染命令列表”批量通过一次或几次 vkQueueSubmit / ID3D12CommandQueue::ExecuteCommandLists / MTLCommandBuffer commit 提交给显卡。

也就是说,你在 C# 里可能调用了 50 次 DrawMesh,但这 50 次调用只会在本地层合并成几次底层 GPU 提交。P/Invoke 的开销只体现在那 50 次调用上,而不是每个三角片、每个状态切换都 P/Invoke 一次。

如果你直接在 C# 里一个 DrawCall 对应一个 P/Invoke,再对每个三角形循环,那肯定会慢;但 Unity 已经帮你把这些细节都封装在 C++ 里了,你在 C# 里看到的只是高级接口。

C# 这边的“驱动”其实就是 .NET 运行时/CLR(Common Language Runtime) 本身:

  1. .NET 运行时=语言的“驱动”

    • 当你安装了 .NET Framework、.NET Core/5+ 或 Mono,里面就包含了 CLR、垃圾回收、JIT 编译器、Base Class Library(BCL) 等,负责把你的 C# IL 转成机器码并执行。

    • 这就类似于显卡厂商编写的 GPU 驱动:它实现了 Graphics API 规范,把高层调用(DrawCall)翻译给硬件;而 CLR 则实现了 ECMA-335 标准,把 C#、VB、F# 编译出来的 IL 翻译给 CPU。

跨语言调用=互操作(Interop)

  • C# 想调用 C++ 写的库,一般通过 P/Invoke[DllImport]) 或 C++/CLI、或 COM,本质上也是 CLR 调用本地 DLL 导出的 C 风格函数。

  • 这套互操作机制由 CLR 提供,调用时也会牵涉“托管 ↔ 非托管”上下文切换、参数封送(marshalling)等,就像 GPU API 驱动要封装命令缓冲、驱动开销一样。

不需要给每种语言都写一个“驱动”

  • CLR 是同一套运行时/规范,不管是 C#、VB 还是 F#,都跑在同一个 CLR 上。

  • 你只需要安装一次对应平台的 .NET 运行时,就能运行任意 .NET 语言编译的程序,不用为每个语言再各写一份“驱动”。

你写的 Java 源码(.java)首先被 javac 编译成 Java 字节码(.class 文件),这是一种平台无关的中间格式,不是机器码,也不是 C++。

Java JVM.NET CLR / Mono
输入.class(Java 字节码).dll/.exe(IL 中间语言)
解释执行解释器 + JIT解释器 + JIT
AOT 支持GraalVM Native Image 等.NET Native / Mono AOT
调用本地库(Interop)JNI(Java Native Interface)P/Invoke / C++/CLI
生态Spring、Android Runtime、GraalVM 等Unity、ASP.NET、Xamarin、Mono

可编程化与可定制化:Scriptable Render Pipeline(SRP)

  • 传统引擎(如 Unity 5 之前的内置管线,Unreal 4.XX 的默认前向/延迟管线)对渲染流程是“黑盒”式的,只有少量参数可调。

  • 次世代管线(Unity 的 URP/HDRP、Unreal Engine 5 的自定义管线、Godot 4 的 Vulkan 渲染)都采用了可脚本化渲染管线(Scriptable Render Pipeline)理念

  • 次世代管线几乎都把基于物理的渲染(PBR, Physically Based Rendering)作为核心:

    • 统一材质模型:F0、粗糙度(Roughness)、金属度(Metallic)、法线、环境光遮蔽等统一标准;

    • 能量守恒:确保光的反射和吸收满足真实物理规律;

    • 一致性:无论场景光照如何变化,材质表现都保持物理自洽。

高效的光照计算:延迟+Clustered/Tiled/Nanite

  • 延迟渲染(Deferred Rendering):先把几何信息写入 G‐Buffer,在屏幕空间做光照,适合大批点光源;

  • Tiled/Clustered Shading:把屏幕或视锥体划分成小块或体素,把光源分桶管理,只对相关瓦片/簇做光照,提高效率;

  • GPU 驱动渲染(GPU Driven):把剔除、实例化、Draw Call 构建都放到 GPU Compute Shader 中,减轻 CPU 负担;

  • 虚拟化几何(Nanite):Unreal 5 的 Nanite 利用海量细分三角阵列在 GPU 上动态 LOD,实时渲染数十亿三角。

新一代光照与全局光照:光线追踪+Lumen

  • 硬件光线追踪(RTX/DXR/Metal Ray Tracing):实时计算反射、折射、阴影;

  • 软件全局光照(Lumen、SVOGI、Probe Grid):混合光栅与光线追踪做间接照明,既快速又真实;

  • 混合渲染:在不同场景阶段(GI 探针、后期 SSDO、实时 RT)中动态切换,兼顾性能与画质。

渲染剖析与工具链

  • 渲染图(Frame Graph):所有 Render Pass 都以节点化/依赖图方式管理,可视化调试和自动合并;

  • GPU Profiler & RenderDoc:细粒度分析每个 Pass、每个 DrawCall 的性能,支持管线统计与着色器调试;

  • 资产打包与宏:自动提取 Shader Variant、分批预编译常用 Shader,减少运行时编译阻塞。

  • Unity HDRP(High Definition Render Pipeline)

    • 专为高端 PC/主机打造:支持物理光照、延迟+Tiled 光照、体积雾、光线追踪特效。

    • 用户可通过 C# 脚本定制 Frame Graph,增删 Pass;材质系统与 Shader Graph 深度集成。

  • Unity URP(Universal Render Pipeline)

    • 面向移动端和跨平台应用:轻量级延迟或前向+Tiled 渲染,支持 Shader Instancing、轻量级后期。

  • Unreal Engine 5

    • Nanite + Lumen 双核技术,自动化虚拟化几何与全局光照,无需手工烘焙,实时打造电影级场景。

  • Godot 4 Vulkan

    • 集成 Clustered Shading、GI 探针、屏幕空间环境光遮蔽,且引擎代码开源可定制。

次世代渲染管线 = 可编程化+物理真实感+高效光照+混合光线追踪 的一整套现代实时图形架构。
它不仅要在“画质”上接近电影工业级效果,更要在“性能”上充分利用多核 CPU、GPU Compute 和硬件光线追踪等能力,为跨平台的游戏/实时应用提供可扩展、可定制、高性能的渲染解决方案。

“Tiled 光照” 是一种在实时渲染中优化大量点光源/聚光灯的方法,核心思路是把屏幕空间分成一个一个的小“瓦片”(Tile),然后只对每个瓦片中实际影响到的光源做光照计算,而不用让每个像素都跑遍所有光源。这样能大幅减少光照计算量。

  • 屏幕拆分成瓦片

    • 比如把 1920×1080 的屏幕划分为 16×16 像素的小块,得到 120×68 共 8160 个瓦片。

  • 光源—瓦片关联

    • 在一个专门的 Compute Shader(或 CPU 线程)里,遍历场景里所有的点光/聚光,判断它们的影响范围(球体或锥体),再把每个光源加入到与之相交的那些瓦片的“光源列表”里。

    • 最终每个瓦片得到一个「仅包含真正会影响该区域的光源索引」的小列表。

  • 瓦片内像素光照

    • 在主渲染 Pass(通常是延迟光照或前向+Clustered)里,每个像素先定位到它属于哪个瓦片,然后只从该瓦片的光源列表里取出那些光源,逐一做漫反射/镜面反射计算。

    • 这样即使场景有几百、上千个光源,大多数像素最多只要计算几十盏真正可见/有效的光源。

  • 可扩展:场景光源越多,瓦片大小/分辨率可调;也可以升级到“Clustered”三维分块,连同深度一起分区。

  • 跨管线:既能用在延迟渲染(Deferred)里,也能在前向渲染(Forward)中做“Forward+”或“Clustered Forward”优化。

传统做法是在几何阶段就给每个物体算好光照(把灯光的影响一起算进去),只需要一次 Pipeline 就完成。但如果场景有多盏灯,就要对每个物体分别跑几遍顶点→片元,效率会急剧下降。

eferred 渲染(延迟渲染)怎么做的

延迟渲染把上面两个阶段分开,变成:

  1. 第一次:填 G-Buffer

    • 把场景里的所有像素信息(深度、法线、材质参数)写到多个渲染目标上(称为 G-Buffer)。

    • 实际上 GPU 会同时写出几张“贴图”:

      • 一张存 深度

      • 一张存 法线

      • 一张存 漫反射颜色

      • 可能还有张存 金属度/粗糙度,……

  2. 第二次:屏幕空间光照

    • 屏幕上每个像素已经有了上一步的所有必要数据,接下来只需在「屏幕四边形」上做一次遍历:

      • 读取 G-Buffer 里的深度+法线+材质,

      • 针对每个像素列举出少量相关的灯(用 Tiled/Clustered)

      • 计算漫反射+高光+阴影……

    • 这样就不用再跑几何阶段,把所有光照都集中在屏幕上一次性完成。

为什么说“但 G-Buffer 的读写会带来很高的带宽消耗”

  • 写入 G-Buffer:在几何阶段,GPU 要向显存写入多张大贴图(深度、法线、颜色……),这就是“多通道写入”。

  • 读取 G-Buffer:在光照阶段,GPU 又要从显存里读回这些贴图数据(多通道读取),才能拿到每个像素的深度、法线、材质参数。

  • 显存带宽:显卡内存的读写速度相比它计算速度要慢得多,频繁的大规模读写就会成为瓶颈。

假设你的游戏目标是 1080p(1920×1080),在一个典型的 Deferred 渲染中,你可能会用到如下的 G-Buffer:

G-Buffer 通道格式每像素字节数
深度(Depth)32-bit4 B
法线(Normal)RGBA16F8 B
漫反射颜色(Albedo)RGBA84 B
金属度/粗糙度/AO(MRAO)RGBA84 B
合计20 B/px

  1. 写入 G-Buffer(Geometry Pass)
    每帧要向显存写入:

    1920 × 1080 ≈ 2.07 × 10^6 像素 × 20 B/像素 ≈ 41.5 MB/帧

  2. 读取 G-Buffer(Lighting Pass)
    同样地,又要从显存读出这 41.5 MB/帧。

  3. 总带宽消耗

    41.5 MB (写入) + 41.5 MB (读取) = 83 MB 每帧

  4. 不同帧率下的带宽需求

    帧率带宽需求
    60FPS83 MB × 60 ≈ 4.98 GB/s
    144FPS83 MB × 144 ≈ 11.9 GB/s
    240FPS83 MB × 240 ≈ 19.9 GB/s

对比一下市面上典型显卡的理论显存带宽:

  • GTX 1080:≈320 GB/s

  • RTX 3060:≈360 GB/s

  • Radeon RX 6700 XT:≈384 GB/s

虽然看起来 60FPS 只用了 ~5 GB/s(“才”占了带宽的 1–2%),但这只是写读 G-Buffer的部分开销,还不包括:

  • 后续所有的材质贴图采样、环境贴图读写;

  • Shadow Map 的写入和读取;

  • 后期特效(Bloom、TAA、DOF 等)的多轮读写;

  • Compute Shader、Stream-Out、Copy 等其它显存操作。

把这些加起来,显存带宽就很快被攥紧了,尤其在更高分辨率(1440p/4K)或更多 G-Buffer 通道(HDR、法线空间贴图、动量贴图等)时,带宽需求几何倍上升,延迟渲染的“G-Buffer 二次流式读写”成为了最大的性能瓶颈之一。

GPU 核心时钟(Core/Boost Clock)

  • Core Clock(基础频率):GPU 在正常负载下的运行频率。

  • Boost Clock(加速频率):当温度、功耗和负载都允许时,GPU 会自动把核心时钟「超」到更高的值,以提高每秒能执行的着色器指令渲染单元操作的吞吐量。

  • 这两个时钟域只管 GPU “算力” 的快慢——它不决定显存的读写速度

显存时钟(Memory Clock)和总线宽度(Bus Width)

  • Memory Clock:显存芯片(GDDR6/GDDR5/HBM 等)自身的工作频率。通常厂商会给一个有效速率,比如 14 000 MHz(也叫 14 Gbps)。

  • Bus Width:GPU 与显存之间的数据通道宽度,比如 128 bit、192 bit、256 bit。

  • 它们共同决定了显存带宽(Memory Bandwidth),即每秒能搬多少数据给 GPU 核心用。

针对显存(Memory Clock)的工作频率

  • 如果显存标为 14 Gbps(Gigabits per second),可以理解为:

    • 它每秒钟能“嘀嗒”14 × 10⁹ 次,每次在那根数据线上传输 1 bit(比特)信息。

    • GDDR6 中通常有很多并行数据线(一根数据线按 Memory Interface Width,比如 128 bit,总共就是 128 条线同时在跑这个频率)。

  • 为什么说 14 Gbps 而不是 14 GHz?

    • Gbps 是“千兆比特/秒”,是一种经常用在高速串行接口(把宽总线看作一条条并行线)的标法,和 GHz(10⁹ 周期/秒)在含义上很接近,但针对的是“每条线每秒能跑多少比特”。

    • 14 Gbps ≈ 14 × 10⁹ Hz,也就是说那根线每秒钟能“嘀嗒”14 billion 次。

Unreal Engine 的 Lumen 和 Nanite 并不是像 HDRP、URP 那样“脚本化管线”里的一组可插拔节点,而是深度内置在引擎渲染架构里的两大核心子系统。它们在源码层面就和 Deferred/Forward 渲染、材质系统、光线追踪支持等紧密耦合,所以从“普通项目”层面,确实没法像 Unity 那样在 C# 或 Shader Graph 里随意“拆掉”或“重组”它们。

为什么看起来是“定下来了”

  1. 引擎级集成

    • Lumen 和 Nanite 从 5.0+ 开始就是 UE5 的默认渲染子系统,你在项目设置里只能打开或关闭它们,全流程(从 G-Buffer、光线追踪、体素缓存到最终合成)都在引擎核心代码里。

  2. 无 SRP 式脚本化接口

    • Unity 的 HDRP/URP 是建立在 Scriptable Render Pipeline 之上,整个渲染流程的每一步(剔除、Shadow Pass、G-Buffer、Lighting Pass、Post)都暴露给 C#,方便你按需插拔。

    • UE5 则是基于 Render GraphPipeline State Object (PSO) 的 C++ 实现,对项目开放的扩展点主要在“添加”而非“替换”已有步骤。

虽然不能“拆掉整个 Lumen/Nanite”,但如果你有足够 C++ 资源和愿意编译引擎,也能做一些深度定制:

  1. 修改 Renderer 源码

    • 在引擎源码的 Source/Runtime/Renderer/Private 目录下,Lumen、Nanite 相关的模块都是 .cpp/.h 文件,你可以 fork UE5 源码,删改这些实现,重新编译你的定制化渲染管线。

  2. 自定义 Render Graph 节点

    • UE5 的渲染流程是基于 Render Graph 构建的。你可以在 FDeferredShadingSceneRenderer::Render() 等入口,插入自定义的 Render Graph Pass,比如在 Lumen GI 前后做一次额外合成。

  3. 自定义 Shading Model

    • 如果你只想改材质层面的光照模型,可以在 Engine/Shaders/Private/MaterialShared.usf 里新增一个 EBlendMode::Custom 的 Shading Model,改写 GetBaseLightingGetLightingModel 之类函数,仍然保留 Lumen 的 GI。

  4. 插件 + 模块化源码

    • 将你的定制放到一个引擎插件里,并在 .uproject.uplugin 里覆盖 Renderer 模块,便于在不同项目间复用。

对于“风格化效果”——并不推荐改源码

  • 很多定制效果(卡通、像素化、怪诞色调、线框渲染、特定后期合成)都可以用:

    1. 自定义材质 + 后处理(Post Process)

    2. Custom Shading Model / Custom Depth + Stencil

    3. 插件级 Render Graph Pass(在现有渲染流程里插入一个额外 Pass)

  • 这些方式都在引擎开放的高层 API(蓝图/C++ 渲染接口)范围内,不用重新编译整个引擎,升级版本也更方便。

只有在这些情况下才该动源码

  • 性能:你确定自己的游戏在目标平台上已经用尽了所有可调 API,只有改内核才能再提升 10% 帧率。

  • 功能缺失:引擎高层接口确实做不到你想要的底层操作(非常罕见)。

  • 团队人手:有专门的渲染工程师维护一个私有 UE 源码分支,并能承担后续合并和升级的成本。

否则,只为一个风格化 Shader 效果去改动这么庞大的管线,不但风险极高(升级、Bug、兼容性),也大大降低了迭代效率和可维护性。建议优先在现有可扩展接口里做自定义渲染,再视情况决定是不是真的需要“动刀”到引擎级别。

(P2)OBS参数设置_哔哩哔哩_bilibili

音频比特率同理

录制视频到底是哪一轨会被写进去?

这取决于你在“输出设置”中的“录制格式和音轨配置”:

  • 如果你在设置中选择 只启用音轨1 进行录制,那么最终导出的视频就只包含音轨1中的内容。

  • 如果你启用了 多音轨录制(比如 mp4 + 轨道1~3),那么导出的视频将会携带多个音轨,你可以在后期剪辑软件中单独调出每条轨道。

你录了一个教学视频:

  • 轨道1:合成音轨(用于预览听感)

  • 轨道2:桌面音频(PPT 声音 + 视频素材)

  • 轨道3:麦克风(你讲解的声音)

后期你发现某一段麦克风杂音太重、或咳嗽了。
如果你只录了一个合成轨道(桌面 + 麦克风),你没法分离出来,只能忍着或者剪掉整段。

但有了独立轨道:

  • 你可以只剪掉麦克风这段,

  • 或者加上降噪插件处理,

  • 桌面声音完全不受影响。

无论是 .mp4.mkv 还是 .mov,一旦音轨写入进去,它们都是标准化的数据结构

  • 可以被读取 ✅

  • 可以单独静音、剪切、提取、替换 ✅

  • 可以重新封装(不重新编码) ✅

.mkv 是编辑友好的首选格式

(P3)OBS直播(基础篇)_哔哩哔哩_bilibili

转场特效是场景转换的时候产生的过渡

点击开始直播(也就是推流,推流可能更加形象一些),想停止,就停止推流,然后去bzhan上设置停止直播的开关

直播时长,录制时长

总之,需要直播的时候,就使用obs,拿b站或者其他平台给的推流码,填入之后就可以直播自己的电脑屏幕,可以用已经了解的obs的规则去管控自己的直播

视频录制如果要录

obs的文件部分可以转换文件格式,不用担心mkv保存了最好的音轨调整性,然后转别的格式不方便

听到“糊声”,不是酷狗变了,而是 OBS 改变了“你整个系统的音频处理路径”。
想要听到原来的音质,需要从驱动层、音频通道、采样率上做出限制或隔离。

这样就改了

禁用不需要的音频通道

OBS 设置 → 音频:

  • 麦克风/辅助音频1 → 只启用你实际用的

  • 桌面音频 → 启用你系统默认的那一个

  • 其他通道 → 统统设为“禁用”

这样 OBS 就不会偷偷“激活”多个音频输入或输出接口。

OBS 打开后会触发自动采样转换,造成插值失真

改用独立声卡或虚拟音频设备隔离 OBS

如果你有:

  • 外置 USB 声卡

  • 虚拟音频路由(如 VB-Cable、VoiceMeeter)

可以设置:

  • 酷狗 → 播放到真实声卡(耳机)

  • OBS → 捕获虚拟声卡 / 第二设备,互不干扰

这样你听到的不会是 OBS “截获后的信号”。

蓝牙耳机的“听”和“说”功能之间存在模式冲突,只有一个能高质量运行。

模式名称用途特征
🎵 A2DP高质量立体声传输模式听音乐、看视频音质好,但不能用麦克风
🎙 HFP/HSP通话模式(耳机+麦克风)打语音、开麦克风可以说话,但音质极差,像电话音

所以结论正确:

🟢 如果你外接一个独立麦克风(USB 或 3.5mm 插口),
🔁 就可以让蓝牙耳机 只用于播放(A2DP),系统不会切换到 HFP 模式,
🎧 你就能持续享受高质量的音频输出。

的蓝牙音质问题不是 OBS 特有的

这个问题会在所有场景下触发,比如:

  • 微信语音 → 蓝牙瞬间变糊

  • QQ语音 → 音乐变差

  • Discord 开启麦克 → YouTube 音质崩坏

它是 蓝牙协议层面的问题,不是 OBS 的 bug。

声卡就是外接麦克风设备吗?

不是。

  • 有些外接麦克风设备(比如 USB 麦)内置声卡 → 是二合一设备。

  • 但真正意义上的“外置声卡”,是可以连接多个音源、调节电平、具备专业音频处理功能的设备,不一定自带麦克风。

阶段技能模块目标
① 基础图形理解Unity 渲染流程、URP/HDRP管线结构、ShaderGraph基本逻辑能够看懂节点、了解 SRP 是干什么的
② 真实光照原理漫反射、高光、阴影类型(硬/软/SSAO)、光照探针、光照贴图、SH光照能合理解释光照表现背后的机制
③ 性能分析Profiler、Frame Debugger 使用,DrawCall 分析、Batches、RenderTexture 代价能识别“这个场景为什么卡、卡在哪”
④ ShaderGraph 实战节点功能实际应用:边缘光、渐变、遮罩、透明、噪声、流动、特效写法能实现效果,能解释原理(如基于法线/UV/世界空间)
⑤ 渲染管线控制自定义 SRP、RenderFeature、CommandBuffer、Blit 操作、后处理流程对渲染顺序、通道、混合有清晰认知,能做高级控制
⑥ 跨模块优化思维与美术/程序协作,场景组织优化(LOD、烘焙、阴影距离)、动态光源管理能在团队中定位渲染瓶颈,提出合理建议

xxxxxxxxxxxxxxx

一片 1K×4 芯片,一共可以存储 1024 × 4 = 4096 bit = 512 byte

8 片 1K×4 存储芯片,组成一个 4K×8 的存储器

利用了 地址译码器 + 芯片分组 + 数据线并联 的方法来实现容量与字位的同时扩展。

译码器,它根据地址的高位来决定哪一组芯片工作

防止多个芯片同时干活冲突

总线是共享的资源,所有芯片的数据线通常都是接在 同一条数据总线上。同一时刻,只能有一个芯片处于“工作”状态,其他芯片都要“闭嘴”(高阻状态)。不加片选控制,多个芯片都接在数据总线上,一起输出东西➡️ 数据总线上谁说了算?电压会乱跳,变成垃圾数据甚至损坏芯片。

元件访问延迟(大概数量级)说明
寄存器1 个 CPU 时钟周期CPU 内部,最快
L1 Cache~3-4 个时钟周期非常快,但容量小
L2 Cache~10 个周期稍慢一些,容量大些
内存(RAM)~100 个周期或更多太慢了,CPU等得焦躁
硬盘(HDD)几百万周期简直是“乌龟”

Memory Hierarchy(存储层次结构),就是为了在速度、容量、成本之间做平衡。 

CPU 内核内部,电子信号的传输几乎不需要走线;

没有中介,不需要“找地址”、“走总线”、“排队”;

是专为执行指令准备的高速电路(SRAM结构);

L1/L2 Cache:也在芯片里,但要做“内容匹配”,L1 Cache 也在 CPU 芯片上,但要根据地址查找数据是否命中

现代 CPU(以及 GPU)有专门的模块叫:

FPU(Floating Point Unit)浮点运算单元

它在硬件层面执行 IEEE-754 定义的操作。

大多数2D虚拟主播不需要穿戴动捕设备,只使用摄像头 +面部追踪软件即可实现面部表情和头部动作的捕捉。

但如果追求全身动作/复杂动态,才可能用到动捕服或外部设备。

游戏主程序通常是 C++,Lua 只是逻辑脚本,改 Lua 不影响主程序运行

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

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

相关文章

日本生活:日语语言学校-日语作文-沟通无国界(4)-题目:喜欢读书

日本生活&#xff1a;日语语言学校-日语作文-沟通无国界&#xff08;4&#xff09;-题目&#xff1a;喜欢读书 1-前言2-作文原稿3-作文日语和译本&#xff08;1&#xff09;日文原文&#xff08;2&#xff09;对应中文&#xff08;3&#xff09;对应英文 4-老师评语5-自我感想&…

C++优化程序的Tips

转自个人博客 1. 避免创建过多中间变量 过多的中间变量不利于代码的可读性&#xff0c;还会增加内存的使用&#xff0c;而且可能导致额外的计算开销。 将用于同一种情况的变量统一管理&#xff0c;可以使用一种通用的变量来代替多个变量。 2. 函数中习惯使用引用传参而不是返…

C#Blazor应用-跨平台WEB开发VB.NET

在 C# 中实现 Blazor 应用需要结合 Razor 语法和 C# 代码&#xff0c;Blazor 允许使用 C# 同时开发前端和后端逻辑。以下是一个完整的 C# Blazor 实现示例&#xff0c;包含项目创建、基础组件和数据交互等内容&#xff1a; 一、创建 Blazor 项目 使用 Visual Studio 新建项目 …

前端的安全隐患之API恶意调用

永远不要相信前端传来的数据&#xff0c;对于资深开发者而言&#xff0c;这几乎是一种本能&#xff0c;无需过多解释。然而&#xff0c;初入职场的开发新手可能会感到困惑&#xff1a;为何要对前端传来的数据持有如此不信任的态度&#xff1f;难道人与人之间连基本的信任都不存…

基于 Spark 实现 COS 海量数据处理

上周在组内分享了一下这个主题&#xff0c; 我觉得还是摘出一部分当文章输出出来 分享主要包括三个方面&#xff1a; 1. 项目背景 2.Spark 原理 3. Spark 实战 项目背景 主要是将海量日志进行多维度处理&#xff1b; 项目难点 1、数据量大&#xff08;压缩包数量 6TB,60 亿条数…

Unity3D 屏幕点击特效

实现点击屏幕任意位置播放点击特效。 屏幕点击特效 需求 现有一个需求&#xff0c;点击屏幕任意位置&#xff0c;播放一个点击特效。 美术已经做好了特效&#xff0c;效果如图&#xff1a; 特效容器 首先&#xff0c;画布是 Camera 模式&#xff0c;画布底下有一个 UIClic…

MCU编程

MCU 编程基础&#xff1a;概念、架构与实践 一、什么是 MCU 编程&#xff1f; MCU&#xff08;Microcontroller Unit&#xff0c;微控制器&#xff09; 是将 CPU、内存、外设&#xff08;如 GPIO、UART、ADC&#xff09;集成在单一芯片上的小型计算机系统。MCU 编程即针对这些…

Go语言--语法基础6--基本数据类型--数组类型(1)

Go 语言提供了数组类型的数据结构。 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列&#xff0c;这种类型可以是任意的 原始类型例如整型、字符串或者自定义类型。相对于去声明number0,number1, ..., and number99 的变量&#xff0c;使用数组形式 numbers[0], …

左神算法之给定一个数组arr,返回其中的数值的差值等于k的子数组有多少个

目录 1. 题目2. 解释3. 思路4. 代码5. 总结 1. 题目 给定一个数组arr&#xff0c;返回其中的数值的差值等于k的子数组有多少个 2. 解释 略 3. 思路 直接用hashSet进行存储&#xff0c;查这个值加上k后的值是否在数组中 4. 代码 public class Problem01_SubvalueEqualk {…

自回归(AR)与掩码(MLM)的核心区别:续写还是补全?

自回归(AR)与掩码(MLM)的核心区别:用例子秒懂 一、核心机制对比:像“续写”还是“完形填空”? 维度自回归(Autoregressive)掩码语言模型(Masked LM)核心目标根据已生成的token,预测下一个token(顺序生成)预测句子中被“掩码”的token(补全缺失信息)输入输出输入…

后端开发两个月实习总结

前言 本人目前在一家小公司后端开发实习差不多两个月了&#xff0c;现在准备离职了&#xff0c;就这两个月的实习经历写下这篇文章&#xff0c;既是对自己实习的一个总结&#xff0c;也是给正在找实习的小伙伴以及未来即将进入到后端开发这个行业的同学的分享一下经验。 一、个…

Python基础(​​FAISS​和​​Chroma​)

​​1. 索引与查询性能​ ​​指标​​​​FAISS​​​​Chroma​​​​分析​​​​索引构建速度​​72.4秒&#xff08;5551个文本块&#xff09;91.59秒&#xff08;相同数据集&#xff09;FAISS的底层优化&#xff08;如PQ量化&#xff09;加速索引构建&#xff0c;适合批…

Windows下memcpy_s如何在Linux下使用

Windows下代码如下 memcpy_s(pLine->ppBuf[i], m_ColorLineByte, pIn nOffset, m_ColorLineByte); 方案 1&#xff1a;使用标准 memcpy 手动检查&#xff08;最通用&#xff09; // 检查参数有效性 if (pLine->ppBuf[i] nullptr || pIn nullptr || m_ColorLi…

2025年数学算法与自动化控制国际会议(ICMAAC 2025)

2025年数学算法与自动化控制国际会议&#xff08;ICMAAC 2025&#xff09; 2025 International Conference on Mathematical Algorithms and Automation Control 一、大会信息 会议简称&#xff1a;ICMAAC 2025 大会地点&#xff1a;中国长沙 审稿通知&#xff1a;投稿后2-3日…

C语言数组介绍 -- 一维数组和二维数组的创建、初始化、下标、遍历、存储,C99 变长数组

目录 1. 一维数组 1.1 数组的概念 1.2 一维数组的创建 1.3 一维数组的初始化 1.4 数组的类型 1.5 数组下标 1.5.1 数组元素的遍历 1.5.2 数组的输入 1.6 一维数组在内存中的存储 1.7 sizeof 计算数组元素个数 2. 二维数组 2.1 二维数组的创建 2.2 二维数组的初始…

SpringAI + DeepSeek大模型应用开发 - 进阶篇(上)

三、SpringAI 2. 哄哄模拟器 2.1 提示词工程 提示词工程&#xff08;Prompt Engineering&#xff09;&#xff1a;通过优化提示词&#xff0c;使大模型生成尽可能理想的内容&#xff0c;这一过程就叫提示词工程。 &#xff08;1&#xff09;清晰明确的指令 谈谈人工智能 …

Spring Boot实现异常处理

Spring Boot 提供了多种灵活的方式实现异常处理&#xff0c;以下是核心方案和最佳实践&#xff1a; 一、基础异常处理方案 1. ControllerAdvice ExceptionHandler&#xff08;全局处理&#xff09; ControllerAdvice public class GlobalExceptionHandler {// 处理特定异常&…

【目标检测】IOU的概念与Python实例解析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

Vue2中如何使用vue-print-nb打印功能

插件官网地址&#xff1a;vue-print-nb - npm 1.安装 npm install vue-print-nb --save 2.导入打印插件 //main.js import Print from vue-print-nb Vue.use(Print); 3.配置参数 4.页面使用 <div id"printDiv">打印内容</div><el-button v-print&…

Matplotlib快速入门

目录 基本使用 解决中文乱码 一个坐标系绘制多个图像 多个坐标系绘制 基本使用 什么是Matplotlib 是专门用于开发2D图表(包括3D图表)以渐进&#xff0c;交互式方式实现数据可视化 为什么要学习matplotlib 可视化是在整个数据挖掘的关键辅助工具&#xff0c;可以清晰的理解…