真正決定一次繪製範圍的不是 Slot 名稱,而是 Section。
如果兩個Section在Index Buffer中是兩段獨立區域:
overflow-visible!
Section 0
└ Index 0~2999
Section 1
└ Index 7000~9999
中間可能夾著別的Section,GPU不能用一次普通的連續DrawIndexed只畫這兩段,而跳過中間內容。
UE有合批,而且不只一種
1. Dynamic Instancing/Auto-Instancing
相同Static Mesh + 相同Material + 相同Shader與Render State + 兼容的參數綁定 └ 合成Instanced Draw
Epic把它稱為Dynamic Instancing:相同Mesh和Material的Static Mesh Draw可以被合併。
但它主要解決的是:
overflow-visible!
同一個石頭放了100次
└ 從100組Draw
→ 少量Instanced Draw
UE中對應Unity GPU Instancing的主要顯式工具是:
overflow-visible!
Instanced Static Mesh Component
└ ISM
Hierarchical Instanced Static Mesh Component
└ HISM
它們讓相同Static Mesh、相同材質的多個實例共享高效繪製路徑。Epic建議大量重複物件使用ISM;HISM還提供適合大量靜態實例的層級剔除結構。
Epic的Merge Actors文檔明確寫到:普通Merge的Draw Call數等於材質數;把所有材質烘焙合併後,會得到單一Section和單一Draw Call。编辑Epic Games Developers
來自德文 über (讀作 /ˈyːbɐ/,注意德文原音有變母音 ü,英文借用時通常簡化成 /uː/),意思是「在...之上、超越、超級」------德文裡的介係詞/字首,表示「超過、凌駕」的意思。
uber- 這個字首在英文/科技用語裡的常見用法:
被借用當一個強調字首,意思接近「超級的、終極的、統合一切的」:
- uber-cool(超級酷)
- uber-successful(超級成功)
- Übermensch(尼采哲學術語「超人」,德文原詞,über + Mensch人)
所以 "uber shader" 讀作 /ˈuːbər ˈʃeɪdər/,uber 這裡取的是德文「超級、統合」的比喻義,跟叫車 App Uber(那家公司名字其實也是借用同一個德文字根,取「無所不在、凌駕一切的交通服務」的意涵)是同一個詞源邏輯,只是用在完全不同的領域。
section 是「模型資料結構上原本就存在的分區」,不是「切割動作產生的結果」
"get section by static mesh" 和 "create mesh sections" ,這裡的 section 是指把靜態模型(static mesh)的資料,讀取/複製到程序化模型(procedural mesh)裡的技術步驟 ,跟後面「用刀切一半」的 slice 動作,是兩個完全不同階段的東西:
- 先用 section 把模型從一種格式(static)轉換成另一種格式(procedural)------這是準備階段,還沒切
- 再用 slice 真正把模型切成兩半------這才是實際切割動作
为什么不直接把顶点复制六次?
因为两个三角形共享顶点0和顶点2。Index Buffer让多个三角形可以复用Vertex Buffer中的同一个顶点。
Index Buffer让多个三角形可以复用Vertex Buffer中的同一个顶点。
Position Normal Tangent UV Vertex Color Bone Indices Bone Weights
GPU不是看到某个特殊的"分隔符",而是每个Draw Call告诉它:
从哪里开始读,以及读多少个Index。
WIREFRAME会绘制组成三角形的边线,而不填满内部。
一组 shader invocation 组成一个 wave/warp
同一个时钟周期执行完全不同的指令
同一个 SIMD/SIMT 执行单元里的每个 lane
硬件大致是:
overflow-visible!
一个 Instruction Fetch / Decode
└ 解码一次 MUL
一个 Scheduler / Issue
└ 发射一次 MUL
32 个数据 lane
├ lane 0:0.2 × 0.5
├ lane 1:0.7 × 0.5
├ lane 2:0.4 × 0.5
└ ...
关键是:
32 个 lane 拥有不同数据,但共享这一次"现在执行 MUL"的决定。
NVIDIA 的 CUDA 文档同样说明,一个 warp 每次执行一条公共指令;分歧后会执行所有被选择的路径,并关闭不属于当前路径的线程。编辑Intel+1
为什么不给每个 lane 一套独立控制器
理论上完全可以。
可以把硬件改成:
overflow-visible!
lane 0
├ 独立取指
├ 独立解码
├ 独立调度
└ 独立 ALU
lane 1
├ 独立取指
├ 独立解码
├ 独立调度
└ 独立 ALU
......
这样每个 lane 确实可以在同一周期执行不同代码。
但此时它就不再是一组便宜的 GPU SIMD lane,而接近于:
32 个小型、相互独立的处理器。
你需要复制或复杂化很多东西:
overflow-visible!
每线程完全独立执行
├ 更多取指单元
├ 更多指令解码器
├ 更多调度逻辑
├ 更多程序计数器读取通路
├ 更复杂的寄存器寻址
├ 更复杂的功能单元路由
├ 更复杂的缓存访问仲裁
└ 更高的功耗和芯片面积
特别是,如果线程 A 要乘法、线程 B 要除法、线程 C 要纹理采样、线程 D 要 transcendental function,那么硬件要么:
overflow-visible!
方案一
└ 给每个 lane 配置非常完整的功能单元
要么:
overflow-visible!
方案二
└ 建立一个庞大的动态路由网络
└ 把每个 lane 分别送到不同功能单元
两种方案都很昂贵。
GPU 的价值恰恰来自:
overflow-visible!
减少控制硬件
└ 把晶体管面积用于更多 ALU、寄存器和带宽
└ 在大量相似任务上获得极高吞吐量
所以它不是物理上做不到,而是:
一旦为每个 lane 提供真正完整的独立执行能力,GPU 会逐渐失去 GPU 的面积效率和功耗效率。
更小的 Wave
更小的 wave 会降低分支发散的影响。
例如:
overflow-visible!
Wave64
└ 64 个线程必须尽量保持一致
Wave32
└ 只有 32 个线程需要保持一致
如果一块屏幕同时出现皮肤和衣服,较小的 wave 更容易落在单一材质区域中。
但 wave 越小也不是无条件更好:
overflow-visible!
更小的 wave
├ 发散粒度更细
└ 但调度与状态管理数量增加
所以它仍然是吞吐率与灵活性之间的折中。
NVIDIA 将其描述为在 GPU 上动态重新排序线程,以改善执行和数据访问的一致性;DirectX Shader Model 6.9 也已经定义相关接口。不过目前 DirectX 的 SER 机制主要围绕 ray-generation 和 ray-tracing workload,而且规范允许某些设备实现接口但不真正执行重排序。编辑NVIDIA Developer+2编辑Microsoft GitHub+2
每个 lane 大致拥有:
overflow-visible!
lane
├ 自己的数据
├ 自己对应的寄存器元素
├ 自己的 active / inactive 状态
└ 自己的计算结果
但是它没有一整套独立的:
overflow-visible!
├ 取指
├ 解码
├ 指令调度
└ 下一条指令决定
这些控制工作由一组 lane 上方的控制前端共同承担。
一个 warp 的指令需要多少时钟周期、实际分配到多少物理 ALU,取决于 GPU 微架构
需要PIX或RenderDoc这一类帧捕获工具。PIX的GPU Capture会记录应用发出的D3D12 API调用及参数,并允许检查命令、资源和Pipeline状态;它通过位于应用和D3D之间的捕获层观察这些调用。编辑Microsoft for Developers+1
厂商GPU执行层
overflow-visible!
GPU机器指令
Wave / Warp执行
Occupancy
Cache命中
内存停顿
Shader Stall
硬件队列并行度
UE视口基本不会完整暴露这一层。这里通常需要:
overflow-visible!
NVIDIA
└ Nsight Graphics
AMD
└ Radeon GPU Profiler
Intel
└ Graphics Performance Analyzers
这些工具会向更接近厂商GPU硬件的计数器、Shader执行和时间线深入。编辑NVIDIA Developer+2编辑GPUOpen+2
即使使用厂商Profiler,通常也不是毫无遮蔽地看到一切。
所以不存在一个单一的"绝对原始视图"。
读取三角形拓扑 └ 选择线框或边缘可视化路径 └ 设置专用Pipeline State或调试Shader └ 额外提交绘制 └ 把线条合成到视口
-
一个 warp 才是由 32 个线程组成。
-
一个 warp 通常不会一半执行皮肤 Shader,另一半执行另一个物体的不同 Shader。
1 个线程 ≈ 1 次 Shader invocation ≈ 被安排到 1 个 lane 上执行 32 个线程 └ 组成 1 个 NVIDIA warp └ 线程分别占据 lane 0~31
Rasterizer 把三角形投影到屏幕后,判断:
overflow-visible!
这个三角形覆盖了:
├ 屏幕位置 (620, 410)
├ 屏幕位置 (621, 410)
├ 屏幕位置 (622, 410)
├ 屏幕位置 (620, 411)
└ ......
Draw 产生了大量 Pixel Shader invocation
overflow-visible!
Skin Pixel Shader invocation 0
Skin Pixel Shader invocation 1
Skin Pixel Shader invocation 2
......
Skin Pixel Shader invocation 12,000
硬件不会逐个执行,而会将它们打包:
overflow-visible!
Warp 0
├ lane 0:Skin invocation 0
├ lane 1:Skin invocation 1
├ lane 2:Skin invocation 2
├ ...
└ lane 31:Skin invocation 31
Warp 1
├ lane 0:Skin invocation 32
├ lane 1:Skin invocation 33
└ ...
Warp 2
└ ...
因此可以暂时理解为:
NVIDIA GPU 通常一次以 32 个 Shader invocation 为一个 warp 来调度。
它不是一个固定的、从左到右每 32 像素切块的屏幕分区。
不会形成:
overflow-visible!
同一个 warp
├ lane 0~15:执行 SkinPixelShader
└ lane 16~31:执行 WallPixelShader
UE 中,角色皮肤和衣服如果是不同 Material Slot,通常会成为不同的 Mesh Batch / Draw:
角色 Skeletal Mesh ├ Material Slot 0:Skin │ └ Draw Skin sections │ └ Skin shader waves │ ├ Material Slot 1:Cloth │ └ Draw Cloth sections │ └ Cloth shader waves │ └ Material Slot 2:Eye └ Draw Eye sections └ Eye shader waves
所以即便皮肤和衣服在屏幕上紧挨着,正常情况下也不会因为空间相邻就自动塞进同一个 warp。
overflow-visible!
左上角
└ 一路向右、向下
└ 完整走完螢幕
└ 算一次畫面更新
這幾乎是在描述顯示器取得 framebuffer 的方式,而不是 Pixel Shader 的執行順序。
顯示掃描輸出(scan-out)。
先把完整鏈分成三套「分塊」
overflow-visible!
第一套:GPU 渲染分塊
└ Rasterizer / Tile / Quad / Wave
第二套:Frame Buffer 與 Present
└ Back Buffer → Present → 可供顯示
第三套:顯示掃描
└ Scan-out Engine → Display Link → 螢幕面板
它們都可能看起來像「一塊一塊處理」,但沒有一一對應關係。
遊戲完成一張畫面後,結果存放在一張二維影像資源裡:
overflow-visible!
Back Buffer
┌────────────────────┐
│ 已經算好的像素資料 │
│ │
│ │
└────────────────────┘
程式呼叫 Present 後,DXGI/Windows 顯示系統會讓某張 swap-chain buffer 成為即將顯示的畫面。
接下來不是 Shader Core 再跑一次,而是 GPU 中另外一套顯示硬體:
overflow-visible!
Display Engine / Scan-out Engine
└ 從顯示用 buffer 讀取像素
└ 經 DisplayPort / HDMI 傳給螢幕
傳統顯示掃描大致按照畫面行序推進:
overflow-visible!
第 0 行:左 → 右
第 1 行:左 → 右
第 2 行:左 → 右
......
最後一行
假設顯示引擎正在讀 Frame A:
overflow-visible!
已掃描部分
┌────────────────────┐
│ Frame A │
│ Frame A │
├────────────────────┤ ← 掃描位置
│ 尚未讀取 │
│ 尚未讀取 │
└────────────────────┘
此時程式又 Present 了 Frame B,而且沒有等待適當同步。
顯示引擎可能出現:
overflow-visible!
上半部
└ 已經從 Frame A 讀出
下半部
└ 改從 Frame B 讀出
Compute Shader threads 可以共享 group shared memory、同步 barrier,以及共同的 group ID?
GPU Graphics Pipeline └ Vertex Processing
Rasterizer └ 產生 Pixel Shader invocations └ 按 quad / wave 執行 └ 寫入 Render Target
Swap Chain └ 完成一張 Back Buffer └ Present └ 選擇可供顯示的 buffer
Display Engine └ 從 buffer 逐步 scan out └ DisplayPort / HDMI └ 螢幕逐步刷新
Shader + Blend/Depth/Rasterizer 状态组成 D3D12 PSO
Static Switch ├ 运行时Shader可以更短 ├ 不需要执行被关闭的支路 └ 代价:产生新的编译版本
Material Analyzer也专门统计静态开关、基础属性覆盖和静态组件遮罩,因为它们会增加Permutation和存储成本。编辑Epic Games Developers+1
三个互相独立的Static Switch:
overflow-visible!
A:Detail Normal
B:Clear Coat
C:Wetness
理论组合数是:
overflow-visible!
2 × 2 × 2 = 8
十个独立布尔开关的潜在组合是:
overflow-visible!
2¹⁰ = 1024
普通参数变化 └ 同一份DXIL └ 绑定不同Constant Buffer数据 Static Switch变化 └ 不同DXIL Shader Blob └ 通常进入不同的PSO/Shader组合
Translucent
overflow-visible!
最终颜色
= 当前表面颜色 × 透明度
+ 背景颜色 × 剩余权重
它通常需要:
overflow-visible!
读取已经存在的背景颜色
├ 启用Blend State
Unity 常见的几种"合批"
-
SRP Batcher
减少 CPU 切换 Shader 和材质数据的成本(是切換pso的成本,drawcall還是這麼多,這兩個mesh不同,所以還是兩個drawcall),但通常仍然是多个 Draw Call。
-
Static Batching
预先把静态物体的几何数据合并。能减少 Draw Call,但会增加合并后的顶点数据和内存占用,而且不适合会骨骼变形的角色。
-
Dynamic Batching
CPU 每帧把很小的网格拼起来再提交。只适用于规模较小、条件严格的网格;现代硬件上未必划算。
-
GPU Instancing
一次绘制同一个 Mesh 的多个实例。要求几何基本相同,不是"两个不同 Mesh 只因材质相同就合并"。

