原文地址:WebGL学习(2) - 3D场景

经过前面WebGL学习(1) - 三角形的学习,我们已经掌握了webGL的基础知识,也已经能够画出最基本的图形,比如点,线,三角形,矩形等。有了2D绘图的基础,现在终于可以进入精彩的3D世界了,来看一下这一节要实现的3D的效果吧。

实际效果:webGL3D场景



webGL渲染流程

重温一下webGL的渲染流程,这一节在第3、4、5、6步骤需要学习新的内容。其中写入数据交叉存放缓冲区,设置隐藏面消除,清空深度缓冲都是比较简单的部分。重点和难点是在3D变换的环节,在理解了矩阵的原理基础上,这次使用了《WebGL编程指南》提供的矩阵操作库。

  1. 获取webGL绘图上下文
  2. 初始化着色器
  3. 创建、绑定缓冲区对象
  4. 3D变换
  5. 向顶点着色器和片元着色器写入数据(数据交叉存放缓冲区)
  6. 设置canvas背景色,设置隐藏面消除
  7. 清空canvas|清空深度缓冲
  8. 绘制

着色器

着色器代码修改为下面,我们现在需要为每个顶点都使用不同的颜色,所以使用到了varying限定符的变量,这个变量目的就是连接顶点和片元着色器,把顶点信息和颜色信息结合起来。看到顶点着色器和片元着色器都有的v_color变量了吗?其实就是通过全局变量传递。

顶点着色器

<script type="x-shader/x-vertex" id="vs">
attribute vec4 a_Position; //顶点
uniform mat4 u_MvpMatrix;//模型视点投影矩阵
attribute vec4 a_Color;
varying vec4 v_color;// 连接片元着色器
void main() {
gl_Position = u_MvpMatrix * a_Position;
v_color=a_Color;//传递给片元着色器变量
}
</script>

片元着色器

<script type="x-shader/x-fragment" id="fs">
precision mediump float; // 精度限定
varying vec4 v_color; //从顶点着色器接收
void main() {
gl_FragColor = v_color;
}
</script>

3D坐标系

第1、2、3步骤前面文章已经介绍,现在我们直接进入3D的环节。3D比2D主要就是多了深度信息,用坐标系来描述就是,除xy轴外,还多了z轴。webGL的坐标系跟我们web的坐标系是不一样的,首先它原点不是在左上角而是位于中间,xyz方向也不同。


视点和视线

接着引入一个概念,视点,也就是定义观察者的位置,观察者能看多远,观察者的方向,直接看图吧


上方向就是观测者从哪个方向看,(0,1,0)是正常的Y轴正方向,(1,0,0)就相当于物体向左旋90度,等于我们把头打横看物体。通过定义视点矩阵,我们看到的图形的形状会产生变化的,就和我们实际环境不同的角度位置观察同一物体是一样一样的。我们调用矩阵库中的方法,会产生出一个4X4的矩阵,具体中间的产生过程,可以看源代码。

// (视点,观察目标点,上方向)
setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ)

投影可视空间

只有指定了可视空间,webGL才会去绘制图形,有两种类型:

一是盒状可视空间,由正射投影产生,它产生的图形,前后物体没有大小区别,都是一样高宽。



调用矩阵库的setOrtho方法,产生矩阵

// 正视投影 (left,right,bottom,top,near,far), 组成一个正方体的可视空间
setOrtho(left, right, bottom, top, near, far);

二是四棱锥可视空间,由透视投影产生,透视投影产生的3D场景更加真实自然,它产生的图形具有近大远小的透视效果,当然性能消耗相对正射投影高一些。



调用setPerspective方法,同理生成矩阵

// 投影矩阵(fov可视空间底面和顶面夹角<大于0>,近裁截面宽高比,近裁截面位置<大于0>,远裁截面位置<大于0> )
setPerspective(fovy, aspect, near, far)

数据交叉存放缓冲区

我们既可以给不同的信息分别创建单独缓冲区,也可以给不同的信息创建同一块合用的缓冲区,前者适合数据量小的情况,我们现在实现第二种情况:给不同的信息创建一块缓冲区,并交叉存放。首先用一个数组同时存放顶点信息和顶点对应的颜色信息,接着创建缓冲区后调用gl.vertexAttribPointer(),该方法有定义每个分量的个数,每一行的个数以及偏移数,当然相邻顶点数和偏移量要乘以单位字节,具体看代码的注释。

