自由学习记录(77)
官方模版、、都不用了,记得之前用gitextension 的时候也好像有这种问题,也不知道怎么回事
用自己的就行了
网上说什么都没用,还是要自己老实写,配上截图工具截屏目录直接转文字过去,其实字都不要打多少的
一张很深刻的图、、、,最后的gitignore优雅
Implementing a Dissolve Effect with Shaders and Particles in Three.js | Codrops
Tutorial 27 - Billboarding and the Geometry Shader
多个 primitive 并行执行的时序对你不可见
geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream)
“geo 提供了这种额外的分组处理方式,是它唯一的作用吗?”
—— 是的。它正是用来把一组三角形(这里是 3 个顶点构成的三角形)变成 0 个、1 个或者更多三角形/四边形(通过三角形条带)。这正是 VS/FS 无法提供的 “几何生成” 能力。
如果完全不构建广告牌
geo shader 里面只执行这个
for (int j = 0; j < 3; j++)
{
g2f o;
o.pos = UnityObjectToClipPos(IN[j].objPos);
o.uv = TRANSFORM_TEX(IN[j].uv, _MainTex);
o.normal = UnityObjectToWorldNormal(IN[j].normal);
o.worldPos = float4(mul(unity_ObjectToWorld, IN[j].objPos).xyz, -1);
triStream.Append(o);
}
frag接受的是在顶点的属性被栅格化后,变成一个一个内部的像素,那这个像素里的属性值是谁的?
不会是geo传入的三角形中的任意一个顶点的一套属性,而是在一切顶点都已经插值完成之后,属于像素的属性?
是的! Fragment Shader 接收到的每个像素(fragment)输入值并不是某个顶点的直接属性,而是通过 几何着色器(或顶点着色器)输出的那三个顶点在三角形内的插值结果。也就是说,像素级的属性只在三角形内部才具有意义,是 所有顶点贡献后的“混合值”。
像素属性,是屏幕层面的,光栅化的结果,就是1920x1080这些每一个像素上的插值的后的一套属性 ,frag shader的目标是对应分辨率下的光栅化处理结果 所以frag shader是因为设备的不同而产生各自光栅化效果的像素个数,的最终部分?----完全yes
假设输出目标分辨率为 1920 × 1080(逻辑像素或样本数),那最多会生成约 1920 × 1080 个 fragment(每个未被丢弃的采样点对应一个片段)。这种情况下,如果有 MSAA(多样本反锯齿),Fragment Shader 可能每个像素执行多次。
Fragment Shader执行次数与分辨率(和采样数)直接相关;
fragment 数量 ≈ Resolution × samples
float dissolve_value = tex2Dlod(_DissolveTexture, float4(avgUV, 0, 0)).r;
float t = clamp(_Weight * 2 - dissolve_value, 0, 1);
与常见的 tex2D()
采样方式相比,有几个 本质上的不同点
没有梯度信息 → 不能交由GPU自动选 mipmap
几何着色器 (GS) 和顶点着色器 (VS) 并不运行在屏幕空间采样网格上,即它们不会获得像素级的梯度信息 (ddx()
/ ddy()
),也无法由硬件计算合理的 mipmap 级别。
tex2Dlod(sampler, float4(u, v, 0, LOD))
是一个显式指定采样 mipmap 级别的函数。
VS 或 GS 中,如果你必须采样纹理又没有梯度,就 必须手动传入 LOD,一般写 0
表示 base 级别(全分辨率)。
为什么在 Geometry Shader 中必须用 tex2Dlod()
?
在你的 geom()
里,你使用了 avgUV
而不是屏幕坐标。
Geometry Shader 不具备自动计算 mipmap 的能力,也没有片元的梯度输入——因此正常的 tex2D()
调用本身是非法的。
-
当片元从三角形 rasterizer 阶段生成后,它会由三角形顶点插值得出多个属性值(uv、worldPos、normal 等)。
-
然而,GPU还需要「知道这个属性在屏幕上变化有多快」——这就是梯度输入(partial derivatives),可以决定纹理采样时该用哪一层 mipmap、是否切换各向异性过滤等。
这种衍生的差值,不是顶点自带的,而是在光栅阶段根据前后左右 fragment 自动计算出来的。 aclockworkberry.comGame Development Stack Exchange
如何在 Shader 里“访问”梯度?
在 HLSL/CG 中,你可以用这两个内建函数:
float ddx(float v); // 水平方向一像素变化率
float ddy(float v); // 垂直方向一像素变化率
它们分别计算某个变量在 x 或 y 方向的差值,比如 ddx(i.uv.x) 表示当前像素 uv.x 与右边像素的差值。
developer.download.nvidia.com
函数结果对一个 float2 或 float3 也可 element-wise 插值。
还可组合成 fwidth(v) = abs(ddx(v)) + abs(ddy(v)),表示该值在屏幕上的“总变化率”。通常用于实现无别名阶跃/渐变(step gradient)切割效果。
aclockworkberry.com
它是如何计算得来的?GPU 结构揭秘
-
GPU 会把渲染目标的像素划分成 2×2 的像素块(quad),其中同一组的 4 个片元共享派发一起执行 Fragment Shader;
-
ddx(v)
就是用 quad 中右上角与左上角这两个 fragment 的v
值差(vertical 为底与顶)来估算的; -
如果 Shader 被分支分流了(如
if (...) ddx(...)
),某些 fragment 路径没跑时,ddx
的值可能是未定义的(误差或错误)。 aclockworkberry.comGame Development Stack Exchange
-
VS/GS 没有屏幕空间像素信息,也无 quad 概念,因此 GPU 无法估算一个变量的像素梯度值;
-
如果你在 VS 或 GS 写
tex2D()
,编译器也会报错,因为不知道该如何为 mipmap 选择级别; -
而 Fragment Shader 正好在 raster 之后,因此自动拥有这些梯度输入。
An introduction to shader derivative functions | A Clockwork Berry
float smooth = smoothstep(edge0, edge1, fwidth(i.uv.x));
当 warp(UV扭曲)、偏移、大范围扭曲时,你可以用 ddx(i.uv) 的结果手动控制 tex2Dlod(),确保 lod 选择稳定。
在后期纹理 warping、极坐标映射、屏幕空间算法(如线框、网格显示、高宽比)中,梯度控制几乎是必需品。
“片元的梯度输入”指的是 Fragment Shader 中 ddx()
/ ddy()
等函数返回的 屏幕空间插值值变化率 —— GPU 在片元光栅化阶段组织片元为 2×2 像素块后自动计算的差值。它是 mipmap 选择、程序滤波、抗锯齿、扭曲贴图等效果的重要基础,而顶部的 VS/GS 阶段是无法访问这一信息的。
dissolve 级联、流动法线、无缝抗锯齿边界、极坐标扭曲采样等,都是与此相关的
float2 flowUV = TRANSFORM_TEX(mul(unity_ObjectToWorld, avgPos).xz, _FlowMap);
float4 flowVector = remapFlowTexture(tex2Dlod(_FlowMap, float4(flowUV, 0, 0)));
float3 pseudoRandomPos = avgPos + _Direction;
pseudoRandomPos += flowVector.xyz * _Exapnd;
(Flow Map) 去给粒子添加“随风飘散”的局部偏移,即使 mesh 面朝向或形状不规则,也能让流散变形看起来自然又有层次感。
Flow Map 是一张二维向量贴图(通常 R=横向,G=纵向,归一化到 [0,1] 区间)
它不是噪声,而是**精准描绘各片区域想让粒子“往哪个方向飘”**的矢量数据 (相当于每个像素保存了一个 velocity vector)
靠近某边缘的三角片 UV 所对应的 flowVector 值可能指向斜下方向,而另一片可能向右上。这种方式比简单随机更具有真实感。
// remap 流向值:范围从 [0,1] 转换成 [-1,1]
float2 flow = tex2D(_FlowMap, flowUV).xy * 2 - 1;
float3 pseudoRandomPos = avgPos + _Direction + flow * _Exapnd;
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
-
name
是你在 ShaderLabProperties
中声明的贴图名,比如_MainTex
; -
Unity 会自动生成一个名为
name##_ST
的 float4 uniform:(关于默认uv的属性)-
.xy
存储的是 UV 重复(tiling); -
.zw
存储的是 UV 偏移(offset)。
-
IN.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw
先缩放 (tiling
),再偏移 (offset
) UV。
direction4是控制大方向的一个属性
tex2Dlod(_DissolveTexture, float4(avgUV, 0, 0)).r;
确实是向 tex2Dlod() 传入了一个 4 维向量(float4),让你觉得“多余”,但它其实是 API 设计决定的正确用法。
为什么 tex2Dlod()
必须传入 float4
而不是 float2
根据 HLSL 文档,tex2Dlod(sampler2D s, float4 uv)
的签名明确要求第二个参数是一个完全的四维向量:
-
uv.xy
:用于纹理的 UV 坐标 -
uv.z
:在 2D 纹理中通常被忽略(可设为 0) -
uv.w
:用于指定 mipmap 级别(LOD),即你想手动控制采样的分辨率层级 Microsoft Learn
简而言之:你传入 (u, v, z, lod)
,其中只有 u
、v
和 lod
是有效维度,剩下的 z
只填充占位。
float dissolve_value = tex2Dlod(_DissolveTexture, float4(avgUV, 0, 0)).r;
float t = clamp(_Weight * 2 - dissolve_value, 0, 1);
weight是0到1的限制范围,懂了
clamp是加保险的,,重点是控制范围,完全减和完全加的范围,最终需要是0到1的范围
才能转换给图像逻辑使用
float3 right = UNITY_MATRIX_IT_MV[0].xyz; // 摄像机右侧方向 → X 轴
float3 up = UNITY_MATRIX_IT_MV[1].xyz; // 摄像机上方向 → Y 轴
// 然后用 radius * right + radius * up 构造四边形 → 所有 billboards 默认是朝向屏幕面。
➡️ 结果是所有粒子都会面向屏幕,且它们的「本地平面方向」一致,所以花瓣都是同样的“扭蛋角度”,缺乏随机性、美感或辨识度。
float3 right = UNITY_MATRIX_IT_MV[0].xyz;
float3 up = UNITY_MATRIX_IT_MV[1].xyz;
这两个得到的坐标系是哪个,为什么直接可以当right 和up ,都不用归一化
UNITY_MATRIX_IT_MV
是 Model×View 矩阵 的 逆转置矩阵,通常用于将法线从物体空间正确变换到摄像机(视图)空间,
IT 版本用于转换法线
float3 right = UNITY_MATRIX_IT_MV[0].xyz;
float3 up = UNITY_MATRIX_IT_MV[1].xyz;
正是取 该矩阵的第 0 行和第 1 行的 .xyz 分量,而根据贴在 Gist 上的 Unity 社区经验,这两行分别等效于:
row[0].xyz → 摄像机空间的 Right(右方向向量)
row[1].xyz → 摄像机空间的 Up(上方向向量)
可以直接当做标准向量,不用再 normalize(归一化)?
理论上,inverse-transpose 在仅含旋转 + 均匀缩放的 ModelView 上不会改变轴向长度正交特性。
如果你的 unity_ObjectToWorld
里没有非均匀缩放(即没有不同方向 scale ≠ 1、或 shear),那么使用这个习惯于转换法线的矩阵,其行向量已经是长度接近 1 的正交基。
Bonus:更自然花瓣姿态(不强制朝屏幕)
如果你想让花瓣朝向“世界垂直/重力方向”,而不是屏幕,可用类似思路构建:
float3 worldUp = float3(0,1,0); float3 forward = normalize(cross(avgNormal, worldUp)); // 花瓣法线与 Up 做 cross → 世界空间方向 float3 right = normalize(cross(forward, avgNormal)); float3 up = cross(avgNormal, right); // 使三轴正交// 然后同样用 roll angle 随机旋转 right/up(绕 avgNormal)
这样花瓣保持自然“飘散倒地”的朝向(平面法线为 avgNormal),而每片依旧有随机翻转。
fixed4 col = (1, 1, 1, _Opacity);
说这一行有错误,可能,,,就是你这里的值初始化了又不用,所以就警告了
雪地路径 Shader Graph—Unity_哔哩哔哩_bilibili
[Unity] 集群行为初探-鱼群_哔哩哔哩_bilibili
参考:https://github.com/Shinao/Unity-GPU-Boids?tab=readme-ov-file
Unity 鱼群(School of Fish / Flocking)
涵盖 Boids 算法、VFX Graph、Shader Graph 等实现路径
Cam Ayres 的“Shader Graph + VFX Graph 制作鱼群”(即 《Unity 6: Creating a School of Fish…》),核心都依赖 Shader Graph/VFX Graph。而这两者 只能在 Unity 的 SRP 管线(也即 URP/HDRP)下使用
https://www.youtube.com/watch?v=voegALuuO2I
Shinao/Unity-GPU-Boids: GPU powered boids with multiple implementations
How to achieve boid/flocking effect (e.g. fish school) in VFX Graph without Compute Shader? - #2 by OrsonFavrel - Unity Engine - Unity Discussions
https://github.com/Streamweaver/FishFlocker?utm_source=chatgpt.com
“Colorful FX” 是一个 很久之前的 Unity 插件,主要用于后处理调色和色彩校正,让 Unity 场景具有类似 Photoshop 效果的视觉表现。
提供一系列高质量、可自定义的 后期处理效果,包括 PCC、色调转换、LUT、色彩提炼、动效预设、模糊、饱和度调整等—类似游戏版 Photoshop 调色工具。
❓为什么现在较少使用?
-
随着 Unity Post-processing Stack 和 URP/HDRP 的普及,Unity 官方已经提供更加现代、集成度高的调色与后期处理解决方案;
-
很多效果如 Bloom、Tone mapping、Color grading、Vignette 等都内置支持,无需额外插件;
-
Colorful FX 自 2012 年发布以来几乎不再更新,目前兼容性有限,尤其对 URP/HDRP 支持不佳。
“Colorful FX 是 Unity 早期为 Built-in 管线提供的高效调色与后处理特效插件,但现在更多被 Unity 官方后期管线所替代。”
https://www.bilibili.com/video/BV1DG4y1n7md/?vd_source=8edbc527019213f5a0f28f3a4b395636
Git 并不跟踪“文件的差异”,而是将每次 commit
时项目里的所有文件生成一次 全量快照(snapshot),只保存改变过的内容。对于未变化的文件,它会用引用链接已有存储以节省空间
Checkout...回到某个老版本看看当时的代码
Revert...创建一个“反向的提交”来撤销它的改动
「我刚删了不要的脚本,现在 checkout 到后一个节点,当前这个节点就会消失吗?」
🧠 答案:
❌ 不会“消失”,但会暂时 “看不见”
你当前这个节点(删除了不要的脚本
)是一个 提交记录(commit),它永远保留在 Git 历史里,只要有分支指向它,或者你回去引用它,它就不会被 Git 清理。
但⚠️如果你 checkout 到后面节点,而这个节点 没有分支指向它(如 HEAD
脱离状态下新提交的),它就会成为孤儿 commit,之后 Git 有可能自动清理它(GC)。
detached HEAD 是你离开了“正常历史线”,回头看了一眼旧日时刻;
但如果你不小心写了新东西没保存分支,那些改动就可能随风而去。
ref:
手把手教你用git管理unity游戏项目,让你少掉5根头发_哔哩哔哩_bilibili
Git 是开源的,用在本地,不依赖网络
github 是云端,可以管控本地的git,(前提是本地的git存在了)
xxxx
假设你有一个 Unity 项目,并且想要 Git 管理它,其中包含 Assets/
、ProjectSettings/
等文件:
用 Git:
-
在项目根目录运行
git init
,本地初始化 Git 仓库; -
编辑
.gitignore
忽略Library/
,Temp/
等缓存文件; -
用
git add Assets ProjectSettings
,第一次提交; -
修改脚本或场景后用
git commit
生成更新快照; -
随时通过
git checkout
或git revert
回退历史版本。
xxxx
“Git GUI” 是 Git 软件自带,目录下 右键 更多属性里调出
然后是里面具体的管理-----可以在GUI里做(不用,因为gui里ignore都不能写,功能太少)
创建.git文件夹在unity总文件夹里之后,这个项目打开脚本时,VScode自动就变了
VS Code 的 Git 功能默认在打开文件夹是自动检测最近的
.git
仓库;实际上,这些就是 VS Code 调用了底层
git status
命令,自动显示每个文件状态,以简化你的版本控制流程(无需手动运行命令行)。
.gitignore可以去掉repository里不要管的文件夹
xxx
这里直接在vscode里面git完全管理
直接点分支,create repository,自动对unity项目根目录下创建git文件夹
Commit
或 Commit Staged 都
需要先 Stage,Commit All 则直接提交所有修改
暂存Staging,让你可以add挑出更改--提交
只有进行了 commit(提交)之后,Git 才会记录那些改动,并且之后才会在 UI 或 git status
输出中显示那些「改变了的文件」
半天下来,git可能损坏文件,但是是最好的,要注意绑定的关系变化就是,模型被绑定了就不要删了
https://docs.unity3d.com/Packages/com.unity.visualscripting%401.9/manual/vs-version-control.html
答辩官方文档,差评(虽然估计是自己哪里没注意到)
右键菜单中的 “Reset current branch to this Commit...” 操作
Unity 粒子系统(Particle System)的底层原理和构成模块,可以从三个核心阶段拆解:发射 → 模拟更新 → 渲染。
Particle System模拟 / 更新阶段(Simulation)
-
每帧基于时间间隔,更新每个粒子状态:
-
位置更新:由速度控制,结合重力、风力、外力模块(Force over Lifetime)、噪声模块等;
-
生命周期:递减剩余寿命,寿命结束则销毁;
-
属性变化:大小、颜色、旋转、透明度可随生命周期插值变化(Size over Lifetime、Color over Lifetime);
-
-
工作流程为:模拟 → lifetime 判断 → 属性插值 → 位置更新。
-
粒子最终以 2D 点精灵 (Point Sprites) 或 billboard 四边形形式渲染,每个粒子朝向摄像机;
-
可以通过材质、纹理、混合模式等控制效果;
-
可进一步定制为使用 Mesh 或 GPU 渲染(或 Visual Effect Graph 模块支持百万级粒子)。
Unity 粒子 特有模块和高级功能
-
子发射器(Sub-Emitter):某些粒子出生、死亡或碰撞时触发再次发射子粒子;
-
碰撞模块(Collision):支持粒子与场景 collider 或刚体交互,无需写代码;
-
脚本 API:可通过粒子系统脚本接口控制粒子生命周期事件、自定义行为;
-
Visual Effect Graph(VFX Graph):基于 GPU 的可视化图形编辑工具,支持大规模 GPU 粒子渲染。
Shader Graph 如何接入 VFX Graph
-
首先,你需要在 Shader Graph 的设置中启用 “Support VFX Graph”,并指定正确的渲染管线(HDRP/URP)
Unity Discussions+9Unity Documentation+9Unity+9 -
然后在 VFX Graph 的输出 Context 中选择该 Shader Graph,即可用自定义 Shader 来定义粒子着色
Unity Documentation+2Unity Discussions+2
这样你能完全控制粒子的视觉属性,比如发光、变色、顶点动画(如渐变形变、扭曲等)都可由 Shader Graph 处理。
Unity 的 Visual Effect Graph(VFX Graph) 是 Unity 针对高性能 GPU 粒子效果设计的节点式可视化编辑系统
VFX Graph 完全在 GPU 上执行粒子发射、模拟与渲染流程,利用 GPU 并行处理数百万甚至上亿粒子,高性能、低 CPU 占用
VFX Graph 基于 GPU 渲染 时,其核心技术是通过 Compute Shader 将粒子模拟与粒子渲染一并交由 GPU 控制,然后根据 Shader Graph(或 HLSL)定义每个粒子的外观与行为。
VFX粒子模拟(Update Loop)
-
每帧由 GPU compute shader 更新粒子状态(重点,一般用GPU更新逻辑,而这里使用GPU了):位置由速度更新,并应用引力、风力、噪声等模块。
-
生命周期结束的粒子自动销毁,属性如颜色、透明度、大小随着时间变化插值控制
VFX渲染输出(Rendering)
-
支持多种渲染类型:点精灵(Point Sprites)、Billboard 四边形、Mesh、线条(Trail)、条带(Strip)等。
-
可结合 Shader Graph 自定义像素与片段着色器,实现纹理动画(Flipbook)、变形、混合模式、LOD、HDRP/URP 支持等渲染细节
VFX交互与事件控制
-
事件系统(Events):粒子在生命周期中可触发子发射器、发送事件到其他系统(如触发摄像机震动、声音、碰撞反馈)。
-
碰撞与环境交互:支持深度缓冲、Signed Distance Fields(SDF)作为碰撞输入,使粒子与场景几何体互动Unity。
-
与 ECS / DOTS 集成:VFX Graph 可与 DOTS 架构结合使用,将实体系统 CPU 数据提供给 GPU 缓冲区,实现模拟与渲染的协同运算Reddit。
性能与优化
-
使用 GPU 并行计算,不会频繁切换 CPU↔GPU,保持高帧率。
-
内置可见性剔除(bounding volume culling)、粒子 LOD、粒子遮挡透明度优化机制支持数万个实例高效渲染
知乎/论坛语录 & 经验引用
“Unity DOTS + VFX Graph 超强… 每分钟发射百万颗子弹还能保持编辑器 120fps。”
“通过在粒子上添加一个 custom BulletID,将实体与粒子匹配,再把碰撞列表传给 VFX Graph,使粒子知道自己应该销毁。”
来自 Reddit 用户经验分享,说明 VFX Graph 可接受 CPU→GPU 双向数据,实时控制粒子行为Unity Documentation+5Reddit+580.lv+5。
https://www.reddit.com/r/Unity3D/comments/y29exn/i_love_to_combine_vfx_graph_and_shader_graph_have/?utm_source=chatgpt.com