我的好大佬啊,
- bloopers(NG片段、拍攝失誤花絮,通常是搞笑、出錯的片段,跟「behind-the-scenes」不完全一樣,bloopers 更聚焦在「笑料、NG」)
- outtakes(被剪掉的片段,類似 bloopers,常指沒有被放進正式版本裡的片段)
Tile 通常
GPU 当前处理 framebuffer 中某个矩形区域的工作单位。
Framebuffer └ 一整张二维结果图,长期存在于 GPU 内存
Framebuffer 在哪里?
以你的 Windows 11 + UE + D3D12 + 独立显卡为例:
overflow-visible!
UE
└ FRDGTexture / FTextureRHI
D3D12
└ ID3D12Resource
└ Render Target / Depth Stencil / Back Buffer
GPU 虚拟内存
└ 驱动分配实际物理存储
物理位置
└ 通常主要位于显卡 VRAM
例如 2560×1440 的最终颜色图:
overflow-visible!
Back Buffer
├ 像素 (0,0)
├ 像素 (1,0)
├ ...
└ 像素 (2559,1439)
不要把 framebuffer 理解成"显存中只有一张屏幕图"。
UE 一帧中可能同时存在很多类似 framebuffer 的二维资源:
overflow-visible!
GPU VRAM
├ Scene Depth
├ GBuffer A
├ GBuffer B
├ GBuffer C
├ Velocity
├ Scene Color
├ Lumen 中间结果
├ TSR History
├ 后处理临时纹理
└ Swap Chain Back Buffer
现代图形 API 更常用 Render Target、Depth Target、Image、Texture Resource 等准确名称。"Framebuffer"通常是泛指当前用于保存颜色、深度等渲染结果的一组图像。
每次Texture Sample大致涉及:
overflow-visible!
计算UV和Mip
└ 查询Texture Cache
├ 命中:较便宜
└ 未命中:从更远层级读取纹理数据
└ 最终可能访问显存
一个ALU很多但纹理很少的Shader,可能处于Compute-bound;一个指令不多但大量读取纹理和Render Target的Shader,可能是Bandwidth-bound。
带宽需求 ≈ 屏幕像素数 × 每像素读写字节数 × Pass数量 × Overdraw
tile,主要指 raster tile / render tile。
它首先只是一个坐标范围:
overflow-visible!
整张 Render Target:2560×1440
某个 Tile:
x = 320~335
y = 160~175
也就是:
overflow-visible!
Framebuffer
┌──────────────────────────────┐
│ │
│ ┌────────────┐ │
│ │ 当前 Tile │ │
│ └────────────┘ │
│ │
└──────────────────────────────┘
因此 tile 首先"位于":
Framebuffer 的二维坐标空间中。
物理上,tile 的像素数据在哪里?
这取决于 GPU 架构。
Tile-based GPU
在典型的移动端 Tile-Based Deferred Renderer 中:
overflow-visible!
外部内存 / 显存
└ 完整 Framebuffer
GPU 芯片内部
└ 小型高速 Tile Buffer
└ 只容纳当前一个或少数几个 tile
流程是:
overflow-visible!
1. 把场景几何按屏幕 tile 分类
2. 选择一个 tile
└ 例如 16×16 或 32×32 区域
3. 在 GPU 芯片内部计算该 tile
├ Color
├ Depth
├ Stencil
└ MSAA samples
4. tile 完成
└ resolve / store
5. 写回外部 framebuffer
6. 处理下一个 tile
关系就是:
overflow-visible!
VRAM / DRAM
└ 完整 Framebuffer
↑
│ Tile 完成后写回
│
GPU On-Chip Memory
└ 当前 Tile 的临时颜色和深度
Arm 对 tile-based rendering 的说明正是:处理中的 tile 数据保留在片上 tile buffer,结束后才把结果写回外部内存中的 framebuffer,从而减少外部内存带宽。编辑Arm Developer+1
Tile 和 wave 又在哪里相遇?
假设 GPU 正在处理 framebuffer 中的一个区域:
overflow-visible!
Tile:16×16
└ 256 个像素位置
Rasterizer 判断哪些三角形覆盖它:
overflow-visible!
Tile
├ 三角形 A 覆盖 80 个位置
├ 三角形 B 覆盖 120 个位置
└ 其他位置没有覆盖
然后产生 Pixel Shader invocation:
overflow-visible!
Fragments
└ Pixel Shader threads
└ 被组成多个 wave / warp
如果是 32-lane warp,忽略边缘、overdraw、helper lane 和 MSAA:
overflow-visible!
256 invocations
└ 最少大致需要 8 个 warps
[numthreads(8, 8, 1)]
但这是 Compute Shader 的工作网格,不等于光栅器内部 tile,也不等于显示器 scan-out 区域。