/**
* 混合缓冲区(包括顶点,颜色),每一行前3个是顶点信息,后3个是颜色信息
*/
var verticeColors=new Float32Array([
0.0, 1.0, -2.0, 0.3, 1.0, 0.4,
-0.5, -1.0, -2.0, 0.3, 1.0, 0.4,
0.5, -1.0, -2.0, 1.0, 0.4, 0.4, 0.0, 1.0, -1.0, 1.0, 1.0, 0.4,
-0.5, -1.0, -1.0, 1.0, 1.0, 0.4,
0.5, -1.0, -1.0, 1.0, 0.4, 0.4, 0.0, 1.0, 0.0, 0.4, 0.4, 1.0,
-0.5, -1.0, 0.0, 0.4, 0.4, 1.0,
0.5, -1.0, 0.0, 1.0, 0.4, 0.4,
]);
// 创建缓冲区
if(!createBuffer(verticeColors)){
console.log('Failed to create the buffer object');
return;
} // 每个元素的字节
var FSIZE = verticeColors.BYTES_PER_ELEMENT;
// 获取顶点位置
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
// (地址,每个顶点分量的个数<1-4>,数据类型<整形,符点等>,是否归一化,指定相邻两个顶点间字节数<默认0>,指定缓冲区对象偏移字节数量<默认0>)
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 6*FSIZE, 0);
// Enable the assignment to a_Position variable
gl.enableVertexAttribArray(a_Position); // 获取a_Color变量的存储地址并赋值
var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 6*FSIZE, 2*FSIZE);
gl.enableVertexAttribArray(a_Color);

3D相关的其他设置

开启隐藏面消除可以减少渲染量,提高性能,同时还可以避免顺序不一致时,后面的图形盖住前面的图形。而多边形偏移,可以避免深度很接近的两个图形产生冲突。当然每次重新渲染的时候,在清屏的同时清除深度缓冲,具体实现请看代码。

// 开启隐藏面消除
gl.enable(gl.DEPTH_TEST);
//清屏|清深度缓冲
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 启用多边形偏移,避免深度冲突
gl.enable(gl.POLYGON_OFFSET_FILL);
//设置偏移量
gl.polygonOffset(1.0, 1.0);

执行动画

来看一下我们执行动画的部分,首先设置好用于位移旋转的模型矩阵,然后依次产生视点矩阵,投影矩阵,接着把它们相乘产生出mvp矩阵,然后传入变量,最后绘图。在绘制完第一组图形的时候,将前面的mvp矩阵再左移2个单位,再绘制一遍,于是就产生出了第二组图形。具体的逻辑情况代码注释。

var angle=0;
// 执行动画
(function animate(){
// 旋转位移 等于绕原点Y旋转
modelMatrix.setRotate((angle++)%360,0,1,0);
modelMatrix.translate(1, 0, 1);
// (视点,观察目标点,上方向)
viewMatrix.setLookAt(-0.25, -0.25, 5, 0, 0, -100, 0, 1, 0);
// 投影矩阵(fov可视空间底面和顶面夹角<大于0>,近裁截面宽高比,近裁截面位置<大于0>,远裁截面位置<大于0> )
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
// 矩阵相乘
mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
// 赋值
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); //清屏|清深度缓冲
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 启用多边形偏移,避免深度冲突
gl.enable(gl.POLYGON_OFFSET_FILL); // (基本图形,第几个顶点,执行几次),修改基本图形项可以生成点,线,三角形,矩形,扇形等
gl.drawArrays(gl.TRIANGLES, 0, 9); //位移后,再将前面3个三角形重新绘制
modelMatrix.translate(-2, 0, 0);
mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); //设置偏移量
gl.polygonOffset(1.0, 1.0);
gl.drawArrays(gl.TRIANGLES, 0, 9); requestAnimationFrame(animate);
}());

总结

学习完3D场景后,我们又再一次领略到了线性代数中矩阵在图形学中的重要作用。3D的矩阵转换才是需要空间思维和深入理解的部分,其他地方说实话就是学习如何调用api。

最后,献上主体的全部代码

