MineGraph Docs Help

着色器 基本概念和轶事

何谓着色器?

对于一个完整的现代图形应用程序(基于如 OpenGL、Vulkan、DirectX 等图形库)来说,着色器是它渲染场景的手段。
我们知道图形应用程序的目的是读取模型文件或硬编码几何体,并在屏幕上绘制。着色器就描述了我们传入的几何体在屏幕上的何种位置何种方式绘制。

如果将资源包比作食材,那么着色器就是调料,上好的食材固然重要,但是调料也是激发出食材风味不可或缺的部分。

渲染模组/引擎如何帮助光影和游戏交流

渲染模组/引擎(下简称渲染模组)充当了游戏和光影的桥梁。由于原版所提供的信息极其有限(许多效果必须的变量没有直接提供, 缓冲区 也不足),如果想仅利用原版资源包的着色器来编写光影,无异于自讨苦吃。
渲染模组利用模组加载器提供的 接口 或直接对 Minecraft 源代码进行逆向工程并注入,接管了 Minecraft 的原版渲染管线,并提供了大量信息和更多缓冲区,为光影开发提供便利。

场景在着色器中发生了什么?

着色器类型

光影通常以多个着色器组成,着色器接收渲染模组提供的各种变量,以及先前乃至上一帧计算好的存入缓冲区的信息,按照程序进行计算后输出到指定的缓冲区。
不同渲染模组的工作原理不尽相同,所对应的光影包规格也有所区别。

一个着色器中又可以细分为多个阶段,也就是我们常说的顶点着色器、像素着色器等,这里按照通常管线的顺序,简单列举一下每种着色器计算的对象:

顶点着色器

Vertex Shader ,它的主要职责是变换坐标,包括顶点坐标、纹理坐标等,也可以处理顶点的颜色,计算对象为每个顶点(逐顶点操作)。

几何着色器

Geometry Shader ,这个阶段是可选的 ,它的主要职责是生成新的顶点,计算对象是每个图元,可以通过特定的索引值确定需要处理的顶点。

片段着色器

Fragment Shader ,它的主要职责是处理像素的颜色,也是大多数效果程序所处的位置,计算对象是每个像素。

计算着色器

Compute Shader ,这个阶段是可选的 ,它负责进行抽象计算。可以任意存取缓冲区,但是不能传入自定义变量,也没有默认输出。

当仅考虑顶点着色器和像素着色器时,在 上文 中我们所提到的所谓“ 何种位置 ”大多数时候就在顶点着色器中进行处理,而以“ 何种方式 ”则是顶点着色器和像素着色器的共同作用。

光栅化

光栅化通过将场景映射到二维平面上并分划为一个个的小格子,且仅保留每个格子中心点的信息来将几何体离散,然后一一对应到输出对象的最小单元(通常是缓冲区或屏幕的像素)上,也叫栅格化 (与 Photoshop 等图像处理软件中的同名功能一致)。

场景离散之后每个像素上都有确定的几何信息(法线、位置等)和纹理信息,然后根据这些几何信息利用数学方法来处理每个像素内容,因此这种方法也会造成一定的几何走样 (也就是我们通常所说的锯齿)。

“光栅化”这个说法通常是用作与光线追踪的对立:前者在栅格化之后再进行数学上的各种光照拟合和其他后期处理;而后者通过模拟光子运动计算光照之后直到需要输出图像或者进行其他后期处理时才栅格化。

渲染方法的发展

在计算机最早起步的阶段,还没有各种图形库和接口供开发者使用,那时候通常是通过准备特殊的图块字符映射表,然后将场景通过各种特殊的字符打印在屏幕上,那时候的图形也以 2D 为主。

随着计算机和 GPU 发展,3D 图形兴起,各种 3D 图形库也开始发展起来,OpenGL 就是其中之一。最早的图形库使用固定渲染管线 ,整个管线按照图形库的内置次序计算顶点光照、阴影等效果。开发者只可以配置渲染参数,没法精细控制每个几何体的效果,也没法自定义如何处理每个像素。

由于固定管线过于死板,希望能更自由地控制每个图元和像素的呼声越来越高, 可编程管线应运而生。

在可编程管线刚刚发展起来的早期图形程序中,渲染思想是将一类几何体全部准备好并传入特定着色器,然后立即在传入的几何体上计算诸如阴影和反射等效果,再输出到屏幕上。这就是我们现在所说的向前渲染法 (Forward Shading)。

