gpu编程如何一步步学习_【授权翻译】如何开始学习图形学编程

f077e8c20608c5a79ace1c5a83d2dac9.png

【授权翻译】如何开始学习图形学编程

原文作者:Eric Arnebäck
原文链接: https:// erkaman.github.io/posts /beginner_computer_graphics.html
有关作者:Playdead 的图形程序工程师(Playdead 目前的作品有 INSIDE LIMBO )
相关链接:作者的 Gmail,Github,Linkedin,Twitter,YouTube
译者:Angus

前段时间,我在推特开放个人私信,邀请所有人来问我有关计算机图形学的问题。自那以来,我最常被问到的问题就是「我该如何开始学习图形学编程呢?」。鉴于这个问题最常被问到,我也重复回答了这一问题许多次了,我决定在这篇文章中总结我的所有建议。

建议1:从光线追踪(Raytracing)和光栅化(Rasterization)开始

近年来,出现了许多针对 GPU 硬件进行编码的图形 API: Direct3D、OpenGL、Vulkan、Metal、WebGL 等等。从这些 API 开始学习图形学编程的话,可能会很难上手,因为它们通常需要很多「样板代码(boilerplate code)」,而且我认为它们根本不适合初学者。对于一个完全的图形学初学者来说,使用这些 API,即使是「绘制第一个三角形」也是一项艰巨的任务。当然,我们也可以选择使用 Unity 和 Unreal Engine 这样的游戏引擎。在这种情况下,游戏引擎将为您完成与图形 API 「对话」的繁琐工作。但是我认为对于一个完全的初学者来说,即使是一个游戏引擎也有太多的东西需要去学习,而这些时间应该花在一些更简单的事情上。

因此,我建议初学者试着自己编写 Raytracer 或 Software Rasterizer(或者两个都试一试!)。简单地说,Raytracer (对应光线追踪)是一个这样的程序:它通过从屏幕上的每个像素发出光线来渲染 3D 场景,并进行大量的求交计算和物理光照计算,以计算出每个像素的最终颜色。而 Software Rasterizer (对应光栅化)呢,它是这样渲染 3D 场景(这些场景在大多数情况下只是一堆 triangle,即三角形)的:对于每一个我们要在屏幕上绘制的三角形,我们先找出这个三角形涵盖了屏幕上的哪些像素,然后对于其中的每一个像素,我们计算出光线是怎么与这个像素对应的 三角形上的点进行交互的。通过这种光线的交互计算,我们得到了像素的最终颜色。光栅化要比光线追踪快得多,也是现代 GPU 绘制 3D 场景的算法。Software Rasterization 简单的说就是我们在 CPU 上做这个 Rasterization,而不是在 GPU 上。

光线追踪和光栅化实际上是两种很简单的算法,对于初学者来说,实现它们要比理解现代图形 API 容易得多。此外,通过亲自实现这些算法,初学者将了解计算机图形学的许多基本概念,如点积、叉积、变换矩阵、摄像机等等,无需把时间浪费在与现代图形 API 搏斗。我相信这些图形 API 已经劝退了许多图形初学者,而把 Software Rasterizer 或 Raytracer 作为您的第一个图形学编程项目是战胜这个拦路虎的好方法。

我注意到,在学习图形 API 之前先学着写一个 Software Rasterizer,比起直接学习图形 API 有一个巨大优势——那就是在你使用 API 时,遇到错误你会感觉更容易 debug 了。因为这些 API 基本上只是提供一个到基于 GPU 的 Rasterizer 的接口,如果你知道了这些 API 在幕后是如何工作的(即一个 Rasterizer 是如何工作的),那么 debug 代码就会变得容易得多。

对于如何写一个 Raytracer,我总会推荐别人去阅读 Peter Shirley 撰写的三本书(现已完全免费)。

对于如何写一个 Software Rasterizer,请参阅这些:1,2,3,4。

建议2:学习必要的数学知识

