作者:张辰
单位:中国移动智慧家庭运营中心
来源:移动Labs
随着硬件计算能力越来越强大,GPU的功能在近几年也得到了前所未有的提升,无论是游戏狂热爱好者还是不爱玩游戏的普通用户都在5G和AR VR的变革中受到了不同程度的影响,有的可能从技术提升中得到了更好的游戏体验,有的可能在虚拟现实中找到了另一种娱乐方式,比如迪士尼裸眼3D技术让游客可以体验到身临其境般的感觉,这些技术的基础都与渲染技术密切相关,那这次我们就来了解下基础的渲染技术。
Part 01、渲染技术基本API
目前通用的底层渲染使用的API接口主要有以下几种:
微软公司在Microsoft Windows操作系统上所开发的一套3D绘图编程接口,是DirectX的一部分,目前广为各家显示卡所支援,是与OpenGL同为电脑绘图软体和电脑游戏最常使用的两套绘图编程接口之一,目前只在Windows平台上可用。
OpenGL(开放图形库或者“开放式图形库”)是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。OpenGL常用于CAD、虚拟实境、科学可视化程序和电子游戏开发。
OpenGL的高效实现(利用了图形加速硬件)存在于Windows,部分UNIX平台和Mac OS,对于不同的平台,opengl开发出了不同的版本如OpenGL,OpenGL Es,WebGL,满足了渲染接口对不同平台的兼容性。
Vulkan是一个跨平台的2D和3D绘图应用程序接口(API),最早由科纳斯组织(Khronos Group) 在2015年游戏开发者大会(GDC)上发表。科纳斯最先把VulkanAPI称为“下一代OpenGL行动”(next generation OpenGL initiative)或“glNext”,但在正式宣布Vulkan之后这些名字就没有再使用了。就像OpenGL,Vulkan针对实时3D程序(如电子游戏)设计,Vulkan并计划提供高性能和低CPU管理负担(overhead),这也是Direct3D12和AMD的Mantle的目标。Vulkan兼容Mantle的一个分支,并使用了Mantle的一些组件。Vulkan旨在提供更低的CPU开销与更直接的GPU控制,其理念大致与Direct3D 12和Mantle类似。Vulkan不再使用OpenGL的状态机设计,内部也不保存全局状态变量。显示资源全然由应用层负责管理,包括内存管理、线程管理、多线程绘制命令产生、渲染队列提交等。应用程序能够充分利用CPU的多核多线程的计算资源,降低CPU等待,降低延迟。但带来的问题是:线程间的同步问题也由应用程序负责,从而对开发人员的要求也更高。
在WWDC 2014上,Apple为游戏开发者推出了新的平台技术 Metal,该技术能够为3D图像提高10倍的渲染性能,并支持大家熟悉的游戏引擎及公司。Metal 是一项全新的技术,专为开发高临场感主机游戏的开发者打造,可让开发者全力发挥A7和A8芯片的性能。该技术经过优化,使处理器和图形处理器能够协同工作来实现最优性能。它专为多线程而设计,并提供各种出色工具将所有素材整合在Xcode中。
Part 02、用OpenGL来渲染一个简单的三角形
我们前面了解了目前常用的图形学渲染API,那我们现在就用其中的一种来实践一下吧。
我们的目标是通过提供的API接口来渲染一个三角形,所以我们要先了解一下用OpenGL渲染的一些基础知识。
2.1 渲染管线
无论是OpenGL 还是其他的渲染API,其设计的逻辑都是将渲染的流程抽象为管线的形式(Graphics Pipeline),顾名思义,渲染管线就是将3D或者2D的图像通过一系列的处理,最终显示在屏幕上的处理流水线,类似于工厂用生产猪肉罐头的产品线,无非是到哪一步做哪些事。下面我们看下基础的流水线图(visio):
首先,我们要准备好顶点数据,可以看做一个顶点的数组,后续渲染的操作都是由顶点的计算开始的,一个三维的顶点数据在顶点着色器中会经过坐标系变换、光照计算、投影计算等最终留下需要渲染的顶点,送到下一步继续处理。
在上一步得到的顶点数据中,通过开发者配置的图元信息,来将得到的顶点组装成图元,如(顶点,线,三角形,多边形等),然后将装配好的图元送到几何着色器中重新细化几何图案。如原本的图元是三角形,可以通过添加顶点的方式,将其变换为多个三角形,也可以变换为多边形等,最终输出新的图元信息到光栅化阶段。
在光栅化阶段,图元信息会被采样,通过不同的算法进行的采样可以得到不同像素值,最终生成不同的像素片段,这个片段可以供片段着色器使用,以此来进行着色操作。
在片段着色器中,通过光栅化得到的片段,在shader中进行像素计算,输出像素值进行着色,着色后的片段,需要经过最后一步,即测试(深度测试,模板测试等),再经过混合着色最终显示到屏幕上。
2.2 代码部分
首先,我们创建一个顶点数组:
float vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f };
顶点的值归一化到(-1.0f - 1.0f)方便绘制,然后,我们创建渲染所使用的顶点shader和片段shader。
const char *vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "void main()\n" "{\n" " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" "}\0"; const char *fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" "void main()\n" "{\n" " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\n\0";
并且将两个shader通过动态编译的方式完成GPU部分的编译、链接,最终生成程序使用的Program。
glCompileShader(vShader); glCompileShader(fShader); glLinkProgram(shaderProgram);
然后,我们将要创建存储顶点信息的顶点属性buffer,
glGenVertexArrays(1, &VAO);
可以创建一个管理顶点信息顶点数组。
glGenBuffers(1, &VBO);
可以创建一个用于存储顶点数据的buffer。然后我们绑定buffer并上传数据:
glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW)
使能顶点属性,并且指定数据起始位置:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glUseProgram(shaderProgram); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3);
然后在主循环中,调用创建好的shaderProgram和VAO,绑定到当前的渲染状态中,通过glDrawXXX来绘制图元,我们要绘制的是一个三角形,所以顶点是3个,索引是0。
我们在绘制之后就可以看到如下的结果了:
这样,一个简单的OpenGL的例子就完成了,是不是看起来一点都不复杂。当我们熟练了整个流程,我们就可以绘制更加复杂的模型,添加更加真实的效果,感兴趣的话那就赶紧动手试试吧!
本文转自:移动Labs,作者:张辰,转载此文目的在于传递更多信息,版权归原作者所有。如不支持转载,请联系小编demi@eetrend.com删除。