Kajiya--Kay 其實不是從某套更深的毛髮物理方程嚴格推導出來的。原論文自己就說得很清楚:diffuse 部分是把 Lambert shading 用到一根很小的圓柱上;specular 部分則是類似 Phong 的 ad hoc model。 编辑ACM Digital Library+1
整條歷史鏈大概是:
Lambert → Phong →「圓柱沒有唯一 normal」這個問題 → Kajiya--Kay → Marschner。
先看他們真正卡住的地方。
1989 年 Kajiya 和 Timothy Kay 做的論文叫《Rendering Fur with Three Dimensional Textures》。它首先是篇 fur / volumetric texel rendering 論文,不是一篇純粹研究 BRDF 的論文。他們想表示大量非常細的毛,而不是給每根毛建立大量 polygon。编辑ACM Digital Library+1
於是問題來了:
普通 surface shader 有
N
因為你在算一塊表面的光照。
但一根極細的毛,他們近似成:
cylinder
你真正穩定知道的是毛髮的軸:
T
也就是 tangent。
而圓柱周圍不是一個 normal:
N
而是一整圈:
N(ϕ)
這一步就是 Kajiya--Kay 出現的真正原因。
後來 2003 Marschner 等人真的去測量實際人髮纖維的散射,發現 Kajiya--Kay 預測不了若干非常明顯的現象,所以才把 hair 當成具有折射、內部傳輸以及 cuticle 結構的 dielectric fiber,得到今天熟悉的:
R,TT,TRT
三類主要 scattering paths。编辑Stanford Graphics+1
比較一下就很清楚:
so many lives ahead
→ 前面還有很多條生命/很多人的人生
so much life ahead
→ 未來還有大量生活、人生經歷
而這裡還多了一個 of:
so much of life ahead
這會稍微偏向「人生這整個東西中,還有很大一部分在未來」。
overflow-visible!
Computer Graphics
└ Rendering
├ Geometry / Visibility
│ ├ Rasterization
│ ├ Ray intersection
│ └ Occlusion
│
├ Light Transport
│ ├ Path Tracing
│ ├ Photon Mapping
│ ├ Bidirectional methods
│ └ Multiple scattering algorithms
│
├ Appearance / Scattering Models ← 這裡
│ ├ BRDF / BSDF
│ ├ Microfacet models
│ ├ Subsurface scattering
│ ├ Participating media
│ └ Hair / Fiber scattering ← Kajiya--Kay
│ ├ Kajiya--Kay
│ ├ Marschner
│ └ 現代 hair BCSDF / fiber models
│
├ Sampling / Monte Carlo
├ Reconstruction / Denoising
└ Display / Tone mapping
PBRT 第四版基本就是按這個分類放的:第 9 章叫 Reflection Models ,裡面先建立 BRDF、BTDF、BSDF,然后有 dielectric、conductor、measured BSDF,最后单独有 Scattering from Hair 。也就是说,现代教材会非常明确地把 hair equation 看成「局部散射模型」这一类,而不是光传输算法。
Kajiya--Kay 研究的不是:
「光从灯出发,经过整个场景,最后怎么到 camera?」
这个属于 light transport。
它研究的是:
「假设一束光已经撞到这根 hair fiber 上了,那么它朝观察方向出去多少?」
也就是这个局部问题:
(Li,ωi,ωo,hair properties)→Lo
所以如果把 Rendering Equation 展开:
Lo(x,ωo)=Le+∫Kajiya--Kay / Marschner 研究這裡f(x,ωi,ωo)Li(x,ωi)∣n⋅ωi∣dωi
Kajiya--Kay 主要在设计中间这个:
f
也就是:
「材料/物體對光的局部 response 是什麼?」
这就是 Appearance Modeling 的核心问题。
对于普通表面:
overflow-visible!
Appearance model
└ Surface scattering
├ Lambert
├ Phong
├ Blinn--Phong
├ Cook--Torrance
├ GGX
└ Oren--Nayar
对于特殊结构:
overflow-visible!
Appearance model
└ Structured / volumetric scattering
├ Skin
│ └ subsurface scattering
│
├ Cloth
│ └ fiber / sheen scattering
│
├ Hair
│ └ fiber scattering
│ ├ Kajiya--Kay
│ └ Marschner
│
├ Smoke / cloud
│ └ phase function
│
└ Thin film
└ interference
所以你之前一直看的 Cook--Torrance,其实和 Kajiya--Kay 在同一个大层级。
区别只是:
overflow-visible!
Reflectance / scattering model
├ Cook--Torrance
│ └ 普通粗糙表面的 microfacet scattering
│
└ Kajiya--Kay
└ 细长 cylinder / hair fiber 的 anisotropic scattering
这也解释为什么你会感觉它们「都是一堆公式」。
它们本来就是同一批人在解决同一种抽象问题:
给定材料微观结构,建立一个函数,描述入射方向 → 出射方向的能量分布。
Marschner 2003 的论文名字甚至直接叫 Light Scattering from Human Hair Fibers 。他们明确说,当时图形学通常使用 Kajiya--Kay 的 phenomenological model,而他们通过实际测量 hair scattering,发现了 Kajiya--Kay 不能表达的多个 specular highlights 和绕 fiber axis 的散射变化,然后建立更物理的模型。编辑Stanford Graphics+1
PBRT 目录 = 一个 physically based renderer 的"系统解剖图",而不是整个 rendering research community 的严格分类表。
Cook 和 Torrance 1981/1982 的论文标题本身就是 A Reflectance Model for Computer Graphics ;PBRT 也把它放在 Reflection Models / microfacet reflectance 这条历史线上。编辑pbr-book.org+1
实际 production 更常见的是:
overflow-visible!
选择已有 D/F/G
↓
调整它们的参数化
↓
为性能做 approximation
↓
为了艺术控制增加 mapping / remap
↓
必要时补 energy compensation
而不是随手重新发明 D。
更重要的是:在 Cook--Torrance 外额外加一个概念,也完全不一定是 Research。
例如:
fr=fdiffuse+fmicrofacet+fclearcoat+fsheen
你刚刚"加了新的 lobe"。
但 clearcoat、sheen 都已经是成熟概念。
这仍然可能只是:
material model composition / production shading
不是研究。
UE Substrate 甚至就是把这个想法工程化了:Material 可以由 BSDF blocks 组成,再进行 layering / mixing;Epic 把它描述成一种 modular、BSDF-based material authoring framework。编辑Epic Games Developers+1
一个完全没有渲染背景的例子:
y=ax+b
这是一个模型族。你可以随便调:
a,b
但无论怎么调,都不可能得到:
y=x2
你当然可以说:
那我直接把代码改成
a*x*x+b不就行了?
可以。
但此时问题不是"线性模型经过调整解决了抛物线",而是:
你已经不再使用线性模型。
这就是渲染里"现有 abstraction 无法解决"的准确含义。
光碟表面从宏观看是平面,但放大以后,它不是随机粗糙:
overflow-visible!
宏观 CD
↓
一条从内向外盘旋的螺旋数据轨道
↓
取一个非常小的局部区域
↓
螺旋几乎可以看成一排平行轨道
|||||||||||||||||||
|||||||||||||||||||
|||||||||||||||||||
CD 的典型 track pitch 大约是 1.6 μm,也就是相邻轨道只隔 1.6 微米,已经和可见光波长 0.4--0.7 μm 是同一个数量级。因此这排周期结构会直接成为反射式 diffraction grating。不同波长满足衍射条件的出射方向不同,所以白光被拆成不同颜色。Optica 的实际光学实验也直接把 CD 的轨道作为约 1.6 μm pitch 的 diffraction grating 使用。编辑Optica Publishing Group+1
关键就在这里:
overflow-visible!
沿轨道方向 T
────────────────────
跨轨道方向 B
↑
│ ║ ║ ║ ║ ║ ║
│ ║ ║ ║ ║ ║ ║
这两个方向的表面结构完全不同。
沿着轨道走,表面变化相对小;垂直跨过轨道走,会不断碰到周期性的 pit/land/track 结构。
所以从传统 microfacet BRDF 的角度看,它天然就不是:
αx=αy
而更接近:
αT=αB
这正是 anisotropic roughness 的来源:微表面的 slope distribution 在两个切线方向上不同。PBRT 对 anisotropic microfacet distribution 的表达实际上就是给两个切线方向独立的 αx,αy。编辑PBR Book+1
an-
└ not
isotropic
└ iso = same
└ tropic = direction / turning