我的下一条建议是,你应该学习一些图形学编程必备的数学知识。别担心,我作为一个图形程序员,在日常工作中使用到的数学概念和数学方法其实出乎意料的少,所以你要学的数学知识可能没你想象的那么多。当你刚开始学习图形学时,「线性代数」将是你主要用到的数学工具。一些会被你经常用到的线性代数中的概念如下:

  • 点积。
  • 叉积。
  • 球面坐标。
  • 变换矩阵(提示:作为图形程序员,你将主要使用 4x4 的矩阵,所以不要花任何时间研究大型矩阵)。
  • 旋转矩阵、缩放矩阵、平移矩阵、齐次坐标、四元数。
  • 标准正交基矩阵。
  • 求交计算。大部分是像求光线与一个球体、一个平面或一个三角形的交点这种求交运算。
  • 列主序和行主序。这是很多初学者会被绊倒的小概念,请确保你能完全理解它。看 这篇文章
  • 如何用观察矩阵、透视变换矩阵表示一个摄像机。这是一个难倒很多初学者的知识点,所以这是一个值得你仔细学和深度学的知识点。对于透视变换矩阵,请参阅 这个教程。对于观察矩阵,请参阅 这个 。

从初学者到中级水平,除了上述的数学知识,你基本上不会遇到其他的数学知识。但一旦你进入像「基于物理的着色」这样的主题,一个被称为「微积分」的数学领域也变得有用,但这是另一个故事了:-)。

我会在下面罗列一些优秀的线性代数学习教程。Immersive linear algebra 是一个优秀的在线版的数学书。Essence of linear algebra 是一个可以让你可视化地学习很多概念的视频系列。OpenGL tutorial 也解释了一些简单却有用的线性代数概念。另一个优秀的学习资源是 Morgan McGuire 的 The Graphics Codex(译者也强烈推荐,IOS 上可以找到同名 APP)。

建议3:「绘制第一个三角形」时的一些 debugging 技巧

e28ce60aa29e2d1dad6f591f4cb22afb.png
「绘制第一个三角形」程序截图

一旦你写过一个 Raytracer 或 Rasterizer,你在学习图形 API 时就会感觉更自信。图形 API 的「Hello World」是「绘制第一个三角形」 ,然而要用图形 API 绘制出第一个三角形可能会出人意料的难,这是因为通常我们必须要先写一大堆样板代码,而且 debug 图形学代码对初学者来说会很难。如果你在绘制第一个三角形时遇到问题,并且得到的是黑屏而不是三角形,我将在下面列出一些 debugging 建议。这是我遇到相同问题时,用来排查错误的方法步骤,你可以逐条试验:

  • 通常来说,问题会出在透视矩阵和观察矩阵上, 因为这俩非常容易错。在顶点着色器中,对每个顶点来说,你首先把它乘上模型矩阵,然后是观察矩阵、透视矩阵,最后做一次透视除法(尽管这个最后的除法是在幕后自动进行的,通常不需要你亲手做)。尝试手动完成这个过程,以检查你所用矩阵的正确性。如果你期望一个顶点是可见的,那么在透视除法之后,这个顶点的坐标就应该是标准化设备坐标(Normalized Device Coordinates)了——x 的范围应该在[-1, +1],y 的范围在[-1, +1],z 的范围也在[-1, +1](OpenGL 是如此,但对 Direct3D 来说,z 的范围在[0, 1])。如果你期望可见的这个顶点的坐标值不在这些范围中,那么这个顶点是不可见的(因为所有坐标不在这个范围内的顶点都将被硬件裁剪),你的这些矩阵可能哪出错了。
  • 你是否记得把深度缓存清理为有意义的值?比如说,如果你使用 Direct3D 的深度测试函数D3DCMP_LESS,接着你把深度缓存都清理为 0,然后你会发现屏幕上没有任何绘制结果,这是因为所有顶点都没有通过深度测试。总之,确保你自己完全理解了深度测试,并配置合理的深度测试设置。
  • 确保你正确地传递了矩阵(如透视矩阵、观察矩阵)给 GPU。 数据没有被正确地传递给 GPU 这种事时有发生。你可以用一些 GPU 的 debugging 工具 例如 RenderDoc 来检查你传递的矩阵。同样的,确保你传递的顶点数据都是正确的。只传递了部分顶点数据是一个常犯的错误。
  • 背面剔除(Backface Culling)是另一个让很多初学者感到棘手的细节。例如,在 OpenGL 中,默认情况下所有的背面三角形都被剔除,不渲染。但如果你不小心制作了一个背面三角形(例如你顶点顺序搞反了)并尝试渲染它,它将不会被渲染出来。我的建议是,当你试图渲染你的第一个三角形时,暂时禁用背面剔除。
  • 检查图形 API 函数返回的所有错误代码,因为它们可能包含有用的信息。如果你使用的 API (比如 Vulkan)能够访问某种 Debugging Layer,那么你应该试着用一下。
  • 对于做任何有关图形学的 debugging,我强烈推荐你学习使用一些 GPU 的 debugging 工具,比如 RenderDoc 或 Nsight。对于你的图形应用程序运行的每一步,这些工具都会提供一个 GPU 当前状态的预览给你。它们能让你轻松愉快地检查矩阵有没有被正确传递,深度缓存正不正确,深度测试、背面剔除的设置正不正确等等。你在图形 API 中能进行的所有设置,都能在这些工具中进行检查。RenderDoc 的另一个我非常喜欢且经常使用的特性是,它允许你逐步遍历像素的片段着色器(不过在编写本文时,这个特性似乎是 Direct3D 独有的)。你只需单击一个像素,RenderDoc 允许您逐步通过片段着色器进行计算,并为像素赋予其当前颜色值。下面的动图展示了这个过程:我单击一个橙色的像素,然后逐步执行片段着色器中的计算,从而最终使得像素被赋予这个橙色。如果你想知道 RenderDoc 还有哪些功能和特性,可以去 Baldur Karlsson的 YouTube 频道 看看。

