[WebGL入门]十六,绘制多个模型

翻译 2014年08月11日 00:21:04

注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,如果翻译有误,欢迎大家指正。


本次的demo的运行结果


顶点缓存的重复利用

上一篇文章中,向多边形的三个顶点中添加了颜色这个新的顶点属性,给多边形涂抹了颜色。而且知道,使用新的VBO可以给对顶点属性进行自由的扩张。
那么,这次来挑战一下同时绘制多个模型。但是,不准备新的VBO,还利用上次的VBO,就是说,重复利用VBO,只操作坐标变换矩阵,来绘制多个模型。
所以,这次着色器相关的内容完全不变,完全使用上次的顶点着色器和片段着色器,javascript代码变更的地方也不多,下面重点说一下变更的部分。


坐标变换矩阵的重复利用

这次绘制多个模型,坐标变换矩阵相关也是重复利用。
也就是说,在实际中,将多个模型绘制到不同的位置的时候,必须要操作的坐标变换矩阵只有模型变换矩阵,而决定镜头的位置的视图变换矩阵和决定屏幕范围的投影变换矩阵这两个坐标变换矩阵,都可以使用同一个。
步骤如下,按照下面这样操作矩阵的话就可以了。
・准备好视图和投影两个坐标变换矩阵
・提前将两个矩阵相乘并保存起来(以下、pv)
・准备第一个模型坐标变换矩阵(以下、m1)
・m1和pv相乘,传给uniform
・绘制第一个模型
・准备第二个模型坐标变换矩阵(以下、m2)
・m2和pv相乘,传给uniform
・绘制第二个模型
・刷新context
主要是,视图坐标变换矩阵和投影坐标变换矩阵提前相乘并保存起来,然后等模型坐标变换矩阵准备好之后,再与之前的结果相乘,传给uniform,然后绘图。
那么,来看一下实际的代码吧。

>script.js中的部分代码

//各种矩阵的生成和初始化
var mMatrix = m.identity(m.create());
var vMatrix = m.identity(m.create());
var pMatrix = m.identity(m.create());
var tmpMatrix = m.identity(m.create());
var mvpMatrix = m.identity(m.create());

//视图x投影坐标变换矩阵
m.lookAt([0.0, 0.0, 3.0], [0, 0, 0], [0, 1, 0], vMatrix);
m.perspective(90, c.width / c.height, 0.1, 100, pMatrix);
m.multiply(pMatrix, vMatrix, tmpMatrix);

//移动第一个模型的模型坐标变换矩阵
m.translate(mMatrix, [1.5, 0.0, 0.0], mMatrix);

//模型x视图x投影(第一个模型)
m.multiply(tmpMatrix, mMatrix, mvpMatrix);

//将坐标变换矩阵传入uniformLocation,并绘图(第一个模型)
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);

//移动第二个模型的模型坐标变换矩阵
m.identity(mMatrix);
m.translate(mMatrix, [-1.5, 0.0, 0.0], mMatrix);

//模型x视图x投影(第二个模型)
m.multiply(tmpMatrix, mMatrix, mvpMatrix);

//将坐标变换矩阵传入uniformLocation,并绘图(第二个模型)
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);

//刷新context
gl.flush();
重点是,为了暂时保存视图和投影坐标变换矩阵,新声明了一个tmpMatrix这样的使用方法。使用matIV.multiply将视图坐标变换矩阵和投影坐标变换矩阵相乘,然后结果保存到tmpMatrix中。
然后,使用matIV.translate来操作模型坐标变换矩阵,将第一个模型向X方向移动1.5,然后将这个模型坐标变换矩阵和tmpMatrix相乘,传给uniform并进行绘图。
第二个模型的坐标矩阵,和之前相反,向X方向上移动-1.5,然后和第一个一样,和tmpMatrix相乘,传给uniform并进行绘图。
最后,刷新context,在canvas上绘制两个多边形。这样,利用VBO和一部分坐标变换矩阵,省去了不必要的代码,绘制了两个多边形。
需要注意的是,对第二个模型准备模型坐标变换矩阵的时候,首先在最初的时候使用matIV.identity对矩阵进行初始化。如果,不进行初始化直接使用matIV.translate的话,会受前一回移动的影响,导致结果改变。进行完初始化之后再进行操作,是为了避免类似这样的错误发生。


