webgl世界 matrix入门分分快三计划

作者:分分快三计划

1/ 矩阵的源于

正好有聊到PMatrix/VMatrix/MMatrix这八个词,他们中的Matrix就是矩阵的意味,矩阵是干什么的?用来改换顶点位置音信的,先牢记那句话,然后大家先从canvas2D动手相信一下大家有四个100*100的canvas画布,然后画一个矩形

XHTML

<canvas width="100" height="100"></canvas> ctx.rect(40, 40, 20, 20); ctx.fill();

1
2
3
<canvas width="100" height="100"></canvas>
ctx.rect(40, 40, 20, 20);
ctx.fill();

代码很不难,在画布中间画了二个矩形

前些天大家期待将圆向左移动10px

JavaScript

ctx.rect(30, 40, 20, 20); ctx.fill();

1
2
ctx.rect(30, 40, 20, 20);
ctx.fill();

结果如下:

源码地址:
结果展现:分分快三计划 1

 

更动rect方法第叁个参数就能够了,很简短,因为rect()对应的就是多少个矩形,是二个对象,canvas2D是目的等级的画布操作,而webgl不是,webgl是片元级其他操作,大家操作的是终点
用webgl如何画一个矩形?地址如下,可以间接查看源码

源码地址:
结果展现:

分分快三计划 2

此间我们能够看看position那几个数组,这里面存的正是矩形4个点的极端音讯,大家能够通过操作改变在那之中式茶食的值来改动地点(页面源码也得以看出完成),不过扪心自问那样不累吗?有没有能够三回性改换某些物体全部顶点的点子啊?
有,那正是矩阵,magic is coming

1  0  0  0
0  1  0  0
0  0  1  0
0  0  0  1

上边那个是二个单位矩阵(矩阵最基础的学问这里就隐瞒了),我们用那个乘八个极限(2,1,0)来看看
分分快三计划 3

并从未什么样变化啊!那大家换一个矩阵来看

1  0  0  1
0  1  0  0
0  0  1  0
0  0  0  1

再乘此前那一个顶点,开采终点的x已经变化了!
分分快三计划 4

万一您再多用多少个顶点试一下就能够发掘,无论大家用哪些顶点,都会拿走如此的四个x坐标 1这么三个结出
来,纪念一下大家事先的目标,未来是否有了一种三回性改造顶点地方的法子呢?

 

2/ 矩阵原理介绍
恰好大家改动了矩阵15个值中的一个,就使得矩阵有退换顶点的技艺,我们可不可以计算一下矩阵种种值的原理呢?当然是能够的,如下图

分分快三计划 5
这里深灰的x,y,z分别对应多个方向上的偏移

分分快三计划 6
此处黄色的x,y,z分别对应两个样子上的缩放

然后是精华的环抱各类轴的旋转矩阵(回忆的时候注意围绕y轴转动时,多少个三角形函数的符号……)
分分快三计划 7

再有剪切(skew)效果的转移矩阵,这里用个x轴的事例来浮现
分分快三计划 8

此间都以某一种单一功能的改变矩阵,能够相乘合营使用的,很简单。大家这里关键来找一下法则,如同有着的操作都以环绕着红框这一块来的
分分快三计划 9
其实也相比好明白,因为矩阵这里每一行对应了个坐标
分分快三计划 10

那么难题来了,最上面那行干啥用的?
一个极端,坐标(x,y,z),那么些是在笛Carl坐标系中的表示,在3D世界中大家会将其转移为齐次坐标系,也正是成为了(x,y,z,w),那样的样式(以前那么多图中w=1)
矩阵的末梢一行也就象征着齐次坐标,那么齐次坐标有啥功效?比很多书上都会说齐次坐标能够不一致贰个坐标是点只怕向量,点的话齐次项是1,向量的话齐次项是0(所以以前图中w=1)
对此webgl中的Matrix来讲齐次项有何用处呢?也许说那几个第四行更换了有哪些受益呢?一句话总结(敲黑板,划注重)
它能够让实体有透视的功用
举个例证,远近有名的透视矩阵,如图
分分快三计划 11
在第四行的第三列就有值,而不像在此之前的是0;还会有二个细节正是第四行的第四列是0,并不是事先的1

写到这里的时候我纠结了,要不要详细的把正视和透视投影矩阵推导写一下,可是考虑到篇幅,实在是倒霉放在这里了,不然那篇小说要太长了,因为背后还大概有内容
绝大繁多3D程序开荒者可能不是很关注透视矩阵(PMatrix),只是掌握有那二遍事,用上那些矩阵可以近大远小,然后代码上也便是glMatrix.setPerspective(……)一下就行了
之所以决定背后单独再写一篇,特地说下正视透视矩阵的推理、矩阵的优化这么些文化
此间就权且打住,大家先只思考红框部分的矩阵所带来的转移
分分快三计划 12

切实完成

此处拿 M0, M1, M2, M3 的求解来比喻:
M0* Xe M1* Ye M2* Ze M3= (-Ze)*(-n*Xe/Ze-left )*2/(right-left) Ze
M1 = 2n/(right-left)
M2 = 0
M3 = (right left)/(right-left)
M4 = 0

4/ 结语

关于具体的PMatrix和OMatrix是怎么来的,Matrix能或不能够举香港行政局地优化,我们后一次再说~

不符合规律和提议的招待留言一同批评~

1 赞 1 收藏 评论

分分快三计划 13

4、装饰星星

光秃秃的多少个文字鲜明缺乏,所以大家还亟需一些点缀,就用几个点作为星星,特别轻便
注意暗许渲染webgl.POINTS是方形的,所以我们得在fragment shader中加工管理一下