Arm 将这种 render pass 模型描述为:attachment 数据在 tile 内处理,通常只在 render pass 结束时产生外部内存输出;如果结果后续不需要,甚至可以完全跳过 store。编辑Arm Developer+1
即使手机是统一内存:
overflow-visible!
CPU 和 GPU
└ 共享同一组 LPDDR
这组 LPDDR 对 GPU 核心而言,仍然是 off-chip memory。
为什么"片上"和"外部"差别这么大
这里的"外部"不是指 GPU 以外的另一台设备。
它指的是:
overflow-visible!
GPU 芯片内部
├ Shader Core
├ Cache
└ Tile SRAM
与:
overflow-visible!
GPU 芯片外部
└ LPDDR / GDDR / 系统 DRAM
而访问 tile SRAM 是:
overflow-visible!
Shader Core
→ GPU 芯片内部 SRAM
后者通常更省能耗、延迟更低,而且不会占用外部内存带宽。Apple 也直接把 TBDR 的价值表述为避免 GPU 与 system memory 之间昂贵的往返访问。编辑Apple Developer
因此移动端强调 tile,不是因为 framebuffer 的定义变了,而是因为:
overflow-visible!
手机 SoC
├ 功耗预算小
├ 内存带宽有限
├ CPU/GPU/NPU 共用 LPDDR
└ 无法频繁搬运大量 framebuffer 数据
Imagination 将降低 system-memory bandwidth 视为 PowerVR TBDR 的核心设计原则。编辑Imgtec Documentation