这在 3D 图形程序刚起步的早期是没什么问题的,那时候场景中的几何体还不多。然而随着场景几何体增多、几何体之间相对于视角的遮挡越来越频繁,这种做法开始产生越来越多不必要的开销。因为每个着色器都会将所有传入的几何体计算一遍,即使在之前或之后的着色器中这个几何体会被更靠前的几何体遮挡。

出于上述原因, 延迟渲染法 (Deferred Shading)应运而生,它的思想是不再在传入几何体的阶段立即计算大多数效果,而是分为两个阶段:

  1. 几何缓冲阶段 :通过着色器将纯色场景和诸如法线贴图和反射贴图等全部作为纹理映射到几何体上,再分别写入多个 缓冲区 ,并通过 颜色附件 在着色器之间进行传递;

  2. 延迟处理阶段 :之后的着色器读取对应缓冲区的这些信息,在铺屏四边形上统一计算光照、反射等其他效果。

延迟渲染还提供了一个优势,在向前渲染法中,像素着色器仅会对图元覆盖区域进行着色,这就导致了溢出类的特效难以正确绘制(比如镜头特效和 环境光遮蔽 等),而延迟渲染法由于使用了铺屏四边形,所以可以在屏幕上任意位置进行绘制。
但同时由于其使用缓冲区的特性和场景几何信息的不可插值性 1MSAA 之类提升内部分辨率再进行降采样的抗锯齿方法也就不能使用了 2

  • [1] 想象两个一远一近的像素,它们的颜色代表了它们的坐标,那么两个像素的颜色应该是确定且等于坐标的 。如果将它们的颜色取平均后再赋回,那么它们的颜色就不等于坐标了,也就失去了几何意义

  • [2] 严格来说,如果在延迟渲染阶段不使用场景几何信息,或者保留降采样之前的几何信息,那么延迟渲染和 MSAA 也是可以共存的。

光影的发家史

开端之前

修补这个世界

在古早的 Minecraft 中(JE Alpha v1.2.2以前),替换纹理是一件很困难的事。那时候还没有游戏内资源包甚至纹理包的支持,如果玩家想要替换纹理,只能手动替换游戏的 .jar 文件中的资源。

然而当时的游戏只支持由基础分辨率为 16x 的单个方块拼接而成,总大小为 256x 的纹理集。如果强行将其替换为更高清的纹理集就会出现错误乃至直接崩溃。

截止纹理包出现前最后一个版本(a1.2.1_01)的 terrain.png

出于人们在互联网早年间特有的浪漫主义而产生的对高清纹理的追求 (其实不能加载高清纹理是个漏洞)xau 于 2010 年 10 月 1 日发布了 MCPatcher,它的主要目的就是修补对高清纹理的兼容性,后来还逐渐扩展了连接纹理等其他特性,直到 JE 1.8.8 停更前由 Kahr 进行维护。

快一点,再快一点

早期虽然游戏的内容较少,但硬件性能也普遍较弱。 出于人们对性能亘古不变的追求 ,2011 年 1 月 11 日, Scaevolus 的 FPS Boost 模组横空出世,它还有一个现如今我们看起来既熟悉又陌生名字:Optimine。

就像它的名字一样,Optimine 的主要目标就是 Optimise Minecraft ,提供了很多针对渲染管线的优化。

Optimine 发布后不久,它被 sp614x 接手,开始着手添加更多优化功能并吸收 MCPatcher 的功能来获取更好的兼容性和性能,同时它的名字也变成了我们所熟知的 OptiFine。

随着 OptiFine 的更新,自 JE 1.8 起,它已经包含了 MCPatcher 的几乎所有功能,也标志着 MCPatcher 正式退出历史的舞台。

如今 OptiFine 的文件通常是 <preview_>OptiFine_<Minecraft版本>_HD_U_<OptiFine版本>.jar ,事实上在 OptiFine 刚刚发布时,除了 文件名中 U 所代表的 Ultra 以外,还有更多其他的版本。这是 OptiFine 的 MinecraftForum 发布页 上对以前存在的各个 OptiFine 版本所提供功能的列表:

功能\版本

轻量 (Light)

标准 (Standard)

平滑 (Smooth)

多核 (Multi-Core)

抗锯齿 (AA)

极致 (Ultra)

性能优化

高清纹理

高清字体

更好的草地和雪地

连接纹理

随机实体

扩展选项

消除顿卡

多核 CPU 支持

抗锯齿和各向异性过滤

第一缕光

