基于上一篇OpenGL的渲染原理,这两周又陆续接触了一些关于WebGL绘图的一些内容,因为刚入门,很多东西又很晦涩,所以特意花了小半天的时间整理了一下,特此记录。

零  画一个多边形吧!

  把一个多边形画上屏幕分几步?

  答:分三步,第一,打开屏幕(1-1.从HTML中获取Canvas对象;1-2.从Canvas拿到WebGL的Context);第二,把数据画好(2-1.编译着色器;2-2.准备数据模型;2-3.顶点缓存VBO的生成和通知);第三,画上去(3-1.发出绘图命令,更新Canvas并渲染)。

   画布和画笔:创建Canvas && 获取WebGL的Context

  在开始WebGL的绘制故事之前,我们得先来认识一下Canvas,因为这玩意是我们之后绘图的基础底板:“Canvas元素创造了一个固定大小画布,并提供了一个或多个渲染上下文,用来绘制和处理要展示的内容”(摘自MDN)。按照定义,我们可以将其理解为渲染任务的中转站,因为最终绘图的输出是要将数据交给屏幕展示的,Canvas只是作为中间暂存待渲染数据的中转站,也可以看作是一个具有仿屏幕像素矩阵数据结构的容器,大概类似于中间缓存一类的概念。那为啥不直接在屏幕上输出画出来呢?之前看到过这样一个解释,缓存的作用在于下一帧没有及时渲染出来的时候(渲染时间超出了人眼的最低感知帧率,最低帧率为24帧),当前帧的数据就能够替代下一帧,以此来保证过程的完整性。

  WebGL的API提供了能够在支持的浏览器中无插件地绘制交互式的2D/3D图像,而WebGL中最重要的对象:渲染上下文WebGLRenderingContext是基于OpenGL ES 2.0的绘图上下文所实现,一般用在HTML5的<canvas>元素内绘图等任务上。而WebGLRenderingContext可以看作是渲染任务的“核心CPU“,所有的操作:从视口剪裁、状态信息、数据缓冲区、着色器的创建和调用、缓冲区绘制等等任务,都由WebGLRenderingContext调配和使用,所以,在任何Web程序开始绘制之前,我们所需要做的第一件事情就是创建一个画布作为数据容器,并将其与WebGL的上下文进行绑定,这样一来,我们既有了画布,又有了画笔,就可以开始绘制我们想要的图形了。

二  调色盘:着色器

  在传统的OpenGL的固定管线中,我们对于渲染过程的控制力度是有限的。从上一篇OpenGL基础里来看,在顶点操作和图元装配、纹理化、片元着色等过程中,我们能控制的只有调用底层硬件厂商提供的接口参数,使用固定的程序去进行上述过程的处理。这种级别的控制非常弱,记得不久前在知乎上看到了一个关于固定管线控制的比喻:“扳开关”,我觉得十分贴切,这个概念有点类似于铁道上的扳道工,火车的前进方向只能在铺设好的轨道上选择,如果没有轨道的地方,火车自然就没法开到。渲染同理,如果对于渲染效果有更高更灵活的要求(或者你无法接受硬件厂商提供的笨重不堪的渲染参数配置,想用更能让人接受的方式去定制属于自己的渲染结果),那么固定管线的处理方式就基本可以不考虑了。

图1 扳开关 

  那懒人就不能拿起画笔在屏幕上作画了吗?幸运的是,我们遇到了可编程渲染管线。它的出现给上面遇到的难题带来的答案,从图2可以看出:可编程渲染管线中出现了两个处理器:顶点着色器vertex shader和片元着色器fragment shader。这两个处理器绕过了传统的顶点操作和图元装配、片元着色等过程,通过自身的可编程特性,分别接手控制了顶点坐标转换、像素颜色计算的工作:

1)在顶点着色器Vertex Shader的处理阶段,顶点数据从GPU显存中读取出来,顶点着色器Vertex Shader可以对每个顶点进行模型视图变换、投影变换等顶点处理工作,替代了固定管线中的顶点处理管线;

<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec4 a_color; uniform mat3 u_matrix; varying vec4 v_color; void main() {
gl_Position = vec4((vec3(a_position, 1)).xy, 0, 1); v_color = a_color;
}
</script>

2)顶点着色器处理完之后,管线会对各个顶点进行光栅化处理。由于顶点着色器的计算次数由顶点的数量决定,即n个顶点对应着n个顶点像素数据,但在一个由若干个像素组成的图元中,非顶点像素的颜色该如何确定呢?此时就需要给大家介绍一个新对象:数据类型Varyings,从下面一段简短的顶点着色器DEMO中可以看出来,我们在顶点着色器中定义了好几种数据类型,有attribute,uniform以及varying。但来得早不如来得巧,我们先来认识一下Varying。

  Varying是一个变量,作为一个信使连接着顶点着色器Vertex Shader和片元着色器Fragment Shader。在一般情况下,顶点着色器Vertex Shader会计算出每个顶点的颜色、坐标等值,并用Varying变量来存储这些值。回到刚才提出的问题,非顶点的像素如何确定自己的值呢?这就需要片元着色器来理解这个信使了,好在片元着色器和顶点着色器之间有个光栅器牵线搭桥,当顶点着色器传来Varying类型的顶点值时,光栅器会指定一种插值模式,指导片元着色器按像素逐个进行渲染绘制。