SV_* 是 HLSL 中由 Direct3D 規範保留的「系統語義」。把變數標記為 : SV_X,就是把這個變數綁定到一個由圖形管線定義的接口端點;它在哪個階段可用、由誰產生、由誰消費、數值如何解釋以及會造成什麼管線行為,都由圖形 API 規範決定,而不是 Shader 作者自由約定。
: SV_POSITION └─ 宣告這個返回值要交給 Rasterizer,作為頂點位置
Rasterizer 不會進入你的函式裡搜尋:
overflow-visible!
哪個 float4 看起來像位置?
哪個變數叫 pos?
哪一行做了矩陣乘法?
它只接收正式的 Shader 輸出接口。Direct3D 明確要求送往 Rasterizer 的頂點輸出至少包含最終四分量位置,並用 SV_POSITION 標記;Rasterizer 隨後才執行裁切、透視除法、Viewport 映射和片元生成。编辑Microsoft Learn+1
False 意思只是:
overflow-visible!
目前這張材質圖
├─ 沒有 Scene Color 節點
├─ 沒有 PerInstanceRandom 節點
├─ 沒有 PerInstanceCustomData 節點
└─ 沒有 VertexInterpolator 節點
不是說:
overflow-visible!
這個材質現在沒有隨機數
這個材質現在沒有 instance
這個材質現在是靜止的
Epic 對 PerInstanceRandom 的定義是:
InstancedStaticMeshComponent 為每個 instance 提供一個隨機值;同一 instance 內它保持不變,但不同 instance 可以不同。编辑Epic Games Developers+1
因此它的完整路徑是:
overflow-visible!
Material Asset
└─ 圖中存在 PerInstanceRandom 節點
└─ 編譯器標記:
Has Per Instance Random = True
└─ Shader 編譯出讀取該輸入的程式碼
└─ Draw 時由 InstancedStaticMeshComponent 提供數值
而且這裡的 Random 也不等於「每幀亂跳」:
overflow-visible!
同一個 instance
└─ 通常保持同一個 random value
不同 instance
└─ random value 不同
所以更準確的名稱其實接近:
overflow-visible!
Uses Stable Per-Instance Random Input
只是 UI 為了簡短寫成了:
overflow-visible!
Has Per Instance Random
但它其實是全方向的
你說「不是全方位地看到」,這部分反了。
Environment Map 通常就是要描述某一個空間點周圍的全部方向 。例如 Cubemap 用六個面覆蓋完整方向域;Shader 使用反射向量去查相應方向。编辑Microsoft Learn+1
你截圖右側也明確寫了:
overflow-visible!
the computation of the texture coordinates
is possible for all viewing directions
也就是:
無論攝影機從哪個方向觀看物體,都應該能計算對應的環境貼圖座標。

MatCap 与 Sphere Map 的图片外观可以非常相似,真正区别在 Shader 怎样生成采样坐标:
经典 MatCap:
ViewSpaceNormal.xy → UV
Sphere Map:
Normal + ViewDirection → ReflectionVector → UV
這份 PDF 在「文獻搜索體系」裡,不應先歸類成普通的 SIGGRAPH 論文。它的準確身份是:
overflow-visible!
ACM SIGGRAPH 2000
└─ Courses 教學課程
└─ Course 27
└─ Approaches for Procedural Shading on Graphics Hardware
└─ Course Notes 課程講義合集
└─ Uses of Environment Maps
└─ Environment Maps and Their Applications
作者:Wolfgang Heidrich
SIGGRAPH 2000 的 Course 27 標題頁明確寫的是 "SIGGRAPH 2000 Course 27 Notes --- Approaches for Procedural Shading on Graphics Hardware" 。而課程目錄則把這份 PDF 放在 "Uses of environment maps -- Heidrich" 這一節下。编辑UMBC User Pages+1
所以最準確的稱呼是:
SIGGRAPH Course Notes 中的一篇 tutorial/survey chapter。

使用完整標題加作者:
overflow-visible!
"Environment Maps and Their Applications" Wolfgang Heidrich
或者加文獻類型:
overflow-visible!
"Environment Maps and Their Applications" "course notes"
"Environment Maps and Their Applications" SIGGRAPH 2000
Wolfgang Heidrich environment maps tutorial
overflow-visible!
environment maps
因為現在這個詞還大量指向機器人、SLAM、無線網路的 radio environment map,以及 AI agent 的 environment map。
paraboloid /pəˈræbəlɔɪd/(最可能,拼字最接近)
數學/幾何學術語,「拋物面」------由拋物線(parabola)旋轉或延伸而成的三維曲面,常見於衛星天線、探照燈反射面的形狀(利用拋物面能把光線/訊號聚焦到一點的特性)
另外,這門課後來延續成了 Real-Time Shading 的課程與書籍體系。2001 年的課程頁面明確說它之前的名稱就是 "Approaches for Procedural Shading on Graphics Hardware";Marc Olano、John Hart、Wolfgang Heidrich 和 Michael McCool 又在 2002 年出版了《Real-Time Shading》。编辑UMBC CSEE+2编辑Illinois Experts+2

https://lunakittems.artstation.com/projects/vJqAQA
https://www.reddit.com/r/Unity3D/comments/1seqrq9/a_different_way_to_do_stylized_eyes_in_unity_i/

cornea
「擴張、放大、變寬的動作或狀態」
- 例句:"nitric oxide causes dilation of the blood vessels"(一氧化氮會導致血管擴張)
- 截圖上方也提到 "Pupil dilation"(瞳孔放大/擴張)------這是很常見的醫學用語,指瞳孔因光線變化、情緒、藥物等因素而放大
「(對某個主題)長篇大論地說明/書寫」
- 例句:"the main editorial involved no dilation on the privileges or responsibilities of citizenship"(這篇社論並沒有針對公民的權利或義務做長篇闡述)
順便回應你之前在虛幻引擎影片裡看到的 "time dilation":
那段影片裡提到的 time dilation (時間膨脹/時間延緩)用的就是這個字的第一個意思 ------「擴張、延展」,在遊戲開發語境裡,time dilation 指把時間的流速拉長/放慢(類似「子彈時間 bullet time」效果),讓遊戲裡的時間變慢,產生類似電影裡慢動作的效果,這也解釋了為什麼那支切割教學影片裡用 time dilation 來製造「慢動作切割方塊」的畫面。

