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

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
B
Blog RSS Feed
GbyAI
GbyAI
Google DeepMind News
Google DeepMind News
B
Blog
博客园 - 司徒正美
Vercel News
Vercel News
A
About on SuperTechFans
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
Recent Announcements
Recent Announcements
S
SegmentFault 最新的问题
博客园_首页
Apple Machine Learning Research
Apple Machine Learning Research
Stack Overflow Blog
Stack Overflow Blog
L
LINUX DO - 热门话题
Y
Y Combinator Blog
F
Full Disclosure
月光博客
月光博客
C
Cyber Attacks, Cyber Crime and Cyber Security
MongoDB | Blog
MongoDB | Blog
The Cloudflare Blog
The Hacker News
The Hacker News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
A
Arctic Wolf
Scott Helme
Scott Helme
V
Visual Studio Blog
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tor Project blog
P
Privacy International News Feed
Spread Privacy
Spread Privacy
G
GRAHAM CLULEY
Microsoft Security Blog
Microsoft Security Blog
N
News and Events Feed by Topic
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
PCI Perspectives
PCI Perspectives
小众软件
小众软件
博客园 - 【当耐特】
Cloudbric
Cloudbric
S
Secure Thoughts
L
LINUX DO - 最新话题
Google Online Security Blog
Google Online Security Blog
T
Troy Hunt's Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
W
WeLiveSecurity
V
Vulnerabilities – Threatpost
人人都是产品经理
人人都是产品经理
酷 壳 – CoolShell
酷 壳 – CoolShell
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
量子位

重归混沌的BLOG

给silly实现了一个ernro模块 | 重归混沌的BLOG 给silly实现了一个ernro模块 | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 如何基于LanguageServerProtocol来编写lint工具 谈谈游戏服务器中RPC模块的设计 谈谈游戏服务器代码抽象 最近碰到的一个分布式一致性问题 谈谈游戏服务器的自动化测试 对Raft协议的一点理解 使用mmap来学习/proc/pid/smaps 2023(完) 再次实现了一个Lua性能分析器 终于给Silly的定时器增加了取消功能 一次虚拟内存排查经历 游戏服务器分布式数据的一种同步的思路 为silly增加了互斥锁 2022(完) Go语言之闭包篇 一例误用unsafe包引起的内存问题 Go语言之内存篇 初识Go语言 重新抽象图形API 给Lua实现了一个数学库 谈谈跨平台图形API的抽象 寻路和Flocking算法的结合 行为树的一种高效实现 内测过程中Shader出现的问题 彻底解决多国语言 谈谈数据库的选型 再谈Lua热更新(终) 初窥Rust 关于游戏服务器的服务拆分 ECS的初步实现 ECS初探 屏幕空间(SreenSpace)的想象力 一些对辐射度量学的理解 深度缓冲和半透明渲染 Mysql的间隙锁 更新一些GPU相关知识 2020 地形渲染之爬过的坑 Lua5.3 GC源码阅读(5) 实现一个数据库存储队列 再谈分布式服务架构 游戏上线一个月后的反思 一次并发Bug 双向链表的三种实现 谈谈随机数的使用 再谈性能优化 2019 Lua中的函数式编程 重构登录逻辑 Unity资源管理(续) 谈谈Unity的资源管理 一次关于Cache的性能分析 历史之2018 DC3算法 移动平台native代码遭遇的坑 从CPU层面谈谈优化 开卷有益(UNIX编程艺术篇) GC竞争问题 通过Mesh投影来实现贴花系统 谈谈我对数据同步的理解 又一个类型提升引起的Bug Lua5.3 GC源码阅读(4) Lua5.3 GC源码阅读(3) Lua5.3 GC源码阅读(2) Lua5.3 GC源码阅读(1) 三角形光栅化时遇到的坑 一次git事故 再见2017 又一个lua调试器 客户端缓存落地方案 Paxos算法 HTTP服务器的特点 一次性能优化经历 关于CPU分支预测 C程序中让两个不同版本的库共存 实现了一个AOI模块 一个高可伸缩的游戏服务器架构 关于网络协议封装的一些新想法
再学计算机图形学入门
重归混沌 · 2020-10-11 · via 重归混沌的BLOG

在网上查资料时,无意间发现了一门课叫《现代计算机图形学入门》。于是事隔将近3年后,我再一次尝试图形学入门。这次学习从8月20号开始,一直到10月11日,约持续了一个半月。

