惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
Project Zero
Project Zero
K
Kaspersky official blog
G
Google Developers Blog
T
Threat Research - Cisco Blogs
T
The Blog of Author Tim Ferriss
Cyberwarzone
Cyberwarzone
Y
Y Combinator Blog
Recorded Future
Recorded Future
Blog — PlanetScale
Blog — PlanetScale
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cisco Talos Blog
Cisco Talos Blog
Latest news
Latest news
Microsoft Security Blog
Microsoft Security Blog
H
Help Net Security
S
Schneier on Security
P
Palo Alto Networks Blog
H
Hacker News: Front Page
N
News and Events Feed by Topic
N
Netflix TechBlog - Medium
博客园 - Franky
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
SecWiki News
SecWiki News
Cloudbric
Cloudbric
TaoSecurity Blog
TaoSecurity Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The Hacker News
The Hacker News
C
Check Point Blog
L
LangChain Blog
腾讯CDC
小众软件
小众软件
T
Tenable Blog
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
L
LINUX DO - 最新话题
A
About on SuperTechFans
Google Online Security Blog
Google Online Security Blog
C
Cisco Blogs
Recent Announcements
Recent Announcements
Hacker News: Ask HN
Hacker News: Ask HN
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Vercel News
Vercel News
雷峰网
雷峰网
美团技术团队
D
DataBreaches.Net
Martin Fowler
Martin Fowler
Help Net Security
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
F
Full Disclosure
博客园_首页

博客园 - 斌伯

【DX12龙书】第十一章 模板 【DX12龙书】第十章 混合 【DX12龙书】第九章 纹理贴图 【DX12龙书】第八章 光照 【DX12龙书】第七章 利用Direct3D绘制几何体(续) 【DX12龙书】第六章 利用Direct3D绘制几何体 【DX12龙书】第五章 渲染管线 【DX12龙书】第四章 Direct3D的初始化 Godot开发环境与学习资源 源码开放的引擎研究 海龟交易法操作商品期货 重新开始 使用node_redis进行redis数据库crud操作 ACTGame项目 我所理解的网络游戏<?>:战斗逻辑设计 我所理解的网络游戏<一>:网游的顶层设计 《Real Time Rendering》第四章 图形变换 《程序员的自我修养》学习笔记(一):简介 《Real Time Rendering》第三章 图形处理单元 《TCP/IP协议详解》学习笔记(一):概述 《Windows via C/C++》学习笔记(二):Working with Characters and String 《Real Time Rendering》第二章 图形渲染管线 《Real Time Rendering》第三版 翻译 《Windows via C/C++》学习笔记(一):Error handling
顺序无关半透明资料
斌伯 · 2026-05-17 · via 博客园 - 斌伯

详细说明顺序无关半透明(OIT)

下面我把 OIT(Order‑Independent Transparency,顺序无关半透明) 讲得非常细:它解决什么问题、原理、主流算法、优缺点、和普通 Alpha Blend 的区别,以及你最关心的:为什么它可以同时开启 Alpha Blend + 开启深度写入


一、传统 Alpha Blend 的致命痛点(为什么需要 OIT)

标准半透明渲染做法:

  1. 先画所有不透明物体(深度写入 ON)

  2. 把所有半透明物体距离相机从远到近排序

  3. 关闭深度写入(DepthWrite=OFF),开启 Alpha Blend,按顺序绘制

三大硬伤

  1. 必须 CPU 排序,性能开销大

    • 每帧排序所有透明物体 / 三角形

    • 场景复杂时(粒子、玻璃、植被)成本很高

  2. 物体交叉 / 互相穿透时,根本排不出正确顺序

    • 两个互相交叉的半透明面片:不存在全局正确的绘制顺序

    • 结果必然有部分区域渲染错乱(颜色叠反、边缘闪烁)

    image 358x269

  3. 半透明物体不能写入深度,导致 EarlyZ 对它们完全无效

    • 透明物体之间永远无法互相遮挡,只能靠绘制顺序

一句话:传统 Alpha Blend 是 “物体级排序”,只能处理简单、分离的透明物体;一旦复杂就崩。


二、OIT 的核心思想:从 “物体排序” 变成 “像素排序”

OIT 的本质:

不要求提前对物体排序,而是让 GPU 在每个像素位置,收集所有覆盖该像素的透明片元(fragment),在屏幕空间统一按深度排序,最后正确混合。