Cornea 角膜 ------ 眼球最前面透明的「保護玻璃」,截圖裡形容成「虹膜的擋風玻璃」很貼切
Retina 視網膜 ------ 眼球最內側後方,負責「感光成像」,像相機的「感光元件/底片」
- Sclera 鞏膜 ------ 就是眼白,包覆整個眼球的白色外殼(眼睛的「外牆」)
- Conjunctiva 結膜 ------ 覆蓋在鞏膜和眼瞼內側的一層透明黏膜(保護、潤滑用)
六個方向的那種不叫 MatCap Texture,它叫:
overflow-visible!
Cubemap / Cube Texture / Cubic Environment Map
Environment Map ├─ Cubemap │ └─ 六個方形面表示完整方向 │ ├─ Sphere Map / Lat-long / Octahedral Map │ └─ 用一張 2D 圖重新排列完整方向 │ └─ MatCap └─ 用一張 2D 圖保存球面材質的最終外觀

「六張圖」不一定代表六個磁碟文件。它在 GPU 裡通常是一個邏輯資源:
overflow-visible!
TextureCube
└─ 6 個 array slices / faces
輸入文件也可能是:
overflow-visible!
六個獨立文件
一張十字排列圖
一張橫向 strip
一張 HDRI,導入後轉成 Cubemap
眼睛模型的 3 大組成元件
-
眼球(The eye ball)
-
結構: 極為簡單的半球體模型(Hemisphere)。
-
用途: 套用主要的眼睛貼圖與眼球著色器(Eye Shader)。
-
-
眼睛覆蓋層(The eye overlay)
-
結構: 覆蓋在眼球與眼瞼(眼皮)接縫處的小型網格模型。
-
用途: 專門用來計算與產生環境光遮蔽(Ambient Occlusion),讓眼球與眼瞼之間的過渡更自然、更有嵌入感,避免眼球看起來像是浮在臉上。
-
-
睫毛(The eye lashes)
-
結構: 獨立的簡單網格模型。
-
用途: 套用帶有透明通道(Alpha Blended)的睫毛貼圖。
-


CRYENGINE | Documentation - Eye Shader (Pre-3.6)
六個面的那種是 Cubemap Texture,不算 MatCap Texture。單張圖片也不一定是 MatCap,它可能是另一種 Environment Map 參數化。判斷標準不是圖片數量,而是圖片中的像素表示「環境方向」還是「材質表面朝向的最終外觀」。

BSSRDF 則允許:
overflow-visible!
光在位置 Pi 進入
└─ 在物體內部散射
└─ 從另一個位置 Po 離開
普通 BRDF 假設:
overflow-visible!
光在位置 P 進入
└─ 仍然從位置 P 離開
典型材質:
overflow-visible!
皮膚
蠟
牛奶
玉石
大理石
一個材質不是只能有一個反射模式。
overflow-visible!
Material BSDF
├─ Diffuse lobe
├─ Specular reflection lobe
├─ Clear coat lobe
├─ Sheen lobe
└─ Transmission lobe
例如塑膠:
overflow-visible!
塑膠 BSDF
├─ 表面介質高光
│ └─ Specular microfacet BRDF
└─ 材料內部散射後返回
└─ Diffuse BRDF
金屬:
overflow-visible!
金屬 BSDF
├─ Conductive specular BRDF
└─ 幾乎沒有普通 diffuse lobe
玻璃:
overflow-visible!
玻璃 BSDF
├─ Specular reflection BRDF
└─ Specular transmission BTDF
最常見的 Microfacet BRDF
現代 PBR 的 Specular 通常使用微表面模型:
overflow-visible!
宏觀看起來平滑的表面
└─ 微觀上由大量微小鏡面組成
典型形式:
fr=D F G4(N⋅L)(N⋅V)f_r = \frac{D\,F\,G} {4(N\cdot L)(N\cdot V)}fr=4(N⋅L)(N⋅V)DFG
overflow-visible!
D:Normal Distribution Function
└─ 有多少 microfacet 朝向 Half Vector
└─ Roughness 主要影響這裡
F:Fresnel
└─ 不同觀看角度反射多少光
G:Geometry / Visibility
└─ microfacet 之間的遮蔽與陰影
分母
└─ 幾何投影和正規化
Roughness └─ 控制 specular lobe 的寬度和形狀
Lambert Diffuse 則是最簡單的漫反射 BRDF:
fd=ρπf_d=\frac{\rho}{\pi}fd=πρ
其中 ρ\rhoρ 是表面反照率。
UE 5.8 的 Substrate 把材質理解為 Slab of Matter,每個 Slab 是一個 principled BSDF 表示
overflow-visible!
Substrate Material
└─ Slab BSDF
├─ Diffuse response
├─ Specular response
├─ Roughness
├─ Mean free path
├─ Transmittance
└─ F0 / F90 等物理參數
多層材質:
overflow-visible!
Top Slab:透明塗層
└─ Vertical Layer Operator
└─ Bottom Slab:金屬/塑膠/皮膚
這不是單純:
overflow-visible!
Color A + Color B
而是接近:
overflow-visible!
光先與上層 BSDF 交互
├─ 一部分反射
└─ 一部分進入下層
└─ 再與下層 BSDF 交互
Substrate 的層級是:
overflow-visible!
Material Graph
└─ 建立 Slab BSDF
└─ 組合多個 Slab
└─ 編譯成引擎可執行的材質表示
└─ 在 Base Pass/Lighting Pass 等位置使用
- impersonator /ɪmˈpɜːsəneɪtər/,意思是「模仿者、模仿藝人」------指模仿某個名人(外表、聲音、表演風格)的人,常見於模仿秀、翻唱歌手等
overflow-visible!
Shift + 點擊 Red 左側眼睛
└ 只顯示 Red,其他通道全部隱藏
同理可以查看 Green、Blue、Alpha。普通點擊眼睛只是逐個顯示/隱藏;Shift + 點擊眼睛 會隔離該通道。编辑GIMP Documentation