JavaScript

precision highp float;   void main() {     float dist = distance(gl_PointCoord, vec2(0.5, 0.5)); // 总结距离     if(dist < 0.5) {         gl_FragColor = vec4(0.9, 0.9, 0.8, pow((1.0 - dist * 2.0), 3.0));     } else {         discard; // 丢弃     } }

1
2
3
4
5
6
7
8
9
10
precision highp float;
 
void main() {
    float dist = distance(gl_PointCoord, vec2(0.5, 0.5)); // 计算距离
    if(dist < 0.5) {
        gl_FragColor = vec4(0.9, 0.9, 0.8, pow((1.0 - dist * 2.0), 3.0));
    } else {
        discard; // 丢弃
    }
}

考虑
Xp = (Xep - left)*2/(right-left) -1      (Xep  = -n* Xe/Ze)
Yp = (Yep - left)*2/(right-left) -1      (Yep  = -n* Ye/Ze)
Zp = A* 1/Ze B

webgl世界 matrix入门

2017/01/18 · HTML5 · matrix, WebGL

初稿出处: AlloyTeam   

本次未有推动娱乐啊,本来依然图谋用二个小游戏来介绍阴影,可是开掘阴影那块想完完整整介绍一回太大了,涉及到比非常多,再加上作业时间的忐忑,所以就一时半刻抛弃了游戏,先好好介绍一回webgl中的Matrix。

这篇小说算是webgl的基础知识,因为借使想不走马看花的说阴影的话,须要打牢一定的根底,小说中我奋力把知识点讲的更易懂。内容侧向刚上手webgl的校友,至少知道着色器是怎么,webgl中drawElements那样的API会采取~

小说的标题是Matrix is magic,矩阵对于3D世界来讲确实是法力一般的留存,说起webgl中的矩阵,PMatrix/VMatrix/MMatrix那八个大家深信不会面生,那就正文let’s go~

怎么说webgl生成物体麻烦

咱俩先稍微相比下中央图形的创导代码
矩形:
canvas2D

JavaScript

ctx1.rect(50, 50, 100, 100); ctx1.fill();

1
2
ctx1.rect(50, 50, 100, 100);
ctx1.fill();

webgl(shader和webgl情状代码忽略)

JavaScript

var aPo = [     -0.5, -0.5, 0,     0.5, -0.5, 0,     0.5, 0.5, 0,     -0.5, 0.5, 0 ];   var aIndex = [0, 1, 2, 0, 2, 3];   webgl.bindBuffer(webgl.ARRAY_BUFFER, webgl.createBuffer()); webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(aPo), webgl.STATIC_DRAW); webgl.vertexAttribPointer(aPosition, 3, webgl.FLOAT, false, 0, 0);   webgl.vertexAttrib3f(aColor, 0, 0, 0);   webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, webgl.createBuffer()); webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint16Array(aIndex), webgl.STATIC_DRAW);   webgl.drawElements(webgl.TRIANGLES, 6, webgl.UNSIGNED_SHORT, 0);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var aPo = [
    -0.5, -0.5, 0,
    0.5, -0.5, 0,
    0.5, 0.5, 0,
    -0.5, 0.5, 0
];
 
var aIndex = [0, 1, 2, 0, 2, 3];
 
webgl.bindBuffer(webgl.ARRAY_BUFFER, webgl.createBuffer());
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(aPo), webgl.STATIC_DRAW);
webgl.vertexAttribPointer(aPosition, 3, webgl.FLOAT, false, 0, 0);
 
webgl.vertexAttrib3f(aColor, 0, 0, 0);
 
webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, webgl.createBuffer());
webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint16Array(aIndex), webgl.STATIC_DRAW);
 
webgl.drawElements(webgl.TRIANGLES, 6, webgl.UNSIGNED_SHORT, 0);

完全代码地址:
结果:
分分快三计划 14

圆:
canvas2D

JavaScript

ctx1.arc(100, 100, 50, 0, Math.PI * 2, false); ctx1.fill();

1
2
ctx1.arc(100, 100, 50, 0, Math.PI * 2, false);
ctx1.fill();

webgl

JavaScript

var angle; var x, y; var aPo = [0, 0, 0]; var aIndex = []; var s = 1; for(var i = 1; i <= 36; i ) {     angle = Math.PI * 2 * (i / 36);     x = Math.cos(angle) * 0.5;     y = Math.sin(angle) * 0.5;       aPo.push(x, y, 0);       aIndex.push(0, s, s 1);       s ; }   aIndex[aIndex.length - 1] = 1; // hack一下   webgl.bindBuffer(webgl.ARRAY_BUFFER, webgl.createBuffer()); webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(aPo), webgl.STATIC_DRAW); webgl.vertexAttribPointer(aPosition, 3, webgl.FLOAT, false, 0, 0);   webgl.vertexAttrib3f(aColor, 0, 0, 0);   webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, webgl.createBuffer()); webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint16Array(aIndex), webgl.STATIC_DRAW);   webgl.drawElements(webgl.TRIANGLES, aIndex.length, webgl.UNSIGNED_SHORT, 0);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var angle;
var x, y;
var aPo = [0, 0, 0];
var aIndex = [];
var s = 1;
for(var i = 1; i <= 36; i ) {
    angle = Math.PI * 2 * (i / 36);
    x = Math.cos(angle) * 0.5;
    y = Math.sin(angle) * 0.5;
 
    aPo.push(x, y, 0);
 
    aIndex.push(0, s, s 1);
 
    s ;
}
 