3)片元着色器Fragment Shader在光栅化工具(这个工具我也没有仔细研究,暂时当作一个黑盒吧)的指导下进行工作(片元在上一篇OpenGL基础中已经提到过了,所谓片元即指光栅化后的图元)。片元着色器fs的主要工作是为当前光栅化的像素提供颜色值,屏幕中的每一个像素都需要调用一次片元着色器Fragment Shader,每次调用都会从一个特殊的全局变量gl_FragColor中获取颜色信息。

<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment"> precision mediump float; varying vec4 v_color; void main() {
gl_FragColor = v_color;
}
</script>

图2 可编程渲染管线

三 轮廓骨架线

  在前面的步骤大概能够初探一二之后,下一步就是在显存中创建顶点对象VBO了:所谓VBO,顶点缓冲区对象( Vertex buffer object )这个概念来自于OpenGL,其概念定义为一个将顶点Vertex的各类属性信息(如vertex坐标,vertex法向量以及颜色等)存储在显存的一块专用的缓存区中,在执行渲染命令时,可直接从显存中取出VBO。由于整个过程都是在GPU中进行,不同于之前传统的绘制方式(CPU命令GPU执行绘制动作,反复传输大量的顶点数据到GPU中,渲染速度较慢),所以一般将VBO视为一个能够改善数据传输效率的对象。

  那么VBO是如何在WebGL中应用的呢?我们通常第一步通过createBuffer方法创建一个缓存对象VBO,通过图3的MDN中的定义可以看出,返回值VBO可以是颜色或者顶点坐标值的缓冲对象。此时只需调用gl.bufferData向gl_ARRAY_BUFFER中写入数据,再使用gl. bindBuffer方法就可以把buffer数据与gl上下文中的ARRAY_BUFFER关联起来,也就是把顶点数据成功地写入了GPU显存中。

图3 WebGLBuffer

    function main(){
      ......
//Create a buffer & bind buffer
//创建buffer数据,并绑定到gl的上下文中
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
//set Geometry
//填充buffer
setGeometry(gl);
      ......
}
function setGeometry(gl) {
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-150, -100,
150, -100,
-150, 100,
150, -100,
-150, 100,
150, 100]),
gl.STATIC_DRAW);
}

  进行到上述阶段为止,渲染之前的初始化工作基本完成:画布空间已创建  -->  将画布Canvas与WebGL环境绑定 ---> 创建并指定了相关自定义着色器填充顶点数据 ---> 创建顶点缓存VBO。一切准备就绪,千军万马一般的屏幕像素们都在等着一个明确渲染绘图指令,只待gl令旗一挥,在GPU的指挥下,千万个屏幕像素将会按照规定的位置和颜色,以迅雷不及掩耳之势一蹴而就,以你所规定的样式完美地呈现在你的屏幕上。

四 画!

  一切准备都已完成,所有任务执行资源就位后,我们再来一起看看最后一步的绘图渲染命令是如何发出的。

  简单来看,我们可以把绘图渲染部分分为以下三部分:

    1)画布清洁

    2)指定环境

    3)执行着色程序

//Rendering code: 渲染代码
function drawScene() {        //-----------------------画布清洁---------------------
webglUtils.resizeCanvasToDisplaySize(gl.canvas); //covert from clip space to pixels
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); //clear canvas
gl.clear(gl.COLOR_BUFFER_BIT); //direct to our program
gl.useProgram(program);
//------------------------指定环境-------------------------------
//打开属性attribute开关
gl.enableVertexAttribArray(positionAttributeLocation); //对当前状态进行绑定 : 绑定已经完成填充点数据的buffer数据块positionBuffer
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 步长(byte),每个顶点数据所占的字节数:0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer //vertexAttribPointer:顶点属性指路牌
//告诉显卡从当前绑定的缓冲区(drawScene方法中的bindBuffer)中读取顶点数据vertex data
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset);
//------------------------------执行着色程序-------------------------------------// Draw the geometry.
var primitiveType = gl.TRIANGLES; //绘制图元模式
var offset = 0;//从缓冲区开始读取数据的首地址偏移first下标
var count = 6;//绘制顶点数据的个数,即Shader代码的运行次数
gl.drawArrays(primitiveType, offset, count);
}
//==================================================================================

  具体的WebGLRenderingContext提供的接口我在这里就不赘述了,这篇仅仅只是为了带给大家一个如何在浏览器中绘制渲染图形的概念,之后应该会有针对各个环节的专题,毕竟才刚刚入坑,来日方长!