先看這句話字面上想表達什麼:
"Why is it that people who sing are better at speaking than they are at talking normally?"
= 「為什麼會唱歌的人,講話會比他們平常說話還要厲害?」
問題出在哪:
- speaking(說話、發聲表達)
- talking normally(平常正常說話)
這兩者在英文裡幾乎是同義詞,一般情況下沒有明確區分「speaking」和「talking」誰更高級/正式,所以整句話變成「說話比說話厲害」,邏輯上顯得很奇怪、甚至有點同義反覆(tautology)的感覺。
Skeletal Mesh/Asset Preview Viewport 裡:
overflow-visible!
按住 L
└ 同時按住滑鼠左鍵拖動
└ 旋轉預覽場景的 Directional Light
不是 Ctrl + L。Ctrl + L 通常是 Level Viewport 裡操控場景太陽光;資產預覽窗口使用的是 L + LMB Drag 。Epic 的 UE 5.8 文件也將其列為 L + Mouse Move/Hold L and drag with the left mouse button。编辑Epic Games Developers+1
Ramp 更适合美术控制:
- 可以决定阴影颜色;
- 可以控制分界是硬还是软;
- 可以增加第三、第四色阶;
- 可以让不同材质使用不同明暗风格;
- 改贴图即可调整效果,不必修改 Shader。
- vermin /ˈvɜːmɪn/ = 「害獸、害蟲」,泛指老鼠、田鼠這類會危害農作物或環境衛生的小型野生動物
- humanely /ˈhjuːmeɪnli/ = 「人道地」------強調用盡量減少痛苦、快速致死的方式進行,而不是殘忍虐殺
- shot(過去分詞,shoot 的過去分詞)= 「被射殺」
"is effectively dead. The twitching is literally just nerves."
= 「(牠)實際上已經死了。那些抽搐純粹只是神經反應。」
這句話的背景知識(常見於狩獵/害獸防治影片):
動物被射殺瞬間死亡後,身體有時還會出現抽搐(twitching) ,這是神經系統的殘餘反射動作,不代表動物還活著、還在痛苦掙扎,講者在解釋這一點,避免觀眾誤以為動物「死不透、還在受苦」。
KIA = Killed In Action(軍事用語「陣亡」,這裡被戲謔地借用來統計「被射殺確認死亡的老鼠數量」,68隻)
riddle 這個字的動詞意思:
「(被大量的洞、痕跡等)佈滿、遍布」------通常是負面、密集、到處都是的畫面感:
- riddled with bullet holes(佈滿彈孔)
- riddled with mistakes(錯誤百出、到處都是錯)
- riddled with rats(老鼠成災、到處都是老鼠)
"Nice little ambush point."
逐字翻譯:
= 「(這是個)不錯的小埋伏點。」
單字解釋:
ambush /ˈæmbʊʃ/,重音在第一音節
- 名詞:埋伏、伏擊
- 動詞:伏擊、突襲
- ambush point = 埋伏地點,軍事/狩獵用語,指「適合躲藏起來、等待獵物/敵人出現再突然行動的位置」
"Rancher takes care of big Predator problem | 70 Coyotes Down"
= 「牧場主人解決了嚴重的掠食者問題 | 70隻郊狼(北美狼)倒下」
"But managed to uh redeem myself."
逐字翻譯:
= 「但(我)還是設法挽回了面子/扳回一城。」
"rancher my ass bro is a full spec ops unit 😭"
= 「什麼牧場主人啊,老兄根本就是一整支特種部隊。」
- "my ass" (口語粗俗用法)------放在名詞後面表示「才怪、屁啦」,強烈質疑/否定前面那個說法,類似中文「什麼XX啊」的嘲諷語氣
- "Rancher, my ass" = 「牧場主人?才怪咧」
- spec ops(specialized operations 的縮寫)= 「特種部隊」
留言2(@janedoecheezepizza,5.6K讚):
"This man never lost on a dog round on cod zombies"
= 「這傢伙在《決勝時刻:殭屍模式》的『狗狗關卡』裡肯定從沒輸過。」
- cod = Call of Duty(《決勝時刻》,知名射擊遊戲系列)的縮寫
- zombies(殭屍模式)= 《決勝時刻》裡一個很受歡迎的殭屍生存遊戲模式
- "dog round" = 殭屍模式裡,偶爾會出現的一波「特殊殭屍狗」關卡,是玩家公認最容易失手、最緊張刺激的回合之一
"gifted with the cat distribution system"------這是網路流行梗,"cat distribution system"(貓咪分配系統)是一個幽默的說法,意指「貓好像會自己神秘地找到適合的人類/家庭收養牠們」,彷彿宇宙中真的存在一套系統會把流浪貓分配給有緣人
NVD = Normal Vaginal Delivery(自然產、經陰道自然分娩)------相對於剖腹產(C-section),指產婦經由產道自然生產的方式
Delayed cry (延遲哭泣)------指新生兒出生後沒有立即哭出聲,正常情況下新生兒出生應該馬上啼哭(這是肺部開始自主呼吸的重要指標),如果延遲哭泣,代表新生兒可能有呼吸窘迫或缺氧的狀況,需要緊急處理
Full Resuscitation /ˌriːsəsɪˈteɪʃən/ = 「完整復甦(急救)程序」
- resuscitation 這個字用你熟悉的規則看:-tation 讀 /teɪʃən/(符合 -tion 顎化規則)
- 指針對新生兒進行的一系列急救措施(清除呼吸道、刺激呼吸、必要時人工給氧或心肺復甦)
Shocking legs
這裡不是字面上的「令人震驚的腿」,而是英式口語裡的:
shocking
└ extremely bad
└ terrible / awful
└ 糟糕透了、難看得要命
完整句本來是:
He had shocking legs.
他的腿難看得要命。
但口語中直接把主語和動詞全部刪掉:
Shocking legs.
為什麼使用 had
he had nice legs
使用過去式,是因為 Alex 現在已經失去雙腿。這句同時存在兩層意思:
過去他的腿也並不好看;
現在他沒有腿了,但這不影響她愛他。
所以這個笑話利用了 had 的現實背景。假如只是普通伴侶,說:
He didn't have nice legs.
原句是:
Alex has been home with his family for six weeks, but is now returning to hospital for an extended period of rehabilitation.
"Are you going to be okay?"
意思是:
Alex 已經在家和家人生活了六週,但現在要再次回到醫院,接受一段較長時間的復健。
「一個人會沒事嗎/能應付得來嗎?」
這裡的 rehabilitation 是不可數名詞,表示整體的復健過程,不是一次次獨立的「復健」。
rehabilitation
└ 恢復身體功能與獨立生活能力的整體過程
├ 物理治療
├ 職能治療
├ 義肢訓練
└ 日常生活技能訓練
所以不是:
several rehabilitations
I wasn't prepared for what came my way.
我沒有準備好應對後來發生在我身上的事。
cats are coddled
└ 貓被嬌養、被過度保護
like plush toys
└ 像對待絨毛玩具一樣
coddle
coddle 不只是一般的「照顧」。
它帶有:
給予過度保護
└ 過分寵愛
└ 不把對方當成能獨立行動的存在
└ Texture Coordinate:產生原始座標 (X, Y, Z) └ Mapping:平移、旋轉、縮放這組座標
https://cdna.artstation.com/p/media_assets/images/images/000/668/470/original/10.gif?1605038889