aIndex[aIndex.length - 1] = 1; // hack一下
 
webgl.bindBuffer(webgl.ARRAY_BUFFER, webgl.createBuffer());
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(aPo), webgl.STATIC_DRAW);
webgl.vertexAttribPointer(aPosition, 3, webgl.FLOAT, false, 0, 0);
 
webgl.vertexAttrib3f(aColor, 0, 0, 0);
 
webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, webgl.createBuffer());
webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint16Array(aIndex), webgl.STATIC_DRAW);
 
webgl.drawElements(webgl.TRIANGLES, aIndex.length, webgl.UNSIGNED_SHORT, 0);

总体代码地址:
结果:
分分快三计划 15

小结:大家抛开shader中的代码和webgl伊始化遭受的代码,开采webgl比canvas2D就是麻烦众多哟。光是三种基本图形就多了那样多行代码,抓其根本多的因由正是因为大家供给顶点音信。轻便如矩形我们能够直接写出它的顶点,可是复杂一点的圆,大家还得用数学方法去变通,显然阻碍了人类文明的腾飞。
相相比数学方法变通,假使我们能一直拿走顶点音讯那应该是最棒的,有未有火速的方法获得极限音讯吗?
有,使用建立模型软件生成obj文件。

Obj文件轻易的话正是满含三个3D模型音讯的文书,这里新闻包蕴:顶点、纹理、法线以及该3D模型中纹理所使用的贴图
上边这一个是二个obj文件的地址:

与上述同类做的指标是为了让在view space中有着在视锥平截体内的点,X数值投影到近平面上,都在near平面上的left到right。
接下去是求最终在device space里x,y,z的数值,device space是坐标为-1到1的立方体。个中x/2 0.5,y/2 0.5 分别再乘以窗口长度宽度就是显示屏上像素的任务。那么显示器那些像素的Z数值就足以按以下方法求出:
急需把view space中(left,right,top,bottom,near,far)的frustum调换成长度宽度为(-1,1)的四方体内,正是说,把Xe,Ye,Ze都映射到[-1,1]的界定内。前边已经求出了Xe在camera space near平面上的投影Xep。那一个影子的数值就是在frustum平截体near平面上的地点。
把平截体near平面映射到-1,1的纺锤形很简单,X方向按如下映射:
Xp = (Xep - left)*2/(right-left) -1  。

3/ webgl的坐标系

笔者们眼前bb了那么多,可以总结一下就是“矩阵是用来改动顶点坐标地点的!”,能够如此精通对吗(不领悟的再回去看下第一节里面包车型客车各个图)

那再看下小说开首说的PMatrix/VMatrix/MMatrix多少个,那多少个货都是矩阵啊,都以来改换顶点地方坐标的,再加多矩阵也是足以结合的哟,为啥那八个货要分开呢?

率先,那多个货分开说是为着便于通晓,因为它们各司其职

MMatrix --->  模型矩阵(用于物体在世界中变化)
VMatrix --->  视图矩阵(用于世界中录像机的变化)
PMatrix --->  透视矩阵

模型矩阵和视图矩阵具体的规律和事关笔者事先这篇射击小游戏文章里有说过,它们的转移的貌似正是仿射变换,也等于活动、旋转之类的转移
此处稍微回想一下规律,具体细节就不再说了
这两货一个是先旋转,后运动(MMatrix),另三个是先活动,后旋转(VMatrix)
但就那一个小分别,令人感觉三个是实体自个儿在扭转,一个是录像机在转移

好啊,着重说下PMatrix。这里不是来演绎出它怎样有透视效果的,这里是讲它除了透视的另一大遮掩的功能
谈到那边,先打三个断点,然后我们思量另一个难题

canvas2D淑节webgl中画布的差距

它们在DOM中的宽高都以通过设置canvas标签上width和height属性来设置的,那很一致。但webgl中我们的坐标空间是-1 ~ 1

分分快三计划 16
(width=800,height=600中canvas2D中,矩形左顶点居中时,rect方法的前三个参数)

分分快三计划 17
(width=800,height=600中webgl中,矩形左顶点居中时,左顶点的坐标)

大家会发觉x坐标小于-1依旧超越1的的话就不会来得了(y同理),x和y很好明白,因为荧屏是2D的,画布是2D的,2D就独有x,y,相当于大家直观上所看到的东西
那z坐标靠什么样来拜谒啊?

对比

首先至少有四个物体,它们的z坐标分化,那几个z坐标会决定它们在荧屏上显得的地点(或许说覆盖)的光景,让我们试试看

JavaScript