在现代玩家眼中,原版 Minecraft 画面多少有些单调,而在早年间这种情况则更甚。由于那时游戏内容较少,画面单一,光照粗糙(平滑光照直到 2011 年 2 月 22 日的 JE Beta 1.3 才加入),早期的 Minecraft 呈现出来一种很标准的梦核质感(甚至可以说在一定程度上催生了 Herobrine 的都市传说),玩家们也迫切地想要改变这一点。

终于,在 2011 年 1 月 7 日, daxnitro 发布了 GLSL 光影核心模组,它重新编写了一套可自定义管线,并定义了沿用至今的由资源包提供的法线和高光纹理。

JE 1.8 起,OptiFine 合并了光影核心的功能并继续开发,也首次推出了光影设置功能。由于其本身对原版进行了深度魔改,因而能提供很多相关变量,OptiFine 光影也由此开始快速发展。而光影核心则仍由 karyonix 针对基于其编写的老旧光影进行兼容性维护 1 ,直到 JE 1.12 正式停更。

  • [1] 事实上几乎所有光影都能无缝转换到 OptiFine 运行,但是基于 OptiFine 编写的光影则可能无法在光影核心上正常运行,兼容性维护只是让玩家和整合包作者在遇到 OptiFine 冲突时可以将光影核心作为备选方案。

鱼和熊掌

由于 OptiFine 对原版游戏程序的深度魔改和逆向工程,一定程度上导致了 OptiFine 无法开源,也就间接导致了其在 Forge 上就常和许多模组冲突。除了那些因为程序冲突而直接崩溃的,更多的模组是由于自身管线并不规范,导致 OptiFine 光影无法兼容(特别是又正好赶上 JE 1.6 附近的第一次模组大爆发,OptiFine 和光影作者几乎不可能主动做逆向兼容)。

JE 1.13 扁平化之后,由于 OptiFine 与原版游戏的深度绑定,间接导致了其需要跟进重构,促使了在正式版发布后 OptiFine 前所未有的长达三个月的空窗期(从 JE 1.13 开发版算起正好一年)。而 Forge 由于其屎山代码过于复杂,更是直接跳过了 JE 1.131.13.1 ,直到 JE 1.13.2 发布后四个月的 2019 年 2 月才姗姗来迟。

忍受不了 Forge 这个屎山, FabricMC 团队于 JE 1.14 推出了与其团队同名的模组加载器 Fabric 。其将 API 和加载器独立,并且只提供最为基础的功能来达到快速更新和高性能的目的。而 OptiFine 从未主动兼容过 Fabric,玩家只能通过第三方模组 OptiFabric 作为桥梁将 OptiFine 在 Fabric 环境下加载,然而作为桥梁的 OptiFabric 兼容性更加灾难,导致 OptiFine 几乎和大半 Fabric 模组不兼容。

漫漫征程

JE 1.16 发布后不久, 出于人们对性能亘古不变的追求CaffeineMC 团队开发的 Fabric 独占的优化模组 Sodium 横空出世。由于 JE 1.16 发布的下界更新过于庞大,导致自 JE 1.13 以来的性能债开始变得越来越不可忽视。Sodium 的目的很简单,那就是爆改原版渲染来提升性能,也由于其暴力的优化方式,通常需要安装 Indium 来保证兼容性(特别是修改了管线和添加了渲染特性的模组)。而迫切地想要在 Fabric 上提供模组兼容性良好、原生的光影支持,由 coderbot 发起, IMS 维护至今的 Iris 也几乎同时诞生。

由于 OptiFine 闭源,Iris 决定通过兼容特定光影来进而逐步还原 OptiFine 的光影功能,最早一批兼容的光影几乎都集中在 SVS 和 BSL 及其魔改光影。早期的 Iris 在其内部捆绑特定版本的 Sodium 来保证性能。Iris 1.1.2 及之后的版本正式兼容并不再内置 Sodium,但是仍然需要后者才能运行。

如今,Iris 已经将重心放在了开发独占特性上,但是其对 OptiFine 光影功能的还原并不尽如人意,当修改 Iris 代码使其独立运行时,它的性能表现也难以言喻。Iris 兼容性优先的策略也让其和 OptiFine 对待管线冲突的方法不尽相同,然而过于激进的兼容策略反而产生了某些渲染缺陷,比如植物魔法的魔力池中魔力的着色器会被直接跳过,导致无法正确渲染。更有一些长期占据 Issue 的问题也没能得到及时解决(几何缓冲混合方式下溢到延迟处理,以及最近才被解决的 block.properties 中的宏直接被删除而不是解析并替换)。