关键点:

  • 排序从 CPU → 物体级 → 每帧一次

  • 变成 GPU → 像素级 → 每个像素独立排序

  • 渲染时不需要管绘制顺序,随便画

image 391x293


三、OIT 的两大类型:精确 OIT vs 近似 OIT

精确 OIT(Exact OIT)—— 结果完全正确

代表算法:

  • 每像素链表 PPL(Per‑Pixel Linked Lists)

  • 深度剥离 Depth Peeling(多遍)

  • A‑Buffer / S‑Buffer(片元缓存)

共同特点:

  • 存储每个像素的所有片元(颜色 + 深度 + Alpha)

  • 最后 按深度从小到大排序,再做标准的 over 混合

  • 结果和 “完美排序的传统 Alpha Blend” 一模一样,无误差

典型流程(PPL,最常用的精确 OIT)

  1. 初始化两个全屏缓冲区

    • StartOffsetBuffer:每个像素的链表头指针

    • FragmentBuffer:全局片元池(存颜色 + 深度)

  2. 渲染所有半透明物体(不排序!)

    • 开启 Alpha Blend + 开启深度写入(关键!)

    • 片元着色器:

      • 计算颜色、深度、Alpha

      • FragmentBuffer 追加一条记录

      • 更新当前像素链表指针

    • 因为开启深度写入,透明物体之间可以正常遮挡,EarlyZ 也能工作!

  3. 后处理 Pass:逐像素排序 + 混合

    • 遍历每个像素的链表

    • 按深度从远到近排序所有片元

    • 执行标准 Alpha 混合:dst = src·α + dst·(1−α)

优点:结果完美、支持复杂交叉透明、透明物体可写深度、EarlyZ 有效缺点:显存开销大(需存所有片元)、GPU 占用高

近似 OIT(Approximate OIT)—— 速度快、有误差

代表算法:

  • 加权混合 OIT(Weighted Blended OIT)

  • 自适应透明(Adaptive Transparency)

  • 深度剥离(少量层数)

核心思路:

  • 不存储所有片元,而是用数学公式直接累积颜色与透明度

  • 不需要排序,单遍即可完成

  • 牺牲一点精度,换取极高性能

Weighted Blended OIT(最经典近似 OIT) 为例:公式(简化):

accumColor += color * alpha * weight
accumAlpha += alpha * weight

最后归一化:

finalColor = accumColor / accumAlpha
  • weight 通常用 深度函数(远的片元权重高),近似模拟 “远→近” 混合

  • 完全不需要排序、不需要链表、不需要多遍

  • 可以开启深度写入,透明物体之间正常遮挡

优点:单遍、极低开销、易实现、支持 EarlyZ缺点:结果是近似的,厚 / 高透明度物体可能有轻微色差或过亮


四、OIT 为什么能:Alpha Blend + 开启深度写入

回到你之前的疑问:

“Alpha 混合” 与 “禁用深度写入” 不是绑定的吗?

答案:只有传统排序 Alpha Blend 才必须禁用深度写入;OIT 完全不需要!

传统 Alpha Blend 为什么必须禁用深度写入

  • 依赖从远到近绘制顺序

  • 如果开启深度写入:先画的近物会挡住后画的远物,顺序直接报废

  • 所以只能:关深度写入 → 不遮挡 → 靠顺序叠

OIT 为什么可以开启深度写入

  • OIT 不依赖绘制顺序,所有片元都会被收集并在最后排序

  • 开启深度写入带来两大好处:

    1. 透明物体之间可以正常遮挡(符合物理直觉)

    2. EarlyZ 对透明物体生效:被不透明物体挡住的透明像素会被提前剔除,大幅节省 PS 开销

一句话总结:

  • 传统 Alpha Blend:关深度写入 → 靠顺序 → 不能遮挡 → EarlyZ 无效

  • OIT:开深度写入 → 不靠顺序 → 能遮挡 → EarlyZ 有效


五、OIT 的典型应用场景

  1. 复杂玻璃 / 建筑透明(多层、交叉)

  2. 粒子系统(烟雾、火焰、魔法特效,大量重叠)

  3. 植被渲染(树叶半透明、互相交错)

  4. 透明 UI / 体积光 / 镜头光晕

  5. 需要透明物体互相遮挡的场景