但是这次的学习体验和上次是完全不同的,最直接的感受有3点。

  1. 《现代计算机图形学入门》要比《3D游戏编程大师技巧》中的内容现代的多,内容和知识体系都更全面。

  2. 现代计算机的性能远超《3D游戏编程大师技巧》著作当时,因此很多为了提高性能的Trick已经没有必要使用。这会使我们花费大量精力在局部细节,而不能窥其全貌。一个最简单的例子就是,一个简单的lerp就让我花费的大量时间来调试

  3. 《现代计算机图形学入门》把主要精力都放在了如何渲染上,至于一些优化手段很少提及,比如三角形裁切,剔除等。这只是一些加速的优化手段,并不影响最终的渲染效果。这些优化在入门阶段,其实并不重要。而在《3D游戏编程大师技巧》中我们会沉浸在各种优化技巧中,而失去了渲染的全貌。

下面记录一下这次学习的新收获。


  1. 透视矫正(光栅化)

在纹理采样时,所有3维坐标都已经被投影到一个2维平面,但是纹理坐标属于3维空间。因此2维坐标下的插值系数不能用在3维坐标进行插值, 下面先来看一下为什么不能插值。

我们先定义q(x,y,z,1)为3维坐标,r(x,y,z,w)为齐次坐标,s(x,y,z, 1) 为投影坐标。

Xr = aXq + b; Yr = cYr + d; Zr = e * Zq + f; Wr = g;

q’ = lerp(q) = q1 + (q2 – q1) t;
r’ = lerp(r) = r1 + (r2 – r1)
t = q’ (a,c,e) + ((b,d,f) + ((b,d,f)’ – (b,d,f)) t)

可以看到虽然q到r的变换不是线性变换,但是有一部分变量是由q*t来贡献的,因此在齐次空间下直接对3维空间坐标进行插值,并没有太大的问题。

再来看一下齐次坐标到投影坐标的变换。

Xs = Xr / Wr; Ys = Yr / Wr; Zs = Zr / Wr;

先对齐次坐标r进行lerp,得到r’ = (lerp(Xr), lerp(Yr), lerp(Zr), lerp(Wr)).

再对r’做投影变换,得到s’ = lerp(s) = (lerp(Xr)/lerp(Wr), lerp(Yr)/lerp(Wr), lerp(Zr)/lerp(Wr), 1) = lerp(r) / lerp(Wr).

这与直接对s做插值是一样的(在投影坐标系之后,不会再次执行齐次除法)。

根据q’和r’的公式推导,可以看到lerp(r)和lerp(q)是可以共用插值系数的。

再根据s’的公式推导,可以得出lerp(s) 和 lerp(q) 的插值系数多了一个lerp(Wr).

这就是为什么,在投影坐标系对3维坐标系下的坐标做插值时,需要先做一次投影除法,将其变换到投影坐标系下,做完插值后,再除以lerp(1/w)来变回3维坐标系。

有一点需要额外提的是,有些资料提到除以lerp(1/z)也可以。但这其实不正确,因为投影变换本质上压缩了z坐标的值,相比w(实际上是3维空间下的z)来讲,会有更大误差的。

  1. 光线追踪

这次学习过程中,最大的收获就要数光线追踪了。在之前的印象中,我一直以为,渲染就是模型空间->世界空间->投影空间->屏幕空间坐标系之间的转换然后再将其光栅化成像。

这是我第一次了解到,原来另一种更好的成像方式叫光线追踪,甚至还有更真实的路径追踪。

  1. 一些零碎的额外收获

正交平移矩阵用的是坐标,所以如果相机本来就在原点那么(l+r) / 2 刚好也等于0。可能这就是为什么要选用[-1,1] 而不是[0,2] 来定义NDC的原因。

右手坐标系,脸朝向的地方为z = -1。因此在计算z深度时需要对z做反转。BUT,在计算投影矩阵时,我们一般会对进屏幕和远平面取负。因此在光栅化时就不需要再次补偿了。

使用一个矩阵M对一个三角形的三个顶点做变换后,使用M来变换法线向量有可能会使法线向量变形。由于切线向量就是三角形的一个边,因此切线不会发生形变。因此,当需要变换法线时,需要额外算出一个矩阵。具体计算公式在《Mathematics for 3D Game Programming and Computer Graphics Third Edition》中第4.5节有详细推导。

球面与射线相交处的法线,并不是从从射线原点到球心的向量。因为射线可能斜着打中球面(甚至会只擦中一点)

中位线和法线的夹角并不能代表出射光线和视线的夹角,是为了更好的光照效果,blin-phong故意引入的。其中一个副作用是,运算更快了。

在微表面模型下,不同的粗糙程度会有不同的概率密度函数,这是因为光线的分布不一样。例如在镜面反射下,大部分光线能量都会沿着某一个特定的方向射出。其他方位的光线概率密度就会特别低。