附加:attribute属性添加的函数化

上次的文章中也涉及到了,增加顶点属性的时候,如果将相应的处理函数化的话可以提高效率,虽然和这次的主题(绘制多个模型)没有关系,但是因为demo中进行了函数化,所以简单的解说一下。

>VBO的绑定相关

// 绑定VBO相关的函数
function set_attribute(vbo, attL, attS){
    // 处理从参数中得到的数组
    for(var i in vbo){
        // 绑定缓存
        gl.bindBuffer(gl.ARRAY_BUFFER, vbo[i]);
        
        // 将attributeLocation设置为有效
        gl.enableVertexAttribArray(attL[i]);
        
        //通知并添加attributeLocation
        gl.vertexAttribPointer(attL[i], attS[i], gl.FLOAT, false, 0, 0);
    }
}
这个函数有三个参数,三个参数都是数组。使用for ~ in来循环VBO中的属性,并进行绑定和添加。一般,需要准备很多个VBO,所以将这里函数化,以后可以省略很多代码了吧。

总结

这次操作的是模型坐标变换矩阵,介绍了重复利用VBO,视图和投影坐标变换矩阵,进行多个模型的绘制的方法。
绘制很多个简单的模型,图形的时候,像这次的做法一样,可以使处理变的简洁一些,避免写很多多余的代码。
这次的demo,HTML的代码和上一次是完全一样的,就是说,顶点着色器和片段着色器没有做任何调整。javascript代码有了一些变化,所以贴出所有代码。另外,在文章的最后面,添加了demo的链接,有支持WebGL的浏览器的话,可以直接打开链接看一下效果。


下次,进一步操作模型坐标变换矩阵,对绘制的模型进行各种各样的处理。

>script.js的全部代码

onload = function(){
    // canvas对象获取
    var c = document.getElementById('canvas');
    c.width = 300;
    c.height = 300;

    // webgl的context获取
    var gl = c.getContext('webgl') || c.getContext('experimental-webgl');
    
    // 设定canvas初始化的颜色
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    
    // 设定canvas初始化时候的深度
    gl.clearDepth(1.0);
    
    // canvas的初始化
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    
    // 顶点着色器和片段着色器的生成
    var v_shader = create_shader('vs');
    var f_shader = create_shader('fs');
    
    // 程序对象的生成和连接
    var prg = create_program(v_shader, f_shader);
    
    // attributeLocation的获取
    var attLocation = new Array(2);
    attLocation[0] = gl.getAttribLocation(prg, 'position');
    attLocation[1] = gl.getAttribLocation(prg, 'color');
    
	// 将元素数attribute保存到数组中
	var attStride = new Array(2);
	attStride[0] = 3;
	attStride[1] = 4;

	// 保存顶点的位置情报的数组
	var vertex_position = [
	     0.0, 1.0, 0.0,
 	    1.0, 0.0, 0.0,
	    -1.0, 0.0, 0.0
	];

	// 保存顶点的颜色情报的数组
    var vertex_color = [
        1.0, 0.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0
    ];
    
    // 生成VBO
    var pos_vbo = create_vbo(position);
    var col_vbo = create_vbo(color);
    
    // 将VBO进行绑定并添加
    set_attribute([pos_vbo, col_vbo], attLocation, attStride);
    
    // 获取uniformLocation
    var uniLocation = gl.getUniformLocation(prg, 'mvpMatrix');
    
    // 使用minMatrix.js对矩阵的相关处理
    // matIV对象生成
    var m = new matIV();
    
//各种矩阵的生成和初始化
var mMatrix = m.identity(m.create());
var vMatrix = m.identity(m.create());
var pMatrix = m.identity(m.create());
var tmpMatrix = m.identity(m.create());
var mvpMatrix = m.identity(m.create());

//视图x投影坐标变换矩阵
m.lookAt([0.0, 0.0, 3.0], [0, 0, 0], [0, 1, 0], vMatrix);
m.perspective(90, c.width / c.height, 0.1, 100, pMatrix);
m.multiply(pMatrix, vMatrix, tmpMatrix);

//移动第一个模型的模型坐标变换矩阵
m.translate(mMatrix, [1.5, 0.0, 0.0], mMatrix);

//模型x视图x投影(第一个模型)
m.multiply(tmpMatrix, mMatrix, mvpMatrix);

//将坐标变换矩阵传入uniformLocation,并绘图(第一个模型)
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);