var aPo = [ -0.2, -0.2, -0.5, 0.2, -0.2, -0.5, 0.2, 0.2, -0.5, -0.2, 0.2, -0.5 ]; var aIndex = [0, 1, 2, 0, 2, 3]; webgl.bindBuffer(webgl.ARRAY_BUFFER, webgl.createBuffer()); webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(aPo), webgl.STATIC_DRAW); webgl.vertexAttribPointer(aPosition, 3, webgl.FLOAT, false, 0, 0); webgl.vertexAttrib3f(aColor, 1, 0, 0); webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, webgl.createBuffer()); webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint16Array(aIndex), webgl.STATIC_DRAW); // 先画贰个z轴是-0.5的矩形,颜色是新民主主义革命 webgl.drawElements(webgl.T翼虎IANGLES, 6, webgl.UNSIGNED_SHORT, 0); aPo = [ 0, -0.4, -0.8, 0.4, -0.4, -0.8, 0.4, 0, -0.8, 0, 0, -0.8 ]; webgl.bindBuffer(webgl.ARRAY_BUFFER, webgl.createBuffer()); webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(aPo), webgl.STATIC_DRAW); webgl.vertexAttribPointer(aPosition, 3, webgl.FLOAT, false, 0, 0); webgl.vertexAttrib3f(aColor, 0, 1, 0); // 再画二个z轴是-0.8的矩形,颜色是白灰 webgl.drawElements(webgl.TCR-VIANGLES, 6, webgl.UNSIGNED_SHORT, 0);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var aPo = [
    -0.2, -0.2, -0.5,
    0.2, -0.2, -0.5,
    0.2, 0.2, -0.5,
    -0.2, 0.2, -0.5
];
var aIndex = [0, 1, 2, 0, 2, 3];
webgl.bindBuffer(webgl.ARRAY_BUFFER, webgl.createBuffer());
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(aPo), webgl.STATIC_DRAW);
webgl.vertexAttribPointer(aPosition, 3, webgl.FLOAT, false, 0, 0);
webgl.vertexAttrib3f(aColor, 1, 0, 0);
webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, webgl.createBuffer());
webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint16Array(aIndex), webgl.STATIC_DRAW);
// 先画一个z轴是-0.5的矩形,颜色是红色
webgl.drawElements(webgl.TRIANGLES, 6, webgl.UNSIGNED_SHORT, 0);
aPo = [
    0, -0.4, -0.8,
    0.4, -0.4, -0.8,
    0.4, 0, -0.8,
    0, 0, -0.8
];
webgl.bindBuffer(webgl.ARRAY_BUFFER, webgl.createBuffer());
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(aPo), webgl.STATIC_DRAW);
webgl.vertexAttribPointer(aPosition, 3, webgl.FLOAT, false, 0, 0);
webgl.vertexAttrib3f(aColor, 0, 1, 0);
// 再画一个z轴是-0.8的矩形,颜色是绿色
webgl.drawElements(webgl.TRIANGLES, 6, webgl.UNSIGNED_SHORT, 0);

瞩目开启深度测量试验,不然就没戏啊
(不开启深度测量检验,Computer会无视顶点的z坐标新闻,只关怀drawElements(drawArrays)方法的调用顺序,最终画的一定是最上一层)

代码中A矩形(灰色)的z值为-0.5, B矩形(豉豆红)的z值为-0.8,最后画布上什么人会覆盖什么人吧?
倘使自己问的是x=0.5和x=0.8以内,什么人在左,哪个人在右,笔者深信各样人都规定知道,因为那太熟了,显示器正是2D的,画布坐标x轴正是右大左小便是如此的呗

那大家越来越深层的设想下何以x和y的岗位没人狐疑,因为“左臂坐标系”和“右臂坐标系”中x,y轴是同样的,如图所示

分分快三计划 18

而左边手坐标系和侧边坐标系中的z轴正方向差异,二个是荧屏向内,多个是荧屏向外,所以能够以为
假定右边坐标系下,B矩形(z=-0.8)小于A矩形(z=-0.5),那么相应覆盖了A矩形,左边手坐标系的话恰恰相反

事实胜于雄辩,我们之所以运营一下代码

查看结果:

能够观察B矩形是覆盖了A矩形的,也就表示webgl是左边坐标系

excuse me???全部文章说webgl都以左手坐标系啊,为何这里依旧是左边手坐标系?

答案正是webgl中所说的左臂坐标系,其实是一种标准,是可望开垦者共同依据的正经,但是webgl本身,是不在乎物体是左侧坐标系照旧左手坐标系的

可事实在日前,webgl左手坐标系的凭证大家也看到了,那是干吗?刚刚说的有一点含糊,不应有是“webgl是侧面坐标系”,而应该说“webgl的剪裁空间是依据左臂坐标系来的”

剪裁空间词如其名,正是用来把超越坐标空间的东西切割掉(-1 ~ 1),个中裁剪空间的z坐标正是遵从左边手坐标系来的

代码中我们有操作这几个裁剪空间啊?有!回到断点的岗位!

固然PMatrix它除了完毕透视效果的另三个技艺!
实际无论是PMatrix(透视投影矩阵)依旧OMatrix(注重投影矩阵),它们都会操作裁剪空间,在那之中有一步正是将右边手坐标系给调换为右臂坐标系

怎么转车的,来,大家用这么些单位矩阵试一下

1  0  0  0
0  1  0  0
0  0  -1  0
0  0  0  1

只需求大家将z轴反转,就能够拿走将裁剪空间由左边手坐标系转变为左边手坐标系了。用事先的矩形A和矩形B再试三次看看

地址:

果不其然如此!

这么大家就掌握到了webgl世界中多少个最棒根本的Matrix了

2、读取解析obj文件

JavaScript

