opengl超级宝典(opengl值得学吗怎么学)

学习了一段时间的 OpenGL ES,并在公司的项目中得到了运用,也算是有了一些积累,现在分享一些当初学习的资源,大家一起来学习,共同交流进步。

关于学习方式

在分享资源之前,简单地聊聊学习的方式。

有句名言说的好:

书籍的人类进步的阶梯

在需要解决一些未知领域的问题、完成一些未知领域的需求时,是必须要去学习一些新东西的。

而在学习这些新东西时,不要太依赖于搜索引擎了,不然只是当下解决了某些问题、完成了某些需求。

通过看一些博客文章、看一些文章分析,在某些时刻确实是很有帮助的,但总是会存在一些碎片化知识,没有系统地形成知识网络,此时掌握的仅仅是技巧。还是要通过系统地去学习某些知识内容,在脑海里面有个完整的知识体系。

这个简单的道理大家都懂,就不多说了~

简单上手

作为程序员学习一项内容,最重要的就是 Hello World 了。

《OpenGL ES 应用开发实践指南》

opengl超级宝典(opengl值得学吗)

这本书比较通俗易懂,直接上手使用 OpenGL ES,可以说是手把手教学了。

作为初学者,最重要的是啥?环境配置、Demo 运行呀~~~

在 《OpenGL ES 应用开发实践指南》里面,跟着书中的章节顺序走,每一章都会有代码示例,也算是一步步引导了。

你可以暂时不求甚解,先把示例工程运行起来,等熟练了再去深究原理。

美中不足的是,这本书针对的 OpenGL ES 版本是 2.0 的,在 OpenGL ES 3.x 中的一些特性无法体验到了,而且现在的手机大多支持 OpenGL ES 3.x 版本了,不过要是考虑到兼容低版本的情况,还是可以使用 OpenGL ES 2.0 版本的。

这本书是翻译过来的,它的英文原版封面如下:

opengl超级宝典(opengl值得学吗)

《OpenGL ES 应用开发实践指南》

简单上手了 OpenGL ES 2.0 之后,该了解一下 OpenGL Shading Language (GLSL)了。

GLSL 就是着色器脚本语言,这个语言是用来给 GPU 运行的,灵活地使用它才能更好地掌握 OpenGL ES,要知道现在手机相机上的一些滤镜效果都是通过 GLSL 来实现的哦。

《OpenGL® Shading Language, Second Edition》

opengl超级宝典(opengl值得学吗)

《OpenGL® Shading Language, Second Edition》

这本书是英文版的,讲解了 GLSL 的一些语法,基于的版本是 OpenGL ES 2.0 的,正好和前面的书籍配套学习了,而且英文难度不大,易懂。

该书中同样有很多例子可以实践,比如光照、阴影、噪音等。

通过这两本书的配套练习,可以掌握 OpenGL ES 2.x 版本的基本内容了。

当然了,除此之外,你还需要更多的练习。

可以参考这本书,获得更多打怪晋级的经验:

opengl超级宝典(opengl值得学吗)

《Android 3D 游戏开发技术宝典》

《Android 3D 游戏开发技术宝典》一书中有很多可以在实践中用到的内容,具体内容就等大家自行探索了~~~

高阶版本

当然了,学会了 OpenGL ES 2.0 再去看 OpenGL ES 3.x 就容易多了。

这两者在 GLSL 上是有一些变化的,另外 OpenGL ES 3.x 支持的渲染效果更好,而且支持的特性更多。

关于 OpenGL ES 3.x 版本的学习,有如下书籍推荐:

opengl超级宝典(opengl值得学吗)

OpenGL ES 3.x 游戏开发

opengl超级宝典(opengl值得学吗)

OpenGL ES 3.0 编程指南

在 Android 后续系统版本中,都开始使用 Vulkan 来替代 OpenGL 了。

等掌握了 OpenGL ES 之后,下一个就是 Vulkan 了~~~

另外关于书籍推荐,其实大家可以到京东或者当当上搜索一下关键字就知道了,目前市面上关于 OpenGL ES 的书籍也不多,搜来搜去也就是那几本书啦~~~对于其他领域的书籍情况类似…