6791e7ce2a0e8b9f6094c2655ad88ef4.gif
RenderDoc 使用演示

建议4:适合初学者练手的项目

在我看来,要学好图形学编程的最好方法是自己去实现各种渲染技术。我将在下面给出一个适合初学者实现和学习的项目的列表。

  • 使用球面坐标制作一个球面网格(Mesh),并渲染它。
  • 实现简单的漫反射(Diffuse)和光泽反射(Specular)着色器。
  • 实现方向光(Directional Lights),点光(Point Lights)和聚光灯(Spot Lights)。
  • 高度图(Heightmap)渲染。
  • 为简单的网格格式(如Wavefront.obj)编写一个简单的解析器(Parser),将其导入程序并进行渲染。特别是,尝试一下导入并使用纹理(Textures)渲染网格物体。
  • 实现一个简单的 Minecraft 风格的渲染器。渲染一个Minecraft-like 的世界其实出乎意料的简单,而且很具有学习价值。
  • 使用立方体贴图(Cubemaps) 渲染反射效果。
  • 使用阴影贴图(Shadow Maps) 渲染阴影。
  • 实现视体剔除(View Frustum Culling)。这是一种简单但非常实用的优化技术。
  • 实现粒子系统的渲染。
  • 学习如何实现伽马矫正(Gamma Correction)。
  • 实现法线贴图(Normal Mapping)。
  • 学习如何使用实例渲染(Instanced Rendering)有效地渲染大量网格。
  • 给网格蒙皮(Skinning),然后让它动起来。

接下来是一些进阶的高级技术:

  • 实现各种后处理(Post-processing)效果。像 辉光(Bloom,使用高斯模糊),环境光遮蔽(Ambient Occlusion)与 屏幕空间环境光遮蔽(SSAO),反走样(Anti-aliasing)与快速近似反走样(FXAA)。
  • 实现延迟着色(Deferred Shading),这是一种用于渲染许多光源的技术。

文章到此结束。这就是我对「如何开始学习图形学编程?」这个问题的所有建议。


本文排版遵循 中文文案排版指北 。

因译者水平有限,部分专业术语翻译若有问题,欢迎大家在评论区指正:D。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值