2022 年 7 月,Iris 的 Forge 非官方移植版 Oculus 在 Modrinth 上架,其目的与 Iris 一致,为 Forge 模组提供更好的光影兼容性。

渲染龙和它的光影朋友们

脚镣之舞

最早的基岩版光影基于 OpenGL ES 的 GLSL 或 DirectX 的 HLSL Windows / XBox 。由于接口限制,基岩版光影可以实现的效果非常少(甚至比 JE 1.16 之后的原版资源包着色器还少),并且为 向前渲染管线 ,但仍可以通过一系列奇技淫巧实现基于物理的渲染。平和的日子就这样过去,直到 SDGP 宣布胎死腹中,而微软宣布和英伟达为 Minecraft 基岩版带来光线追踪。

恶龙诞生

为了迎接即将到来的光线追踪,雷德蒙德工作室联合英伟达的开发人员为基岩版编写 (缝合封装) 了一套渲染引擎,其名曰渲染龙(Render Dragon)。

理想情况下 ,带有渲染龙的 Windows 基岩版能够通过其调用 RTX 2000 Nvidia/RX 6000 AMD/Arc Intel 系列及以上显卡的光线追踪加速单元以提升光线追踪的效率,而其他平台上其则能够起到优化作用。

然而事情并不总尽如人意,渲染龙的实际表现过于糟糕:

  • 其算法加密破坏了很多东西,除了光影还有例如区块显示、红石能量显示、亮度显示、夜视、透视、小地图这些原本都能由第三方光影实现的功能。而它们的消失给一些玩家带来了不少困扰。

  • 官方光追虽然在准确性上力压 Java 版一头,但其主要得益于 DirectX RayTracing ,在代码质量和细节调校上做的并不尽如人意,也对主打写实风格的地图与纹理创作者造成了很大困扰。

目前,渲染龙已覆盖到了各种 PC、移动设备及主机的基岩版上,替代了原本的渲染方案,而基于原始方案的第三方着色器也受到了牵连,直接导致了第三方光影的灭绝。

平台

游戏版本

备注

XBox

1.13.0.13

PS4

1.14.0

Windows 10

1.15.0.8

仅在 RTX Beta 版本上启用

Windows 10

1.16.200.51

期间经历多次反复禁用和启用,此处仅给出最终启用时间

Android / iOS / iPadOS

1.18.30.20

Android 经历多次反复禁用和启用,最终和苹果平台一起推出

Fire / NS/ Windows 10 (x86)

1.18.30

渲染龙在各平台基岩版的启用时间表(按版本顺序)

这些也是渲染龙在开发者群体内风评不佳的原因 (事实上在玩家群体内也没好到哪去)

屠龙之路

在此之后,渲染龙虽被成功破解,但由于破解团队收到微软的 DMCA 致函 1 ,以及新的光影编写方法过于麻烦 2 ,基岩版光影仍然沉寂。

  • [1] DMCA 致函是一种版权警告,当时团队将代码上传到了 GitHub,被致函删库。

  • [2] 当时编写光影需反编译游戏后进行编辑,再将特定文件回编译并放到固定文件夹替换相关文件,然后重启游戏。

不过事情在 2023 年迎来了转机。基岩版在这年 7 月发布了 延迟渲染 更新,提供了各种接口和信息。久旱逢甘霖,希望基岩版光影自此开始能进入新的时代。

附录:光影加载器圣经

关于光影加载器我有四不用。

第一,不用 Oculus,因为它 。别人都是要么给模组开发者史吃,要么给光影纹理开发者史吃,它干脆大发慈悲让所有人都雨露均沾。

第二,不用 Iris,因为它 。从三年前号称作为 OptiFine 的高兼容性替代品在 Fabric 上接管所有光影,到现在这嫌代码侵权那嫌光影太旧,一边只管兼容模组不修 BUG,一边推出自己的独占接口吸引 OptiFine 光影的开发者过来,最后发现吔到了一大口史。

第三,不用 Canvas,因为它 。Canvas 的初心是给模组提供渲染 API,在上面开发光影 = NTR,要被浸猪笼后烧死。

第四,不用 OptiFine,因为它 。一直以来,高清修复对内都为光影纹理提供了一个稳定的开发环境,对外从来不在乎模组兼容性,对自己人不可谓不忠诚。

Last modified: 23 August 2024