深入理解

当你已经掌握了 OpenGL ES 的大部分内容,并且可以简单的运用他们了,这时候再想去深入理解它们,那就必须要说到 OpenGL ES 学习中的红宝书和蓝宝书了。

红宝书指的是 《OpenGL 编程指南》,目前已经出到了第九版了,蓝宝书指的是《OpenGL 超级宝典》目前已经出到了第五版了。

opengl超级宝典(opengl值得学吗)

红宝书与蓝宝书

这两本书就没有前面那么多代码示例了,更多的是讲解一些原理相关的内容,而且也不是特别针对 Android 开发环境来讲的。这两本书更多是还是当做工具书来使用,当某些知识点不清晰时,看看书查漏补缺~~~(反正我是当工具书用了)

听说,下雨天,代码和书籍更配哦~

显然,光是看书是不够的,纸上得来终觉浅,绝知此事要躬行。

在 OpenGL ES 开发中,有一些项目是必看的:

  • https://github.com/CyberAgent/android-gpuimage
  • https://github.com/BradLarson/GPUImage2
  • https://github.com/google/grafika

这些项目中可以看到 OpenGL ES 在相机滤镜和视频录制方面的运用~

最后

俗话说:

独学而无友,则孤陋而寡闻

光是掌握了这些书上的内容还是不够的,更多的是需要交流和讨论,让这些知识在每一次的探讨中变得更加生动灵活,不再是枯燥的代码和理论。

要是二维码过期了,加微信 ezglumes 好友,备注 OpenGL ,拉你入群~

本文中提到的书籍资源,皆可在我的 Github 地址上下载得到:

https://github.com/glumes/AndroidOpenGLTutorial

OpenGL是业界使用最广泛的2D和3D图形API,将成千上万的应用程序带到各种计算机平台上。它与窗口系统和操作系统无关,并且网络透明。 OpenGL使PC、工作站和超级计算类型硬件的软件开发人员能够在CAD、内容创建、能源、娱乐、游戏开发、制造、医疗和虚拟现实等市场中创建高性能,视觉上引人注目的图形软件应用程序。 OpenGL公开了最新图形硬件的所有功能。经常做UI的开发者有可能一眼就看出OpenGL的UI界面和其它技术制作界面的区别。OpenGL第一个版本就很成熟,后续不断升级,是一个超级平台,欢迎大家来学习OpenGL开发。

OpenGL 4.6概览

OpenGL 4.6和OpenGL阴影语言4.60规范于2017年7月31日发布。

OpenGL 4.6的新功能包括:

GL_ARB_gl_spirv和GL_ARB_spirv_extensions标准化对OpenGL的SPIR-V支持

GL_ARB_indirect_parameters和GL_ARB_shader_draw_parameters用于减少与渲染几何批量相关的CPU开销

GL_ARB_pipeline_statistics_query和GL_ARB_transform_feedback_overflow_query标准化OpenGL对Direct3D中可用功能的支持

GL_ARB_texture_filter_anisotropic(基于GL_EXT_texture_filter_anisotropic)将先前IP限制的功能引入OpenGL,以改善纹理场景的视觉质量

GL_ARB_polygon_offset_clamp(基于GL_EXT_polygon_offset_clamp)可抑制与渲染阴影相关的常见视觉伪影,即“漏光”

GL_ARB_shader_atomic_counter_ops和GL_ARB_shader_group_vote添加了所有桌面供应商支持的着色器内在函数,以改善功能和性能
GL_KHR_no_error通过允许应用程序指示其期望无错误操作来减少驱动程序开销,因此无需生成错误
OpenGL 4.6的新扩展包括:

GL_KHR_parallel_shader_compile
WGL_ARB_create_context_no_error
GLX_ARB_create_context_no_error
GL_EXT_memory_object
GL_EXT_memory_object_fd
GL_EXT_memory_object_win32
GL_EXT_semaphore
GL_EXT_semaphore_fd
GL_EXT_semaphore_win32
GL_EXT_win32_keyed_mutex