作者在文中寫道:
"looking over the model, finding areas that either blend into the background or lack discernible form, and putting in lights..."
意思是:在審視模型時,尋找那些「跟背景融為一體」或者「因為缺乏光影對比,導致看不清幾何立體輪廓」的地方。
真正需要管的是:
无论灯从哪里来,角色材质是否能够稳定、可控、性能合理地响应它。
测试灯光组不是最终游戏布光,而是材质实验室。就像测试动画不代表角色永远只播放测试动作。
我檢查了圖片,它是:
overflow-visible!
1024 × 32
而:
overflow-visible!
1024 = 32 × 32
所以它正好是:
overflow-visible!
32 個小格
每格 32 × 32
= 32 × 32 × 32 個查找點
也就是一個 32³ 的顏色立方體。
Epic 的 UE 5.8 文件展示的是同一種結構,只是用較低解析度的 16³ LUT,攤平後是 256×16;你這張則是更細的 32³ LUT。
在攝影學中,EV 是由「光圈、快門速度」共同決定的數值。UE 改採基於物理的光照計算(PBR)後,導入了 EV100 作為標準:
- 定義 :當感光度固定在 ISO 100 、光圈為 f/1.0 、快門時間為 1秒 時,其曝光量定義為 EV100 = 0。
人物材質先算出一個 RGB,再把這個 RGB 轉成這張 1024×32 圖上的座標。
EV100 不是光照强度,也不是最终画面亮度。
它是一个以 ISO 100 为基准、用 log₂ 表示的相机曝光档位:
EV100=log2(N2t⋅100ISO)EV_{100}=\log_2\left(\frac{N^2}{t}\cdot\frac{100}{ISO}\right)EV100=log2(tN2⋅ISO100)
其中:
-
NNN:光圈 F-number,例如 f/2.8 中的 2.8
-
ttt:快门打开时间,单位为秒,例如 1/60 秒
-
ISOISOISO:感光度
UE 的物理相机曝光正是根据光圈、快门和 ISO 计算 EV100。编辑Epic Games Developers+1
EV100 在 UE 渲染链中的位置
overflow-visible!
场景中的灯光
├─ Directional Light:Lux
├─ Point / Spot / Rect Light:Lumen 或 Candela
└─ Emissive / Sky:cd/m²
↓
表面接收到光,着色器计算 HDR Scene Color
↓
曝光测光
├─ Manual:相机参数或固定 EV100
└─ Auto Exposure:分析画面亮度直方图
↓
得到 Target EV100
↓
转换成线性 Exposure multiplier
↓
Scene Color × Exposure
↓
Tonemapper / Local Exposure / Color Grading
↓
SDR 或 HDR 显示输出
为什么 EV100 每增加 1,画面就暗一半
暂时忽略曝光补偿,曝光倍率的核心关系可以写成:
Exposure=2−EV100Exposure=2^{-EV_{100}}Exposure=2−EV100
也就是:
| EV100 | 曝光倍率 |
|---|---|
| 0 | 1 |
| 1 | 1/2 |
| 2 | 1/4 |
| 3 | 1/8 |
| 8 | 1/256 |
| 15 | 1/32768 |
Min EV100 / Max EV100
它们不是输出亮度,而是自动曝光允许移动的范围:
overflow-visible!
自动曝光计算 Target EV100
└─ Clamp(Target EV100, Min EV100, Max EV100)
└─ Actual EV100 逐渐向它移动
例如:
overflow-visible!
Min EV100 = 5
Max EV100 = 12
意味着自动曝光只能在 5 到 12 档之间适应。
Pre-Exposure 又是什么
UE 并不一定等到后处理阶段才把完整曝光乘上去。为了防止现实级灯光数值使低精度 Scene Color Render Target 溢出,UE 会把前一帧的曝光值提前应用到着色器计算中。
overflow-visible!
巨大 HDR 光照结果
└─ Shader 内应用 Previous Frame Pre-Exposure
└─ 把数值搬到较安全的范围
└─ 写入 Scene Color
└─ 后续阶段恢复正确曝光语义
它的作用主要是数值范围重映射:
- 降低 Render Target 溢出风险
EV 是 Exposure Value 的縮寫。
感光度固定為 ISO 100 的基準下
現實世界的亮度跨度太過恐怖(太陽 vs. 螢幕)
電腦螢幕的發光能力和現實世界相比,弱小得像一根火柴。
- 現實的太陽 :亮度高達 1,000,000,000 Nits(亮度單位)。
- 現實的點燈室內 :大約只有 100 ~ 300 Nits。
- 您的普通電腦螢幕 :最大亮度頂多 300 ~ 500 Nits 。
如果電腦強制只用一個絕對亮度基準(不准像相機那樣調整快門和 ISO 100),當玩家從大白天的戶外走進山洞時,因為太陽和山洞的物理光線相差了百萬倍, 您的電腦螢幕根本沒有足夠的物理發光範圍來同時呈現這兩種極端 。如果不用 ISO/EV 去做動態「平移(壓縮)」,畫面不是全白(瞎掉)就是全黑。
「肆意調整」其實是舊時代的壞習慣(現在正在被校正)
您說的「不該肆意調」完全說中了現代遊戲開發的轉折點:
- 以前的舊遊戲(非物理渲染) :美術覺得太暗就拼命調大燈光強度,覺得太亮就調低曝光。這導致同一個角色換張地圖就直接毀容、變色。
- 現在的 UE5(Lumen 時代) :Epic Games 官方現在極度反對肆意調整。他們目前的標準工作流就是**「鎖定 EV/ISO基準」** 。
- 官方要求開發者:太陽光必須死死固定在 100,000 Lux(大自然物理值)。
- 鏡頭的 EV100 必須鎖定在符合大白天的 14。
- 不准任何人私自去調亮度 。如果畫面不對,代表你的材質反射率做錯了,必須回去修改材質。
HDR Render Target 使用浮点格式。它虽然可以存储大于 1.0 的值,但仍然不是无限大:
overflow-visible!
8-bit UNORM
└─ 通常只能表示 0~1
└─ 超出时被 Clamp
16-bit Float
└─ 可以表示很大的 HDR 数值
└─ 但仍然存在最大有限值
└─ 超出后可能成为 Inf、NaN 或产生异常结果
所以"Render Target 溢出"准确来说是:
Shader 算出了一个数值,但目标纹理使用的像素格式没有足够的数值范围保存它。
现实尺度灯光之间的跨度非常大。暗室可能只有几个 Lux,日光可能达到数万甚至十万 Lux;高亮反射、自发光、Bloom 输入和多盏灯累积后,Scene Color 中的数字可能进一步放大。Epic 明确指出,极亮光源可能使低精度 Scene Color Render Target 出现算术溢出或黑斑,因此 UE 使用 Pre-Exposure 缩小写入 Scene Color 所需的数值范围。编辑Epic Games Developers+1
EV100 是数字相机观察 HDR 场景时的曝光标尺,而不是数字世界灯光本身的亮度标尺。
变灰正是在告诉你:Manual 模式不使用这些参数。
需要纠正一个容易混淆的地方:这个 Post Process 面板在关闭 Apply Physical Camera Exposure 后,通常并没有一个单独让你填写"Manual EV100"的输入框。此时画面基准主要由 Manual 的固定曝光公式和 Exposure Compensation 决定。
所以对你最实用的理解是:
这三个设置负责固定相机;画面太黑或太亮以后优先调整灯光,不再让相机追着场景变化。
RD= Ramp Diffuse 或 Ramp/DiffuseRS= Ramp Specular 或 Ramp/Specular
后面的兰伯特、GGX 高光、阴影、灯光累加都由 UE 的 Default Lit Shading Model 在材质节点之后完成,所以你看不到那些节点,但它们仍然存在:
Base Color
↓
UE Default Lit:N·L、阴影、间接光、Specular BRDF
↓
曝光与 Tonemapper
如果把 LUT 用来重映射 Base Color,流程只是:
BaseColor Texture → LUT → Base Color → UE 默认光照
它只改变"送入光照计算的颜色",没有替代兰伯特,也没有决定明暗边界。
生效链路:材质重新编译 → Shader permutation / HLSL 生成 → RHI 创建 GPU shader 与管线状态 → 图形 API → 显卡驱动与操作系统 → GPU 执行。
Unlit 不是"不走虚幻的渲染管线",而是"不走虚幻的光照模型"。
放到 UE 裡看,就是這條鏈
overflow-visible!
Blender
└ 計算平滑法線
└ 編碼成 float2
└ 寫入 EXTRATUV0
FBX
└ 將額外 UV Channel 一起匯出
UE Skeletal Mesh
└ UV Channel 1 / 2 / 3
UE Material
└ TextureCoordinate
└ Coordinate Index 指向該通道
└ ×2 - 1
└ 重建/解碼 Normal
├ 用於 World Position Offset 描邊
└ 用於自訂頭髮光照
UE 官方也把 mesh UV 描述為逐頂點屬性;材質可以通過不同的 Texture Coordinate 通道取得它們。
因为法线向量的长度恒为1(Rho是球面坐标中到原点的距离),所以不需要存储Rho,只需存储表示方向的两个角度参数Theta(方位角,绕Z轴旋转的角度)和Phi(极角,与Z轴的夹角)。原文中球面坐标编码法线的方法正是利用了这一点,通过atan2(n.y, n.x)计算Theta,直接取n.z(对应Phi的余弦值)来简化存储,把三维向量压缩成二维数据。
实际工程中的选择
| 方法 | 存什么 | 优点 | 缺点 |
|---|---|---|---|
| Stereo Projection | n.x, n.y | 解码快,简单 | 赤道附近精度差 |
| 球面坐标 | θ, n.z | 各方向精度较均匀 | 两极不稳定,解码需三角函数 |
| 八面体编码 | (做了八面体投影后的 xy) | 精度均匀,解码快,无极点问题 | 编码稍复杂 |
实际上在现代图形学中,八面体编码反而是更主流的选择,因为它兼顾了精度均匀和解码效率,避免了前两种方法的各自痛点。
压缩方法(球面坐标、八面体等)
Vertex Color 與額外 UV 都是在增加頂點屬性,不是在增加幾何面數。它們都會增加 Vertex Buffer 資料;預設 RGBA8 Vertex Color 和 half2 UV 通常都是每個 Render Vertex 4 bytes,但 UV 把位元集中在兩個分量上,所以更適合高精度的二維法線編碼。

