前端可视化OpenGLWebG

中科白癜风公益惠民活动 https://m-mip.39.net/fk/mipso_5796518.html

作者:窦金兰—字节跳动IES前端工程师

希望通过这篇文章,大家能够对OpenGL/WebGL有一个基础的认识~~

OpenGL定义

OpenGL是一套规范,不是接口,学习这套规范,就可以在支持OpenGL的机器上正常使用这些规范,在显示器上看到绘制的结果。

这套接口是Khronos这个组织在维护。怎么维护呢?就是写一个说明书,指导各个GPU厂家,如果他们要支持OpenGL的话,要怎么实现一个具体的OpenGL库。比如Khronos说要实现glDrawArray这个接口,那么硬件厂家就得在它的库里实现这个接口。如果不实现,那么就不算支持OpenGL。当然也有一些接口不一定要实现。

厂家实现的OpenGL库的内容,其实就是厂家自己的团队整合自己的图形知识以及GPU硬件的指令,这些OpenGL的实现通常被称为“驱动”,它们负责将OpenGL定义的API命令翻译为GPU指令。因此使用时只需要安装显卡的驱动。

既然是在GPU上运行的OpenGL,那么接下来我们来了解一下GPU~

GPU概念

显卡处理器称为图形处理器(即GPU),它是显卡的“心脏”,与CPU类似,只不过GPU是专为执行复杂的数学和几何计算而设计的,这些计算是图形渲染所必需的。一些最快速的GPU集成的晶体管数甚至超过了普通CPU。

GPU的工作

现代的GPU功能涵盖了图形显示的方方面面,这里只取一个简单的方向作为例子。这个立方体渲染的例子,会有助于理解接下来会讲到的GLSL(OpenGL着色器)语言。

大家可能都见过上面这张图,这是老版本DirectX(是由微软公司创建的一系列专为多媒体以及游戏开发的应用程序接口)的一项测试,就是一个旋转的立方体。显示出一个这样的立方体要经过很多步骤,我们先考虑简单一点的,想象一下他只是一个线框,没有侧面的“X”图像。再简化一点,连线都没有,就是八个点(立方体有八个顶点的)。那么问题就简化成如何让这八个点转起来。

首先,在创造这个立方体的时候,肯定有八个顶点的坐标,坐标都是用向量表示的,因而至少也是个三维向量。然后“旋转”这个变换,在线性代数里面是用一个矩阵来表示的。向量旋转,是用向量乘以这个矩阵。把这八个点转一下,就是进行八次向量与矩阵的乘法而已。

这种计算并不复杂,拆开来看无非就是几次乘积加一起,就是计算量比较大。八个点就要算八次,个点就要算次。这就是GPU工作的一部分,顶点变换,这也是最简单的一部分。

通过这个例子可以先思考一下,想要渲染出一个图形,就需要告诉GPU图形的顶点(即坐标向量),如果需要变化(如:平移、旋转、缩放等),就需要告之对应的矩阵,这也就是文章后面要说的GLSL语言核心需要做的事情~

CPU与GPU区别大揭秘

CPU和GPU因为最初用来处理的任务就不同,所以设计上有很大的区别。它们分别针对了两种不同的应用场景。

CPU需要很强的通用性来处理各种不同的数据类型,同时又要逻辑判断又会引入大量的分支跳转和中断的处理。这些都使得CPU的内部结构异常复杂。而GPU面对的则是类型高度统一的、相互无依赖的大规模数据和不需要被打断的纯净的计算环境。

于是CPU和GPU就呈现出非常不同的架构(示意图):

其中绿色的是计算单元,橙红色的是存储单元,橙黄色的是控制单元。

GPU采用了数量众多的计算单元和超长的流水线,但只有非常简单的控制逻辑并省去了Cache。而CPU不仅被Cache占据了大量空间,而且还有有复杂的控制逻辑和诸多优化电路,相比之下计算能力只是CPU很小的一部分。