var	canvas=document.getElementById('canvas'),
gl=get3DContext(canvas,true); function main() {
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
} if (!createShaders(gl, 'fs', 'vs')) {
console.log('Failed to intialize shaders.');
return;
} /**
* 混合缓冲区(包括顶点,颜色)
*/
var verticeColors=new Float32Array([
0.0, 1.0, -2.0, 0.3, 1.0, 0.4,
-0.5, -1.0, -2.0, 0.3, 1.0, 0.4,
0.5, -1.0, -2.0, 1.0, 0.4, 0.4, 0.0, 1.0, -1.0, 1.0, 1.0, 0.4,
-0.5, -1.0, -1.0, 1.0, 1.0, 0.4,
0.5, -1.0, -1.0, 1.0, 0.4, 0.4, 0.0, 1.0, 0.0, 0.4, 0.4, 1.0,
-0.5, -1.0, 0.0, 0.4, 0.4, 1.0,
0.5, -1.0, 0.0, 1.0, 0.4, 0.4,
]);
// 创建缓冲区
if(!createBuffer(verticeColors)){
console.log('Failed to create the buffer object');
return;
} // 每个元素的字节
var FSIZE = verticeColors.BYTES_PER_ELEMENT;
// 获取顶点位置
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
// (地址,每个顶点分量的个数<1-4>,数据类型<整形,符点等>,是否归一化,指定相邻两个顶点间字节数<默认0>,指定缓冲区对象偏移字节数量<默认0>)
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 6*FSIZE, 0);
// Enable the assignment to a_Position variable
gl.enableVertexAttribArray(a_Position); // 获取a_Color变量的存储地址并赋值
var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 6*FSIZE, 2*FSIZE);
gl.enableVertexAttribArray(a_Color); var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
if(!u_MvpMatrix) {
console.log('Failed to get the storage location of u_MvpMatrix');
return;
}
// 设置背景颜色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 开启隐藏面消除
gl.enable(gl.DEPTH_TEST); var modelMatrix = new Matrix4(); // 模型矩阵
var viewMatrix = new Matrix4(); // 视点矩阵
var projMatrix = new Matrix4(); // 投影矩阵
var mvpMatrix = new Matrix4(); // 用于相乘用
var angle=0;
// 执行动画
(function animate(){
// 旋转位移 等于绕原点Y旋转
modelMatrix.setRotate((angle++)%360,0,1,0);
modelMatrix.translate(1, 0, 1);
// (视点,观察目标点,上方向)
viewMatrix.setLookAt(-0.25, -0.25, 5, 0, 0, -100, 0, 1, 0);
// 投影矩阵(fov可视空间底面和顶面夹角<大于0>,近裁截面宽高比,近裁截面位置<大于0>,远裁截面位置<大于0> )
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
// 矩阵相乘
mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
// 赋值
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); //清屏|清深度缓冲
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 启用多边形偏移,避免深度冲突
gl.enable(gl.POLYGON_OFFSET_FILL); // (基本图形,第几个顶点,执行几次),修改基本图形项可以生成点,线,三角形,矩形,扇形等
gl.drawArrays(gl.TRIANGLES, 0, 9); //位移后,再将前面3个三角形重新绘制
modelMatrix.translate(-2, 0, 0);
mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); //设置偏移量
gl.polygonOffset(1.0, 1.0);
gl.drawArrays(gl.TRIANGLES, 0, 9); requestAnimationFrame(animate);
}());
} main();

