计算机图形学(1)

您所在的位置:网站首页 英伟达绘图显卡 计算机图形学(1)

计算机图形学(1)

#计算机图形学(1)| 来源: 网络整理| 查看: 265

前言

 

        这次讲解的是GPU如何来渲染我们的图像,了解GPU渲染管线以及相应的代码编写流程,并通过讲解上一节的代码来具体了解渲染的过程。         由于篇幅和个人知识有限,有些地方存在错误和不足,还望大家提出建议并指正。

目录

一、GPU是怎么画画的(GPU渲染管线流程)

        1. 渲染流水线

        2. 应用程序阶段

        3. 几何阶段

         4. 光栅化阶段

二、怎么告诉GPU画什么,怎么画

        1. 窗口和视口

        2. EBO、VBO和VAO

        3. 着色器

三、代码是怎么实现的

        1. 初始化相关设置并创建窗口与视口

        2. 生成和绑定VBO和VAO

        3. 顶点着色器和片段着色器

        4. 渲染三角形

        5. 后续处理

一、GPU是怎么画画的(GPU渲染管线流程)         1. 渲染流水线

        图像的渲染过程可概括为如下三个步骤,渲染管线的作用主要是:在给定虚拟相机、三维物体、光源、照明模式,以及纹理等诸多条件下生成或绘制一张二维图像

        众所周知,CPU是一台计算机的核心,在计算机的图像中,渲染的起点同样是CPU而非GPU。渲染的时候,CPU会将需要绘制的数据信息发送给GPU,也就是告诉GPU:“嘿!老哥,帮我把这些东西画在屏幕上。”这就是渲染的第一个阶段,应用程序阶段。         在接收到CPU老大哥发来的画画命令和数据后,GPU就开始画画了。GPU拿到的数据是最原始的数据,没有经过任何加工,所以在画到屏幕之前,GPU要对数据进行一系列的处理。         首先,原始数据中一般都是三维坐标和二维坐标的数据,GPU需要将这些数据处理成能在屏幕上显示的屏幕坐标数据,也就是说GPU要处理这些图形应该画在屏幕的哪个位置。这个就是渲染管线中的几何阶段。         然后知道了要在哪里画画了,接下来就是要开始上色了。根据几何阶段变换后的数据,计算出屏幕上每个像素的颜色值,然后输出到屏幕。这就是光栅化阶段。         GPU完成的工作就是GPU的渲染流水线,也叫GPU渲染管线。接下来详细介绍每一个阶段的工作。

        2. 应用程序阶段

        该阶段是CPU将需要显示在屏幕上显示绘制出来的几何体,也就是会制图元,如点、线、矩形等输入到渲染管线的下一阶段(几何阶段)。数据包括图元的顶点数据、摄像机位置、光照纹理等。

        3. 几何阶段

        几何阶段的功能是将顶点数据进行屏幕映射。其中包括:

将各个图元放入到世界坐标系中,即进行模型变换根据光照纹理等计算顶点处材质的着色效果根据摄像机的位置、取景范围进行观察变换和裁剪最后进行屏幕映射,即把三维模型转换到屏幕坐标系中

        几何阶段的步骤如下图所示

        其中,在顶点着色器中完成模型变换、视图变换、顶点着色,在几何、曲面细分着色器中完成顶点的增删和曲面的细分,在裁剪步骤中完成投影变换和裁剪,最终进行屏幕坐标映射。 

         4. 光栅化阶段

        光栅化阶段的功能是给每个像素正确配色,以便绘制整幅图形。由于输入的是三角形的顶点,所以根据三角形的表面差异遍历每个三角形,计算每个像素点的颜色值,再根据可见性等进行合并,得到最后的图形

        其中,三角形设置是将映射到屏幕后的三角形顶点连接成三角形网格,然后通过三角形遍历得到每个三角形对屏幕像素的覆盖情况,接着通过片元着色器计算出每个三角形覆盖的像素颜色值,最后的片元操作是对所有片元进行遮挡、透明、融合等处理,最后得到每个像素点的颜色值用于绘制。