六、OIT 与传统 Alpha Blend 对比(核心差异)

表格


七、一句话总结 OIT

OIT 是把半透明渲染从 “靠 CPU 排好顺序再画”,变成 “随便画、GPU 每个像素自己排好再混合” 的一类技术;它允许透明物体开启深度写入、互相遮挡并享受 EarlyZ,彻底解决了传统 Alpha Blend 的排序与交叉错乱问题。

一、PPL OIT 内存布局图(你可直接照此手绘)

两块核心缓冲区

1)StartOffsetBuffer(和屏幕一样大)

结构:RWByteAddressBufferRWStructuredBuffer<uint>每个像素存一个链表头指针,指向 FragmentBuffer 中的节点索引。初始值:0xFFFFFFFF(-1,表示空)

屏幕像素 (0,0)  →  head = 2
屏幕像素 (0,1)  →  head = 5
屏幕像素 (0,2)  →  head = 0xFFFFFFFF(空)
...
屏幕像素 (x,y)  →  head = index

2)FragmentBuffer(屏幕大小 × N,节点池)

每个节点结构:

struct Fragment{float4 color;    // 源颜色 + alphafloat depth;      // 深度uint next;        // 下一个节点索引};

结构像这样:

FragmentBuffer[ 0 ] → { color, depth, next=3 }
FragmentBuffer[ 1 ] → { color, depth, next=... }
FragmentBuffer[ 2 ] → { color, depth, next=1 } → 这是像素(0,0)的链表头
FragmentBuffer[ 3 ] → { color, depth, next=0xFFFFFFFF }

链表结构(一个像素的样子)

像素(0,0)
StartOffset = 2
 ↓
Fragment[2] → next=1
               ↓
             Fragment[1] → next=3
                           ↓
                         Fragment[3] → next=0xFFFFFFFF(结束)

一句话总结

  • StartOffsetBuffer:每个像素一条链表的

  • FragmentBuffer:全局大池子,存所有透明片元

  • 原子计数器:分配下一个可用节点(IncrementUint


二、HLSL 伪代码(工程可直接用)

1)最简单最常用:Weighted Blended OIT(业界标准)

两个渲染目标:

  • Accum:R16G16B16A16_FLOAT存:accum += rgba * alpha * weight

  • Reveal:R16_FLOAT存:reveal *= (1 - alpha)

HLSL 透明绘制 Pass

// 输出RT_OUTPUT(float4, 0) accum;RT_OUTPUT(float, 1) reveal;void PS(...){float4 col = ...; // 纹理采样 + 光照float alpha = col.a;// 权重:远处更重要(典型公式)float weight = 1.0 / (0.01 + pixelDepth * pixelDepth);// 累加
    accum = float4(col.rgb * alpha * weight, alpha * weight);
    reveal = (1.0 - alpha);}

合成 Pass

float4 PS(...){float4 accum = AccumTex.Sample(sam, uv);float reveal = RevealTex.Sample(sam, uv).r;float3 rgb = accum.rgb / accum.a;float alpha = 1.0 - reveal;return float4(rgb, alpha);}

优点:单遍、快、显存低、全平台支持。现在游戏里最常用的 OIT。


2)精确 OIT:PPL(逐像素链表)HLSL

透明 Pass:收集片元

RWStructuredBuffer<uint> StartOffset;RWStructuredBuffer<Fragment> FragmentBuffer;RWByteAddressBuffer Counter;void PS(...){float4 col = ...;float depth = ...;// 原子分配节点uint newIdx = 0;
    Counter.InterlockedAdd(0, 1, newIdx);// 拿到当前像素链表头uint2 pix = uint2(floor(pin.pos.xy));uint head = 0;
    StartOffset.InterlockedExchange(pix.x + pix.y * width, newIdx, head);// 写入节点
    Fragment f;
    f.color = col;
    f.depth = depth;
    f.next = head;

    FragmentBuffer[newIdx] = f;}

合成 Pass:排序 + 混合

void PS(...){// 1. 遍历链表,把所有fragment读到数组
    Fragment fragments[8];int count = 0;uint idx = StartOffset[pix];while (idx != 0xFFFFFFFF && count < 8){
        fragments[count++] = FragmentBuffer[idx];
        idx = FragmentBuffer[idx].next;}// 2. 按深度从远到近排序SortFragmentsByDepth(fragments, count);// 3. 混合float3 color = 0.0;float alpha = 0.0;for (int i=0; i<count; i++){
        color = color * (1 - fragments[i].color.a) + fragments[i].color.rgb * fragments[i].color.a;
        alpha = alpha * (1 - fragments[i].color.a) + fragments[i].color.a;}return float4(color, alpha);}