WebGL学习(2) - 3D场景的更多相关文章

  1. WebGL学习(3) - 3D模型

      原文地址:WebGL学习(3) - 3D模型   相信很多人是以创建逼真酷炫的三维效果为目标而学习webGL的吧,首先我就是

  2. 点云深度学习的3D场景理解

    转载请注明本文链接: https://www.cnblogs.com/Libo-Master/p/9759130.html PointNet: Deep Learning on Point Sets ...

  3. 基于 HTML5 的 WebGL 技术构建 3D 场景(一)

    今天和大家分享的是 3D 系列之 3D 预定义模型. HT for Web 提供了多种基础类型供用户建模使用,不同于传统的 3D 建模方式,HT 的建模核心都是基于 API 的接口方式,通过 HT 预 ...

  4. 基于 HTML5 WebGL 构建智能城市 3D 场景

    前言 随着城市规模的扩大,传统的方式很难彻底地展示城市的全貌,但随着 3D 技术的应用,出现了 3D 城市群的方式以动态,交互式地把城市全貌呈现出来.配合智能城市系统,通过 Web 可视化的方式,使得 ...

  5. 基于 HTML5 WebGL 的 3D 场景中的灯光效果

    构建 3D 的场景除了创建模型,对模型设置颜色和贴图外,还需要有灯光的效果才能更逼真的反映真实世界的场景.这个例子我觉得既美观又代表性很强,所以拿出来给大家分享一下. 本例地址:http://www. ...

  6. WebGL 入门-WebGL简介与3D图形学

    什么是WebGL? WebGL是一项使用JavaScript实现3D绘图的技术,浏览器无需插件支持,Web开发者就能借助系统显卡(GPU)进行编写代码从而呈现3D场景和对象. WebGL基于OpenG ...

  7. WebGL学习(1) - 三角形

    原文地址:WebGL学习(1) - 三角形 还记得第一次看到canvas的粒子特效的时候,真的把我给惊艳到了,原来在浏览器也能做出这么棒的效果.结合<HTML5 Canvas核心技术>和网 ...

  8. WebGL学习之纹理盒

    原文地址:WebGL学习之纹理盒 我们之前已经学习过二维纹理 gl.TEXTURE_2D,而且还使用它实现了各种效果.但还有一种立方体纹理 gl.TEXTURE_CUBE_MAP,它包含了6个纹理代表 ...

  9. 基于 webGL 的元素周期表 3D 交互展示

    前言 之前在网上看到别人写的有关元素周期表的文章,深深的勾起了一波回忆,记忆里初中时期背的“氢氦锂铍硼,碳氮氧氟氖,钠镁铝硅磷,硫氯氩钾钙”.“养(氧)龟(硅)铝铁盖(钙),哪(钠)家(钾)没(镁)青 ...

随机推荐

  1. ConvertUtils.register注册转换器

    当用到BeanUtils的populate.copyProperties方法或者getProperty,setProperty方法其实都会调用convert进行转换 但Converter只支持一些基本 ...

  2. JavaWeb学习笔记——jquery中的dom操作

     jquery中的dom操作 废话不说:直接上例子: 1.添加节点-html页面 Append:向每个匹配的元素内部追加内容. <body> <ul id="city& ...

  3. every();some();filter();map();forEach()各自区别:

    every();some();filter();map();forEach()各自区别: (1)every()方法:(返回值为boolean类型) 对数组每一项都执行测试函数,知道获得对指定的函数返回 ...

  4. SqlServer和Oracle中一些常用的sql语句5 流程控制语句

    --在sql语句中 begin...end 用来设定一个程序块 相关于c#中的{} declare @yz real,@w int --声明变量 set @w=120 --为变量赋值 if @w< ...

  5. WebApi实现自定义错误日志

    一.Mvc错误处理默认有添加HandleErrorAttribute默认的过滤器,但是我们有可能要捕捉这个错误并记录系统日志那么这个过滤器就不够用了,所以我们要自定义Mvc及Web Api各自的错误处 ...

  6. gitlab服务器搭建教程

    gitlab服务器搭建教程 ----2016年终总结 三 参考https://bbs.gitlab.cc/topic/35/gitlab-ce-8-7-%E6%BA%90%E7%A0%81%E5%AE ...

  7. [js高手之路]打造通用的匀速运动框架

    本文,是接着上文[js高手之路]匀速运动与实例实战(侧边栏,淡入淡出)继续的,在这篇文章的最后,我们做了2个小实例:侧边栏与改变透明度的淡入淡出效果,本文我们把上文的animate函数,继续改造,让变 ...

  8. Collection和Map的默认扩容参数

    初始大小:调用无参构造函数时默认的容量 加载因子:超过 (当前容量*加载因子) 时会进行扩容 扩容因子:扩容时增加的容量为 (当前容量*扩容因子)        容器         初始容量    ...

  9. Intel Core Microarchitecture Pipeline

    Intel微处理器近20年从Pentium发展到Skylake,得益于制作工艺上的巨大发展,处理器的性能得到了非常大的增强,功能模块增多,不过其指令处理pipeline的主干部分算不上有特别大的变化, ...

  10. break和continue 的区别

    区别 break和continue都可在循环语句里面使用,也都可以控制外层的循环.但是continue只能在循环语句里面使用,break也可以使用在switch语句里面. break具体作用在循环语句 ...