计算机图形学:绘制3D OpenGL动画和理解图形概念
计算机图形学的最终目标是满足人类视觉系统
最近出版了一本名为《计算机图形学 原理及实践》的书。这本书让我深深感动的是引言中对计算机构建真实图像的理解:我们致力于的最终目标是以视觉元素的形式进行交流,尤其是与人的交流。 这句话的潜台词是:在解决图形问题和建立模型时,必须考虑到人类视觉系统的影响。 就我个人而言,我深受启发。
例如,如果你的视力依然存在,那么使用60HZ刷新率你就不会感到卡住;例如,最小的角分辨率约为1弧度,300ppi以上的屏幕几乎会出现颗粒感;例如,可见光只能在一定频率范围内感受到,而红外线和各类紫外线对人类来说是不可见的;例如,人类的感光细胞只有在每次接收光子时积累了一定量的能量时才会产生神经信号。昏暗的光线需要放大瞳孔并长时间凝视才能看清楚;例如,二维草图就看不清楚。能给人一种立体感。这不仅仅是时间和灯光特效的作用。事实上,任何能够在视觉系统中引发大致正确反应的刺激都会被大脑识别并形成一定的感知。这也是我们视觉系统的作用。强大的自适应能力使我们能够在很多计算无法完全模拟的情况下(例如光照)采取合理的近似,并且仍然使结果看起来令人满意。
人类视觉系统的特点决定了当今所有视觉设备从硬件到软件的设计和发展方向。建议学习新知识时,也应该尽量跳出来,俯瞰知识的景观和本领域的发展方向,了解“从哪里来,到哪里去”的背景。它可能会鼓励您做出新的发现。比如,当你看到一些复杂的设计时,你会恍然大悟。开胃菜就到这里了,现在让我们进入今天的主题。
实例和知识点列表
多种类型的图形元素:![]()
3D动画:![]()
这里我放了上面两个效果的源码实现。如果有收获,别忘了留下star鼓励哦~
这是视觉专题的第二篇文章。上述两个示例是使用固定管道实现的。使用固定管线的原因,一是可以更快地达到效果,二是可以避免因不了解GLSL语法而对渲染过程的理解造成混乱。实例中包含的知识点有:
- OpenGL坐标系
- OpenGL矩阵变换
- OpenGL基本图元
- OpenGL渲染技巧
- OpenGL纹理映射
结合两者阅读文章提供的示例(仅仅练习和思考检查是不够的)在本文中,您将获得:
- 使用 OpenGL 固定管道
- 使用基本 OpenGL 图元
- 简单的 OpenGL 纹理
- OpenGL 几种渲染的优缺点技术及其使用
- OpenGL矩阵变换与坐标变换的关系
在此感谢CC老师_HelloCoder(温柔的胖C)提供的可视化课程和资源,帮助我快速上手图形编程。
我们强烈建议您下载我为您准备的示例的源代码。先运行一下看看各个效果,然后结合文章和自己的理解自己测试一下各个参数所做的改变,然后改成自己想要的。
注意:这不是一篇解释代码注释和API用法的文章。所有必要的注释都已在源代码中为您编写。我不需要把时间浪费在搜索引擎可以一次性回答的问题上。写作的空间。 本文的重点是在之前了解各个知识模块的基础上,进一步加深对关键知识点的理解。
1。 OpenGL 坐标系
1。为什么需要坐标系?
总之,我们现实世界中的一切都可以因为坐标系的存在而被抽象为可测量的值:位置、长度、速度、空间和时间。深入一点,这包括爱因斯坦的相对论。通俗地说,坐标系是我们认识多维世界的基础。它可以为我们提供对空间和时间的直观描述。当然,如果从空间角度想象四维甚至更多维的坐标系,这以人类目前的认知是很难理解的。现在让我们将它们视为矩阵。我相信大佬们的线性代数一定比我的好。
2。有哪些类型的坐标系?
模型/物体空间的坐标系:为了计算方便,通常选择建模物体的中心作为坐标系原点,这样模型的所有点在 x y z 之间的坐标都在-0.5~0.5。例如,我们将立方体建模为立方体,以立方体的中心为原点(未指定,只是为了计算方便),建立正确的坐标系作为立方体的模型空间坐标系 。该坐标系给出了立方体所有点的坐标信息。
场景空间坐标系:所有对象模型必须存在于一个或多个场景(由一组对象和光源组成的模型)中,包括特定场景空间的坐标系统是场景空间坐标系,更准确地说(打开你的空间想象力):如果顶部立方体后面当前在桌子上(假设桌面是规则的矩形),则桌子位于房间中中间(假设房间立方体)是桌子中间的一个立方体。此时,以房间中心为起点的右侧坐标系就建立为场景空间坐标系。假设表格的y坐标为5,然后将上述模型坐标系中所有立方体节点坐标的y值加5,就得到其相对于场景空间坐标系的坐标。
相机空间的坐标系:如果我们仍然使用上层房间作为场景,如果我们需要展示(看到)桌子上的骰子,我们需要将相机(我们的眼睛)放在一个房间中合适的位置,这个对应位置(坐标)也是相对于场景坐标系而言的。现在以相机镜头的点(我们的眼睛)为起点,建立一个新的右手坐标系(镜头/眼睛直视的方向为z轴负方向),使得上方的所有场景场景空间可以表示为该坐标系中的坐标,我们将其称为相机空间坐标系。
像素空间坐标系:上述场景中每个商店的相机坐标最终将转换为标准化设备坐标(NDC)。在此坐标系中,可见场景的 x 和 y 坐标表示为 -1 和 1 之间的浮点值,z 坐标为负。 ([-1,1]范围之外的x、y不在相机(眼睛)的视野内;z > 0的物体在相机(眼睛)后面),那么这些视点将变为像素坐标是我们可以看到的屏幕上的像素点(视网膜上对应的图像)。屏幕上像素点的坐标系(通常为(0, 0)位于左上角)为像素空间坐标系。
注意:OpenGL 在标准化设备坐标系中使用左手坐标系(投影矩阵交换左右手)。
3。坐标系之间的变换关系
上述计算机图形学中的坐标系对应着从图形数据到显示的几个步骤。仅此描述当然不足以完全理解它们。这张图展示了整个过程以及各个变换过程的作用:
简单解释一下坐标系变换过程:
- 将相机移动到要拍摄的位置,调整方向(视图变换、视图变换)
- 将要拍摄的物体移动到场景中所需的位置(模型变换,模型变换)
步骤1和2是实现同一件事的两种方法:物体不动,相机移动/ 相机不会移动物体,因此这两个步骤通常合并为一个步骤——模型视图转换。这个过程的结果是构建了一个独立的、统一的空间系统,可以变换场景中的所有物体。进入场景空间坐标系。
- 调整相机的焦距或调整变焦倍数(投影变换)
- 对得到的图像进行拉伸或压缩,变换为需要的尺寸(视场变换、视场变换)与3个步骤是3是捕获场景的尺度变化,而这里是结果的变形。
这里有一个重要的点,点击控制板:步骤3中设置的焦距或变焦比实际上是在相机取景器设置中视锥体(视场中的方锥体)上图),图中阴影部分为Frustum (截锥),截锥之外的物体会被去除,如果图元穿过截锥的某个面,OpenGL会进行快照那个原始的。这是OpenGL的一个特性,可以解决使相机附近的物体无限大的问题。此外,可以增强那些没有绘制得太远或超出视野的对象。渲染性能和深度精度。此外,步骤 3 还计算 透视投影 (近大和远小效果)的参数(齐次坐标中的 (x,y,z,w) w )。 ? ,多用于保持物体正确尺寸和它们之间的角度的场景,例如建筑平面图和计算机辅助设计领域,因此这里不再赘述。它们的投影方式区别如下:
附录(二):齐次坐标
上面提到的透视投影,相信大家都知道近大远小的效果,因为光线从整个景观经过我们的瞳孔,最终在视网膜上形成图像,这就是透视投影的结果。然而,在投影空间中,会产生许多与欧洲空间相反的感知结果。例如,两条共面平行线将相交:
随着视线进一步后退,铁轨之间的距离会减小,直到它们在地平线(无穷远点)相交。
图形在透视空间的几何变换,如果不能将矩阵乘法和加法的运算结合起来,计算将会非常复杂。为了解决这个问题,德国数学家(奥古斯特·费迪南德·莫比乌斯)提出了齐次坐标系:用N+1个量来表示N维坐标。例如,在二维齐次坐标系中,我们引入一个分量 w 来表示 (X, Y, w) 形式的二维点 (x, y)。均质转化率为:
x = X/w
y = Y/w
复制代码。坐标系中的最后一个分量是 0,它可以表示无穷远点:(x, y, 0)。齐次坐标允许将平移、旋转、缩放和透视投影表达为矩阵和向量乘法的一般向量运算,这是笛卡尔坐标所不具备的优点。简而言之,它的意义主要有两个:一是区分向量和点;二是区分向量和点。二是执行仿射变换(仿射变换)很简单。
那为什么可以区分向量和点呢?为什么改进转换更容易?此摘要将帮助回答您的问题:齐次坐标和变换矩阵。如果您没有权限下载本文档,请在评论区留言,我会私发完整的PDF给您。
2.OpenGL矩阵变换
说了这么多,矩阵运算和仿射物体变换之间的正确关系是什么?我们来一一分解。? n 形式的乘法遵循结合律:C(BA)=(CB)A=CBA,因此可以将几个变换矩阵直接相乘,得到最终的变换矩阵,从而减少计算次数,改善程序美观
2。线性变换和矩阵
平移
上图显示了一个 4x4 酉矩阵。任何4维向量乘以这个矩阵仍然是它本身。同样,如果我们想沿x轴移动物体。如果方向平移 1,而 y 和 z 保持不变,则将物体的 (x, y, z, 1) 中的所有顶点乘以矩阵:
同理,其他轴方向的平移类似。 缩放
如果理解了上面平移的计算过程,缩放矩阵其实就很容易推断出来了。例如,如果我们想要将一个对象的大小调整为原始大小的 3 倍,我们将把该对象的所有顶点更改为:( x,y,z,1 ) 乘以这个矩阵:
由于每个组件都是单独控制的,通过改变不同组件的值,可以实现不同方向不同的缩放效果。另外,如果缩放时对象的中心不在(0,0,0),则缩放也会使对象远离(0,0,0)或靠近(0,0,0)。如果你不想改变物体到原点的距离,你可以使用上面刚刚学到的方法。平移矩阵首先将对象的中心移动到原点,然后将其缩小,最后使用平移矩阵的逆将对象移回其原始位置。 旋转
假设你了解了上面提到的齐次坐标以及上面矩阵运算和线性变换的关系,那么将所有物体节点的坐标乘以下面的矩阵,你猜怎么着?什么样的手术?
答案是绕z轴逆时针旋转物体θ°。不明白?闭上眼睛,绕 z 轴旋转。这是否意味着所有顶点都在 xy 平面上旋转?这是否相当于每个点绕其所在二维坐标系原点逆时针旋转θ°?这些问题还不清楚吗?这实际上是一道高中数学题:如何求点p(x,y)绕原点旋转θ°(这里是逆时针旋转)到点p`(s,t)的坐标。直接上证明吧...其实我也是花了相当长的时间才明白的(╮(╯▽╰)╭高中数学大概都还给数学老师了)
如果你弄清楚了上面的计算过程现在很清楚,绕 x 轴和 y 轴旋转是不可能的。结合上面已知的结合律,封装一个支持同时围绕x、y和z旋转一定程度的函数并不困难:只需将三个矩阵按顺序相乘即可。另外,和缩放效果类似,如果旋转时物体的中心不在原点,就会造成物体旋转,整个位置也绕原点旋转的效果:
解决方法是一样的:首先将物体的中心移动到原点并等待旋转完成。移回原来的位置:
透视投影
与之前的平移、缩放、旋转相比,这个变换过程相对复杂,但是不用慌,我们只需要指定节点的变换矩阵即可,因为线性关系是普遍存在的。在透视投影中,必须考虑两种情况:
- 中心对称的视轴、z轴和圆锥体中心。
- 不对称的视锥类似于我们坐在火车上看窗外。窗户的中心不正对着我们的眼镜。
首先考虑第一种情况:
我们的目的是计算视图横截面中的点投影到附近平面上的对应点的坐标。假设近平面的宽度、高度、Z值为width、height、Znear,远平面的Z值为Zfar。结合空间几何知识,不难推导出如下对应关系:
对于第二种情况,实际上是在第一种的基础上将近平面的位置平移到xy平面。如果在xy平面中用近平面(矩形)的位置来表示变换矩阵的上、左、右、下,那么变换矩阵的x维度和y维度的第三个分量z可以为根据近平面的位置进行相应调整:
如果你理解了这一点,你就完全掌握了使用frustum这组功能。如果你没有彻底理解,不要怀疑自己。我愚蠢的大脑还没来得及思考,就已经把书画完了。对于那些有线性代数基础的人来说,我相信这是小菜一碟。
3。理解矩阵
目前为止我们所接触到的矩阵都是图形学中最基本、最简单的应用。至于对矩阵的深入理解,我大学里没有线性代数课程,所以我不会在这里留下其他人(我讨厌附加信息......),我会在学习后与您分享一些经验。推荐大家看一下孟岩老师写的理解矩阵。 (一共三篇文章,建议从头开始看。博主把向量、坐标系、矩阵之间的关系解释得很透彻,很直观,不是抽象:空间的本质特征是接受运动,矩阵的本质在于它是在线性空间中,线性变换的描述。在线性空间中,只要我们选择一个基集,任何线性变换都可以用一定的矩阵来描述。 )总之,在线性空间中选定一个基后,向量描述对象,矩阵描述对象的变换,通过矩阵和向量相乘完成变换。 当然,这些理解和想法可能不完全正确,或者可能存在一些不准确之处,但仍然可以为我们更好地理解矩阵提供很多有价值的参考。
PS:烧脑真的很有趣……嗯,别问我怎么知道的。
3。基本的OpenGL基元和绘图规则
前面的硬骨头我们已经啃完了,让我们学习一些更轻松的东西吧~
1。什么是基元
由于OpenGL的主要功能是将图形渲染到帧缓冲区,因此复杂的对象必须被分解为小的基本单元。这些小单位是原始。它包括三种形状:点、线和三角形。当它们的分布密度足够高时,它们可以表达2D和3D物体的形状。 OpenGL 包含许多用于渲染此类图元的函数。这些函数允许我们指定图元在内存中的放置、渲染数量和渲染格式,甚至在单个函数调用中复制同一组图元。数量。
点、线和三角形是大多数图形硬件 (GPE) 支持直接光栅化操作的基本图元类型。 OpenGL还支持其他图元类型如面片和相邻图元,但这些不能直接光栅化,今天我们将介绍前面的三种图元类型。
2。绘制方法有哪些?
Point
Point 是一个四维齐次坐标值。因此,该点实际上并不具有区域,在OpenGL中它是由屏幕(或绘图缓冲区)上的矩形区域模拟的。在渲染点基元时,OpenGL将使用一组光栅化规则来确定点覆盖的像素位置。用于模拟四边形 点 到 glPointSize() 的边长。默认尺寸为 1.0。您还可以通过在着色器中键入值来更改它。
线、带状线和环
一条独立的线由一对顶点表示。也可以连接多个节点来表示一组连接线。那些从头到尾封闭的称为循环(线循环),那些开头和结尾开放的称为带状线(线带)。像点线原则上是没有面积的,所以需要特殊的规则来确定哪些像素值会受到光栅化的影响。您可以使用 glLineWidth() 设置线段的宽度。默认值为 1.0。假设线段的宽度为n(n>1),那么线段会水平或垂直复制n次,复制方向取决于线段扩展的主方向是X(水平)还是Y (垂直)。
线段光栅化规则的简单总结(菱形输出):假设屏幕上的方形区域中每个像素都有一个菱形。当从 A 点到 B 点的线段被光栅化时,如果该线段经过菱形的假想边缘,那么它应该会影响该像素——除非菱形包含精确的 B 点(即终点)距离的点在菱形内)。所以如果继续从B点到C点再画一条线段,B点的像素只会更新一次。这个规则也是边缘产生锯齿的主要原因——非水平和非垂直的对角线段会有一些像素的假想菱形边缘不相连,这些像素不会参与光栅化。然后一些像素丢失,这就是我们看到的锯齿。稍后我会详细讨论如何删除别名。
三角形、条带和扇形
三角形就像 OpenGL 初学者的欢迎世界对于任何第一次接触编程的程序员来说。这是构成所有复杂 3D 图形的基本结构。渲染多个三角形时,每个三角形完全独立于其他三角形。三角形的渲染是通过将三个顶点投影到屏幕上来完成的。如果屏幕像素的样本值位于投影链接三角形边的内半空间,则会被光栅化,这意味着:
- 两个三角形共享一条边(即共享一对顶点),则不能同时在两个三角形中找到样本值。
- 将三角形公共边光栅化的过程不会产生任何裂纹,也不会多次绘制。 (重复绘制会导致混色计算不正确等问题)
这对于光栅化三角条或三角扇的过程非常重要。三角形条从第四个顶点开始,会与前一个三角形的最后两个顶点形成一个新三角形,依此类推:
渲染时扇区,第一个A顶点将作为公共点存在(例如,当绘制圆时,圆的中心),它将作为每个后续三角形的组成部分。然后每两个顶点将形成一个具有此公共点的新三角形:
三角形条和扇形可以表示任何复杂程度的凸多边形特征。
总结如下:
图中,GL_POLYGON和GL_QUADS是多边形与四边形的连接方式。赶紧结合内附案例,亲自体验一下各个图形元素的使用和有用场景吧。 ?距离,影响其在近平面上的投影。对于不透明物体,当多个表面在投影路径中重叠时,最近的表面将可见。但不同深度的表面并不总是那么平静。
由于硬件对浮点精度的支持有限,当两个顶点投影到同一点上的值非常接近时,即使深度值在数学上不同,在计算机上也可以认为是相同的。 ,尤其是经过透视变换后,z值的精度可能会更低。这种现象会影响多个像素,导致显示结果闪烁和重叠。尽管这种情况并不常见,但如果遇到它,请务必选中此选项。
事实上,投影变换会在每个方向上一定程度地降低坐标的精度:距离近平面越远,精度就越低。举个简单的例子:物体越长,细节就越少。 ? (GL_CW)。您还可以通过glFrontFace(GL_CW)将顺时针设置到前面。您应该了解此设置的成本。
球体、环面和茶壶都是由相同方向的多边形组成,即完全逆时针或完全逆时针。莫比乌斯带和克莱因瓶则不然。假设有一个不透明的立方体。构成其表面的多边形的顶点方向均为逆时针方向。透视投影到近平面后,有些顶点的方向变成了顺时针:是的,就是这些顶点组成了你看不到的部分——背面,它们永远会被正面覆盖:
你可以通过设置 有关通过 从更专业的角度来说,判断多边形的一个面是正面还是背面,取决于窗口系统下多边形面积的计算。计算及解释公式(看懂即可): 色彩理论 如果你买了显示器,你可能见过这个参数:色数,标准值通常是1677万或10.7亿。那么这个数字代表什么以及它来自哪里呢?这与计算机图形学有什么关系? 大多数显示器使用三种基色(红色、蓝色和绿色)的组合来形成颜色值。它们构成了屏幕的整个色阶。我们将其称为 RGB 颜色空间并使用这三种颜色。组合来表达单一颜色。我们之所以能够用这三种颜色表达如此大范围的视觉光谱,是因为这三种颜色非常接近人眼视锥细胞响应曲线的中心区域。这与本文开头提到的想法完全一致。 OpenGL 除了三个 RGB 分量之外,还添加了第四个分量 alpha,即 RGBA 颜色空间。另外 OpenGL 还支持 sRGB 色彩空间。 回答前面的问题。在现实的物理世界中,光的频率和强度不断变化,使得颜色值的数量等于无穷大。但对于计算机来说,它的帧缓冲资源是有限的,因此它只能量化不断变化的强度,以限制可以显示的颜色数量。分配给代表强度变化范围的每个分量的大小称为像素深度(位深度)。例如,每个颜色分量使用 8 位 ([0.255]) 来存储其强度。这三个颜色分量(不包括alpha分量)总共可以表示的颜色数量为2^8 * 2^8 * 2^8 = 2^24 = 16777216 ≈ 1677万种,数量颜色数,10位对应为10.7亿种。如今,所有主要显示器都是 8 位的。 混合 API的使用介绍以及混合相关的参数说明这里就不展开了,会在纹理中详细说明-相关章节。先结合案例来了解一下简单的使用。 这部分内容已经写好了,但是有些句子的表达需要改进,尽量避免歧义和难以理解的句子结构……这周末就更新到这里啦~ 本文的两个例子包含了很多知识点。彻底掌握每一点并不容易。刚开始学习时,往往忌讳一味钻研某一点,因为这样很容易出问题。陷入困境并半途而废。所以本文的主要目的是先熟悉一下 和是什么以及如何使用。我尝试将它们结合起来,融入到实际场景中去描述。首先,我希望能够得到大家的理解。其次我还可以把不同的知识点串联起来,帮助大家对整个图有一个基本的概览。 作者:小柯长江glCullFace(GL_BACK)+启用面剔除glEnable(GL_CULL_FACE ) 直接投射,可以大大提高绘图性能。当然,您当然可以排除正面:GL_FRONT或所有多边形:GL_FRONT_AND_BACK。如有必要,请使用 glDisable(GL_CULL_FACE) 禁用面剔除,例如绘制透明对象。 glEnable() 和 glDisable() 启用和禁用的更多可选参数类型,请参阅本文。 3。混色
这意味着每个像素至少存储三个字节的数据,并且每种类型的颜色缓冲区写入屏幕上每个像素的数据总量始终相同。
如果输入片段通过了所有适当的片段测试,则可以通过某种算法将其与颜色缓冲区中的当前值混合。最简单且默认的方法是直接覆盖现有值(如果它是不透明的,则不能将其视为合并)。如果我们想要实现多片彩色玻璃重叠的透明效果,首先要启用glEnable(GL_BLEND)混色,然后指定混色算法,如:glBlendFunc(GL_SRC_ALPHA) , GL_ONE_MINUS_SRC_ALPHA)各参数的取值如下: 5.质感
6。总结
链接:https://juejin.im/post/5ce4a8cef265da1bca51b159
来源:掘金
版权归作者所有。商业转载请联系作者获得许可。非商业转载请注明来源。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网