比如两个方向:
sT=∂T∂h,sB=∂B∂h
如果统计结果是:
Var(sT)≪Var(sB)
它直白地说:
overflow-visible!
沿 T 方向走
高度变化很平缓
→ slope 分布窄
沿 B 方向走
上下起伏很厉害
→ slope 分布宽
这就是 anisotropic roughness 很底层的一种说法。NVIDIA 关于 anisotropic microfacet 的工作也会直接从 anisotropic scaling / slope distribution 的角度描述这种结构。编辑NVIDIA+1
所以以后你看到:
-
microfacet normal:强调"小面的朝向" -
microfacet slope:强调"小面相对于宏观表面的倾斜程度" -
slope distribution:强调"所有这些倾斜程度的统计分布" -
anisotropic slope distribution:强调"沿 T/B 两个方向,倾斜统计不同"
今天游戏开发里大家嘴里说:
"Cook-Torrance BRDF"
经常实际上指的是上面这个 specular microfacet BRDF:
fs=4N⋅LN⋅VDFG
然后另外再放一个 diffuse:
fr=fd+fs
例如最简单:
fd=πρ
于是整个材质的 fr 才是:
fr=πρd+4(N⋅L)(N⋅V)DFG
所以如果你问:
"Cook--Torrance 是不是在构建那个 f?"
拉丝金属有一个方向:
T
你转动拉丝方向,高光也应该跟着旋转。
但是你的 isotropic Cook--Torrance 根本没有:
T
这个输入。
于是你把整个场景的 L,V 绕着 N 转一圈,只要它们与 N 的夹角不变,isotropic distribution 对这些方位没有区别。
也就是说它具有一个结构性的不变量:
f(RNL,RNV)=f(L,V)
其中 RN 表示绕法线 N 旋转。
这不是 roughness 调得不好。
是这个函数本身根本"不知道哪个方向叫拉丝方向"。
于是怎么办?
增加:
T,B
以及:
αx,αy
变成 anisotropic microfacet。
overflow-visible!
isotropic microfacet
├ N
└ α
↓ 扩展模型
anisotropic microfacet
├ N
├ T / B
├ αx
└ αy
注意这个案例非常重要:
Cook/microfacet 这个大 abstraction 并没有失败。
失败的是:
isotropic microfacet 这个更小的模型。
所以我们扩展到 anisotropic microfacet,仍然待在 microfacet theory 里面。PBRT 也正是这样组织的。编辑PBR Book
Marschner 等人做的正是这种实验:他们实际测量单根人发在不同入射/出射方向下的 scattering,发现了 Kajiya--Kay 没有预测出的、视觉上显著的现象 ,包括 multiple specular highlights、out-of-plane scattering,以及随 fiber axis 周围旋转而变化的 scattering。编辑Stanford Graphics+1
这就是一句非常标准的:
existing model cannot explain the observations。
不是:
"我不会调参数。"
而是:
∀θ,fold(x;θ)=fmeasured(x)
在整个测量域上不存在一组参数能够把那些现象同时复现出来。
Unity 现在 URP 依然明确把 transparent 放在 opaque 后的独立 transparent pass;它甚至提供 _CameraOpaqueTexture,定义就是在透明物体开始绘制之前保存 opaque scene 的结果。编辑Unity Documentation+1
overflow-visible!
普通 Alpha Blend
└─ 运算具有顺序依赖
└─ 必须知道谁在后、谁在前
└─ 通常 back-to-front
这才是 Unity Transparent 3000 内通常 back-to-front 的根本原因。官方至今仍明确这样定义 Transparent queue。编辑Unity Documentation+1
Unity 6 的官方文档到现在仍然提醒 transparent object 会出现 sorting 问题,尤其是相交、互相包围、尺寸差异大的透明 mesh;这就是同一个问题一直延续到今天的直接证据。编辑Unity Documentation
https://graphics.stanford.edu/papers/hair/hair-sg03final.pdf
透明物体有 depth,也通常做 ZTest;但普通 alpha-blended transparent 的颜色 Pass 通常
ZWrite Off。
Unity 2022.3 的 Transparent shader 体系就是这种常见设计。编辑Unity Documentation+1
你可以把它拆成三个完全不同的动作:
overflow-visible!
Fragment 产生自己的 depth
│
├─ ZTest:拿自己的 depth 和 Depth Buffer 比
│
└─ ZWrite:要不要把自己的 depth 覆盖进 Depth Buffer
假设这个 cube 是同一个 Mesh、同一个 submesh、同一个 material/pass,那么通常就是一次 Draw,12 个三角形都走同一套 VS/FS、ZTest、Cull、Blend 等状态。Unity 的 Mesh 本身保存 index data,Input Assembler 会根据 index buffer 里的 index 组合出一个个 triangle。编辑Unity Documentation+1
你说"顺序是不是建模的时候定下来",可以沿着这个理解:
overflow-visible!
Blender / DCC
└─ 面、三角形存在某种排列
↓ Export
FBX
↓ Import
Unity Mesh
└─ Index Buffer
├─ triangle 0
├─ triangle 1
├─ triangle 2
├─ ...
└─ triangle 11
所以确实存在一个三角形列表顺序,它最初可以来自建模/导出阶段。
但 Unity 导入时还可能重新排。当前 Unity 官方 Optimize Mesh 默认 Everything,明确允许 Unity 为 GPU 性能重新排列 polygons、vertices 和 indices。也就是说,你在 Blender 里创建面的顺序,并不保证最后还是 GPU index buffer 里的顺序。编辑Unity Documentation
更关键的是,即使最终 index buffer 是:
overflow-visible!
triangle 0 = 前面
triangle 1 = 前面
triangle 2 = 右面
triangle 3 = 右面
triangle 4 = 后面
triangle 5 = 后面
...
也不要把它想成 GPU:
overflow-visible!
先完整画前面
→ 再完整画右面
→ 再完整画后面
一次 DrawIndexed 更接近:
overflow-visible!
Index Buffer
↓
12 triangles 被送进 graphics pipeline
↓
GPU 大规模并行处理
├─ 一批 vertices
├─ 一批 primitives
├─ rasterization
└─ 大量 fragment/pixel invocations
Unity 的 ZWrite On 本身严格来说是"允许当前 draw 更新当前绑定的 depth buffer",并不等于"创建一张 Depth Texture"。Unity 官方也明确区分了两者:Camera Depth Texture 有时直接来自原生 Z-buffer,有时由额外 pass 生成。编辑Unity Documentation+1
UE 的层级基本是:
overflow-visible!
Unity
Material / Shader
└ Pass
└ ZWrite On / Off
└ Depth-Stencil Render State
└ GPU Depth Buffer
UE 不是没有 ZWrite 这个底层能力,而是把它从普通 Material 作者手里收到了 Renderer/Pass 层。
UE 更像:
overflow-visible!
UE Material
├ Blend Mode = Opaque / Masked / Translucent
├ Disable Depth Test ← 只有部分情况暴露
├ Pixel Depth Offset ← 改写进去的 depth 值
└ Allow Custom Depth Writes ← 注意:CustomDepth,不是 SceneDepth
Renderer
└ Mesh Pass
├ Depth Prepass
├ Base Pass
└ Translucency Pass
↓
MeshPassProcessor / PSO
↓
FDepthStencilStateInitializerRHI
└ bEnableDepthWrite
↓
D3D12 Depth-Stencil State
也就是说,UE 不是没有 ZWrite 这个底层能力,而是把它从普通 Material 作者手里收到了 Renderer/Pass 层。
Epic 5.8 的 RHI API 其实直接证明了这一点。FDepthStencilStateInitializerRHI 里面就有:
overflow-visible!
C++
bool bEnableDepthWrite;
ECompareFunction DepthTest;
而 TStaticDepthStencilState 第一个模板参数也是:
overflow-visible!
C++
template<
bool bEnableDepthWrite,
ECompareFunction DepthTest,
...
>
Epic 的 5.8 Material Properties 文档里确实只有 Disable Depth Test 和 Allow Custom Depth Writes 这种接口,而没有通用的 Enable/Disable Scene Depth Write。并且官方明确说明 Disable Depth Test 只对 translucent 有意义;Allow Custom Depth Writes 则只是让 translucent material 编译额外 shader,以写入 Custom Depth 。编辑Epic Games Developers+1
所以千万不要把:
overflow-visible!
Allow Custom Depth Writes
理解成 UE 版本的:
overflow-visible!
ZWrite On
它们不是同一个 buffer:
overflow-visible!
Scene Depth
├ 主渲染管线使用
├ Early-Z / occlusion / deferred 等依赖它
└ SceneDepth 节点看到的是这一类场景深度信息
Custom Depth
├ 单独的一份可选 buffer
├ Primitive 可选择 Render CustomDepth Pass
└ Outline / stencil / 自定义效果常用
Unity 把很多固定功能渲染状态直接写在 ShaderLab Pass {} 里给你看;UE 的普通 Material Graph 则只是描述 material evaluation,真正这个 draw 的 DepthWrite = true/false 是 Renderer 在构造对应 Mesh Pass 的 PSO 时替你决定的。 UE 的 RHI 最终仍然明确存在 bEnableDepthWrite,所以到了 D3D12/RenderDoc 那层,你依然可以观察到真正的 Depth-Stencil State。编辑Epic Games Developers
先看 Custom Depth 到底特殊在哪。
overflow-visible!
SceneDepth
└ 主渲染管线的"真实可见性深度"
├ Early Z / Depth Pass
├ Base Pass depth test
├ 遮挡关系
├ 后续很多屏幕空间效果
└ Renderer 自己维护其一致性
CustomDepth
└ 额外的一张独立 Depth Texture
├ 只有 Render CustomDepth Pass = true 的 Primitive 才进去
├ 可以完全不影响 SceneDepth
├ 可以附带 Custom Stencil 0~255
├ 主要给 Post Process / masking / outline / 标记对象
└ 可以整张 texture 按需创建甚至完全关闭
真的是一条独立 Mesh Pass
UE 5.8 的 EMeshPass::Type 里面同时存在:
overflow-visible!
C++
DepthPass
BasePass
...
CustomDepth
...
CSMShadowDepth
VSMShadowDepth
所以 CustomDepth 在 Renderer 眼里就是单独的一类 pass。编辑Epic Games Developers
硬件/API 层实际上是:
overflow-visible!
GPU Raster Pipeline
└ Depth Buffer
└ 可用,但不是强制
D3D12
├ 可以创建 DSV
├ 可以不创建/不绑定 DSV
├ DepthEnable = true / false
└ DepthWriteMask = ALL / ZERO
overflow-visible!
GPU硬件
└ 提供 Depth Test / Depth Write / Hi-Z 能力
└ 不强制使用
Graphics API
└ 提供 Depth Resource / DSV / Depth State
└ 不强制绑定
UE Renderer
└ 决定主场景需要 SceneDepth
├ 创建资源
├ 安排谁写
├ 安排什么时候写
├ 安排什么时候 read-only
└ 后续 Pass 使用它
Material
└ 通常只是参与这个系统
└ 并不拥有 SceneDepth 本身
所以我之前使用 Renderer 这个词,真正想强调的正是:SceneDepth 的"必需性"应该归因到 UE 的 renderer 算法体系,而不能再往下归因成 GPU 的强制规则。
Daniil Trifonov
→ dah-nee-EEL TREE-fuh-nof
→ 中文粗略:「達尼伊爾・特里弗諾夫」
Daniil 重音在最後:da-ni-IL 。Trifonov 是俄語姓氏,重音在第一節,俄語詞尾的 v 實際會清化,接近 f ,所以會聽起來像 TREE-fuh-nof 。他的英語訪談也可以直接聽本人名字所處的語音環境。编辑YouTube+1
Liszt
→ 接近英文 list
→ 不是「利茲特」、也不是把 sz 和 t 全部分開念。
因為這是匈牙利姓氏,匈牙利語 sz 對應 /s/;整體就是 /list/,其中 i 是短音。编辑Forvo+1
Mephisto Waltz
→ muh-FISS-toh waltz
→ 「麼-FISS-偷 沃爾茨」
Mephisto 的重音在 phis ;它是 Mephistopheles(梅菲斯特/魔鬼)的簡稱。這首曲子的題材正是 Faust 傳說中的 Mephisto。编辑LA Phil
Unity Renderer / Render Pipeline └ 决定 ├ 这一时刻绑定哪张 Color Render Target ├ 这张 RT 是 Camera Color? ├ 中间纹理? ├ GBuffer? └ 哪个 Pass 在什么时候画进去 Unity ShaderLab Pass └ Blend SrcAlpha OneMinusSrcAlpha └ 声明: "这个 Draw 写当前绑定的 Render Target 时, 使用这种 Blend State"
梅菲斯特(Mephisto)通常是 Mephistopheles(梅菲斯托费勒斯) 的简称。它最核心的来源是"浮士德传说":一个人为了知识、力量、享乐等,与魔鬼进行交易;Mephistopheles 就是那个诱惑、帮助并试图毁掉浮士德的魔鬼角色。这个名字至少在 1587 年出版的德语《Faustbuch》中已经出现,后来马洛、尤其歌德的《浮士德》让它变成欧洲文化里极有辨识度的名字。编辑Wikipedia+1
所以它和"撒旦"不完全是一回事。更准确的关系大概是:
撒旦 / Devil
└ 一个更大的宗教、神话概念
└ Mephistopheles
└ 浮士德故事体系中具体的魔鬼角色
└ Mephisto
└ Mephistopheles 的短称
一个很容易误解的地方是:Mephistopheles 并不是《圣经》里一个古老的魔王本名。 它实际上是浮士德文学传统中形成的角色名,后来才不断被文学、歌剧、音乐、游戏借用。名字本身的词源甚至没有完全确定;传统上常解释成类似"厌恶光明 / 不爱光明者",但现代研究对具体构词仍有争议。编辑Wikipedia+1
这也解释了为什么会"见过很多次"。现在看到一个角色叫 Mephisto,通常不应该只理解成"名字听起来像恶魔",而是在调用一个相当具体的文化模板:
诱惑者 + 操纵者 + 魔鬼 + 聪明/讽刺 + 与某个人形成危险搭档关系。
而且这里的关联更直接。李斯特的《第一梅菲斯特圆舞曲》描绘的是诗人 Nikolaus Lenau 版本的《浮士德》故事:Mephistopheles 带 Faust 进入乡村酒馆,自己拿起小提琴,把舞会煽动到近乎疯狂的状态。 所以曲子里那种炫技、躁动、诱惑性很强的感觉,本来就是在"演"这个魔鬼。编辑LA Phil+1
Unity 官方现在仍然把 Blend 定义为一个修改 GPU render state 的 ShaderLab command,而且可以放在 Pass 或 SubShader 中。编辑Unity Documentation+1
这句话没有说:
overflow-visible!
我要混合 CameraColor
它只是在说:
overflow-visible!
无论当前这个 Draw 的 Color Render Target 是谁:
PS 输出 = Source
RT 已有值 = Destination
执行:
Source * SrcAlpha
+
Destination * (1-SrcAlpha)
Microsoft 对 D3D12 的定义也是这个结构:blend state 属于 graphics PSO,并由 Output Merger 对 pixel shader 输出和当前 render target 中已经存在的值进行组合。编辑Microsoft Learn+1
例如现在 Unity Renderer 做:
overflow-visible!
Renderer
│
├ 创建 CameraColorTexture
│
├ 把 CameraColorTexture 绑定为当前 Render Target
│
└ Draw TransparentObject
│
├ PS 输出
│ float4(1,0,0,0.5)
│
└ ShaderLab:
Blend SrcAlpha OneMinusSrcAlpha
讲扒和弦时,也会明确采用"一边播放音乐,一边弹钢琴核对"的办法,只是通常称为"核对""测试""排除",而不会叫"夹逼法"。编辑好和弦 - NiceChord.com+1
属于"绝对音高搜索"。随着扒谱能力提高,人一般会逐渐从:
一个音一个音上下试
变成:
先找到一个基准音 → 听音程关系 → 一串音直接推出
Unity Renderer 做:
overflow-visible!
Renderer
│
├ 创建 CameraColorTexture
│
├ 把 CameraColorTexture 绑定为当前 Render Target
│
└ Draw TransparentObject
│
├ PS 输出
│ float4(1,0,0,0.5)
│
└ ShaderLab:
Blend SrcAlpha OneMinusSrcAlpha
GPU 到 Output Merger 时:
overflow-visible!
Pixel Shader
↓
Source Color
↓
┌───────────────┐
│ Blend Hardware│ ← Blend State
└───────────────┘
↑
Destination Color
↑
当前绑定的 Color Render Target
↓
写回同一个 Render Target
这个 Destination 就是你现在在找的那个"缓冲区里的已有像素"。
Microsoft 的 D3D12 文档甚至直接把这两个量定义成:
overflow-visible!
Source
= Pixel Shader 输出
Destination
= Render Target 中已经存在的值
然后 Output Merger 做 read-modify-write。编辑Microsoft Learn+1