而GPU的工作大部分就是这样,计算量大,但没什么技术含量,而且需要重复很多次。就像你有个工作需要算几亿次一百以内加减乘除一样,最好的办法就是雇上几十个小学生一起算,一人算一部分,反正这些计算也没什么技术含量,纯粹体力活,人海战术而已。而CPU则像老教授,积分微分都会算,一个老教授资顶二十个小学生。GPU就是这样,用很多简单的计算单元去完成大量的计算任务。不过这种策略基于一个前提,就是每个小学生工作没有什么依赖性,是互相独立的,即GPU的计算单元所做的事情是互相独立的。

还有一些任务涉及到步骤的问题,不能把执行顺序颠倒了。这种比较复杂的问题都是CPU来做的。

GPU的运算速度取决于雇了多少小学生,CPU的运算速度取决于请了多厉害的教授。教授处理复杂任务的能力是碾压小学生的,但是对于没那么复杂的任务,还是人多力量大。不过现在的GPU也能做一些稍微复杂的工作,但还是需要CPU把数据给GPU才能开始干活,因此还是靠CPU来管的。

至此为止,GPU的内容先了解到这里,接下来我们继续回到OpenGL。

OpenGLES

OpenGLES与WebGL有关,WebGL是基于OpenGLES2.0的JavascriptAPI,因此我们在这里先来了解一下OpenGLES。

OpenGLES是OpenGL的子集,专门针对手机/PDA(掌上电脑,如:条形扫码器,POS机等)/游戏主机等嵌入式设备设计的。OpenGLES主要直接提供Capi,各自平台根据习惯提供一层包装(比如Android提供了Java的包装,iOS提供了obj-c的包装)。

虽然OpenGLES是OpenGL的子集,但是OpenGL与OpenGLES还是有一点区别,比如他们的数据类型会存在一些不一样:

OpenGLES没有double型(浮点)数据类型,而是加入了高性能的定点小数数据类型;

OpenGLES没有glBegin/glEnd/glVertex,只能用glDrawArrays/glDraw......;

没有实时将非压缩图片数据转成压缩贴图的功能,程序必须直接提供压缩好的贴图;

...

可实现滤镜效果??

这里可以简单看一些直接使用OpenGL实现的滤镜效果

缩放、出窍、抖动、闪白、毛刺

灰度、旋涡、马赛克

分屏

注意:这些直接使用OpenGL实现滤镜效果的例子可以了解一下,但是团队项目中使用到的滤镜效果是通过effectsdk统一支持(Windows/Mac/Android/iOS),因此如果需要实现effectcreatorforweb,则需要在浏览器支持effectsdk的使用,以保证多端统一,且支持现有功能效果。

至此,除了GLSL语言以及具体API,OpenGL的基础知识就这么多了。OpenGL是在移动端/桌面端使用,那么在Web端呢?就是大家熟悉的WebGL了,我们一起来看一下~

WebGL一些WebGL应用场景??

3D的数据可视化

kaspersky

3D游戏开发

WebCamMesh

webglgames

CubeSlam

打造炫酷的交互

WebGLbookcase

H5宣传页面广告

淘宝双11VR邀请函H5页面

进行3D产品/物体展示

WebGLreflection

...

概念

WebGL是一种3D绘图标准,这种绘图技术标准把JavaScript和OpenGLES2.0结合在一起,通过HTML5的Canvas来和DOM打交道,为HTML5Canvas提供硬件3D加速渲染。WebGL技术标准免去了开发网页专用渲染插件的麻烦,可被用于创建具有复杂3D结构的网站页面,甚至可以用来设计3D网页游戏等。

与OpenGL的关系

通过上述概念可以看出,WebGL将JavaScript和OpenGLES2.0结合在一起,因此也会使用GLSL(OpenGLShaderLanguage)作为ShadingLanguage(一种顶点计算和着色的语言,缓存编译到GPU,由GPU来执行)。

说白了,就是通过浏览器提供的接口,我们能够直接和底层的OpenGL库打交道。由于能直接调用底层接口,并且有硬件加速,因此WebGL要比普通的Canvas2DApi性能要高出不少。这里有一个WebGL和Canvas2DApi性能的对比实验结果,横坐标是绘制任务数量,纵坐标是页面的FPS(画面每秒传输帧数)。