WebGL入门

Web前端开发比较热门,这里专门介绍WebGL开发。其它介绍请关注上面OpenGL开源社区里的链接

Web应用程序很有吸引力,因为它们是共享您的工作的绝佳方式。用户所需要做的只是打开一个Web链接。 WebGL是浏览器实现的界面,可让Web应用程序访问功能强大的硬件加速渲染。无需插件或安装。 WebGL基于OpenGL的移动版本; “ OpenGL ES”不需要最新的图形硬件,具有很高的便携性,并且可以在较旧的台式机和大多数移动设备上运行。而且,甚至更好的是,您无需为每个操作系统都进行特殊的构建。您可能会发现WebGL创建程序的速度要比台式机和移动设备快几倍。
我将在本页顶部解释如何制作旋转字符,并介绍基本的启动,JavaScript的使用,加载纹理,着色器,网格和文件加载以及矩阵处理。为了避免多余的教程内容,我将假设您至少已经完成了现代OpenGL编程的基础知识-这意味着您对着色器和顶点缓冲区有所了解。我将假定读者熟悉基本的HTML,但不一定熟悉JavaScript或更新的Web界面。我们将不会使用任何框架或高级接口,因为已经有足够的资源来使用这些框架或高级接口。

技术概述

编写WebGL软件时,您会发现自己在几种语言之间切换。

Language 功能

HTML5 编写带矩形画布的页面,画布内进行渲染

JavaScript 调用GL函数,用AJAX加载资源,处理用户输入,编写主逻辑

GLSL 编写shaders来定义渲染的风格

我们将只使用5中的一项或多项新功能来编写一些基本的HTML。JavaScript具有一个文件加载接口,通常称为“ AJAX”(异步JavaScript和XML),因为它旨在将XML文件序列化为JavaScript。对象,但我们将其用作通用文件加载器,该加载器返回包含文件内容的字符串。着色器语言有一个或两个非常小的区别,与OpenGL ES相同,具有基于OpenGL ES 2.0规范的WebGL 1.0和基于ES 3.0的WebGL 2.0。

Basic HTML Skeleton 基本HTML骨架

我们可以从制作一个非常简单的HTML网页开始。

我们首先制作一个简单的HTML网页,上面有一个画布区域(以阴影显示)。我们将能够使用任何其他Web界面元素与我们的代码进行交互。
WebGL软件的基本概念是我们编写一个普通的网页,并使用新的HTML5画布标签定义渲染将绘制到的页面区域(空白矩形)。您可以使用HTML的任何形式,文本区域和其他元素来与您的可视化进行交互-可以说我们内置了用户界面库。
我开始是这样的:

<html>
<head></head>
<body>
<canvas id="mycanvas">
This text will appear if the browser doesn't support HTML5.
</canvas><br />
<small><i>
A model from a recent Ludum Dare game jam.
You can view the source code of the page to see how I display this.
</i></small>
</body>
</html>

如果您在浏览器中打开它,您将看不到任何东西-只是默认300×150像素画布所在的空间。我们将在不久后为其附加GL上下文。为此,我们添加一些JavaScript,它将与我们的Web文档对话。
在我们的HTML中,画布是一个简单的标签,带有DOM ID作为代码钩。我们还可以提供宽度和高度属性来指定页面上画布的实际大小。
我们利用DOM(文档对象模型)从我们的JavaScript代码访问网页元素。这就像给每个元素的HTML标签提供id =“ my_thingy”属性一样简单。该浏览器还具有BOM(浏览器对象模型),该BOM提供了内置功能来处理通过鼠标,游戏手柄或键盘进行的用户交互。

JavaScript启动代码

JavaScript取代了C作为图形库的主要接口语言。如果您以前从未使用过JavaScript,那么您应该知道除了名称之外,它与Java语言无关-从人们仍然认为Java是一个好主意的时代起,这就是一种营销计划! JavaScript是一种客户端脚本语言,这意味着客户端的Web浏览器会下载您的整个源代码,然后在自己的CPU上的浏览器中运行它。这意味着您可以执行功能更强大的交互式调试,并且不需要重新编译,但是比C慢一点。我们能在html内任何地方写script,不过我喜欢写在最后,需要加如下:

<script>
            console.log("starting WebGL");
          var canvas = document.getElementById("mycanvas");
                     canvas.width = 512;
                     canvas.height = 256;
            var gl = canvas.getContext("webgl");
          gl.clearColor(0.85, 0.85, 0.85, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            // set some GL states that I want to use for rendering
            gl.cullFace(gl.BACK);
            gl.frontFace(gl.CCW);
            gl.enable(gl.CULL_FACE);
            gl.enable(gl.DEPTH_TEST);
  </script>
          

我在这里介绍一些新概念。第一条指令与printf()的JavaScript等效。如果转到浏览器的开发人员菜单,则可以打开JavaScript控制台,并且应该看到该消息。每次刷新或重新加载页面时都将其打开是很好的,因为它会为您提供很好的错误信息,您可能不会注意到这些错误信息,因为页面可能会继续看起来正确加载。

我使用DOM来获取我的canvas元素作为JavaScript对象。请注意,JavaScript不使用强类型-一切都是一个var对象,我可以告诉您这引起的问题远远超过其解决的问题。无论如何,我的第一个动作是从JavaScript修改画布的width和height属性,这应该更改其在页面上的大小。
接下来,我要求画布使用新的WebGL上下文进行设置,并将其作为名为gl的对象进行跟踪。该对象将成为我们所有WebGL功能的接口。
我的最终指令使用gl对象调用GL函数。它们几乎与OpenGL函数名称和常量相同,除了gl和GL_被删除了,我们通过新对象访问它们。如果您想使背景透明并且要显示页面背景,则可以在此处将aplha通道设置为0.0。如果刷新网页,您应该会看到画布变大并且变色了。
阅读参考,您会注意到WebGL不支持较新的OpenGL的“顶点数组对象”(VAO)。在没有VAO的OpenGL中,这实际上是非常繁琐的渲染,因为每次绘制时都必须设置顶点属性指针。 WebGL有一个VAO扩展。您可以在扩展注册表中看到它。我们可以在脚本块中查询它,这将返回一个新对象,该对象然后是我们与VAO扩展功能子集的接口:

var vao_ext = gl.getExtension ("OES_vertex_array_object");
if (!vao_ext) {
  console.error ("ERROR: Your browser does not support WebGL VAO extension");
}

如果用户的浏览器/系统不支持该功能,则可以使用浏览器控制台的错误日志记录机制进行报告。它还在控制台的输出中包括一个行号链接。

异步加载纹理

实际上,将纹理加载到WebGL中比使用常规OpenGL要容易得多,因为我们不需要图像加载库-HTML已经加载了图像。我们可以使用网页上显示的图像作为纹理,也可以在JavaScript中安静地加载图像。请注意,这是异步的,因此稍后会在您提供图像URL后的代码中实际创建纹理。

var texture = gl.createTexture();
texture.is_loaded = false;
var image = new Image();
image.onload = function () {
  gl.bindTexture (gl.TEXTURE_2D, texture);
  gl.pixelStorei (gl.UNPACK_FLIP_Y_WEBGL, true);
  gl.texImage2D (gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,
    image);
  gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  texture.is_loaded = true;
  console.log ("texture loaded ");
}
image.src = "webgl_starter/heckler.png";

请注意,我做了一些非常不寻常的事情,并在纹理内设置了is_loaded属性。我们知道OpenGL纹理甚至都不是对象-它们当然没有属性!在JavaScript中,我们可以向现有对象动态添加新属性。 由于现代网络使用异步下载模型,因此我们无法确定何时实际下载资源。如果连接不良,主循环启动后一分钟内,加载功能可能甚至无法执行。同时,我们至少将拥有一个有效但为空的纹理,并带有一个要检查的标志。在足以复杂地异步加载文件的C程序中,我们将进行如下设置:

start_loading_shader();
start_loading_texture();
start_loading_mesh();
while ( !everything_has_loaded() ) {
  sleep();
}
// end of loading function

我们不允许在JavaScript中使用跨线程的等待循环,因此,我们可以做的最好的事情就是在主循环中添加if语句检查,这样它就不会在所有必需的资源加载之前尝试绘制。例如:

if (texture.is_loaded) {
  gl.activeTexture (gl.TEXTURE0);
  gl.bindTexture (gl.TEXTURE_2D, texture);
  ...
  // draw with the texture
}
...
// rest of drawing code

您会看到注入一些状态属性为什么有用的原因,以及我们在WebGL演示中看到的资源“弹出”效果从何而来。如果要避免这种奇怪的影响,您可以使用一个函数来检查是否在渲染之前加载了所有资源,而是渲染了一些“加载..”文本,但是从我的经验来看,这个大型商业项目有很多如果要下载大量资源数据,如果他们不得不在移动设备上或连接不良的情况下等待很长时间,则可能会带来非常糟糕的用户体验-您可能会发现用户宁愿选择“弹出”,也不愿等待很长时间。

从HTML脚本加载着色器

我通常希望将着色器存储在外部文件中,但这实际上在JavaScript中有点不方便,JavaScript也希望异步加载它们。对于多部分着色器,使用此机制实际上是一件烦人的事。您可以将两个着色器连接到一个文件中。您还可以将着色器存储在JavaScript字符串中。相反,大多数开发人员发现将着色器放置在自己的脚本块中更加方便,这样您就不必担心字符串格式或异步下载。我将顶点着色器放在其他脚本之上的自己的脚本块中:

<script id="heckler.vert" type="text/glsl">
              attribute vec3 vp;
              attribute vec2 vt;
              attribute vec3 vn;
            uniform mat4 PV, M;
            varying vec2 st;
              varying vec3 n;
          void main () {
              st = vt;
              n = vec3 (PV * M * vec4 (vn, 0.0));
              gl_Position = PV * M * vec4 (vp, 1.0);
              }
  </script>        

我将属性的类型设置为适当的外观,以使浏览器不会认为应该使用JavaScript。我设置了id属性,以便以后获取。用于WebGL 1.0的GLSL使用较旧的OpenGL 2.1和OpenGL ES而不是较新的输入和输出的acoderibute和不同的关键字。同样,我在脚本块中有一个片段着色器:
<script id=”heckler.frag” type=”text/glsl”> \u003cscript id \u003d“ heckler.frag” type \u003d“ text / glsl”\u003e
precision mediump float; 精密中型浮子;

varying vec2 st;
              varying vec3 n;
              uniform sampler2D dm;
void main () {
              vec4 texel = texture2D (dm, st);
            vec3 nn = normalize (n);
              vec3 fwd = normalize (vec3 (0.0, 0.0, -1.0));

              gl_FragColor = texel;
              gl_FragColor.rgb = gl_FragColor.rgb * dot (nn, fwd) * 0.7
              + 0.3 * gl_FragColor.rgb;
              }
  </script>       

我们用varying代替in,,以及内置的gl_FragColor代替了输出变量。 WebGL片段着色器需要在顶部具有确切的精度声明。
要将这些块的内容作为JavaScript字符串获取,请在加载网格后执行以下操作:

          var el =  document.getElementById ("heckler.vert");
          var vs_str =  el.innerHTML;
          el =  document.getElementById ("heckler.frag");
          var fs_str =  el.innerHTML

我只是再次使用DOM来获取字符串。可以将着色器放在可见的文本区域中,然后实时编辑它们,而不是脚本块。 Web元素具有您可以定义的一系列.on ….()函数。当用户单击某些内容或更改了文本时,将触发回调函数-您的函数可以重新编译着色器。
之后,我将定期查看代码来编译着色器,并获取一些变量来保存制服的位置。请注意,我还分别将点,纹理坐标和法线的属性位置分别绑定到0、1和2。

var vs =  gl.createShader (gl.VERTEX_SHADER);
          var fs =  gl.createShader (gl.FRAGMENT_SHADER);
          gl.shaderSource  (vs, vs_str);
          gl.shaderSource  (fs, fs_str);
          gl.compileShader  (vs);
          if  (!gl.getShaderParameter (vs, gl.COMPILE_STATUS)) {
          console.error ("ERROR compiling vert  shader. log: " +
          gl.getShaderInfoLog (vs));
          }
          gl.compileShader  (fs);
          if  (!gl.getShaderParameter (fs, gl.COMPILE_STATUS)) {
          console.error ("ERROR compiling frag  shader. log: " +
          gl.getShaderInfoLog (fs));
          }
          var sp =  gl.createProgram ();
          gl.attachShader  (sp, vs);
          gl.attachShader  (sp, fs);
          gl.bindAttribLocation  (sp, 0, "vp");
          gl.bindAttribLocation  (sp, 1, "vt");
          gl.bindAttribLocation  (sp, 2, "vn");
          gl.linkProgram  (sp);
          if  (!gl.getProgramParameter (sp, gl.LINK_STATUS)) {
          console.error ("ERROR linking program.  log: " + gl.getProgramInfoLog (sp));
          }
          gl.validateProgram  (sp);
          if  (!gl.getProgramParameter(sp, gl.VALIDATE_STATUS)) {
          console.error ("ERROR validating  program. log: " +
          gl.getProgramInfoLog (sp));
          }
          var heckler_PV_loc  = gl.getUniformLocation (sp, "PV");
          var heckler_M_loc  = gl.getUniformLocation (sp, "M");

“ PV”是我的组合投影和视图矩阵,“ M”是我的模型矩阵。尽管类似于C语言中的OpenGL,但您可以看到诸如字符串函数之类的东西更容易处理。如果刷新浏览器并在控制台中查看,则如果该信息不起作用,则会出现错误消息(包括着色器和链接器日志)。如果要确保已加载着色器代码,则可以使用警报(vs_str);抛出带有顶点着色器字符串的警报框。

用AJAX加载网格文件

我有一个要渲染的网格文件-我为最近的游戏卡纸制作的Wavefront .obj文件。我想做的是将.obj文件读入一个字符串,然后使用JavaScript字符串解析函数(非常好)将其分解为点,纹理坐标和法线的列表。我们可以为此使用AJAX,其工作方式如下:

var xmlhttp = new  XMLHttpRequest();
          xmlhttp.open  ("GET", "OUR_URL_STRING_HERE", true);
          xmlhttp.onload =  function (e) {
          var str = xmlhttp.responseText;
          var lines = str.split ('\n');
          for (var i = 0; i < lines.length; i++) {
          //...parsing code goes here...
          }
          }
          xmlhttp.send ();

首先,我们获得一个到新XMLHTTPRequest(AJAX的真实名称)的接口。我们告诉它它将使用HTTP“ GET”请求从URL检索文件,该文件将作为字符串提供。最后,它更喜欢异步工作,这在JavaScript中进行设置可能非常棘手,但是值得获得最佳加载时间。如果您想跳过这些麻烦事,可以将open()函数的第三个参数设置为false,但是浏览器会在控制台中向您投诉。
我们可以以与处理纹理相同的方式使用AJAX处理异步下载-添加is_loaded属性。我还将检查纹理是否已加载,因为如果先加载VAO并使用其他纹理进行渲染,它将看起来很糟糕:

var my_vao =  start_loading_obj ("meshes/my_mesh.obj");

          ...
          // _inside the  main drawing loop_
          if  (my_vao.is_loaded && texture.is_loaded) {
          vao_ext.bindVertexArrayOES (my_vao);
          ...
          // draw stuff that requires the VAO
          }

我不会在此处粘贴50行左右的.obj解析文件,但是您可以在obj_parser.js上查看它。您可以采用多种方法来构造JavaScript对象和函数。 JavaScript的另一个烦人的局限性是,您不能像在C语言中那样真正地从一个函数中传回一个以上的变量。您可能会考虑创建一个包含VAO和点数的网格容器对象,或者只是将顶点点数添加为像我在这里一样,您的VAO中的另一个属性-这取决于您希望在工作代码中拥有多少抽象。

function  start_loading_obj (url) {
          // first create an empty VAO
          var vao = vao_ext.createVertexArrayOES ();
          // inject an is_loaded boolean
          vao.is_loaded = false;
          // inject point count into VAO (yeah...)
          vao.pc = 0;
            var xmlhttp = new XMLHttpRequest();
            xmlhttp.open ("GET", url, true);
            xmlhttp.onload = function (e) {
            var str = xmlhttp.responseText;
            var lines = str.split ('\n');
            for (var i = 0; i < lines.length; i++) {
            //...parsing code goes here...
            }
            // store loaded state and point count in  VAO
            vao.pc = sorted_vp.length / 3;
            vao.is_loaded = true;
            }
            // start loading
            xmlhttp.send ();
            // return the empty VAO
            return vao;
            }       

请注意,类似于纹理创建,第一步是创建有效但为空的VAO。下载开始时,它将返回给函数调用者。这意味着,即使下载尚未完成,我们的主程序仍然对最终的VAO具有有效的句柄,并且可以检查其状态。发送功能开始HTTP握手。文件实际下载到客户端后,在原始函数调用返回后的某个时间onload回调函数将启动。在测试时很容易在这里犯一个错误。在本地网络上,下载不会有延迟。从另一个具有大网格的大陆,延迟可能会花费一些时间-在测试异步下载代码时,请检查可能最长和最坏的连接。您肯定会因很少的检查标志和回调而犯错误,并且拥有假定某些内容未经过充分检查就下载的代码。 在解析代码中,您可以使用str.split(’\\ n’);命令返回一个字符串数组;文件中的每一行一个,并使用for(var i= 0; i<clines.length; i ++){请注意,JavaScript使用动态数组(基本上是C ++向量),并且它们始终知道自己的长度。
要包含外部JavaScript文件,我们只需添加另一个脚本块,然后指定src =“ path_to_file.js”属性即可。

<script src="webgl_starter/obj_parser.js">

            </script>

我在这里有我文件的相对路径。请注意,您必须具有结束脚本标记-您不能像使用其他类型的HTML元素一样具有单个自闭合的<script />标记。这个新的脚本块实际上应该出现在我们的其他块之前,以便浏览器在使用它之前对其进行解析。如果您不这样做,它仍然可以工作,但是浏览器可能会警告您,它被迫降低了脚本的加载效率。
跨域文件访问
如果您是从桌面加载网页,则AJAX可能会投诉并拒绝加载文件,因为它违反了安全策略。您可以将内容放在本地网络服务器上-有许多轻量级服务器可用。您可以使用–disable-web-security命令行标志启动Chrome以忽略此预防措施,也可以仅使用Firefox,Firefox应该完全忽略此问题并加载文件。
创建矩阵
您将需要一组用于JavaScript的向量和矩阵数学函数。布兰登·琼斯(Brandon Jones)的gl-matrix是一个非常受欢迎的库。我也将我的数学库移植到JavaScript。您当然也可以自己编写。仅将矩阵和向量保留为JavaScript的本机数组格式比创建自定义数据结构更容易。
值得一看的是JavaScript数学库的源代码,以了解函数,参数和数组在JavaScript中的工作方式。函数没有声明-只是定义。它们都以函数关键字而不是数据类型为前缀,并且可以返回或不返回值。参数只是给定名称,不需要var前缀。可以进行curry和更高级的功能编程。数组以方括号内的逗号分隔值列表形式给出。使用以下任一方法创建一个空数组

var my_array = []

要么

var my_array = new Array ().

我只在数学库中包含另一个脚本块。我使用外观熟悉的函数创建视图和投影矩阵:

var cam_dirty =  true;
          var V = look_at  ([0.0, 0.4, 1.0], [0.0, 0.4, 0.0],
          normalise_vec3 ([0.0, 1.0, 0.0]));
          var aspect =  canvas.clientWidth / canvas.clientHeight;
          var P =  perspective (67.0, aspect, 0.1, 100.0);
          var PV =  mult_mat4_mat4 (P, V);

注意:我的画布尺寸是整数,但是这里的除法是浮点除法,而不是整数除法。这可能是您第一次接触到可怕的JavaScript。如果您想要特定的数据类型,通常必须使用特定的截断或解析函数来强制执行它-在大多数情况下,浮点数或字符串似乎是JavaScript中默认的一种假定数据类型。
画循环
要在JavaScript中循环,最好的策略是创建函数,将要循环的代码放入其中,并在完成时通知浏览器您要再次调用此函数。不幸的是,请求重复调用的实际功能似乎尚未在所有浏览器上标准化。我从Google的webgl-utils.js文件中摘录了一个片段,以将其封装在跨浏览器功能中:

          window.requestAnimFrame  = (function() {
          return  window.requestAnimationFrame ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame ||
          window.oRequestAnimationFrame ||
          window.msRequestAnimationFrame ||
          function(callback, element) {
          return window.setTimeout (callback, 1000 /  60);
          };
          })();

如果将其放在脚本块中的某个地方,我们可以调用它。我们还将编写一个重复函数来绘制图形。如果愿意,可以在启动代码之后输入:

          var  previous_millis;
          function main_loop  () {
            // update timers
            var current_millis = performance.now ();
            var elapsed_millis = current_millis -  previous_millis;
            previous_millis = current_millis;
            var elapsed_s = elapsed_millis / 1000.0;
        

            // draw
            gl.clear (gl.COLOR_BUFFER_BIT |  gl.DEPTH_BUFFER_BIT);
            gl.activeTexture (gl.TEXTURE0);
            gl.bindTexture (gl.TEXTURE_2D, texture);
            gl.useProgram (sp);
            if (cam_dirty) {
            gl.uniformMatrix4fv (heckler_PV_loc,  gl.FALSE, new Float32Array (PV));
            cam_dirty = false;
            }
            var R = rotate_y_deg (identity_mat4 (),  current_millis * 0.075);
            gl.uniformMatrix4fv (heckler_M_loc, gl.FALSE,  new Float32Array (R));
            vao_ext.bindVertexArrayOES (heckler_vao);
            gl.drawArrays (gl.TRIANGLES, 0,  heckler_vao.pc);
          

            // "automatically re-call this function  please"
            window.requestAnimFrame (main_loop, canvas);
            }        

我喜欢在循环中添加一些计时器,以便可以制作动画,测量帧速率等。您可以使用浏览器的performance.now()函数来执行此操作,这可以使您自页面加载后以毫秒为单位,系统的最高精度可达纳秒级-比JavaScript的数据和时间函数可靠得多。之后,我开始绘制代码。我更新了投影的矩阵统一形式,并查看是否尚未更新。矩阵的统一更新功能有些特殊之处。在WebGL中,必须将transposition参数设置为false-它不会为您进行转换。最好将矩阵数组强制为32位浮点数据类型,您可以通过创建一个新的Float32Array对象来实现。在函数的最后,我再次调用了请求调用函数。缓冲区交换和最终图像在画布上的实际绘制都是自动处理的。在脚本块的末尾,我们实际上可以调用此函数,它将开始循环过程:

          previous_millis =  performance.now();
          main_loop ();

这些就是WebGL的基础知识,如果您之前已经做过一些OpenGL的话,这些知识就足够了。

进阶

浏览器具有许多您可以访问的功能。您可以查看用户与鼠标,键盘,移动设备的触摸屏,甚至是新的W3C游戏手柄/操纵杆界面的交互。您可以查看设置更复杂的异步文件流。
拥有来自HTML的一组GUI工具不容小under。您可以在图表,滑块,按钮上使用Web的所有附加功能,最重要的是,您可以进行文本渲染。如果您愿意通过CSS将其覆盖在画布上。您还可以将画布浮动在其他Web内容上,并使背景透明。
浏览器具有内置的调试,源,步进和监视列表工具。您可以在浏览器中进行一些非常全面的调试,甚至在应用程序运行时输入新的JavaScript代码。尝试这些工具是一个好主意。
请确保观看新的WebGL 2.0标准的开发,并查看您的浏览器是否已运行此版本的早期版本。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注