优点:完美正确。缺点:显存不可控、性能高、移动端不友好。


三、DX12 vs Vulkan OIT 实现差异(极简版)

1)Per-Pixel Linked List 差异

DX12

  • 使用:UAV + StructuredBuffer

  • 原子操作:InterlockedExchangeInterlockedAdd

  • 绑定:SetGraphicsRootDescriptorTable

Vulkan

  • 使用:StorageImage / StorageBuffer

  • 原子操作:atomicUintAddatomicExchange

  • 绑定:DescriptorSet + binding

2)逻辑完全一样,只是 API 名字不同

  • 链表头缓冲

  • 节点缓冲

  • 原子计数器

  • 收集 → 排序 → 合成

OIT 算法本身不区分 API,区别仅在于如何创建 UAV/StorageBuffer。


四、主流 OIT 算法有哪些?现在广泛使用吗?(最重要)

1)现在真正广泛使用的只有一种

✅ Weighted Blended OIT(WBOIT)

  • 单遍、快、显存低、不用排序、不用复杂结构

  • 支持所有平台(PC / 主机 / 移动端)

  • 几乎所有粒子、烟雾、火焰、体积云、头发都用它

  • Unity、Unreal、CE5、自研引擎全都内置

这是工业界唯一 “普及” 的 OIT。


2)精确 OIT 只有少数工作室使用

❌ Depth Peeling(深度剥离)

  • 太老、太慢、多遍渲染

  • 几乎已淘汰

❌ Per-Pixel Linked List(PPL)

  • 结果完美

  • 显存不可控、性能高、移动端不行

  • 只用于影视、高精度渲染、极少游戏

❌ Dual Depth Peeling / Adaptive Transparency

  • 论文多,工程很少用

3)未来:新一代近似 OIT

✅ Layered Weighted Blended

  • 改进 WBOIT,质量更高

  • 开始在 3A 中出现

✅ Ray Tracing OIT

  • 光追直接按顺序混合

  • 下一代透明方案,但目前仍小众


五、最终结论(你一定要记住)

  1. 工业界最主流、唯一广泛使用的 OIT:Weighted Blended

  2. 精确 OIT(PPL/DepthPeeling)只用于研究 / 高精度,不普及

  3. OIT 可以开启深度写入 + 深度测试,Early Z 有效

  4. 传统 Alpha Blend 必须关深度写入、必须排序;OIT 不需要


如果你愿意...

给你画一张 PPL OIT 的内存布局图(StartOffset + FragmentBuffer)
或写一份 HLSL 伪代码(PPL 或 Weighted OIT)
或对比 Direct3D 12 / Vulkan 下的实际实现要点
主流的顺序无关半透明算法有哪些,在现在得到广泛使用了吗

已完成思考

主流顺序无关半透明(OIT)算法详解 + 行业应用现状

先给核心结论:OIT 算法分为精确 OIT(无视觉误差,结果与完美排序的 Alpha Blend 完全一致)近似 OIT(性能优先,误差可控) 两大类;其中近似 OIT(尤其是加权混合 WBOIT)已经在游戏、工业渲染领域得到极广泛的应用,精确 OIT 也在高精度渲染场景中普遍使用,早已不是实验室技术。


一、主流 OIT 算法全解析

所有 OIT 算法的核心目标,都是摆脱传统 Alpha Blend 对「从后往前绘制顺序」的强依赖,解决交叉透明、多层透明的渲染错误问题。下面按「精确 / 近似」分类,讲解当前行业主流的算法,淘汰的老旧方案仅做补充说明。

(一)精确 OIT(Exact OIT)

这类算法会完整收集每个像素的所有透明片元,按深度严格排序后执行标准 Alpha 混合,渲染结果 100% 正确,无任何视觉误差,代价是更高的显存与 GPU 计算开销。

每像素链表法(Per-Pixel Linked Lists, PPL)

当前精确 OIT 的绝对主流方案,也是你之前要求画图的算法,D3D11 引入 UAV(无序访问视图)后开始普及,D3D12/Vulkan 对其支持已非常完善。