从结果中可见,当需要执行大量绘制任务时,WebGL的性能远远超越了Canvas2DApi,达到了后者的3~5倍。

Three.js

为什么会介绍一下这个库,是因为在学习WebGL知识时总会看到一个库:Three.js,那我们这里也来简单的了解一下。Three.js是一个用于在浏览器中绘制3D图形的JS库,其底层实际是对浏览器提供的WebGLApi进行了封装,类似于JS与JQuery的关系,甚至不需要WebGL基础就能够上手使用,但是由于是以WebGL为基础,所以遇到问题还得回来查看WebGL,而WebGL的基础又是OpenGLES,因此OpenGL就显得至关重要了。

OpenGL很重要,而OpenGL还有一个重要部分就是前面多次提到的GLSL(OpenGL着色器语言),接下来我们就来看看这个着色器语言究竟是什么吧~~

GLSL着色器语言

首先要明白,着色器(Shader)是运行在GPU上的小程序。这些小程序为图形渲染管线的某个特定部分而运行。从基本意义上来说,着色器只是一种把输入转化为输出的程序,比如我们要画一个三角形,着色器只是通过读取我们传给它的顶点,颜色,变化等输入,然后经过一系列计算,最终输出图形。

着色器主要分为顶点着色器和片段(像素)着色器,这也是主要的两种着色器,还有一种是几何着色器。每个着色器是非常独立的程序,它们之间不能相互通信,唯一的沟通只能通过输入和输出。通常一个WebGL应用会有多个着色程序。我们可以根据着色起的名字来思考一下他们的作用。顶点着色器,顾名思义就是为了渲染图形的顶点所使用的,回想一下我们刚才讲的GPU的工作,一个立方体的渲染,肯定是先要找到立方体的顶点,这个就是顶点着色器的作用了。顶点找到后,就会连接成线,以及形成平面,那么线段/平面的颜色等就是片段着色器的工作了。

着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。数据类型:

修饰符:

这里只是简单介绍了一下常用概念,关于GLSL概念的详解,可以看一下这里

我们在GPU的工作一节提到过,坐标都是向量表示,变化(比如:旋转/平移/缩放等)都是通过矩阵表示,回到大学线性代数知识,向量(a1=[x1,y1,z1])与矩阵相乘,就会得到另一个向量(a2=[x2,y2,z2]),a1通过矩阵运算,得到了a2,这样就改变了坐标的位置。看到这里就明白了,如何通过计算得出我们想要的结果,就需要线性代数的知识了。(PS:矩阵真的很神奇,几乎一切变化都从这里来,在最后的例子中带大家来看看矩阵带来的魔法吧)

看完着色器的基本知识后,我们就可以看一下渲染的过程了。

WebGL渲染过程

WebGLAPI在了解一门新技术前,我们都会先看看它的开发文档或者API。于是,我们查看WebGL绘图API,发现:

是的,它只能画点、线、三角形。就算是像下面这样的复杂模型,也是一个个三角形画出来的。

简单绘制流程

简单说来,WebGL绘制过程包括以下三步:

获取顶点坐标(使用顶点着色器)

图元装配(这里画出一个个三角形,gl.TRIANGLES)

光栅化(生成片元/片段,即一个个像素点,使用片段/像素着色器)

接下来,我们分步讲解每个步骤。

顶点坐标

顶点坐标从何而来呢?一个立方体还好说,但如果是像上边复杂的茶壶呢?想一下,每个三角形都有三个顶点,而一个茶壶就会有成千上万个顶点,而且还需要精密的计算,显然人的肉眼以及精力是不允许一个一个写这些坐标的。往往它使用三维软件(C4D、MAYA、3DSmax等)导出,或者是框架生成。

获取顶点坐标过程图:

前面两个步骤都很好理解,但是第三部写入缓存区是什么意思呢?由于顶点数据往往成千上万,在获取到顶点坐标后,我们通常会将它存储在缓存区内,方便GPU更快的读取。

图元装配

我们已经知道,图元装配就是由顶点生成一个个图元(即点/线/三角形)。那这个过程是自动完成的吗?答案是并非完全如此。WebGL需要我们先处理顶点,那怎么处理呢?我们先看下图:

第一步就是将上面缓存中的顶点坐标传入了顶点着色器,顶点着色器根据传入的gl.POINTS/gl.LINES/gl.TRIANGLES参数,进行图元装配(通俗一点讲,就是要画点,还是线,还是三角形)

下面是一段顶点着色器代码:

attributevec4position;

voidmain(){

gl_Position=position;

}

这里具体解释一下:attribute修饰符,是由浏览器(javascript)传输给顶点着色器的变量值修饰符;vec4就是包含4个元素的浮点型向量(坐标);position即我们定义的顶点坐标,传入到着色器的;gl_Position是一个内建的传出变量。

这段代码其实就是GPU通过传入的数据找顶点的过程。

光栅化

和图元装配类似,光栅化也是可控的。

在图元生成完毕之后,我们需要给模型“上色”,模型看起来是什么质地(颜色、漫反射、贴图等)、灯光等,而完成这部分工作的,则是运行在GPU的“片元着色器”来完成。如下是一段简单的片元着色器代码:

precisionmediumpfloat;

voidmain(void){

gl_FragColor=vec4(1.0,0.5,1.0,1.0);

}

precision是指向一个整数的指针,返回的该整数是对应格式的精度的位数,用log2取对数的值,暂不做深究。gl_FragColor是一个内建的传出变量,即输出的颜色值,这段代码就是紫粉色。

片元着色器处理流程

片元着色器具体是如何控制颜色生成的呢?

如上图,顶点着色器是有多少顶点,运行了多少次,而片元着色器则是,有多少片元(像素),运行多少次。

整体详细绘制流程

至此,实质上,WebGL经历了如下处理流程(这里我们涉及到的前面没讲到的名词稍微多一点,但是大概涵盖了所有涉及到的内容):

准备数据阶段在这个阶段,我们需要提供顶点坐标、索引(三角形绘制顺序)、uv(决定贴图坐标)、法线(决定光照效果),以及各种矩阵(比如投影矩阵)。

如何传入?

顶点数据存储在缓存区(因为数量巨大),以修饰符attribute传递给顶点着色器;

矩阵则以修饰符uniform传递给顶点着色器。

生成顶点着色器根据我们需要,由Javascript定义一段顶点着色器程序的字符串,生成并且编译成一段着色器程序传递给GPU。传入的顶点着色器程序,是一个字符串,这是WebGLAPI所要求的,会进行编译成着色器语言。我们来大致看一下看一下。

scriptid="2d-vertex-shader"type="notjs"

attributevec4a_position;

voidmain(){

gl_Position=a_position;

}

/script

scriptid="2d-fragment-shader"type="notjs"

precisionmediumpfloat;

voidmain(){

gl_FragColor=vec4(1,0.5,1,1);

}

/script

functiononload(){

...

//着色器字符串程序

constvertexShaderSource=document.getElementById("2d-vertex-shader").text;

constfragmentShaderSource=document.getElementById("2d-fragment-shader").text;

//start将字符串传入,创建顶点以及片段着色器

constvertexShader=createShader(gl,gl.VERTEX_SHADER,vertexShaderSource);

constfragmentShader=createShader(gl,gl.FRAGMENT_SHADER,fragmentShaderSource);

//end创建着色器

...

}

图元装配GPU根据顶点数量,挨个执行顶点着色器程序,生成顶点最终的坐标,完成坐标转换。

生成片元着色器这一步则是解决我们最终绘制出来的效果,它的模型是什么颜色,看起来是什么质地,光照效果,阴影(流程较复杂,需要先渲染到纹理,可以先不


转载请注明:http://www.soiphoto.com/sjyd/sjyd/17894.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了