现代 Unity renderer 可能有:
overflow-visible!
Color Render Targets
├ Camera Color
├ GBuffer0
├ GBuffer1
├ GBuffer2
├ Lighting accumulation target
├ Transparent intermediate target
├ Post-process intermediate
└ 最后 SwapChain Back Buffer
所以:
Transparent intermediate target
可以理解成:
overflow-visible!
Transparent
└─ intermediate target
└─ 给 Transparent/Translucency 相关处理使用的
中间 Render Target
也就是:
overflow-visible!
前面的渲染结果
↓
Transparent pass
↓
[ Transparent intermediate target ] ← 暂时装这里
↓
之后的 composite / post process / ...
↓
最终画面
重点是这个 target 不是最终输出目标,只是管线走到这里的时候暂时产生的一份结果。
Post-process intermediate
就是:
overflow-visible!
Post-process
└─ intermediate [resource / texture / target]
└─ 后处理流程中的中间结果
例如:
overflow-visible!
Scene Color
↓
Bloom
↓
[Intermediate A]
↓
Tonemap
↓
[Intermediate B]
↓
Color grading
↓
Final output
A、B 都可以叫 intermediate。
UE 文档甚至专门说 User Scene Texture 可以作为 user-defined intermediate texture,一个 post-process material 写进去,后面的 post-process material 再读出来,用于 multi-pass blur 之类的效果。编辑Epic Games Developers
所以这个词最好在脑中固定成:
intermediate = 中途产生、供下一阶段继续使用的