核心原理

在 GPU 显存中构建两套核心缓冲区,为每个像素维护一个单向链表,存储所有覆盖该像素的透明片元数据:

  1. StartOffsetBuffer(起始偏移缓冲区):全屏 2D 缓冲区,每个像素存储一个 32 位无符号整数,代表该像素的链表头在全局片元池中的偏移地址,初始值为0xFFFFFFFF(空链表)。

  2. FragmentBuffer(片元缓冲区):一维结构化缓冲区,作为全局片元内存池,每个节点存储:片元颜色 RGBA、深度值、下一个节点的偏移地址。

实现步骤(D3D12)
  1. 预分配缓冲区:根据场景最大透明片元数量,预分配 FragmentBuffer 的大小(通常按全屏像素数的 4~8 倍分配);初始化 StartOffsetBuffer 为全0xFFFFFFFF

  2. 透明物体渲染 Pass

    • 开启深度测试,可开启深度写入(不透明物体会提前遮挡透明片元,Early Z 生效);

    • 像素着色器中,通过InterlockedAdd原子操作,从 FragmentBuffer 中申请一个空闲节点;

    • 将当前片元的颜色、深度、当前像素的链表头偏移写入节点;

    • 原子更新 StartOffsetBuffer 中当前像素的链表头,指向新申请的节点。

  3. 后处理混合 Pass

    • 遍历每个像素的链表,收集所有片元;

    • 按深度值从远到近排序;

    • 执行标准 Alpha 混合公式C = a_s*C_s + (1-a_s)*C_d,得到最终像素颜色。

优缺点
  • ✅ 优点:结果完全正确,支持任意复杂的交叉、多层透明,透明物体可开启深度写入,Early Z 优化生效;

  • ❌ 缺点:显存开销高(需预分配片元池)、GPU 原子操作有开销,极端场景下片元数量会超出预分配上限。

适用场景

建筑可视化、CAD 工业软件、载具座舱渲染、复杂玻璃幕墙等对透明精度要求极高的场景。

A-Buffer 法

PPL 的前身与优化变体,核心思路是为每个像素预分配固定大小的数组,而非动态链表,存储该像素的所有透明片元。

  • 核心差异:无需原子操作构建链表,内存布局更规整,GPU 访问效率更高;

  • 缺点:固定数组大小会浪费显存,或无法容纳极端情况的大量片元;

  • 适用场景:片元数量可控的场景,如粒子系统、UI 透明渲染。

深度剥离(Depth Peeling)

最早的精确 OIT 算法,通过多 Pass 渲染实现,无需 UAV / 原子操作,兼容性极强。

核心原理

通过 N 次渲染 Pass,逐层剥离透明片元:

  1. 第 1Pass:渲染所有物体,记录最近的深度值(最前面的一层);

  2. 第 2Pass:仅渲染深度大于上一层的片元,得到第二层透明片元;

  3. 重复 N 次,直到剥离完所有透明层;

  4. 最后将所有层从后往前混合,得到最终结果。

现状

基础版深度剥离已经很少使用,原因是 Pass 数量与透明层数成正比,性能开销极大;仅双深度剥离(Dual Depth Peeling) 等优化变体,还会在兼容性要求高的老旧平台使用。

K-Buffer 法

A-Buffer 的轻量化变体,仅为每个像素存储前 K 个最近的透明片元,超出 K 层的片元会被合并丢弃。

  • 优点:显存开销可控,性能比完整 PPL 高很多;

  • 缺点:K 值过大会增加开销,K 值过小会丢失远处的透明层,出现误差;

  • 适用场景:移动端、半透明层数可控的游戏场景。


(二)近似 OIT(Approximate OIT)

这类算法不存储、不排序所有片元,而是通过数学公式直接累积片元的颜色与透明度信息,单 Pass / 双 Pass 即可完成渲染,性能极高,误差可控,是当前行业应用最广泛的 OIT 方案

加权混合 OIT(Weighted Blended OIT, WBOIT)

行业绝对主流的 OIT 算法,2013 年由 Morgan McGuire 提出,是目前 90% 以上游戏 / 引擎内置 OIT 的基础方案。

核心原理