【GISER&&Painter】Chapter01:WebGL渲染初体验的更多相关文章

  1. (4)Flask项目模板渲染初体验

    一.准备静态资源 将项目使用到的静态资源拷贝到static目录 二.创建前台首页html 创建templates/home/home.html页面,内容包含导航和底部版权两部分,中间内容区域为模板标签 ...

  2. Handlebars的基本用法 Handlebars.js使用介绍 http://handlebarsjs.com/ Handlebars.js 模板引擎 javascript/jquery模板引擎——Handlebars初体验 handlebars.js 入门(1) 作为一名前端的你,必须掌握的模板引擎:Handlebars 前端数据模板handlebars与jquery整

    Handlebars的基本用法 使用Handlebars,你可以轻松创建语义化模板,Mustache模板和Handlebars是兼容的,所以你可以将Mustache导入Handlebars以使用 Ha ...

  3. 【腾讯Bugly干货分享】基于 Webpack & Vue & Vue-Router 的 SPA 初体验

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d13a57132ff21c38110186 导语 最近这几年的前端圈子,由于 ...

  4. 基于 Webpack & Vue & Vue-Router 的 SPA 初体验

    基于 Webpack & Vue & Vue-Router 的 SPA 初体验 本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com ...

  5. YII学习,初体验 ,对YII的一些理解.

    先说点没用的: 不会选择,选择后不坚持,不断的选择.这是人生中的一个死循环,前两一直迷茫.觉得自己前进方向很不明朗.想去学的东西有很多.想学好YII,想学PYTHON 想学学hadoop什么的,又想研 ...

  6. vue.js2.0 自定义组件初体验

    理解 组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能.在有些情况 ...

  7. flutter初体验

    flutter初体验 和flutter斗争了两个周末,基本弄清楚了这个玩意的布局和一些常用组件了. 在flutter里面,所有东西都是组件Widget.我们像拼接积木一样拼接Widget,拼接的关键词 ...

  8. django初体验 学习笔记

    django环境搭建     1.安装Python     2.ipython         sudo apt-get install ipython         sudo pip instal ...

  9. "xaml+cs"桌面客户端跨平台初体验

    "Xaml+C#"桌面客户端跨平台初体验 前言   随着 .Net 5的到来,微软在 .Net 跨平台路上又开始了一个更高的起点.回顾.Net Core近几年的成果,可谓是让.Ne ...

随机推荐

  1. CSU 1425 Prime Summation

    原题链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1425 DP题. f[i][j]表示当前数字为i,分解式中最大质数为j的方案数,那么,状态 ...

  2. vim 图册

    网上看到的一些图,感觉不错,分享一下 我现在感觉配置文件,很多没有必要,反而很花哨,但是这些基础的东西,反而很高效,实在 VIM的列编辑操作 删除列 1.光标定位到要操作的地方. 2.CTRL+v 进 ...

  3. Linux文件访问和日志

    一.文件系统创建一个文件的过程假设我们想要新增一个文件,此时文件系统的行为是:先确定用户对于欲新增文件的目录是否具有 w 与 x 的权限,若有的话才能新增:根据 inode bitmap 找到没有使用 ...

  4. [转]C/C++关于全局变量和局部变量初始化与不初始化的区别

    原文链接:http://www.kingofcoders.com/viewNews.php?type=newsCpp&id=189&number=4836955386 在C语言里,全局 ...

  5. 二十二 使用__slots__

    正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性.先定义class: class Student(object): pa ...

  6. javascript右下角弹层及自动隐藏

    在编写项目中总会需要有个右下角弹层提示公告的需求,怎么用更简单方面,更简洁代码,实现更好用户体验这个就是我们的所要做的内容.市场这块弹层很多,但功能不尽如人意.下面分享早些时候自己编写,以及现在还在应 ...

  7. 遍历datatable的几种方法

    方法一: DataTable dt = dataSet.Tables[]; ; i < dt.Rows.Count ; i++) { string strName = dt.Rows[i][&q ...

  8. NBUT 1221 Intermediary

    最短路,三进制状态压缩. $dis[i][j]$表示到$i$节点,每个中介用了几次的情况下的最小花费,跑最短路即可. #include<cstdio> #include<cstrin ...

  9. 洛谷P2234 [HNOI2002] 营业额统计 [splay]

    题目传送门 营业额统计 题目描述 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每天 ...

  10. java变量的命名使用规则

    1.环境变量通常是指在操作系统中,用来指定操作系统运行时需要的一些参数 2.变量名以字母.下划线或者美元符(4上面的¥)开头,不能以数字开头,后面跟字母.下划线.美元符.数字,变量名对大小写敏感,无长 ...