var regex = { // 那教头则只去相称了小编们obj文件中用到多少     vertex_pattern: /^vs ([d|.| |-|e|E] )s ([d|.| |-|e|E] )s ([d|.| |-|e|E] )/, // 顶点     normal_pattern: /^vns ([d|.| |-|e|E] )s ([d|.| |-|e|E] )s ([d|.| |-|e|E] )/, // 法线     uv_pattern: /^vts ([d|.| |-|e|E] )s ([d|.| |-|e|E] )/, // 纹理坐标     face_vertex_uv_normal: /^fs (-?d )/(-?d )/(-?d )s (-?d )/(-?d )/(-?d )s (-?d )/(-?d )/(-?d )(?:s (-?d )/(-?d )/(-?d ))?/, // 面信息     material_library_pattern: /^mtllibs ([d|w|.] )/, // 依赖哪二个mtl文件     material_use_pattern: /^usemtls ([S] )/ };   function loadFile(src, cb) {     var xhr = new XMLHttpRequest();       xhr.open('get', src, false);       xhr.onreadystatechange = function() {         if(xhr.readyState === 4) {               cb(xhr.responseText);         }     };       xhr.send(); }   function handleLine(str) {     var result = [];     result = str.split('n');       for(var i = 0; i < result.length; i ) {         if(/^#/.test(result[i]) || !result[i]) { // 注释部分过滤掉             result.splice(i, 1);               i--;         }     }       return result; }   function handleWord(str, obj) {     var firstChar = str.charAt(0);     var secondChar;     var result;       if(firstChar === 'v') {           secondChar = str.charAt(1);           if(secondChar === ' ' && (result = regex.vertex_pattern.exec(str)) !== null) {             obj.position.push( result[1], result[2], result[3]); // 加入到3D对象顶点数组         } else if(secondChar === 'n' && (result = regex.normal_pattern.exec(str)) !== null) {             obj.normalArr.push( result[1], result[2], result[3]); // 到场到3D对象法线数组         } else if(secondChar === 't' && (result = regex.uv_pattern.exec(str)) !== null) {             obj.uvArr.push( result[1], result[2]); // 加入到3D对象纹理坐标数组         }       } else if(firstChar === 'f') {         if((result = regex.face_vertex_uv_normal.exec(str)) !== null) {             obj.addFace(result); // 将顶点、开采、纹理坐标数组形成面         }     } else if((result = regex.material_library_pattern.exec(str)) !== null) {         obj.loadMtl(result[1]); // 加载mtl文件     } else if((result = regex.material_use_pattern.exec(str)) !== null) {         obj.loadImg(result[1]); // 加载图片     } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
var regex = { // 这里正则只去匹配了我们obj文件中用到数据
    vertex_pattern: /^vs ([d|.| |-|e|E] )s ([d|.| |-|e|E] )s ([d|.| |-|e|E] )/, // 顶点
    normal_pattern: /^vns ([d|.| |-|e|E] )s ([d|.| |-|e|E] )s ([d|.| |-|e|E] )/, // 法线
    uv_pattern: /^vts ([d|.| |-|e|E] )s ([d|.| |-|e|E] )/, // 纹理坐标
    face_vertex_uv_normal: /^fs (-?d )/(-?d )/(-?d )s (-?d )/(-?d )/(-?d )s (-?d )/(-?d )/(-?d )(?:s (-?d )/(-?d )/(-?d ))?/, // 面信息
    material_library_pattern: /^mtllibs ([d|w|.] )/, // 依赖哪一个mtl文件
    material_use_pattern: /^usemtls ([S] )/
};
 
function loadFile(src, cb) {
    var xhr = new XMLHttpRequest();
 
    xhr.open('get', src, false);
 
    xhr.onreadystatechange = function() {
        if(xhr.readyState === 4) {
 
            cb(xhr.responseText);
        }
    };
 
    xhr.send();
}
 
function handleLine(str) {
    var result = [];
    result = str.split('n');
 
    for(var i = 0; i < result.length; i ) {
        if(/^#/.test(result[i]) || !result[i]) { // 注释部分过滤掉
            result.splice(i, 1);
 
            i--;
        }
    }
 
    return result;
}
 
function handleWord(str, obj) {
    var firstChar = str.charAt(0);
    var secondChar;
    var result;
 
    if(firstChar === 'v') {
 
        secondChar = str.charAt(1);
 
        if(secondChar === ' ' && (result = regex.vertex_pattern.exec(str)) !== null) {
            obj.position.push( result[1], result[2], result[3]); // 加入到3D对象顶点数组
        } else if(secondChar === 'n' && (result = regex.normal_pattern.exec(str)) !== null) {
            obj.normalArr.push( result[1], result[2], result[3]); // 加入到3D对象法线数组
        } else if(secondChar === 't' && (result = regex.uv_pattern.exec(str)) !== null) {
            obj.uvArr.push( result[1], result[2]); // 加入到3D对象纹理坐标数组
        }
 
    } else if(firstChar === 'f') {
        if((result = regex.face_vertex_uv_normal.exec(str)) !== null) {
            obj.addFace(result); // 将顶点、发现、纹理坐标数组变成面
        }
    } else if((result = regex.material_library_pattern.exec(str)) !== null) {
        obj.loadMtl(result[1]); // 加载mtl文件
    } else if((result = regex.material_use_pattern.exec(str)) !== null) {
        obj.loadImg(result[1]); // 加载图片
    }
}

代码宗旨的地点都进展了解说,注意这里的正则只去相称大家obj文件中带有的字段,别的消息未有去相称,若是有对obj文件全体希望带有的消息成功匹配的同窗能够去看下Threejs中objLoad部分源码

[ 2n/(right-left)   0                                  (right left)/(right-left)    0                            ]
[ 0                 2*near/(top-bottom)                (top bottom)/(top-bottom)    0                            ]
[ 0                 0                                  -(far near)/(far-near)       -2far*near/(far-near)        ]
[ 0                 0                                  -1                           0                            ]

3、将obj中数据真正的使用3D对象中去

JavaScript

Text3d.prototype.addFace = function(data) {     this.addIndex( data[1], data[4], data[7], data[10]);     this.addUv( data[2], data[5], data[8], data[11]);     this.addNormal( data[3], data[6], data[9], data[12]); };   Text3d.prototype.addIndex = function(a, b, c, d) {     if(!d) {         this.index.push(a, b, c);     } else {         this.index.push(a, b, c, a, c, d);     } };   Text3d.prototype.addNormal = function(a, b, c, d) {     if(!d) {         this.normal.push(             3 * this.normalArr[a], 3 * this.normalArr[a] 1, 3 * this.normalArr[a] 2,             3 * this.normalArr[b], 3 * this.normalArr[b] 1, 3 * this.normalArr[b] 2,             3 * this.normalArr[c], 3 * this.normalArr[c] 1, 3 * this.normalArr[c] 2         );     } else {         this.normal.push(             3 * this.normalArr[a], 3 * this.normalArr[a] 1, 3 * this.normalArr[a] 2,             3 * this.normalArr[b], 3 * this.normalArr[b] 1, 3 * this.normalArr[b] 2,             3 * this.normalArr[c], 3 * this.normalArr[c] 1, 3 * this.normalArr[c] 2,             3 * this.normalArr[a], 3 * this.normalArr[a] 1, 3 * this.normalArr[a] 2,             3 * this.normalArr[c], 3 * this.normalArr[c] 1, 3 * this.normalArr[c] 2,             3 * this.normalArr[d], 3 * this.normalArr[d] 1, 3 * this.normalArr[d] 2         );     } };   Text3d.prototype.addUv = function(a, b, c, d) {     if(!d) {         this.uv.push(2 * this.uvArr[a], 2 * this.uvArr[a] 1);         this.uv.push(2 * this.uvArr[b], 2 * this.uvArr[b] 1);         this.uv.push(2 * this.uvArr[c], 2 * this.uvArr[c] 1);     } else {         this.uv.push(2 * this.uvArr[a], 2 * this.uvArr[a] 1);         this.uv.push(2 * this.uvArr[b], 2 * this.uvArr[b] 1);         this.uv.push(2 * this.uvArr[c], 2 * this.uvArr[c] 1);         this.uv.push(2 * this.uvArr[d], 2 * this.uvArr[d] 1);     } };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Text3d.prototype.addFace = function(data) {
    this.addIndex( data[1], data[4], data[7], data[10]);
    this.addUv( data[2], data[5], data[8], data[11]);
    this.addNormal( data[3], data[6], data[9], data[12]);
};
 
Text3d.prototype.addIndex = function(a, b, c, d) {
    if(!d) {
        this.index.push(a, b, c);
    } else {
        this.index.push(a, b, c, a, c, d);
    }
};
 
Text3d.prototype.addNormal = function(a, b, c, d) {
    if(!d) {
        this.normal.push(
            3 * this.normalArr[a], 3 * this.normalArr[a] 1, 3 * this.normalArr[a] 2,
            3 * this.normalArr[b], 3 * this.normalArr[b] 1, 3 * this.normalArr[b] 2,
            3 * this.normalArr[c], 3 * this.normalArr[c] 1, 3 * this.normalArr[c] 2
        );
    } else {
        this.normal.push(
            3 * this.normalArr[a], 3 * this.normalArr[a] 1, 3 * this.normalArr[a] 2,
            3 * this.normalArr[b], 3 * this.normalArr[b] 1, 3 * this.normalArr[b] 2,
            3 * this.normalArr[c], 3 * this.normalArr[c] 1, 3 * this.normalArr[c] 2,
            3 * this.normalArr[a], 3 * this.normalArr[a] 1, 3 * this.normalArr[a] 2,
            3 * this.normalArr[c], 3 * this.normalArr[c] 1, 3 * this.normalArr[c] 2,
            3 * this.normalArr[d], 3 * this.normalArr[d] 1, 3 * this.normalArr[d] 2
        );
    }
};
 
Text3d.prototype.addUv = function(a, b, c, d) {
    if(!d) {
        this.uv.push(2 * this.uvArr[a], 2 * this.uvArr[a] 1);
        this.uv.push(2 * this.uvArr[b], 2 * this.uvArr[b] 1);
        this.uv.push(2 * this.uvArr[c], 2 * this.uvArr[c] 1);
    } else {
        this.uv.push(2 * this.uvArr[a], 2 * this.uvArr[a] 1);
        this.uv.push(2 * this.uvArr[b], 2 * this.uvArr[b] 1);
        this.uv.push(2 * this.uvArr[c], 2 * this.uvArr[c] 1);
        this.uv.push(2 * this.uvArr[d], 2 * this.uvArr[d] 1);
    }
};

此处大家思考到包容obj文件中f(ace)行中4个值的动静,导出obj文件中得以强行选用独有三角面,然而大家在代码中相称一下相比较稳妥

也正是说,不管A和B是何许,在Z BUFFE哈弗中的数值,总是和实体顶点在camera space中的 -1/Ze 成线性的涉及。 大家要做的正是求A 和B。
求A和B,大家运用以下标准:
当Ze = far 时, Zp = 1
当Ze = near时,Zp = -1(在OPENGL下是那般。在D3D下是Zp =0)
那其实是个2元贰次方程。有了两个已知解,能够求出A和B。OPENGL下,
A = 2nf/(f-n), B = (f n)/(f-n)

1、首先建立模型生成obj文件

这里大家采用blender生成文字
分分快三计划 19

日渐被D3D扬弃的W BUFFER,场景远近与W数值是线性的。即,100米的偏离,在near=1 far=101时,每一米在D3D W BUFFEEvoque的意味中,正是大半 1/100 那么大。不过在Z BUFFEOdyssey中就完全不是均匀分配。

结语

亟待关心的是此处作者用了别的一对shader,此时就关乎到了关于是用七个program shader仍然在同二个shader中央银行使if statements,那二者质量怎么着,有何界别
这里将身处下一篇webgl相关优化中去说

正文就到那边呀,有毛病和提出的同伙迎接留言一同商量~!

1 赞 收藏 评论

分分快三计划 20

为了获得4X4 MAT大切诺基IX,大家要求把(Xp,Yp,Zp,1)转为齐次坐标 (-Xp*Ze, -Yp*Ye, -Zp*Ze, -Ze) 。然后由矩阵乘法公式和地方已知坐标,就足以拿走PROJECTION MAT奥迪Q3IX。

简短分析一下那么些obj文件

分分快三计划 21
前两行看到#标志就知晓那么些是注释了,该obj文件是用blender导出的。Blender是一款很好用的建立模型软件,最要紧的它是免费的!

分分快三计划 22
Mtllib(material library)指的是该obj文件所运用的材料库文件(.mtl)
单单的obj生成的模型是白模的,它只含有纹理坐标的新闻,但从没贴图,有纹理坐标也没用

分分快三计划 23
V 顶点vertex
Vt 贴图坐标点
Vn 顶点法线

分分快三计划 24
Usemtl 使用材料库文件中实际哪三个材料

分分快三计划 25
F是面,前边分别对应 顶点索引 / 纹理坐标索引 / 法线索引

那边大部分也都以大家这贰个常用的天性了,还或然有一部分别的的,这里就异常少说,能够google搜一下,相当多介绍很详细的稿子。
要是有了obj文件,那我们的工作也正是将obj文件导入,然后读取内容还要按行分析就能够了。
先放出最后的结果,三个仿照银系的3D文字效果。
在线地址查看:

在这里顺便说一下,2D文字是足以因此深入分析得到3D文字模型数据的,将文字写到canvas上未来读取像素,获取路径。大家这里未有动用该措施,因为固然如此辩白上任何2D文字都能转3D,还能够做出类似input输入文字,3D呈现的作用。不过本文是教大家快捷搭建三个小世界,所以大家依旧接纳blender去建立模型。

末段得到Opengl 的 Perspective Projection Matrix:

4、旋转运动等转移

实体全部导入进去,剩下来的任务就是拓宽转变了,首先大家深入分析一下有怎么着动画效果
因为我们模拟的是叁个自然界,3D文字就好像星球相同,有公转和自转;还大概有就是大家导入的obj文件都是依照(0,0,0)点的,所以大家还须求把它们实行移动操作
先上核心代码~

JavaScript

...... this.angle = this.rotate; // 自转的角度   var s = Math.sin(this.angle); var c = Math.cos(this.angle);   // 公转相关数据 var gs = Math.sin(globalTime * this.revolution); // globalTime是全局的年华 var gc = Math.cos(globalTime * this.revolution);     webgl.uniformMatrix4fv(     this.program.uMMatrix, false, mat4.multiply([             gc,0,-gs,0,             0,1,0,0,             gs,0,gc,0,             0,0,0,1         ], mat4.multiply(             [                 1,0,0,0,                 0,1,0,0,                 0,0,1,0,                 this.x,this.y,this.z,1 // x,y,z是偏移的任务             ],[                 c,0,-s,0,                 0,1,0,0,                 s,0,c,0,                 0,0,0,1             ]         )     ) );

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
......
this.angle = this.rotate; // 自转的角度
 
var s = Math.sin(this.angle);
var c = Math.cos(this.angle);
 
// 公转相关数据
var gs = Math.sin(globalTime * this.revolution); // globalTime是全局的时间
var gc = Math.cos(globalTime * this.revolution);
 
 
webgl.uniformMatrix4fv(
    this.program.uMMatrix, false, mat4.multiply([
            gc,0,-gs,0,
            0,1,0,0,
            gs,0,gc,0,
            0,0,0,1
        ], mat4.multiply(
            [
                1,0,0,0,
                0,1,0,0,
                0,0,1,0,
                this.x,this.y,this.z,1 // x,y,z是偏移的位置
            ],[
                c,0,-s,0,
                0,1,0,0,
                s,0,c,0,
                0,0,0,1
            ]
        )
    )
);

一眼望去uMMatrix(模型矩阵)里面有多少个矩阵,为啥有八个吗,它们的依次有怎样供给么?
因为矩阵不知足交流率,所以大家矩阵的移位和旋转的各种十三分至关心重视要,先平移再旋转和先旋转再平移有如下的距离
(下边图片来自互联网)
先旋转后移动:分分快三计划 26
先平移后旋转:分分快三计划 27
从图中分明看出来先旋转后移动是自转,而先平移后旋转是公转
之所以大家矩阵的逐一一定是 公转 * 平移 * 自转 * 顶点新闻(右乘)
现实矩阵为什么如此写可知上一篇矩阵入门小说
这样多个3D文字的8大行星就产生啦

经过camera space transform 得到
Pe = {Xe,Ye,Ze}

教你用webgl快速创建一个小世界

2017/03/25 · HTML5 · AlloyTeam

初稿出处: AlloyTeam   

Webgl的魔力在于能够制造二个投机的3D世界,但相比较canvas2D来讲,除了物体的移动旋转变换完全凭仗矩阵扩展了复杂度,就连生成三个实体都变得很复杂。

什么样?!为啥不用Threejs?Threejs等库确实能够极大程度的滋长开辟效用,况兼外市点封装的非常棒,可是不推荐初我们直接注重Threejs,最佳是把webgl各州点都学会,再去拥抱Three等相关库。

上篇矩阵入门中牵线了矩阵的基本知识,让大家探听到了基本的仿射转变矩阵,能够对实体实行活动旋转等变化,而那篇文章将教大家迅快速生成成贰个实体,何况结合转换矩阵在物体在你的社会风气里动起来。

注:本文适合稍微有一点点webgl基础的人同学,至少知道shader,知道怎么样画叁个物体在webgl画布中

上边考虑perspective projection matrix。
听他们讲线性代数原理,大家知道不恐怕用三个3x3的matrix对终极(x,y,z)实行透视映射。不可能透过二个3X3的矩阵得到x/z 的花样。从而引入齐次坐标矩阵---4x4 matrix。顶点坐标(x,y,z,w)。
齐次坐标中,顶点(x, y, z, w)也等于(x/w, y/w, z/w, 1)。 看到这一个极端坐标,大家会联想到前方大家最后求出的z buffer数值Zp和单位device space中的Xp坐标。利用矩阵乘法,能够获得贰个矩阵Mp,使得(Xe,Ye,Ze,1)的极端坐标转变为齐次坐标规一化后的 (Xp,Yp,Zp,1) 。  即:
Vp = Mp * Ve  .
Vp是单位配备坐标系的顶峰坐标(Xp,Yp,Zp,1)。Ve是camera space顶点坐标(Xe,Ye,Ze,1)。

这样一来,大家就知晓Z BUFFE大切诺基的数值怎么着求得。先把物体顶点世界坐标Pw转换成camera space中收获Pe。然后再经过透视投影转变,求得Ze->Zp的数值。把那些数值填入Z buffer,就是显卡用来相比较哪个像素在前,哪个像素在后的依据了。
那也正是干吗near和far设置不确切的时候,很轻巧发生z fighting。一般情状下,离显示器相当近的一段距离,已经用掉了70%的z 浮点精度。在用来渲染视角里中远距离的景色时,深度的辨认只靠剩下的10%精度来打开。
实际推导能够看看 

前面若一时间继续 透视更正texture mapping,phong shading以及bump mapping等per-pixel processing光栅化

接下来通过project transform 转为 device space,这里假使转为 Zp 范围 [-1,1](OPENG的Z BUFFER)

透视投影,与Z BUFFEWrangler求值
   
    为啥有透视。因为眼球是个透镜。假诺地球生物进化的都靠超声波探测空中,那恐怕眼睛就不会有成为球,而是别的形状...
何以有人玩3D头晕?个中七个关键的法力是,眼球不完全部是个透镜。所以当视线超过60度,荧屏四周投影的变形就比眼球投影视网膜利害多。并且人脑习于旧贯了考订眼球投影的新闻。猛然有个荧屏上粗糙的上行下效眼球成像,大脑还真不常适应不断。

假如坐标在 world space 是
Pw = {Xw,Yw,Zw}

Pe在near平面包车型客车影子为:
Xep = n* Xe/(-Ze) (n为近平面到eye距离).
只顾这里OPENGL 左边手系camera space是Z轴负方向为眼睛看的方向。当总计投影时,x,y都应有除以三个正的数值。所以Ze取负。

当Xep在left到right变化时,Xp在-1到1变化。
因为显卡硬件(GPU)的扫描线差值都以看破校对的,思量投影后,顶点Xp和Yp都以1/(-Ze) 的线性关系。那么在device单位立方体内,Zp 的范围在[-1,1]里头,Zp也正是Z BUFFEWrangler的数值。依据前边推导,要确认保证透视校订,Zp也是以1/(-Ze)的线性关系。即,总能找到二个公式,使得
Zp = A* 1/(-Ze) B。

请RE D3D PEWranglerSPECTIVE PROJECTION MATPRADOIX推导进度~

Xp*(-Ze) = M0  M1  M2  M3                  Xe
Yp*(-Ze) = M4  M5  M6  M7        x         Ye
Zp*(-Ze) = M8  M9  M10 M11                 Ze
-Ze    = M12 M13 M14 M15                   1

D3D 的左臂系透视投影矩阵和OPENGL有以下分别。
1, D3D device space 不是个立方,是个扁盒子。z的间隔唯有[0,1] 。x,y区间依然[-1,1]
2,D3D的camera space Z轴朝向正方向,总结camera space中投影时不要 Xep = n*Xe/(-Ze), 而是 Xep = n*Xe/Ze
3,D3D中,从camera space的视椎平截体到device space的单位体(扁盒子)水墨画,选择了很离奇的作法。把frustum右上角映射为device单位体的(0,0,0)地方

    Z BUFFE昂Cora数值总括,以及PE奥迪Q3SPECTIVE PROJECTION MATXC90IX设置,使用D3D或然OPENGL,能够直接让显卡达成那几个职业。不过弄清Z BUFFEPRADO怎样总括,以及PROJECT MAT奥迪Q5IX的规律。对于实行各个高档图像管理,非常实用。举个例子shadowmap的应用。近些日子为了拿走好的shadowmap,比比较多个人在怎么样加大shadowmap精度做了比较多全心全意(更改生成shadowmap时的perspective project matrix来变化精度更客观的shadowmap) 。例如透视空间的perspective shadow map,light空间的Light-space perspective shadow,perspective shadowmap变种Trapezoidal shadow maps,考订交易投资影为对数参数投影的 Logarithmic Shadow Maps。其余,Doom3中shadow volume选择的极度远平面透视矩阵绘制stencil shadow volume。都亟需对perspective projection有透顶领会。

以下描述z buffer总计以及perspective projection matrix原理。

本文由分分快三计划发布,转载请注明来源

关键词: 分分快三计划 HTML5 透视投影 Z BUFFER