ROP 不是某一个单独"写颜色"的硬件,而是后端 Raster Operation 阶段的总称;CROP 和 ZROP 是它下面两类功能单元。 NVIDIA 2026 年 Nsight Graphics 的架构文档就是这样拆的。编辑NVIDIA Docs+1
可以直接记成:
overflow-visible!
ROP = Raster Operation
└─ ZROP = Z / Depth Raster Operation
├─ Depth Test
├─ Stencil Test
└─ Depth / Stencil Buffer Update
└─ CROP = Color Raster Operation
├─ Color Blending
└─ Render Target Color Update
其中 Z 是传统 3D 图形学里的 Z-depth(深度) ,所以 ZROP 不是一种奇怪的新 ROP,而是"处理 Z/Depth + Stencil 的那半边 ROP"。NVIDIA 当前文档的原话定义就是:ZROP 负责 depth test、stencil test 和 depth/stencil buffer 更新;CROP 负责最终 color blend 和 render-target 更新。编辑NVIDIA Docs
更重要的是,别把 ROP 理解成 Rasterizer。它们处于流水线的不同位置:
overflow-visible!
Triangle
↓
RASTER
├─ 把 triangle 转成 fragments / samples
└─ coverage 等
↓
PROP = Pre-ROP
├─ 安排 Pixel Shader / Depth Test / Color 的顺序
├─ Early-Z
└─ Late-Z
↓
ROP
├─ ZROP
│ └─ 最终 Depth / Stencil 操作
│
└─ CROP
└─ 最终 Color / Blend / Render Target 写入
NVIDIA 明确把 RASTER、PROP、ZROP、CROP 列成四个不同的 Screen Pipe 硬件区域。RASTER 产生 fragment/sample;PROP 管理这些数据在 pixel shading、depth testing、color blending 之间的流动;最后进入 ZROP/CROP。编辑NVIDIA Docs
例如一个 fragment 的 Pixel Shader 算出了:
overflow-visible!
Shader output color = (1.0, 0.2, 0.1, 0.5)
Depth = 0.37
后面大致可以发生:
overflow-visible!
ZROP
├─ 读当前 Depth Buffer,例如 0.52
├─ 比较 0.37 < 0.52
├─ PASS
└─ 必要时把 Depth 改成 0.37
CROP
├─ 取得 Pixel Shader 输出颜色
├─ 取得 Render Target 原来的颜色
├─ 按 Blend State 做 Src/Dst blending
└─ 更新 Render Target
所以如果现在是在 Nsight Graphics / Nsight Perf SDK 里看到:
overflow-visible!
crop__...
zrop__...
rop__...
这些名字基本就是性能计数器的硬件前缀。官方 Perf SDK 甚至直接定义:
overflow-visible!
ROP
└─ Raster Operation Stage,包含 ZROP 和 CROP
ZROP
└─ final depth-test / stencil-test / related buffer updates
CROP
└─ final color blend into bound render targets
这里还有一个特别容易混的东西:ZCULL ≠ ZROP。
overflow-visible!
Depth 相关硬件
├─ ZCULL
│ └─ 更早、更粗粒度地淘汰肯定不可见的东西
│
└─ ZROP
└─ 最终精确的 depth/stencil test + buffer update
NVIDIA Perf SDK 把 ZCULL 放在 RASTER 这一侧,并描述为 coarse-resolution depth testing;而 ZROP 是 ROP 后端的最终 depth/stencil 操作。编辑NVIDIA Developer
早期 NVIDIA GPU Gems 也是把 framebuffer 后端称作 raster operations (ROP) ,包括 depth/stencil 读写与比较、color 读写以及 blending。编辑NVIDIA Developer
因此三个词最短的语义就是:
ROP = 最终像素状态处理这一整个后端。
ZROP = 决定这个 sample 在深度/模板上能不能留下以及更新 Z/S。
CROP = 决定留下来的颜色怎样和 Render Target 合并并写进去。
https://fyitester.com/zh-CN/textile-color-theory-and-color-difference-evaluation/