八面體壓縮,,,,https://www.bilibili.com/video/BV15csWzTEyV?
Geometry Nodes 可以找到:
overflow-visible!
Geometry Proximity
├ Position → 最近點 Q,float3
└ Distance → 最短距離 |Q-P|,float
Distance 只是例如:
overflow-visible!
0.037
它沒有方向,不能用作法線。Geometry Proximity 的確會計算目標幾何上的最近位置和距離。
Geometry Proximity 的確會計算目標幾何上的最近位置和距離。编辑Blender Documentation
為什麼不能直接使用 Q - P
你可能會想到:
overflow-visible!
N = normalize(Q - P)
只有當模型頂點全部處於球體內部時,這個方向才剛好向外。
overflow-visible!
P 在球內
└ Q - P 指向球面外側
└ 方向正確
P 在球外
└ Q - P 指回球面
└ 方向與球面外法線相反
角色頭髮有些頂點可能在代理球外面,有些可能穿過球面。因此更穩定的做法不是計算 Q-P,而是直接取得代理模型在最近點 Q 上的法線。
Geometry Nodes 裡真正合理的節點鏈
使用一個球或經過調整的頭部代理 Mesh:
overflow-visible!
Object Info
└ Object:代理球/頭部代理模型
└ Transform Space:Relative
└ Geometry
然後:
overflow-visible!
代理模型 Geometry
└ Sample Nearest Surface
├ Mesh:代理模型
├ Value:Normal
└ Sample Position:頭髮的 Position
└ Value 輸出:代理表面最近點的插值法線
Sample Nearest Surface 的用途就是在來源 Mesh 的最近表面位置,取得並插值任意屬性;因此把來源幾何的 Normal 接入 Value,就能為每個頭髮位置取得代理表面的法線。编辑Blender Documentation
如果代理物體是完美球,甚至不用真的建立球
完美球的法線只取決於球心,不取決於半徑。
overflow-visible!
P = 頭髮頂點
C = 球心
N = normalize(P - C)
所以可以只放一個 Empty 表示球心:
overflow-visible!
Position
└ Vector Math:Subtract
├ A:Position
└ B:Empty 的 Location
└ Vector Math:Normalize
└ 球形法線 N
這裡不需要:
overflow-visible!
球的半徑
球面最近點
最短距離
因為不論球多大,沿同一條半徑方向的法線都是相同的。
-
材质描述
UMaterial保存 Shading Model、Blend Mode 等信息,表达"这个材质需要什么"。 -
编译期分支
材质系统据此生成对应的 Shader permutation。Unlit 不只是运行时的一个
if,很多不需要的代码和资源会在编译时被排除。


它不需要 Sky Atmosphere 才能存在。官方資料也將 Exponential Height Fog 定義為獨立的高度密度霧;它還能根據主要 Directional Light 的方向使用不同的霧散射顏色。编辑Epic Games Developers+1
