\[ \newcommand{\d}{\mathrm{d}} \newcommand{\norm}[1]{\left\| #1 \right\|} \]

前言

最近突然又想去搓一个离线渲染器作为玩具项目了,趁机深入看了下Disney Principled BSDF,也算是又从头学了一遍PBR,谨将所学的东西略微记下,以便后续查阅。

基本定义

由于Disney BSDF涉及大量的公式,这里有必要重新对于所有的公式和定义进行澄清。

NDF

法线分布函数NDF究竟是什么?之前我的视线总是聚焦于具体的公式如Beckmann,GGX和GTR等等,很少会去关注NDF的定义。其实在不久之前,我还以为NDF就是法线分布的pdf,满足半球面上的积分为一即可,然而NDF的定义却并非如此。

[1]中对于NDF的描述是 the statistical distribution of surface normals m over the microsurface,这里的\(\mathrm{m}\)指的就是微观法线,也就是我们常说的半程向量\(\mathrm{h}\)。乍一看这个定义似乎说的还是pdf,但NDF实际上是用微观面积与宏观面积的比值来定义的。具体来说,定义宏观面积的微元为\(\mathrm{d}A\),微观法线\(\mathrm{m}\)对应的立体角微元为\(\mathrm{d}\mathrm{\omega}_m\),那么\(D(\mathrm{m})\mathrm{d}\mathrm{\omega}_m\mathrm{d}A\)就等于(微观)法线朝向为\(\mathrm{m}\)微平面的总面积,因此\(D(\mathrm{m})\)项可以写为,其中\(\mathrm{d}A_m\)代表微平面的面积:

\[D(\mathrm{m})=\frac{\d A_m}{\d \mathrm{\omega}_m\mathrm{d}A}\]

然后来看下\(D\)的归一化条件,首先易得微平面投影面积的积分应该为\(\mathrm{d}A\)

阅读全文 »

德语文法小记

最近在用Duolingo学习德语,感觉效率有点低,而且截至Section 2了Duo也没有给出任何比较学院派的语法解释。正好最近搞了个一年免费的Gemini Pro,便想着配合网上的资料和AI补充下德语语法,也权当作为验证不用课本是否能够学好语言的一个小实验😎。

说在前面

为了不产生歧义,对所用的标注订一些标准

  • 专业名词的翻译英语用圆括号(),德语大括号{}

分类

按照语言形态学(Linguistic typology)的分类,大多数语言可以归为以下的两大类,分析语(Analytic)和综合语(Synthetic),分析语又可细分为孤立语(Isolating),而综合语又可细分为屈折语(Fusional/Inflected)、黏着语(Agglutinative)、多式综合语(Polysynthetic)和少式综合语(Oligosynthetic)。

一些典型的例子,汉语和越南语属于孤立语,日语属于黏着语,阿伊努语属于多式综合语,而德语则属于屈折语。

屈折语在Wiki上的定义如下:

1
屈折语(英语:fusional language,或称inflectional language)为综合语(synthetic language)之一种。屈折语和同为综合语的黏着语之间的分别在于屈折语的词素趋向连在一起,较为难以分割,意即屈折语的一个词缀经常同时表达多种意思,而黏着语的一个词缀一般倾向于只表达一种意思。不过黏着语和屈折语之间的界限很多时候并不明显,因此可将黏着语和屈折语之间的关系视为一个连续体,而很多语言则落于“完全的”黏着语和“高度的”屈折语之间。
阅读全文 »

TAA实现小结

项目地址

前言

刚把LearnOpenGL的渲染部分学完,IBL的效果是真不错,但是锯齿挺明显的:

又因为跑的是defer管线,很难上MSAA,想着加一套抗锯齿进去,查了些资料后还是选择了TAA而非FXAA和SMAA等后处理抗锯齿。TAA有很多优点,比如不会随着运动而抖动,同时能够为画面中几乎所有的部分完成抗锯齿,生来就与Defer兼容等等。加入TAA后的效果如下:

锯齿肉眼可见地基本消失了。

实现思路

TAA作为经典算法已经有很多教程了,GDC和Siggraph Course上各大厂商也给出了很完善的解决方案,如果要从头学习的话,建议是先看知乎上的介绍[4],然后是[1]和[5]这两篇很好的教程,另外[1]、[4]和[5]中提到的的很多技巧基本都是在[2]和[3]中提出来的。

阅读全文 »

引子

说在前面,本人并非wmc,同样也是音游苦手。本次只是为了探索Steamdeck的潜力,因此对于一些关于Maimai的概念解释和术语可能不准确,请见谅。

目前的效果是,能在Steamdeck的桌面模式下进行竖屏的游玩,只显示P1,解锁全曲目和难度,支持Freeplay但只能以Guest模式进入游戏,同时MelonLoader无法加载,因此没办法加载Mod如AquaMai。

另外请注意,这只是一次探索,并非最优雅的解决方式。

在开始前请速览下一节列出的资料。

资料速览

  • Maimai版本与Dump的对应关系:https://web.archive.org/web/20230730211439/http://teknoparrot.link.free.fr/multi/maimai%20finale.html

  • SDEZ 1.55 DX Prism Plus Download:https://www.emuline.org/topic/3489-reposted-maimai-finale-dx-dx-splash-universe-festival-sega-ringedge/page/12/#comment-165666

  • SDEZ 1.56 Download:https://dc.evilleaker.com/?lang=zh-cn

  • SDEZ配置指南1:https://performai.evilleaker.com/manual/games/maimai_dx/setup/

  • SDEZ配置指南2(主要看评论区):https://www.94joy.cn/maimai/267/

  • Segatools Git Repo:https://gitea.tendokyu.moe/TeamTofuShop/segatools

建议首先遵循SDEZ配置指南1完成相关资源的下载和配置,出现问题后再从EmulineSDEZ配置指南2的评论区中寻找解决方案。

P.S. Emuline目前已经关闭了注册渠道。

部署指南

阅读全文 »

简介

暴雪在2017年的GDC上做了关于守望先锋中ECS系统设计的分享,那个时候的暴雪啊,反观现在。B站有大佬的译制版本,链接在此

ECS系统

ECS的核心目的是将行为与状态严格解耦。为了做到这一点,ECS系统将架构划分为了实体 Entity、组件 Component还有系统 System,见下图:

  • Component只包含状态以及一些用于读取状态的辅助函数,辅助函数无行为且无副作用,Component的生命周期管理会使用多态来实现,即重写构造和析构函数。

  • Entity基本就是一个Components的集合,另外有唯一的Entity ID作为标识符。

  • System负责纯粹的行为,可以读取并更新Components,System的更新逻辑大致如下,EntityAdmin发起System的更新,每个System会重写自己的Update。System在Update时会遍历行为所涉及到的所有Component,并利用Component中保存的状态完成行为,必要时还可以使用访问同一个Entity上作为Sibling出现的其他类型Component。

行为与状态的解耦

上面的设计初看已经能解决行为与状态的耦合了,但是必须还要考虑System中的一些细节。比如系统A与系统B存在交互,系统B需要访问系统A并且保持一些状态,同时B的行为还会在A中产生副作用。暴雪最初的方法是创建一个全局变量用于维护这些一次性状态,这样做有若干的问题:

  • 增加编译开销,改全局变量会使所涉及到的任何System都重新编译
  • System间产生了耦合,无法确定Side Effect是该在A还是在B中

解决方法就是将这些一次性状态和逻辑移动到每个EntityAdmin中只有一个的Singleton Component中。说它时Component也不尽然,它既有行为又有状态,同时也不归属到Entity而是由Admin直接管理,不过考虑到有状态这一点叫Component也不是不行。

阅读全文 »

这篇文章是对于GAMES 104游戏引擎物理系统一节中GJK算法的学习总结。

Minkowski Sum 闵可夫斯基和

首先明确,一个闭合形状可以通过边界点集来定义。

对于两个形状 \(A\)\(B\) 来说,他们的闵可夫斯基和即为两个集合中的点两两相加形成的点集,记作 \(A+B\) 。一个直观的理解就是将其中的一个形状 \(A\) 所在坐标系的原点平移到另一个形状 \(B\) 的边界上,并沿着 \(B\) 的边界扫过一周,这样得到的新边界即为Minkowski Sum,如下图所示:

闵可夫斯基和有一个重要的性质,即闵可夫斯基和的凸包等于两个形状各自凸包的闵可夫斯基和。这样的话,如果两个形状本就是凸的话,Minkowski Sum的凸包就会恰好包含两个形状内无穷多个点两两相加的结果。

Minkowski Difference 闵可夫斯基差

如果两个形状相交的话,那么一定会存在至少一对 \(A\)\(B\) 中的点 \(p_A\)\(p_B\) ,使得 \(p_A-p_B=0\) 。尝试将这个观察与上面所述的Minkowski Sum联系起来,定义闵可夫斯基差为 \(A-B=A+(-B)\)\((-B)\)\(B\) 中的点绕原点翻转后得到的点集。

P.S. 按照Wiki上的说法,这其实不是Minkowski Difference的正统定义,而是Hermann Minkowski,不过在碰撞检测中只会用到上面的定义,这里就不再深究区别了。

根据Minkowski Sum的性质, \(A-B\) 的中含中包含了所有点对的差值。也就是说如果这个凸包能够包围原点的话,这两个形状就是相交的。这样我们就把一个本来要做无穷次判断才能得到答案的问题成功转化为了一个有界的问题。乍一看,单纯按照这种方法来测试的话复杂度仍是平方级别的。可以证明得到的凸包至多只有 \(|A|+|B|\) 个顶点,[3]中给出了在 \(O(n\log n)\) 内直接构造Minkowski Sum凸包的算法,大致的思路就是首先按照逆时针的顺序对于两个点集中的顶点排序,再使用双指针比较Polar Angle来保证不重不漏,具体可以参考原文章,这里我们不会用到这种算法。

阅读全文 »

前言

近期去了海南旅游,有幸观赏到了美丽的日落,配上洁净的沙滩和硕果累累的椰树可以称得上是种享受。现在想想,这种美丽的晚霞也非首次目睹,然而即便是欣赏了上千次,每当通过那绚烂的大气层看向那浩瀚无垠的银河时仍会感到头晕目眩。

此外,在本人玩过的游戏中,欧卡2中的大气和云也是令人印象深刻,比如下面这张(忘了是在哪里截的了,好像是在荷兰?)。不过这个云感觉不是用噪声做的,太逼真了,层次也分明。之前特意观察过,云似乎是有多张实拍的贴图,随着时间做简单的线性插值的。

当然巫师3的大气也很美,下面这张图好像是在陶森特截的。一个题外话,GAMES104也拿类似的场景举过例子,最终的画面表现中Bloom功不可没。不过感觉这张截图中的Fog似乎用力过猛了。

既然无论是湛蓝的晴天还是翠绿兼血红的晚霞都能让我们浮想联翩,不如手动去创造一个出来。

参与介质和体渲染

显而易见地是,大气是一种参与介质,光会与参与介质发生以下的几种交互:

自发光

阅读全文 »

本文为GAMES 202 PBR Material的笔记

PBR和PBR材质

所谓PBR,指的是渲染过程中的一切都是基于物理的,包括材质、光源、相机和光照传输,不过在实践中一般单指材质。

实时渲染中的PBR Materials

表面的材质主要有两种,Microfacet模型和Disney Principled BRDFs。不过按照课件所说,这两种其实并非是基于物理的,它们仍然遵循着RTR领域的优良传统:hack和近似。

体积渲染的话,关注点更多是在如何快速近似单次或多次散射。

Microfacet(微平面)模型

Microfacet模型将表面近似成若干微小的镜面,并通过控制微平面的法线朝向来模拟不同的材质。回顾一下Microfacet BRDF的公式:

\[ f(\mathbf{i}, \mathbf{o}) = \frac{\mathbf{F}(\mathbf{i}, \mathbf{h})\mathbf{G}(\mathbf{i}, \mathbf{o}, \mathbf{h})\mathbf{D}(\mathbf{h})}{4(\mathbf{n}, \mathbf{i})(\mathbf{n}, \mathbf{o})} \]

其中\(\mathbf{F}\)项是菲涅尔项,可以让材质展现菲涅尔效应,\(\mathbf{G}\)项是阴影-遮挡项(阴影和遮挡其实是两码事,下文会解释),\(\mathbf{G}\)是法线分布。

阅读全文 »

GAMES202 HW3的完成记录~

总览

在延迟渲染管线下,为一个光源为方向光,材质为漫反射 (Diffuse) 的场景实现屏幕空间下的全局光照效果(两次反射)。

作业3共分为三个部分: 1. 实现对场景直接光照的着色 (考虑阴影)。 2. 实现屏幕空间下光线的求交 (SSR)。 3. 实现对场景间接光照的着色。

作业文档里使用的术语是BSDF,不过既然本次作业只会涉及到漫反射材质,下文可能会出现BSDF和BRDF的互换。

Part 1:直接光照

这部分的两个子任务是计算漫反射材质BSDF的值以及光照的强度(包含可见性),非常简单。

首先是EvalDiffuse函数的实现。虽然EvalDiffuse接收了三个参数\(w_i, w_o\)\(uv\),但是对于漫反射材质来说,前两个代表方向的参数都是不需要的。作业说明中提示要用到保存在G-Buffer中的法线信息,意味着计算出来的BSDF是cosine weighted的。知道了这些之后,实现EvalDiffuse就是随手的事啦:

1
2
3
4
5
6
7
vec3 EvalDiffuse(vec3 wi, vec3 wo, vec2 uv) {
vec3 albedo = GetGBufferDiffuse(uv);
vec3 n = GetGBufferNormalWorld(uv);
float cos_theta = dot(n, wi);
vec3 bsdf = albedo * max(0.0, cos_theta) * INV_PI;
return bsdf;
}

漫反射BSDF

阅读全文 »

该文章内容主要来自于虚幻5的官方文档[4]。 # 介绍 一开始只觉得Lumen听起来很高大上,以为它除了渲染之外还有一些内容,最后发现是和Nanite搞混了(难绷)。按照文档的介绍,Lumen其实就是UE5新推出的全局光照(GI)和反射系统。最离谱的是,Lumen可以近似反射了无限次的漫反射光照和高光光照,甚至能在复杂环境中达成实时渲染。Lumen是此前各种屏幕空间技术(SSGI)和距离场环境光遮蔽(DFAO)的替代品。P.S. DFAO是虚幻4中所用的环境光遮蔽方法,文档里描述其与SSAO最大的区别是遮蔽在场景空间遮挡物中进行计算,因此出屏丢失数据不会导致瑕疵,至于原理吗,我猜与GAMES202中所讲的距离场阴影类似。

要启用Lumen,需要在Project Settings->Rnedering中启用Dynamic Global Illumination和Reflections这两项(新项目应该是默认启用的)。

特性

全局光照

  • 不限制弹射次数的间接漫反射光照
  • 真实阴影(在光线追踪器中,阴影就不需要用传统方法去做了)
  • 要求实时性的情况下会以较低的分辨率来计算间接光照 ## 天光(Sky Lighting) 天光在UE中负责模拟来自天空或远景的间接光照[1],Skylight计算的光照会填充场景中的阴影部分,增强环境光照。Lumen也会为半透明材质和体积雾计算一个低质量的全局光照。 ## 自发光材质 自发光材质的贡献在Lumen的Final Gather Process中被计算。目前Final Gather还不太理解,留个坑。 ## 反射 Lumen支持各种粗糙度材质反射,包括清漆(clear coat)和不透明材质的glossy反射,打开对应选项后也能渲染出半透明材质在最前面一层surface上的反射。Lumen还支持渲染单层水面的镜面反射。 ### Clear Coat材质 即基础层上覆盖了薄薄的一层透明涂料,比如车漆[2]。Clear coat材质也可以使用Cook-Torrance microfacet BRDF来表示[3]。 ## 双面树叶 允许光线在树叶中的次表面散射,效果出奇的好。下图右边是开启Two-Sided Foliage之后的效果

设置(略)

补充说明

Lumen的更新速度

因为lumen似乎使用的是类似光照探针的技术并将计算压力分摊到多帧上来保证实时性的,所以局部光照条件的变化会快速传播,而全局的光照变化(如禁用阳光)需要一定的时间才能收敛。实测如果在项目中将主要的平行光设置为不可见的话,场景会缓慢变暗。而且如果仔细观察的话,直接光照会立即消失,间接光照则是缓慢黯淡。

Lumen反射

阅读全文 »
0%