彻底放弃排序,通过深度权重函数为每个透明片元分配权重,直接累积颜色与透明度,最终通过归一化得到近似正确的混合结果。核心公式如下:

  1. 累积 Pass(双渲染目标):

  2. plaintext

// RT0:加权颜色累积
accumColor += color.rgb * color.a * weight
// RT1:加权透明度累积
accumAlpha += color.a * weight
  1. 合成 Pass:

  2. plaintext

// 归一化得到最终颜色,背景色为不透明场景的渲染结果
finalColor = accumColor / max(accumAlpha, 1e-6) + backgroundColor * (1 - saturate(accumAlpha))
  1. 权重函数(核心):权重与片元深度负相关,越远的片元权重越高,近似模拟「从远到近」的混合,最常用的权重公式:

  2. plaintext

weight = max(1e-2, min(1e3, 10000.0 / (1e-5 + pow(depth, 4.0) + 0.01)))
实现核心优势
  • 单 Pass 累积,双 Pass 完成,无需 UAV、原子操作、排序,性能开销极低,仅比传统 Alpha Blend 高一点;

  • 显存开销极小,仅需 2 个全屏渲染目标;

  • 完全不依赖绘制顺序,交叉透明、多层透明的效果远超传统排序 Alpha Blend;

  • 完美兼容所有 GPU,包括移动端,支持 Early Z 优化。

优缺点
  • ✅ 优点:性能极高、实现极简、兼容性极强、无排序开销、显存占用低;

  • ❌ 缺点:结果是近似的,高透明度、厚层透明的场景会出现轻微色差、过亮或过暗的问题。

适用场景

几乎所有实时渲染场景:游戏中的粒子、烟雾、玻璃、植被、UI,移动端游戏,数字孪生,工业可视化等,是当前使用最广的 OIT 算法。

动量守恒 OIT(Moment-Based OIT, MBOIT)

WBOIT 的进阶方案,通过统计动量来近似片元的深度分布,无需排序即可得到接近精确 OIT 的结果,误差远小于 WBOIT。

  • 核心原理:为每个像素存储多个动量值(如 4 个),通过动量函数重建片元的深度分布,计算出正确的混合权重;

  • 优缺点:精度远高于 WBOIT,性能比 PPL 高很多,显存开销仅需 4 个渲染目标;动量数量越多,精度越高,开销越大;

  • 适用场景:主机 / PC 端 3A 游戏,需要更高精度透明效果、但不想用 PPL 的场景。

加权分层 OIT(Weighted Layered Blended OIT, WLBOIT)

WBOIT 的改进版,将透明片元按深度分为 2~4 层,每层单独做加权累积,最后分层混合。

  • 核心优势:分层后大幅降低了 WBOIT 的色差问题,精度接近精确 OIT,性能仅比 WBOIT 略高;

  • 适用场景:PC / 主机游戏,多层透明、高透明度的场景,如水下渲染、多层玻璃。

自适应透明(Adaptive Transparency)

介于精确 OIT 与近似 OIT 之间的方案,先通过一个 Pass 统计每个像素的透明度分布,生成自适应的权重函数,第二个 Pass 做加权混合。

  • 优缺点:误差比 WBOIT 小很多,性能比精确 OIT 高,需要两个 Pass;

  • 适用场景:影视级实时渲染、数字人渲染等对透明精度要求较高的场景。


二、OIT 在当前行业的应用现状

结论:OIT 早已不是小众技术,近似 OIT 已经得到全行业的广泛应用,精确 OIT 也在高精度场景中普遍使用,具体情况如下:

主流引擎的内置支持

所有主流商业 / 开源引擎都已内置 OIT 方案,作为传统 Alpha Blend 的替代选项:

表格

游戏行业的应用情况

  • 移动端游戏:WBOIT 是绝对主流,几乎所有中重度手游的粒子、烟雾、半透明特效都在使用,解决了传统排序 Alpha Blend 的交叉透明错误问题;

  • PC / 主机 3A 游戏:WBOIT 是标配,用于粒子、植被、玻璃;部分高精度场景(如座舱、建筑玻璃)会使用 PPL/MBOIT,解决多层交叉透明的渲染错误;

  • 独立游戏 / 二次元游戏:WBOIT 广泛用于角色半透明服饰、特效、场景透明物体,实现成本极低,效果提升显著。