你看到的软件表达是:
overflow-visible!
Result =
Src × SrcFactor
+
Dst × DstFactor
但硬件真正面对的是:
overflow-visible!
Pixel Shader
↓
Src
当前 Render Target
↓
找到同一 pixel/sample 的旧值
↓
Dst
↓
格式解释 / sample / channel mask
↓
Blend
↓
写回 Render Target
也就是:
overflow-visible!
READ
↓
MODIFY
↓
WRITE
Microsoft 对 D3D12 graphics pipeline 就直接称 Output Merger 通常是针对 Render Target 和 Depth-Stencil View 的 read-modify-write 操作。编辑Microsoft Learn
所以真正昂贵和特殊的是:
overflow-visible!
普通 ALU
│
算 Src * A ...
│
▼
┌─────────────────┐
│ CROP / ROP │
├─────────────────┤
│ 找 RT 旧值 │
│ 保证访问顺序 │
│ Blend │
│ Color mask │
│ sample handling │
│ 更新 RT │
│ 配合缓存/压缩 │
└─────────────────┘
│
▼
Color Target
特别是 Dst 这东西决定了 Blend 不能简单理解成:
"丢给 CUDA Core / shader ALU 算一下不就好了?"
假设两个 fragments 都要写 (100,100):
overflow-visible!
Fragment A
读取 Dst = 0
计算 → 0.5
Fragment B
读取 Dst = ?
B 到底应该看到:
overflow-visible!
0
还是:
overflow-visible!
A 写进去后的 0.5
这已经不只是:
overflow-visible!
a * b + c
的问题了。
而是:
overflow-visible!
同一 Render Target address
↓
读取
↓
修改
↓
写回
↓
下一个 fragment 必须遵守 API 定义的顺序
NVIDIA 所以专门有 PROP 来协调这一段,官方明确说它负责维护 pixel shading、depth testing 和 color blending 的 API ordering。编辑NVIDIA Docs
Depth 更加适合做固定硬件。
因为 Depth 最核心的事情甚至比 blend 更简单:
overflow-visible!
incomingDepth < storedDepth ?
可能就是一次比较。
但它发生的位置极其重要:
overflow-visible!
Rasterization
↓
Depth Test
┌──┴───────────────┐
失败 通过
↓ ↓
整个 fragment Pixel Shader
可以直接杀掉
也就是说,硬件做一个极便宜的比较,可能帮你避免后面几十、几百条 shader instructions。
因此才会进一步发展成:
overflow-visible!
Depth hardware
├ Early-Z
├ Late-Z
├ ZROP
├ Stencil
├ Hierarchical-Z / Hi-Z
├ depth compression
└ fast clear
AMD 现在也仍然有针对 depth target 的专门 fast-clear 路径,并明确指出其 fast clear 可以远快于普通地把整个 target 填一遍。编辑GPUOpen
所以这里可以得到一个非常稳定的硬件设计判断:
"算法简单"恰恰不会降低专用硬件的价值。只要它足够固定、出现频率极高、位于关键数据流节点,而且硬件化以后能避免通用 shader/core 和内存系统承担大量重复工作,它就是极好的固定功能硬件候选。
复杂但高频 └ RT Core └ BVH traversal / intersection 简单但极高频 ├ Texture filtering hardware ├ Rasterizer ├ ZROP └ CROP / Blend
Blend 尤其特殊的一点就是:它位于 Shader ALU 和最终 Render Target 存储之间。它真正值钱的不是那几个乘加,而是把"旧像素读取 → 顺序保证 → 混合 → 写回"这一整个极高吞吐的尾端路径硬件化。 编辑NVIDIA Docs+1
这也是为什么 ROP 这个词比单纯叫"Blend Unit"更能反映它实际在干什么:它本来就是 Raster Operations / 像素最终落到 render target 前后的那组操作。
-
如果所谓"外包"实际是劳务派遣:不能全做。
└ 《劳动合同法》第66条规定,劳务派遣只是补充用工,只能用于"临时性、辅助性、替代性"岗位。编辑SAMR+1
└ 《劳务派遣暂行规定》第4条更直接:被派遣劳动者不得超过用工总量的10% ;这里的"用工总量"=直接签劳动合同的人数+派遣人数。编辑Government of China+1
└ 所以假设一个法律意义上的"用工单位"有100个人,正常情况下不能搞成"20正编+80劳务派遣"。
-
如果是真正的业务外包:没有这个10%限制。
└ 例如游戏公司把"30套角色模型交付""某地图美术资产制作"整个业务包给另一家公司。
└ 发包公司管的是交付物、质量、期限、合同金额 ;至于承包公司的美术几点上班、谁做哪个模型、绩效怎么考,是承包公司自己管理。
└ 这种情况下,那些人本来就不是你的"用工人数",因此不是拿"正编90%+外包10%"这么算。
可以把"做300个道具""做50套动作""场景植被资产生产"外包出去;但如果连"游戏到底长什么样、技术路线是什么、资产规范怎么定、今天需求为什么改、什么算验收合格"的人都外包掉了,甲方自己就逐渐失去定义任务和验收任务的能力。
Screen Pipe ├ PROP │ └ 协调 Pixel Shader / Depth / Blend 的执行顺序 │ ├ ZROP │ ├ Depth Test │ ├ Stencil Test │ └ Depth/Stencil Buffer Update │ └ CROP ├ Color Blend └ Render Target Update
为什么 Depth 和 Stencil 放一起其实很好理解,因为 API 层本身也长期把它们组合成一个体系:
overflow-visible!
D3D12_DEPTH_STENCIL_DESC
├ DepthEnable
├ DepthWriteMask
├ DepthFunc
├ StencilEnable
├ FrontFace
└ BackFace
资源格式也经常就是:
overflow-visible!
D24_UNORM_S8_UINT
24 bit Depth
+
8 bit Stencil
所以一个 fragment 到这里时,很自然就是:
overflow-visible!
fragment/sample
↓
ZROP
├ depth compare
├ stencil compare
├ stencil op
├ depth update
└ stencil update
Screen 绝对不是"只有最终要显示到显示器时才工作" 。这里的 screen 是 screen-space / raster-space 的意思。
RASTER 接收 World Pipe 出来的 primitive,然后产生 pixels/fragments 和 coverage samples,后面的 PROP、Pixel Shader、ROP 再处理这些东西。编辑NVIDIA Docs
所以 Screen Pipe 真正强调的是:
overflow-visible!
进入它之前
└ 我处理的是 triangle / vertex / primitive
└ "世界中的几何"
进入它之后
└ 我处理的是
├ 屏幕坐标 x,y
├ fragment
├ sample
├ coverage
├ depth
└ render-target pixel
raster 是一个历史上更宽的词。
Rasterizer 是:
overflow-visible!
primitive
↓
哪些 pixel/sample 被覆盖?
↓
产生 fragments / coverage
而传统所谓的 Raster Operations 是:
overflow-visible!
这些 rasterized fragments 已经产生了
↓
对 framebuffer 做最后操作
├ Depth
├ Stencil
├ Blend
└ Color write
所以:
overflow-visible!
Rasterizer
≠ Raster Operations
虽然两个名字都带 Raster。
NVIDIA 很早以前的 GPU Gems 就已经把管线末端称为 raster operations (ROP),并明确说这里负责 depth/stencil read/write、depth/stencil comparisons、color read/write 和 alpha blending。编辑NVIDIA Developer
因此这个名字的历史语义其实是:
overflow-visible!
Rasterization pipeline
├ rasterization
│ └ geometry → fragments
│
└ raster operations
└ fragments → framebuffer
这两个概念从很早期的固定功能图形硬件时代就一起存在,于是 ROP 这个名字被留下来了。
现在 NVIDIA 又进一步把以前笼统叫 ROP 的东西拆成:
overflow-visible!
ZROP
└ Z / Stencil Raster Operations
CROP
└ Color Raster Operations
当前 NVIDIA 文档就是这样定义:ZROP 做 depth/stencil test 和 buffer update,CROP 做 final color blend 和 render-target update。编辑NVIDIA Docs
所以你现在可以形成一个非常稳定的名字模型:
overflow-visible!
WORLD PIPE
处理:
triangle / vertex / primitive
↓ rasterization 是分界
SCREEN PIPE
处理:
fragment / sample / pixel
├ RASTER
│ └ primitive → fragments
│
├ PROP
│ └ 协调这些 fragments 后续怎么走
│
├ ZROP
│ └ fragments 对 depth/stencil raster buffer 做操作
│
└ CROP
└ fragments 对 color raster buffer 做操作
传统多 Pass 下采样的瓶颈在于:纹理越小,GPU 耗时中 DrawCall 固定成本占比越高,到了 2×2 这种尺寸时,绝大部分时间都在等调度而不是算像素。SPD 用 compute shader + 原子计数器把所有层级打包在一次调度里完成,消除了小尺寸纹理阶段的 GPU 浪费。
SPD 在 UE 里最大的价值就是把 Bloom 的多 Pass 下采样压缩成单次 Compute Shader 调用,省 DrawCall、省时间,尤其在大分辨率纹理上效果显著。
核心算法和 UE 内部集成的 FidelityFX-SPD 是一致的。
RenderFormer是神经渲染领域的一个代表技术,原文提到它由微软提出,属于数据驱动的渲染方式。
原始數學 parameter space 和「人感受到的變化空間」不一定一致,所以要 remap。Disney 還把 normalized specular 控制映射到約 [0, 0.08] 的 normal-incidence specular reflectance,而不是要求 artist 直接操縱 IOR;目的也是把物理模型包成穩定、合理、容易操作的區域。编辑Disney Animation Media
-
production = 產生、生成、製作某個輸出
-
heuristic = 啟發式/經驗性捷徑,也就是「不完整求解整個問題,而用一套通常夠用的簡化規則」
例如在心理語言學裡,production 指的是「語言產出」:人準備說一句話時,並不會把所有可能句法結構都完整計算一次,而可能依照一些簡單規則選擇一句容易生成的表達。1996 年 Gibson 等人的研究就把某些語料中的句法頻率解釋為由一套獨立於 comprehension system 的 production heuristic 所造成。编辑PubMed+1
可以畫成:
語言系統
├ comprehension
│ └ 聽到/看到一句話 → 怎麼解析它
│
└ production
└ 想表達一個意思 → 怎麼生成一句話
└ production heuristic
= 生成時採用的簡化選擇規則
heuristic:
「通常這樣做效果不錯,那先這樣選。」
而 production heuristic 就再多限定一層:
「在產生某個東西的階段,用什麼捷徑來決定怎麼產生。」
但在製造業,production heuristic 又真的可以是「生產啟發式」
例如工廠有 100 個訂單,要決定下一個做哪個。
可以完整最佳化:
算出全部排列 → 找全域最優解
也可以用:
急單先做
短工時先做
庫存低的先做
這些就是 production heuristics。
technical lineage 的核心其實在 lineage,不是 technical。
lineage 原本是「血統、世系、譜系」:
grandfather → father → son
一代從上一代而來
放到技術裡,就把「生物血統」這個關係借過來:
technology A
└ technology B developed from A
└ technology C inherited ideas from B
這條「誰從誰演變/繼承而來」的線,就是 technical lineage。
主流 rendering technique 基本都应该存在可追溯的 technical lineage。
Panner 輸出一組會隨時間變化的 UV texture coordinates,用來造成 texture 在 U/V 方向持續移動的效果。它不是在移動 texture asset,也不是在移動 mesh。
可以理解成:
overflow-visible!
Panner
└ 操作的東西:UV coordinates
├ U / X direction → Speed X
└ V / Y direction → Speed Y
└ 隨 Time 持續增加 offset
概念上近似:
overflow-visible!
UV_out = UV_in + Time × Speed
然後 Texture Sample 用這組變化後的 UV 去採樣,所以畫面看起來像 texture 一直在「流」。Epic 也明確說它的 Coordinate 是 base UV texture coordinates,Time 決定當前的 panning position。编辑Epic Games Developers+1
攝影:
The camera pans left.
不是:
相機整台往左搬。
通常表示:
overflow-visible!
camera stays roughly here
└ viewing direction sweeps horizontally
← ← ←
所以攝影裡的:
-
pan left -
pan right -
panning shot
都非常正常。
❌
I panned the cube to the left.
如果意思是「在 Unreal 裡把 Cube 的 Location X 往左移」,pan 很怪。
應該是:
I moved the cube to the left.
I translated the cube along the X axis.
因為 pan 通常不是 object transform。
但:
I panned the camera to the left.
正常。
因為說的是 camera viewing motion。
還有一個很重要的 Unreal 語義差異:
The Panner moves the texture.
教程裡經常這樣說,作為視覺描述沒大問題;Epic 自己的文檔也會說讓 texture pan。编辑Epic Games Developers
但如果在講實現機制,這句就不夠精確。
更精確是:
The Panner offsets the UV coordinates over time.
因為實際關係是:
overflow-visible!
Texture
└ 沒有被搬動
Mesh
└ 沒有被搬動
Panner
└ UV coordinates 發生 offset
└ Texture Sample 改變採樣位置
└ 視覺上 texture 像在滑動
所以 Panner 這個名字其實是站在視覺效果層 命名的,不是站在底層數學 operation 命名的。Epic 的 Material Expression 文檔也說 Material Expression 本質上是對輸入執行小段 HLSL 並輸出結果;Panner 的輸出就是 UV coordinates。编辑Epic Games Developers+1
讓 Scene View Extension 在 post-processing 開始時訂閱特定 post-processing pass event。可插的位置包括:
overflow-visible!
BeforeDOF
AfterDOF
TranslucencyAfterDOF
SSRInput
ReplacingTonemapper
MotionBlur
Tonemap
FXAA
SMAA
...
Epic 的 RDG 官方文档明确说,高层 rendering code 使用 RDG 记录 rendering commands,然后 RDG 根据资源关系建立 dependency、安排 transition、pass culling、parallel command recording 等;官方还直接建议 fullscreen pixel shader 使用 FPixelShaderUtils::AddFullscreenPass,compute shader 使用 FComputeShaderUtils::AddPass。编辑Epic Games Developers+2编辑Epic Games Developers+2
所以你在 RenderDoc 里面最终真的应该能看到类似:
overflow-visible!
...
Tonemap?
MyPaperPass
Draw(...)
...
或者 compute 版本:
overflow-visible!
MyPaperComputePass
Dispatch(...)
而不是 Material Graph 里某个节点。
overflow-visible!
ScriptableRendererFeature
↓
AddRenderPasses()
↓
renderer.EnqueuePass(...)
↓
ScriptableRenderPass
Unity 官方甚至直接描述 ScriptableRendererFeature 是用来「inject render passes into the renderer」。编辑Unity Documentation+1
所以概念上:
overflow-visible!
Unity URP
ScriptableRendererFeature
└─ ScriptableRenderPass
UE
SceneViewExtension / Renderer hook
└─ FRDGBuilder
└─ RDG Pass
可以放在你脑子里的同一个位置。
但不要认为 API 一一对应。
UE 没有把整个 Deferred Renderer 做成一个跟 URP Add Renderer Feature 完全相同的 Inspector 插槽体系。
UE 更接近:
overflow-visible!
插件
↓
找到 Renderer 暴露的 hook
↓
拿到 FRDGBuilder
↓
插入 RDG Pass
你真正想找的 UV 原始入口只有:
overflow-visible!
Texture Coordinate
官方名稱是 TextureCoordinate expression,它直接輸出 mesh 的 UV,輸出類型就是 float2。
在 UE 裡插入一個真正參與本幀 GPU 工作、讀寫渲染資源的 RDG Pass,當然可以算「改動渲染管線」。
如果在 3D / shader / UV 語境裡看到 Ellipsoidal UV,通常可以拆成:
ellipsoidal = 橢球狀的、以橢球為基準的
UV = 二維參數座標
所以它大致就是:
把 3D 表面按照「橢球」這個參考形狀,轉換成 UV 座標。
關鍵不是「UV 長得像橢圓」,而是 UV 的計算假設那個物體/投影域接近 ellipsoid。
可以把層級看成:
overflow-visible!
UV mapping / parameterization
└ 需要把 3D surface → 2D coordinates (u,v)
├ Planar
├ Cylindrical
├ Spherical
└ Ellipsoidal
└ 把 reference shape 從 sphere 換成 ellipsoid
ellipsoid 本身就是 sphere 經過不同方向縮放後的形狀,例如:
a2x2+b2y2+c2z2=1
當 a=b=c,它退化成 sphere;當三個軸長不同,就是 ellipsoid。
"I like my toast done on one side" 就是:
「我喜歡吐司只烤一面。」
這裡最值得注意的是 done。
done 在食物語境裡不是「完成了某件事」,而是:
cooked / heated / prepared 到某種程度
例如:
-
How do you like your steak done?= 牛排想要幾分熟? -
well-done= 全熟 -
The potatoes aren't done yet.= 馬鈴薯還沒熟
所以:
my toast done on one side
└ my toast = 我的吐司
└ done = 烤好、烤到想要的程度
└ on one side = 只在其中一面
這句出自 Sting 的 Englishman in New York ;歌曲借 Quentin Crisp 這個英國人在紐約的形象來列舉一些帶有英國身份感、個人習慣色彩的特徵。值得修正一個常見說法:「英國人傳統上都只烤一面」並不可靠 ;英國人自己對此也大量表示這不是普遍習慣,所以更適合把它讀成具體人物的偏好,而不是英國人的標準吃法。编辑Reddit+1
I like my eggs done this way.
不是「我喜歡我的蛋做某件事」,而是 「我喜歡蛋被做成這種狀態。」
假設一個完全平的三角形:
N=(0,0,1)
那下面全部都可以是合法的 tangent:
T=(1,0,0) T=(0,1,0)
甚至:
T=(cosθ,sinθ,0)
因為它們全部滿足:
T⋅N=0
也就是都躺在表面上。
所以你說:
「純頂點模型有一個 normal 已經是極限了,tangent 到底哪裡冒出來?」
這個疑惑其實非常合理。答案是:
單靠 Position + Normal,Tangent 根本算不出唯一答案。你還需要另一份資訊。通常這份資訊就是 UV。
Modesty, propriety can lead to notoriety
-
modesty= 謙遜、不張揚 -
propriety= 得體、合乎禮節/分寸 -
notoriety= 出名、聲名在外,但這個詞通常帶一點「因為某種異常或負面原因而出名」的色彩
所以這句故意製造矛盾:
謙遜、守分寸,竟然也可能讓人「出了名」。
因為如果周圍的人都不這樣,那麼「不張揚」本身反而變得顯眼。
因為如果周圍的人都不這樣,那麼「不張揚」本身反而變得顯眼。
You could end up as the only one
end up as = 最後變成/最後落到某種狀態。
所以:
最後可能只剩下這麼一個人還這樣。
這裡不是字面上的「全世界唯一一個」,而是誇張地說這些特質變得罕見。
Gentleness, sobriety are rare in this society
-
gentleness= 溫和、柔和、不粗暴 -
sobriety這裡比較有意思。
sober 最直接是「沒有喝醉」,所以 sobriety 可以是「清醒、節制、不酗酒」。
Unity 現在的官方文件直接把 vertex tangent 定義成:
沿著表面 texture U 軸的方向。
而 Mesh.RecalculateTangents() 明確使用 vertex positions、normals、texture coordinates 來算 tangent。编辑docs.unity3d.com+1
這一下就把整件事接起來了。
使它變成一個很漂亮的區分:
avoid ≠ flee
避開衝突可以是判斷;
run 描寫的則更像被恐懼奪走行動節奏。
而且後面馬上接:
Manners maketh man
整段一直在定義的 gentleman 並不是階級身份,而是人在壓力下仍維持某種行為形式。
通常就是這樣:
B=N×T
也就是:
Bitangent = cross(Normal, Tangent)
但工程裡通常還要再乘一個「方向符號」:
B=(N×T)⋅s,s∈{−1,+1}
例如 glTF 的標準就是:
B = cross(N, T.xyz) * T.w
其中 T.w 存的是 tangent basis 的 handedness,用來處理 UV 鏡像等情況。编辑Khronos Registry+1
所以你現在可以直接把 TBN 想成:
overflow-visible!
N
↑
|
•────→ T
B = N × T
也就是說,真正需要「額外決定」的通常只有 T;一旦 N + T + handedness 已知,B 基本就被推導出來了,不需要 mesh 再額外存一整條 Bitangent。Direct3D/HLSL 本身雖然歷史上有 BINORMAL semantic,但 shader pipeline 並不要求你一定存它;這些 vertex attributes 本質上是你提供給 shader 的資料。编辑Microsoft Learn
NVIDIA / AMD / Apple
└ 設計晶片
└ TSMC / Samsung / Intel
└ 真正蓋 Fab、生產晶片
└ AMAT / ASML / Lam / TEL / KLA
└ 賣製造晶片的機器
AMAT 官方把自己定位為 materials engineering(材料工程)設備公司,而且它的特點就是產品線非常廣。编辑SEC
AMAT 到底做什麼?
一顆先進晶片不是「刻一下」就做出來,而是在 wafer 上反覆進行數百甚至上千個製程步驟。
AMAT 涵蓋很多步驟:
Applied Materials
└ Semiconductor Systems
├ 薄膜沉積 Deposition
│ ├ PVD
│ ├ CVD
│ └ ALD 等
├ Etch 蝕刻
├ Ion Implantation 離子植入
├ CMP 化學機械研磨
├ Patterning 相關製程
├ Metrology 量測
├ Inspection / eBeam 檢測
├ Transistor / Interconnect 製程
└ Advanced Packaging 先進封裝
└ Applied Global Services(AGS)
├ 維修
├ 零件
├ 設備升級
└ Fab 自動化軟體
官方也明確說,它擁有半導體資本設備產業中「最全面的產品組合」之一。编辑SEC
所以 AMAT 和 ASML 最大的差異非常重要:
ASML = 一個極端強大的核心領域:Lithography 光刻。
AMAT = 很多製程步驟都有設備。
例如台積電建一座先進 Fab,並不是「買 ASML 就完成了」。ASML 把 pattern 曝光到晶圓上之後,還需要沉積材料、蝕刻、填金屬、CMP、量測、檢查......這裡大量都是 AMAT、Lam、TEL、KLA 的生意。
AMAT 最大的競爭對手
不能簡單說「AMAT 對手就是 ASML」,因為半導體設備其實分很多市場。
最直接的大型競爭格局大致是:
| 公司 | 最主要強項 | 和 AMAT 的關係 |
|---|---|---|
| Lam Research (LRCX) | Etch、Deposition | 最直接的大型競爭者之一 |
| Tokyo Electron (TEL) | Etch、Deposition、Coater/Developer、清洗等 | 非常直接,而且產品也很廣 |
| KLA (KLAC) | Inspection、Metrology、Process Control | AMAT 檢測/量測業務的直接競爭者 |
| ASM International | ALD、Epitaxy、Deposition | AMAT 沉積領域競爭者 |
| ASML | DUV/EUV Lithography | 同一設備產業巨頭,但大部分產品不是正面重疊 |
| 中國 AMEC、NAURA 等 | Etch、Deposition 等 | 中國市場日益重要的競爭者 |
尤其 Lam Research 可以記住。
Lam 最近一季做到 $6.72B revenue ,主要設備正是 deposition、etch 等,和 AMAT 高度重疊。编辑Lam Research Newsroom
KLA 則不一樣:
AMAT
└ 「加工 wafer」是核心能力
KLA
└ 「看看你加工得對不對」是核心能力
├ defect inspection
├ metrology
└ yield / process control
KLA FY2026 revenue 已達 $13.58B 。编辑KLA Corporation
而 ASML 更特殊:
ASML
└ 光刻
└ 特別是 EUV
因此 ASML 與 AMAT 都吃半導體資本支出,但不能把它們看成可互換的設備供應商。ASML 2025 年營收為 €32.7B 。编辑ASML
應該是 revenue /ˈrev.ə.nuː/。它確實是經濟、會計裡非常核心的詞:營收、收入 。例如公司賣商品收到的總額,扣成本以前通常先叫 revenue。Merriam-Webster 也把它定義為某個來源產生的總收入。编辑Merriam-Webster
這個詞其實很好記,因為它的詞源不是什麼抽象的「財務」概念,而是:
revenue
└ Old French revenue = 「回來、返回之物」
└ revenir = come back
├ re- = back
└ venir = come
所以最原始的空間模型其實是:
東西出去 → 某些東西「回來」 → return → 收回來的錢 → revenue
15 世紀早期英語裡就已經有「來自土地、財產的收入」這個意思。编辑Etymology Online
因此它跟 return 在概念上真的很近,不是偶然。甚至 revenue 的歷史語義可以粗略理解成「回流進來的東西」。
至於「總讓人想到小徑」,很可能是聲音/字形把它和另一組詞混在一起了:
-
avenue /ˈæv.ə.njuː/:大道、林蔭道,也可以泛指途徑
-
revenue /ˈrev.ə.njuː/:營收
-
ravine /rəˈviːn/:山溝、峽谷
尤其 avenue ↔ revenue 幾乎只差前面的 /æv/ 和 /rev/,尾部都是很醒目的 -venue / -ənuː/,所以很容易產生「道路、小徑」的聯想。
有個更有意思的地方:avenue 和 revenue 還真的有很遠的詞源親緣 。avenue 原先也是法語中「arrival / approach,來到、接近」的概念,而 revenue 是「come back」。也就是兩個詞底下都有某種 come / movement 的空間模型。現在一個凝固成「通往某處的道路」,另一個凝固成「流回來的錢」。
半導體設備五大類巨頭
└ ASML → 光刻王
└ Applied Materials / AMAT → 綜合材料製程設備王,產品最廣
└ Lam Research → 蝕刻 + 沉積強者
└ Tokyo Electron → 日本綜合設備巨頭
└ KLA → 檢測、量測、良率控制王
AI demand
└ TSMC / Samsung / SK Hynix / Micron 等決定擴產
└ WFE(Wafer Fab Equipment)資本支出增加
└ AMAT / Lam / TEL / KLA 訂單增加
一旦 N + T + handedness 已知,B 基本就被推導出來了,不需要 mesh 再額外存一整條 Bitangent。
渲染方程是1986年由James Kajiya提出的计算机图形学核心积分方程,是所有全局照明方法(光线跟踪、路径跟踪等)的理论基础,系统描述了光线在场景中的分布与传输规律,所有主流光照模型如Lambert、Phong、Blinn-Phong乃至PBR都是它的特例。
James T. Kajiya 在 1986 年發表了《The Rendering Equation》,提出著名的渲染方程。論文自己說,它提出一個積分方程,用來統一/generalize 當時多種已知 rendering algorithms。编辑ACM Digital Library+1
三年後,1989 年,他又和 Timothy L. Kay 發表:
Kajiya & Kay --- "Rendering Fur with Three Dimensional Textures"
這才是我們前面一直說的 Kajiya--Kay hair/fur shading model 。编辑ACM Digital Library+1
所以時間線是:
overflow-visible!
James T. Kajiya
1986
└─ The Rendering Equation
└─ 全局光傳輸的框架
└─ ∫ incoming light × scattering ...
1989
└─ Kajiya + Timothy Kay
└─ Rendering Fur with Three Dimensional Textures
└─ Kajiya--Kay
└─ 毛髮 / 纖維的 phenomenological shading model
記住的「Kajiya 是頭髮高光」其實是:
Kajiya--Kay
不是單獨:
Kajiya rendering equation。
Rendering Equation 是外面的 transport framework。
Cook--Torrance、Lambert 這些主要是在回答裡面的:
f
怎麼定義。
而 Path Tracing 又是另一層。
它不是另一個 f:
overflow-visible!
Rendering Equation
↓
有一個積分
↓
這積分通常沒辦法解析求完
↓
怎麼估計它?
Monte Carlo
↓
Path Tracing
所以更穩定的分類其實是:
overflow-visible!
光傳輸問題
└─ Rendering Equation
│
├─ Material scattering
│ └─ f
│ ├─ Lambert
│ ├─ Cook--Torrance
│ ├─ Hair scattering model
│ └─ ...
│
└─ 如何求這個積分
├─ Path Tracing
├─ Bidirectional Path Tracing
├─ Photon Mapping 類方法
└─ 各種近似
https://www.cs.cmu.edu/afs/cs/academic/class/15462-s13/www/lec_slides/86kajiyaRenderingEquation.pdf
L=Le+TL
可以展開成:
L=Le+TLe+T2Le+T3Le+⋯
Veach 後來把這個 operator 形式寫得非常漂亮:它就是 emitted light,加一次 bounce、兩次 bounce、三次 bounce......一直加下去。编辑Computer Science at UCSD+1
突然間:
overflow-visible!
Le
└─ camera 直接看到 light
TLe
└─ 1 bounce
T²Le
└─ 2 bounce
T³Le
└─ 3 bounce
...
可以精確問一個 renderer:
overflow-visible!
Whitted Ray Tracing 到底漏了什麼?
Radiosity 到底假設了什麼?
Path Tracing 到底在估計什麼?
Bidirectional Path Tracing 為什麼也是同一答案?
第一次給 CG 一個統一的 target:不管你發明什麼 renderer,你都可以問它究竟如何近似同一個 light-transport solution。
Epic 5.8 官方定义得很直接:SkyAtmosphereLightDirection 根据 Light Index 获取 Atmospheric Directional Light 的方向,这个 index 必须对应 Directional Light 上的 Atmosphere Sun Light Index。UE 当前只支持两盏 atmosphere lights,通常是 Sun=0、Moon=1。编辑Epic Games Developers+1
所以如果你的场景就是:
overflow-visible!
DirectionalLight_Sun
└─ Atmosphere Sun Light = true
└─ Atmosphere Sun Light Index = 0
Material 里:
overflow-visible!
SkyAtmosphereLightDirection
└─ Light Index = 0
那么根本不需要:
overflow-visible!
Event Tick
→ GetActorRotation
→ GetForwardVector
→ Set Vector Parameter Value
→ MPC
Renderer 自己已经知道这盏太阳灯的方向。你旋转太阳时,这个 Material Expression 跟着变。编辑Epic Games Developers
但它有一个非常重要的限制:
它不是「任意 Directional Light Direction」节点。
它只能访问被登记进 Sky Atmosphere 系统的那两盏 atmospheric directional lights。
不算怪,但它確實很容易讓人第一次看到時誤以為「Actor 裡存了一個 Forward Vector」。
Epic 在 UE 5.7 對 Get Actor Forward Vector 的官方定義就是:
Get the forward (X) vector (length 1.0) from this Actor, in world space. 编辑Epic Games Developers+1
這裡 Get 想表達的是「給我這個 Actor 當前的 Forward Vector」,而不是「Get 一個已經儲存在 Actor 裡的變數」。
所以真正值得區分的是這兩個名字:
Get Actor Forward Vector
└ Target = Actor
└ Rotation 已經隱含在裡面
Get Forward Vector
└ Input = Rotator
└ 明確表示「從這個 Rotation 算出 Forward」
如果讓我從純 API 語義來命名,GetActorForwardVector() 還算自然;如果叫 CalculateActorForwardVector(),反而會把一個非常便宜、非常基礎的 derived property 搞得像某種計算任務。
Get 沒告訴你這東西是 stored property 還是 derived property。 UE 大量 API 都接受這種模糊性;它的 Get 更接近「向這個物件詢問這個值」,而不是 C++ 意義上的「讀取某個字段」。
第二個節點不是再去「獲取一次 Rotation」,而是在做一次表示形式轉換:
旋轉描述
→ 具體的一根世界空間方向箭頭
例如 Actor:
overflow-visible!
Rotation
Pitch = 0
Yaw = 90°
Roll = 0
這已經足以知道 Actor 朝世界 +Y。
但是這份資料本身仍然是:
overflow-visible!
(0°, 90°, 0°)
而不是:
overflow-visible!
Forward = (0, 1, 0)
Get Forward Vector 做的就是把前者變成後者。
如果腦中只留下「lobe = 瓣狀」,這個詞幾乎不可能自然進入渲染診斷語言。因為實際看 Render 時,畫面裡通常根本沒有一個東西長得像「瓣」。
在 BRDF 裡,更有操作性的定義是:
lobe = 某種散射機制,把反射能量集中到哪些方向、集中得多窄、多高。
BRDF 本身是「給定入射方向 ωi,不同出射方向 ωo 上會有多少反射」的函數。固定一個入射方向,再把所有可能的出射方向畫成 3D 極座標圖:某些方向函數值很高,圖形就從球面上「鼓」出去。那個鼓出的區域才被形象地叫做 lobe 。编辑PBR Book
所以:
overflow-visible!
BRDF
└─ directional distribution
└─ 某一群方向得到比較多能量
└─ 在函數圖上形成一個凸起
└─ lobe
「瓣」只是它在函數圖上的外形,不是材質表面真的有一瓣東西。
畫面裡直接看到的是:
overflow-visible!
lobe 畫面結果
窄 / concentrated → 小而銳利的 highlight
寬 / broad → 大而模糊的 highlight
偏向某個方向 → highlight 位置改變
沿一個軸被拉長 → anisotropic 拉絲高光
存在第二個較寬 lobe → 主高光外又有一層柔和 sheen / haze
PBRT 直接用「concentrated」描述這件事:光滑表面的 BSDF 只在很窄的一組方向取得大值;表面越粗糙,這個區域就越不集中。编辑PBR Book+1
看到:
「這個高光太寬了。」
更底層的診斷語言就是:
The specular lobe is too broad.
看到:
「這個金屬的反射集中得過頭,像鏡子。」
就是:
The specular lobe is too narrow / concentrated.
UE 5.8 的 Substrate 其實把這個詞用得非常具體。Substrate Slab BSDF 裡現在有:
overflow-visible!
Primary specular lobe
└─ Roughness
Secondary specular lobe
├─ Second Roughness
└─ Second Roughness Weight
Epic 官方明確說 Second Roughness 控制 secondary specular lobe 的 roughness,而 Second Roughness Weight 控制 primary / secondary specular lobe 的混合比例。编辑Epic Games Developers
這正好可以拿來建立一個非常具體的診斷模型。假設材質高光變成:
overflow-visible!
/\ ← narrow primary lobe
/ \
____/ \____ ← broad secondary lobe
畫面裡可能表現成:
「中間有很銳利的反射,但外面還包著一大片柔和的反射。」
There seems to be a broad secondary specular lobe underneath the sharp primary lobe.
就比「高光看起來有點怪」精確很多。
PBRT 4th edition 现在仍然明确这样定义:BRDF 描述 surface reflection,BTDF 描述 transmission,BSDF 是把两者统一起来的上位抽象。编辑PBR Book+1
所以未来更可能发生的是:
引擎和材质系统越来越倾向把"BSDF"当成主抽象层,但 BRDF 不会消失。
讨论的是「反射」这一部分,BRDF 仍然是正确而且有用的词。
第二種更重要,是:
BSDF 裡的一個 additive scattering component。
例如可以概念性寫:
fr=fdiffuse+fspecular+fcoat
於是有人會直接說:
-
diffuse lobe
-
specular lobe
-
coat lobe
此時 specular lobe 不只是「那個凸起的外觀」,還可能指產生那個方向分布的一整個 BRDF 項。
這也是 Unreal Substrate 裡 Primary Specular Lobe / Second Specular Lobe 的語境。Epic 的 Substrate 設計允許兩個 specular response 疊加,例如一個較銳利、一個較粗糙,用來產生 sharp reflection + hazy reflection 的組合。编辑Epic Games Developers+1
所以若說:
roughness makes the lobe wider
主要是在講函數的 angular support / concentration。
而說:
this material has two specular lobes
通常已經不只是說「高光有兩坨」:
fspec=w1f1(ωi,ωo)+w2f2(ωi,ωo)
是在說 BRDF 模型裡有兩個不同的散射成分。
這才是 lobe 比「高光散/集中」多出來的資訊。
還可以再往下一層。對 microfacet BRDF:
fr=4(n⋅ωi)(n⋅ωo)F(ωi,h)D(h)G(ωi,ωo)
真正控制主要形狀的不是一個叫 lobe 的數學變量,而是 D(h)、F、G 等函數共同形成了一塊高值方向區域。
所以:
overflow-visible!
microfacet parameters
└─ NDF D(h)
+ Fresnel F
+ masking-shadowing G
↓
BRDF f(wi, wo)
↓
在 outgoing-direction domain 中形成高值結構
↓
人把這個結構叫 lobe
lobe 是結果的形態描述,不是造成結果的底層數學機制。
這就能解釋一個之前一直卡住的地方:為什麼從來沒「用過 lobe」。
因為實際使用的是:
overflow-visible!
Roughness
Normal
View Direction
Light Direction
NDF
Fresnel
Geometry term
Dot Product
...
它們共同決定 BRDF evaluate 出來的數值;最後那些數值在方向空間中的形狀,才被論文或文件稱為 lobe。Microfacet BRDF 本身就是從微表面法線分布等因素計算這種方向性反射,而不是呼叫一個叫 lobe 的 primitive。编辑PBR Book
SM6 这次 shader compile 把错误报告出来了 。目前错误文本本身明确指向 CollectionParameter 参数解析,而不是某个 SM6-only feature。CollectionParameter 是 UE 5.8 正常支持的 Material Expression。编辑Epic Games Developers+1
Epic 5.8 的 MPC 裡,每個真正的 collection parameter 都有 ParameterName 和唯一的 Id;這個 Id 就是用來讓 Material 在 rename 後仍能追蹤參數的。编辑Epic Games Developers+1
你現在最值得注意的是:MPC_DirectionalLightDirectionWS 是 Collection 資產的名字,不代表這顆 node 已經選中了裡面的 Vector Parameter。
Epic 5.8 對 ShouldTickIfViewportsOnly() 的定義非常直接:它只是讓 Actor 在 TickType == LEVELTICK_ViewportsOnly 時也能 Tick。它並不負責讓 Viewport 自己開始持續刷新。编辑Epic Games Developers+1
蓝图里的 Event Tick 并不是 C++ Tick() 的真正 override。它实际对应另一个事件------ReceiveTick()。
调用链大致是:
FActorTickFunction
→ C++ Actor::TickActor()
→ 虚函数 Tick()
→ UE 再决定是否派发蓝图 ReceiveTick()
→ 蓝图 Event Tick
因此两者不完全等价:
- 父类 C++
Tick():Actor 已进入视口 Tick 调用链后,直接执行。 - 子蓝图
Event Tick:还需要 UE 额外派发ReceiveTick(),会经过蓝图事件和 World 状态相关判断。 ShouldTickIfViewportsOnly()只是允许 Actor 进入第一层 Tick,并不保证蓝图事件一定在编辑器 World 中按运行时方式派发。- Play 时 World 已完成初始化和 BeginPlay,所以蓝图
Event Tick正常执行。