二、怎么告诉GPU画什么,怎么画

        知道了GPU是怎么画画的,那如何让GPU画我们想要的东西呢?显然,让我们直接和GPU沟通是不实际的,我们需要设置一大堆的寄存器、显存等,效率低且存在出错的风险,且不通硬件的具体设置不尽相同。所以在图形编程中,我们需要借助一些诸如OpenGL、DirectX之类的工具来代替我们跟GPU打交道。这些工具提供的图形编程接口在不同的硬件上进行了抽象,提高编程效率的同时增加的兼容性。         那么,如何使用这些工具来告诉GPU我们要画什么呢?作者学习使用的是OpenGL。         首先,了解过图形渲染流程的我们知道,要绘制一个图形,我们至少需要顶点位置数据以及相应的着色器来着色。并且要看到我们画出来的东西,我们还需要一个窗口,并且设置视口大小,视口顾名思义就是用来观看的一个窗口,超过这个范围的东西我们是看不到的。通俗讲就是现实中,在我们实现范围内的东西我们可以看见,我们视线范围外(比如脑袋后面)的东西我们是看不见的。那么我们该如何创建窗口,定义视口、顶点和着色器呢?         接下来介绍OpenGL中使用的方式,并在下一节使用代码实现。

        1. 窗口和视口

        窗口和视口是两个概念。窗口就是windos中最常见的东西了,比如你现在正在看这篇文章使用的浏览器,它就是一个窗口,窗口就是用来显示的,它可以移动、最大化、最小化,以像素为单位。而OpenGL中的视口是在窗口中可以用于绘图的一块区域,它可以大于、小于或等于窗口大小,一般我们将它的大小设置与窗口等大。在一个窗口中可以创建多个视口,比如不同视口用于显示一个物体的三视图。         OpenGL中的视口坐标系是正规化的空间坐标系,也就是视口的X、Y、Z轴的范围都是[-1.0f, 1.0f],即视口的最左边为X轴的-1.0f,最右边是X轴的1.0f,Y轴是最上边是1.0f,下边是-1.0f,而Z轴是垂直于屏幕的方向,用于表示深度。

        2. EBO、VBO和VAO

        创建好窗口并定义好视口后,就该创建顶点了,那么该怎么创建并管理我们的顶点呢?这里介绍一下EBO、VBO和VAO的概念。         EBO(Element Buffer Object, 也叫IBO:Index Buffer Object)索引缓冲区对象,它用来储存顶点的索引信息。那么它是用来干啥的呢?这边举一个例子,当我们要画一个四边形(即两个三角形面片),我们需要绘制两个三角形,需要六个顶点信息。如下图左图所示[V0,V1,V2]、[V3,V4,V5]为两个需要绘制的三角形面片的顶点集合。显然,V2和V3,以及V1和V4应该是两个完全相同的点,但是在存储时却存在两个不同的拷贝,这不免造成了空间的浪费,所以出现了右图使用索引的方式来确定三角形面片的方式。这种方式下相同的顶点可以通过索引的方式重复使用,所以每个顶点在储存时只需要储存一次即可。如下图右图所示,绿色区域即是四个顶点的索引,通过索引的重复使用[0,1,2]、[1,2,3]来定义两个三角形面片。         由于索引信息大小远远小于顶点信息,所以顶点较多且重复使用较多的情况下,使用索引的方式能大大减少对储存空间的占用。

        VBO(Vertex Buffer Object)顶点缓冲区对象。它主要用来储存顶点的各种信息。使用VBO的好处是将模型的顶点数据存入VBO后,数据不再是由CPU读取内存后送入GPU,而是GPU中直接从显存中读取,从而提高效率。         VAO(Vertex Array Object)顶点数组对象。它主要作用是来管理VBO,它是一个保存了所有顶点数据属性的状态结合 ,储存了顶点数据的格式以及顶点所需的VBO对象的引用。即它是一个VBO引用的集合。VAO的出现是因为VBO在每次绘制时需要绑定顶点信息,当数据量很大时,这种绑定将会很麻烦,而VAO是将多个VBO保存在一个VAO对象中,这样每次绘制模型是要绑定VBO即可。          这三者的简单关系如下,由于篇幅问题,这里不探讨中复杂的关系,要是有人想了解的话之后单独写一篇讲解。(其实我还在学)

        3. 着色器

        有了顶点之后,接下来就是告诉GPU该怎么给我们要的模型上色了。这里就需要用到上面流程中频繁出现的东西——着色器了,而使用着色器需要用到着色器语言,着色器语言编写的代码在程序编译时不编译,只是以文本的方式存在,传到GPU后由显卡驱动进行翻译。由于我们使用的是OpenGL,所以我们使用OpenGL的着色器语言GLSL(OpenGL Shading Language)。GLSL和C语言的语法较为相似,具体将在之后学习过程再整理讲解。         在我们使用的OpenGL版本中,有以下四个着色器:

顶点着色器(vertex shader)几何着色器(geometry shader)片元着色器(fragment shader)曲面细分着色器(tessellation shader)

        由于着色器语言在CPU不编译,所以OpenGL中使用着色器一般分为以下几步:

创建着色器对象源码关联到着色器对象编译着色器创建一个程序对象将着色器对象关联到程序对象 三、代码是怎么实现的

        知道了如何叫GPU画画,接下来就开始用代码来实现吧。这里使用上一节的三角形绘制代码进行讲解。

        1. 初始化相关设置并创建窗口与视口

        该步骤分为四个部分:初始化GLFW、创建窗口、初始化GLAD、创建视口。

        首先初始化GLFW

//初始化GLFW if (GLFW_FALSE == glfwInit()) return -1; //主次版本号 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

        首先直接使用glfw自带的初始化函数glfwInit()来初始化glfw。接着使用glfwWindowHint()函数来配置相应字段。         首先是主次版本号GLFW_CONTEXT_VERSION_MAJOR和GLFW_CONTEXT_VERSION_MINOR这里使用的是3.3版本的OpenGL,所以主次版本都设置为3。接下两句分别是设置使用核心模式以及不允许改变窗口大小。

        接下来创建窗口。

//创建窗口 GLFWwindow* window = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "First", nullptr, nullptr); if (nullptr == window) { std::cout


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3