PhiloGL学习(1)——场景创建及方块欲露还羞出水面
前言
上一篇文章中介绍了我认识PhiloGL框架的机缘以及初步的探讨(见JS前端三维地球渲染——中国各城市航空路线展示),在此文中仅仅对此框架进行了简单介绍并初步介绍了一些该框架的知识。首先三维这个东西本身涉及的技术和知识点就非常多,我也基本属于初次接触;其次学习也需要过程,需要一点点积累,不积跬步无以至千里。
这几天天天加班,但是也利用空闲时间学习了些此框架的基础知识,本文为大家介绍如何创建一个简单的二维场景。
一、 HTML部分
PhiloGL采用canvas来加载三维模型,所以只有在支持HTML5的浏览器才能正常显示PhiloGL的东西。
1.1 添加canvas组建
在html页面的body中添加:
<canvas id="test1" style="border: none;" width="500" height="500"></canvas>
1.2 添加PhiloGL引用
<script type="text/javascript" src="/path/to/PhiloGL.js"></script>
1.3 引用自定义js文件
在此文件中我们会写创建场景逻辑等等,将此文件同样添加到html页面中。
二、 GLSL部分
GLSL(GL Shading Language)是用来在OpenGL中着色的语言,GLSL语言在GPU上执行,PhiloGL也使用GLSL语言进行着色。具体可以查阅相关资料,这里作简单介绍。
GLSL分为两部分,fragment shading(fs) 和 vertext shading(vs),分别为片段着色器和顶点着色器。
2.1 fs.glsl 部分简单定义如下:
#ifdef GL_ES
precision highp float;
#endif
varying vec4 vColor;
void main(void) {
gl_FragColor = vColor;
}
2.2 vs.glsl 部分简单定义如下:
attribute vec3 aVertexPosition;
attribute vec4 aVertexColor;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying vec4 vColor;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vColor = aVertexColor;
}
2.3 GLSL 语法简介
- 变量类型
变量的类型有 void、bool、int、float、vec2、vec3、vec4、mat4等。vec3表示三维、vec4表示4维,mat4表示4*4矩阵。
- attribute
attribute 表示只读的顶点数据,只用在顶点着色器中,即只存在vs中,它必须是全局范围声明的,不能在函数内部。
- varying
varying 表示顶点着色器的输出数据,作为片段着色器的只读输入数据,即在vs中设置后可以在fs中为作为常量使用。例如颜色或纹理坐标,纹理在后面介绍。
- uniform
uniform 表示一致变量,在着色器执行期间一致变量的值是不变的,由外部初始化。一致变量在fs和vs中是共享的,多用于设置摄像头的视角和投影等。它也只能是全局变量。
- 固定常量
- 片段着色器
gl_FragColor 输出的颜色用于随后的像素操作。可以采用上述变量的方式,也可以直接设置固定值。如下:
gl_FragColor = vec4(0.4, 0.5, 0.6, 0.7);
这样使用此fs.glsl的对象就会被设置成此颜色。颜色值小于1为rgba。
- 顶点着色器
gl_Position 输出属性,变换后顶点的位置,用于固定裁剪等操作,所有的顶点着色器必须设置此值。
由于aVertexPosition为vec3,所以vec4(aVertexPosition, 1.0)为根据外部传入的aVertexPosition创建一个vec4变量,第四个值为1.0。
还有一些其他变量及其他常量和函数等,可以自行查阅,后续用到的时候也会再做相应介绍。
2.4 GLSL存放位置
GLSL可以直接以javascript代码块的方式给出,也可以以文件的方式给出。
- 代码块方式
在html文件中添加如下代码块:
<script id="shader-fs" type="x-shader/x-fragment">
#ifdef GL_ES
precision highp float;
#endif
varying vec4 vColor;
void main(void) {
gl_FragColor = vColor;
}
</script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec4 aVertexColor;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying vec4 vColor;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vColor = aVertexColor;
}
</script>
注意两段代码的type类型,分别对应fs和vs。
- 文件方式
分别定义xxx.fs.glsl文件和xxx.vs.glsl文件,将2.1和2.2部分的内容放入其中即可。
以代码块方式和文件方式在调用上会有不同,后面会具体介绍。
三、 自定义JS部分
PhiloGL采用纯javascript语言来渲染三维场景。
3.1 the least demo
使用下面的代码即可创建一个最基本的demo:
function webGLStart() {
PhiloGL('test1', {
program: {
from: 'ids',
vs: 'shader-vs',
fs: 'shader-fs'
},
onError: function(e) {
alert(e);
},
onLoad: function(app) {
var gl = app.gl,
canvas = app.canvas,
program = app.program,
camera = app.camera;
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0, 0, 0, 1);
gl.clearDepth(1);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
program.setBuffers({
'square': {
attribute: 'aVertexPosition',
value: new Float32Array([1, 1, 0, -1, 1, 0, 1, -1, 0, -1, -1, 0]),
size: 3
},
'squareColors': {
attribute: 'aVertexColor',
value: new Float32Array([0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 1, 1, 1, 1]),
size: 4
}
});
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
camera.view.id();
camera.view.$translate(0, 0, -7);
//Draw Square
//set uniforms
program.setUniform('uMVMatrix', camera.view);
program.setUniform('uPMatrix', camera.projection);
program.setBuffer('square');
program.setBuffer('squareColors');
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
});
}
在html文件中添加对此js文件的引用,并在body的onload事件中调用webGLStart函数即可看到在浏览器中加载出一个四边形。
<body onload="webGLStart();">
<canvas id="test1" style="border: none;" width="500" height="500"></canvas>
</body>
接下来介绍PhiloGL类各部分的意义。
3.2 PhiloGL
PhiloGL是框架的顶级类,在其中定义了三维场景的所有模块,如摄像机、场景、GLSL加载、键盘鼠标响应事件等等。
- 与canvas的对应
PhiloGL传入的第一个参数(上文中的test1),即为html页面中的canvas定义的id,PhiloGL根据此值来找到canvas加载三维场景。
- program部分
program部分可以加载多个GLSL语言模块,每一个均有一个vs和一个fs组成。如下:
program : [ {
id : 'advance',
from : 'ids',
vs : 'shader-vs',
fs : 'shader-fs-advance'
}]
其id是为此部分GLSL定义的id,from表示加载来源,vs为对应的vs部分,fs为对应的fs部分。
from可以为ids或者uris,ids表示从script中取,uris表示从文件中取。当设置为uris的时候,需要添加一个path项,用于设置glsl文件存放路径。
- onLoad
onLoad部分控制整个三维场景的加载。
onLoad: function(app) {
var gl = app.gl,
canvas = app.canvas,
program = app.program,
camera = app.camera;
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0, 0, 0, 1);
gl.clearDepth(1);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
program.setBuffers({
'square': {
attribute: 'aVertexPosition',
value: new Float32Array([1, 1, 0, -1, 1, 0, 1, -1, 0, -1, -1, 0]),
size: 3
},
'squareColors': {
attribute: 'aVertexColor',
value: new Float32Array([0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 1, 1, 1, 1]),
size: 4
}
});
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
camera.view.id();
camera.view.$translate(0, 0, -7);
//Draw Square
//set uniforms
program.setUniform('uMVMatrix', camera.view);
program.setUniform('uPMatrix', camera.projection);
program.setBuffer('square');
program.setBuffer('squareColors');
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
app表示整个三维场景对象,gl表示philogl对象,program就是本小节第一部分设置的program即GLSL部分,所以program主要设置的是GLSL中的变量。
program.setBuffers({
'square': {
attribute: 'aVertexPosition',
value: new Float32Array([1, 1, 0, -1, 1, 0, 1, -1, 0, -1, -1, 0]),
size: 3
},
'squareColors': {
attribute: 'aVertexColor',
value: new Float32Array([0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 1, 1, 1, 1]),
size: 4
}
});
此段代码完成了aVertexPosition和aVertexColor两个attribute变量的设置。其中attribute值即为glsl中定义的attribute变量名称,vlaue表示设置的值,size表示变量的尺寸,如果变量类型是vec3则为3、vec4则为4。设置的value为new Float32Array类型,如果为33即9个值且size为3则表示3组值(即三个顶点),如果为44即16个且size为4,则表示为4组值(四个顶点)。
下面与之对应的setBuffer表示对当前对象设置此变量值,因为同一个场景中可以创建多个对象,不同的对象可以使用相同的GLSL语言进行控制,那么就要为这些对象的相同变量设置不同的值,这样就可以通过setBuffer来控制某个对象的变量值。
program.setBuffer('square');
program.setBuffer('squareColors');
通过setUniform设置GLSL中的uniform变量。
program.setUniform('uMVMatrix', camera.view);
program.setUniform('uPMatrix', camera.projection);
此处设置的两个变量均与摄像机(camera)有关,所谓摄像机的概念是说假设现在有个实体场景存在这你要绘制出的对象,那么当我们将摄像机放置在不同位置的时候摄像机拍摄到的场景是不同的,所以此处的摄像机的概念同样如此,表示我们从哪个角度(点)来观察这个对象。当然采用这种方式,每一个对象均需要一个摄像机对其进行拍摄使我们能够正常看到此对象。
camera.view表示摄像机视角,就是摄像机从哪个位置拍摄此物体。
camera.projection表示投影矩阵,简单的说就是一个三维点如何投影在二维平面上。因为摄像机拍摄的对象最终反映到摄像机的镜头里是在一个平面上,这中间就存在投影的问题。公式为:
y ~ Cx
其中x是一个三维的点,C为投影矩阵,y就是投影的结果(二维平面中的点)。
从vs中我们也能看出这一点:
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
uPMatrix传入的就是camera.projection,相当于C矩阵,vec4(aVertexPosition, 1.0)表示三维位置,二者相乘得到物体三维点在摄像机中的投影,再乘以uMVMatrix矩阵,将其从摄像机平面再投影到我们所看的这个平面,这样我们便能看到此物体。
camera.view.$translate表示此摄像机相对起点的偏移。原始位置在(0,0,0)点。
当场景和对象均设置好后,即可进行绘制。
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
注意此处的第三个参数3和4,此值表示绘制的对象是几边形。
类型有 TRIANGLES
, TRIANGLE_STRIP
, POINTS
, LINES
。
TRIANGLES绘制三角形、TRIANGLE_STRIP绘制多边形、POINTS绘制点、LINES绘制线
四、 总结
本文简单介绍了PhiloGL框架如何上手、GLSL语言以及简单的绘制一个方块,当然可能有很多我理解错误或者不深刻的地方欢迎各位大神批评指正!后面一篇文章为大家介绍如何将这个方块动起来。
PhiloGL学习(1)——场景创建及方块欲露还羞出水面的更多相关文章
- PhiloGL学习(2)——骚年,让我们荡起双桨
前言 上一篇文章中简单介绍了PhiloGL框架如何上手.GLSL语言以及简单的绘制一个方块(见PhiloGL学习(1)--场景创建及二维方块加载).本文很简单,我们一起来让这个方块动起来. 一. ...
- PhiloGL学习(6)——深情奉献:快乐的一家
前言 话说上一篇文章结尾讲到这一篇要做一个地球自转以及月球公转的三维动画,提笔,不对,是提键盘开始写的时候脑海中突然出现了几年前春晚风靡的那首歌:蒙古族小丫头唱的快乐的一家.闲言莫提,进入正题. ...
- unity入门—资源导入与场景创建
前言: 从这一篇章开始,我将会通过游戏实例来讲解如何使用unity制作一个标准的游戏,介绍的内容较多,需要整理的东西也多可能中途会有一两天的咕咕咕,预计想要完成两个游戏,一个射击类一个塔防类,从射击类 ...
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
- Netty学习之客户端创建
一.客户端开发时序图 图片来源:Netty权威指南(第2版) 二.Netty客户端开发步骤 使用Netty进行客户端开发主要有以下几个步骤: 1.用户线程创建Bootstrap Bootstrap b ...
- springmvc学习笔记---idea创建springmvc项目
前言: 真的是很久没搞java的web服务开发了, 最近一次搞还是读研的时候, 想来感慨万千. 英雄没落, Eclipse的盟主地位隐隐然有被IntelliJ IDEA超越的趋势. Spring从2. ...
- ASP.NET MVC 5 学习教程:创建连接字符串
原文 ASP.NET MVC 5 学习教程:创建连接字符串 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 ...
- 跟着刚哥学习Spring框架--创建HelloWorld项目(一)
1.Spring框架简介 Spring是一个开源框架,Spring是在2003年兴起的一个轻量级的开源框架,由Rod johnson创建.主要对JavaBean的生命周期进行管理的轻量级框架,Spri ...
- angular学习笔记(二)-创建angular模块
如果在页面的html标签(或任意标签)中添加ng-app,表示对整个页面应用angular来管理. 他是一个模块. 模块有助于把东西从全局命名空间中隔离. 今天学习如何自定义创建模块: <!DO ...
随机推荐
- Java 第十周总结
1. 本周学习总结 2. 书面作业 1.finally (题目4-2) 1.1 截图你的提交结果(出现学号) 1.2 4-2中finally中捕获异常需要注意什么? finally创建一个代码块.该代 ...
- 201521123048 《Java程序设计》第14周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自 ...
- 201521123027<java程序设计>第14周作业总结
1.本周作业总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2.书面作业 Q1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自己 ...
- Python数据类型方法精心整理,不必死记硬背,看看源码一切都有了
Python认为一切皆为对象:比如我们初始化一个list时: li = list('abc') 实际上是实例化了内置模块builtins(python2中为__builtin__模块)中的list类: ...
- 从instr中截取第一个delimiter之前的内容放到outstr中,返回第一个delimiter之后的位置
从instr中截取第一个delimiter之前的内容放到outstr中,返回第一个delimiter之后的位置 char *msstrtok(char *instr, char *outstr, ch ...
- 技巧收集-M1709
2017.09 在macOS中直接复制文件路径,在Finder中选中文件,按下快捷键:Command + Option + C *** 以KB,MB,GB方式显示文件大小 ls -lh 删除超大文本文 ...
- JAVA实现上传文件到服务器、删除服务器文件
使用的jar包: <dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</art ...
- 高德地图JSApi
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...
- 反射结合xml简单的模拟spring创建bean
框架最底层其实就是通过反射实现的,就像spring,当你配置各种各样的bean时都是以配置文件的形式配置的,你需要用到哪些bean就配哪些,spring容器就会根据你的需求去动态加载,这儿写一个简单的 ...
- 关于高德地图Android开发时地图只显示一次、第二次打开不定位的解决办法
我按照高德官方Demo改的 第一次是可以定位的,如左图 第二次就不能定位了,如右图 在onDestory中把aMap置为空即可 aMap = null; 修改完如下图: 原理是第二次打开时aMap不为 ...