工业 / 专业渲染领域

  • 建筑可视化、CAD/CAM 软件:精确 OIT(PPL)是标配,需要精准渲染多层玻璃、钢结构、管线的交叉透明效果,不能有视觉误差;

  • 数字孪生、智慧城市:WBOIT 广泛用于建筑、管网、水体的半透明渲染,平衡性能与效果;

  • 医疗可视化:精确 OIT 用于人体组织、血管的多层透明渲染,需要严格的深度顺序正确。

为什么还有很多场景在用传统排序 Alpha Blend?

不是 OIT 不好用,而是传统 Alpha Blend 有不可替代的优势:

  1. 实现最简单,无需额外的缓冲区、Pass、复杂代码;

  2. 性能开销最低,在半透明物体少、无交叉的场景(如单个 UI 面板、简单玻璃),完全够用;

  3. 美术可控性更高,传统 Alpha Blend 的效果完全由绘制顺序决定,美术可以精准控制层级。

OIT 是传统 Alpha Blend 的补充与升级,而非完全替代,二者在当前的渲染管线中是共存的。


三、D3D12 vs Vulkan 实现 OIT 的核心差异

两者的核心渲染能力完全一致,都能实现所有主流 OIT 算法,仅在工程实现、内存管理、同步机制上有差异:

表格


四、补充:WBOIT 的 D3D12 可用 HLSL 伪代码

完全贴合你 Chapter10 的代码风格,可直接集成到你的渲染管线中:

预定义与缓冲区

// 根签名中绑定:2个UAV渲染目标(累积用),1个SRV(不透明场景的渲染结果)RWTexture2D<float4> gAccumColor : register(u0);RWTexture2D<float>  gAccumAlpha : register(u1);Texture2D<float4>   gSceneColor : register(t0);SamplerState        gsamPointClamp : register(s0);cbuffer cbPass : register(b1){float4x4 gViewProj;float3 gEyePosW;float gNearZ;float gFarZ;}

透明物体渲染的像素着色器(累积 Pass)

struct VertexOut{float4 PosH : SV_POSITION;float3 PosW : POSITION;float3 NormalW : NORMAL;float2 TexC : TEXCOORD;float  Depth : DEPTH;};float4 PS(VertexOut pin) : SV_Target
{// 1. 基础纹理采样与光照计算(和你Chapter10的代码完全一致)float4 diffuseAlbedo = gDiffuseMap.Sample(gsamAnisotropicWrap, pin.TexC) * gDiffuseAlbedo;#ifdef ALPHA_TESTclip(diffuseAlbedo.a - 0.1f);#endif// ... 省略光照计算,得到最终的litColorfloat4 litColor = ComputeLighting(...);
    litColor.a = diffuseAlbedo.a;// 2. 线性化深度,计算权重float linearDepth = (pin.Depth - gNearZ) / (gFarZ - gNearZ);// 标准权重函数:深度越大,权重越低,模拟从远到近的混合float weight = max(1e-2, min(1e3, 10000.0 / (1e-5 + pow(linearDepth, 4.0) + 0.01)));// 3. 原子累积到UAV(D3D12支持像素着色器的UAV原子操作)int2 screenPos = int2(pin.PosH.xy);float4 premulColor = float4(litColor.rgb * litColor.a, litColor.a);InterlockedAdd(gAccumColor[screenPos].r, premulColor.r * weight);InterlockedAdd(gAccumColor[screenPos].g, premulColor.g * weight);InterlockedAdd(gAccumColor[screenPos].b, premulColor.b * weight);InterlockedAdd(gAccumAlpha[screenPos], premulColor.a * weight);return litColor;}

后处理合成 Pass 的像素着色器

float4 PS(float4 posH : SV_POSITION, float2 uv : TEXCOORD) : SV_Target
{int2 screenPos = int2(posH.xy);// 读取累积值float3 accumColor = gAccumColor[screenPos].rgb;float  accumAlpha = gAccumAlpha[screenPos];// 读取不透明场景的背景色float3 backgroundColor = gSceneColor.Sample(gsamPointClamp, uv).rgb;// 归一化合成float3 finalColor;if(accumAlpha < 1e-6){
        finalColor = backgroundColor;}else{float3 transColor = accumColor / accumAlpha;float transmittance = 1.0 - saturate(accumAlpha);
        finalColor = transColor + backgroundColor * transmittance;}return float4(finalColor, 1.0);}