//移动第二个模型的模型坐标变换矩阵
m.identity(mMatrix);
m.translate(mMatrix, [-1.5, 0.0, 0.0], mMatrix);

//模型x视图x投影(第二个模型)
m.multiply(tmpMatrix, mMatrix, mvpMatrix);

//将坐标变换矩阵传入uniformLocation,并绘图(第二个模型)
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);

//刷新context
gl.flush();
    
    // 生成着色器的函数
    function create_shader(id){
        // 用来保存着色器的变量
        var shader;
        
        // 根据id从HTML中获取指定的script标签
        var scriptElement = document.getElementById(id);
        
        // 如果指定的script标签不存在,则返回
        if(!scriptElement){return;}
        
        // 判断script标签的type属性
        switch(scriptElement.type){
            
            // 顶点着色器的时候
            case 'x-shader/x-vertex':
                shader = gl.createShader(gl.VERTEX_SHADER);
                break;
                
            // 片段着色器的时候
            case 'x-shader/x-fragment':
                shader = gl.createShader(gl.FRAGMENT_SHADER);
                break;
            default :
                return;
        }
        
        // 将标签中的代码分配给生成的着色器
        gl.shaderSource(shader, scriptElement.text);
        
        // 编译着色器
        gl.compileShader(shader);
        
        // 判断一下着色器是否编译成功
        if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
            
            // 编译成功,则返回着色器
            return shader;
        }else{
            
            // 编译失败,弹出错误消息
            alert(gl.getShaderInfoLog(shader));
        }
    }
    
    // 程序对象的生成和着色器连接的函数
    function create_program(vs, fs){
        // 程序对象的生成
        var program = gl.createProgram();
        
        // 向程序对象里分配着色器
        gl.attachShader(program, vs);
        gl.attachShader(program, fs);
        
        // 将着色器连接
        gl.linkProgram(program);
        
        // 判断着色器的连接是否成功
        if(gl.getProgramParameter(program, gl.LINK_STATUS)){
        
            // 成功的话,将程序对象设置为有效
            gl.useProgram(program);
            
            // 返回程序对象
            return program;
        }else{
            
            // 如果失败,弹出错误信息
            alert(gl.getProgramInfoLog(program));
        }
    }
    
    // 生成VBO的函数
    function create_vbo(data){
        // 生成缓存对象
        var vbo = gl.createBuffer();
        
        // 绑定缓存
        gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
        
        // 向缓存中写入数据
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
        
        // 将绑定的缓存设为无效
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        
        // 返回生成的VBO
        return vbo;
    }
    
// 绑定VBO相关的函数
function set_attribute(vbo, attL, attS){
    // 处理从参数中得到的数组
    for(var i in vbo){
        // 绑定缓存
        gl.bindBuffer(gl.ARRAY_BUFFER, vbo[i]);
        
        // 将attributeLocation设置为有效
        gl.enableVertexAttribArray(attL[i]);
        
        //通知并添加attributeLocation
        gl.vertexAttribPointer(attL[i], attS[i], gl.FLOAT, false, 0, 0);
    }
}

};

绘制多个模型的demo

http://wgld.org/s/sample_004/


转载请注明:转自lufy_legend的博客http://blog.csdn.net/lufy_legend


腾讯上市马化腾6年创业身家9亿

【2004.06.17 】21世纪经济报道    网络造富神话再次显现。这一次,轮到了QQ的主人腾讯。  6月16日,腾讯控股(700.HK)在香港联交所正式挂牌交易。根据其每股3.70美元的发行价计...
  • zdg
  • zdg
  • 2004-06-20 01:40:00
  • 4225

WebGL学习系列-使用缓冲区对象画多个点

一般而言,我们需要绘制的点的数量非常的多,所以不可能像第一个程序一样一个点一个点绘制,WebGL提供了缓冲区对象,用于处理绘制多个点的数据问题。...
  • yanyan19880509
  • yanyan19880509
  • 2017-02-19 10:15:34
  • 531

11 WebGL绘制基本图形

案例查看地址:点击这里<!doctype html> <html> <head> <meta ...
  • qq_30100043
  • qq_30100043
  • 2017-05-07 22:49:09
  • 600

WebGL学习系列-基本图形变换

经过前面的学习,我们已经可以绘制基本的图形了。本小节将介绍基本的图形变换,介绍在WebGL中,如何对基本的图形进行平移、旋转和缩放。...
  • yanyan19880509
  • yanyan19880509
  • 2017-02-22 23:08:46
  • 462

【阅读笔记之十一】《DIRECTX.9.0.3D游戏开发编程基础》:网格(一)闲话ID3DXMesh接口

网格(一)闲话ID3DXMesh接口     关于网格部分,因为非常重要,故分为几个部分来学习。网格(一)主要讲解一下网格之中使用最多的接口ID3DXMesh。在这部分关于网格的学习完成之后,最后...
  • chenwu_843402773
  • chenwu_843402773
  • 2012-11-30 00:46:28
  • 4043

[WebGL入门]二十,绘制立体模型(圆环体)

这次,并没有对WebGL相关的新技术进行说明,这一点运行一下demo就能知道了,果然,比起绘制简单的板状的多边形来说,像圆环体这样的立体模型绘制起来更有意思吧。 demo中使用的着色器和HTML跟以前...
  • lufy_Legend
  • lufy_Legend
  • 2014-08-15 21:25:38
  • 9477

WebGL之旅(十六) 层级模型

层次模型是指用一颗“有向树”的数据结构来表示表示各类实体以及实体间的联系,树中每一个节点代表一个记录类型,树状结构表示实体型之间的联系。——百度百科 在变换时,父节点的变换会待着子节点一起,而子节点的...
  • xufeng0991
  • xufeng0991
  • 2017-07-30 22:51:00
  • 391

数百个 HTML5 例子学习 HT 图形组件 – WebGL 3D 篇

《数百个 HTML5 例子学习 HT 图形组件 – 拓扑图篇》一文让读者了解了 HT 的 2D 拓扑图组件使用,本文将对 HT 的 3D 功能做个综合性的介绍,以便初学者可快速上手使用 HT 构建例如...
  • u013161495
  • u013161495
  • 2016-08-26 03:01:15
  • 6548

WebGL画一个彩色矩形

画矩形的
  • qq_37338983
  • qq_37338983
  • 2017-06-26 17:21:17
  • 1337

37 WebGL多个模型组成一个复杂的模型

案例查看地址:点击这里<!DOCTYPE html> <html lang="en"> <head&g...
  • qq_30100043
  • qq_30100043
  • 2017-06-10 23:14:33
  • 433
收藏助手
不良信息举报
您举报文章:[WebGL入门]十六,绘制多个模型
举报原因:
原因补充:

(最多只允许输入30个字)