HTML5中国

 找回密码
 立即注册

QQ登录

只需一步,快速开始

HTML5中国 首页 教程视频 实例代码 查看内容

JavaScript Canvas——“WebGL”的注意要点

2016-8-31 10:14| 发布者: Hyukoh| 查看: 415| 评论: 0|原作者: SEGMENTFAULT|来自: 好JSER

摘要: ArrayBuffer的类型。每个ArrayBuffer对象表示的只是内存中指定的字节数,但不会指定这些字节用于保存什么类型的数据。通过ArrayBuffer能做的,就是为了将来使用而分配一定数量的字节。 ...

  WebGL是针对Canvas的3D上下文;OpenGL等是3D图形语言


  类型化数组


  类型化数组也是数组,只不过其元素被设置为特定类型的值。

  数组缓冲器 ArrayBuffer 类型和 byteLength 属性

  类型化数组的核心就是一个名为

  ArrayBuffer 的类型。每个ArrayBuffer对象表示的只是内存中指定的字节数,但不会指定这些字节用于保存什么类型的数据。通过ArrayBuffer能做的,就是为了将来使用而分配一定数量的字节。

  如:

var buffer = new ArrayBuffer(20); //在内存中分配20B

  属性

  byteLength 返回它包含的字节数

  如:

var buffer = new ArrayBuffer(20);
console.log(buffer.byteLength); //20


  数组缓冲器视图


  DataView 数组缓冲器视图

  使用ArrayBuffer(数组缓冲器类型)的一种特别的方式就是用它来创建数组缓冲器视图。其中,最常见的视图是

  •   DataView ,通过它可以选择ArrayBuffer中的一小段字节。为此,可在创建DataView实例的时候传入一个ArrayBuffer、一个可选的字节偏移量(从该字节开始选择)和一个可选的要选择的字节数。

  如:

var view = new DataView(buffer); //新的视图
var view = new DataView(buffer, 6); //开始于字节6的新视图
var view = new DataView(buffer, 6, 9); //开始于字节6,结束于字节9的新视图

  DataView 的属性 byteOffset 和 byteLength

  DataView对象会把字节偏移量以及字符长度信息保存在

  •   byteOffset
  •   byteLength

  两个属性中:

var view = new DataView(buffer, 6, 9); //开始于字节6,结束于字节9的新视图
console.log(view.byteOffset); //6 字节偏移量为6
console.log(view.byteLength); //9 字节长度为9

  buffer属性也可以取得数组缓冲器;


  getter 和 setter 读写方法


  读取和写入DataView的时候,要根据实际操作的数据类型,选择相应的

  •   getter
  •   setter

  如下,列出了DataView支持的数据类型以及相应的读写方法:

  getter:

  •   getInt8(byteOffset) 方法: 在相对于视图开始处的指定字节偏移量位置处获取 Int8 值。
  •   getUint8(byteOffset) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处获取 Uint8 值。
  •   getInt16(byteOffset,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处获取 Int16 值。
  •   getUint16(byteOffset,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处获取 Uint16 值。
  •   getInt32(byteOffset,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处获取 Int32 值。
  •   getUint32(byteOffset,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处获取 Uint32 值。
  •   getFloat32(byteOffset,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处获取 Float32 值。
  •   getFloat64(byteOffset,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处获取 Float64 值。

  setter:

  •   setInt8(byteOffset,value) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处存储 Int8 值。
  •   setUint8(byteOffset,value) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处存储 Uint8 值。
  •   setInt16(byteOffset,value,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处存储 Int16 值。
  •   setUint16(byteOffset,value,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处存储 Uint16 值。
  •   setInt32(byteOffset,value,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处存储 Int32 值。
  •   setUint32(byteOffset,value,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处存储 Uint32 值。
  •   setFloat32(byteOffset,value,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处存储 Float32 值。
  •   setFloat64(byteOffset,value,littleEndian) 方法 (DataView): 在相对于视图开始处的指定字节偏移量位置处存储 Float64 值。

  如:

var buffer = new ArrayBuffer(20);
var view = new DataView(buffer);
view.setUint16(0,25); //0000000000001001
var value = view.getUint8(0); //00000000
console.log(value); //0

  类型化视图在读写数组缓冲器中更加便利:


  类型化视图(类型化数组)


  类型化视图一般也被称为类型化数组,因为它们除了元素必须是某种特定的数据类型外,与常规的数组无异。类型化视图也分几种,而且它们都继承了DataView。

  •   Int8Array :表示8为二补整数。
  •   Uint8Array :表示8位无符号整数。
  •   Int16Array :表示16位二补整数。
  •   Uint16Array :表示16位无符号整数。
  •   Int32Array :表示32为二补整数。
  •   Uint32Array :表示32位无符号整数。
  •   Float32Array :表示32位IEEE浮点值。
  •   Float64Array :表示64位IEEE浮点值。

  需要三个参数,只有第一个是必须的:ArrayBuffer对象、字节偏移量、要包含的字节数,如:

var buffer = new ArrayBuffer(20);
var int8s = new Int8Array(buffer);

  注意:20B的ArrayBuffer可以保存20个Int8Array或Uint8Array,或者10个Int16Array或Uint16Array,或者5个Int32Array或Uint32Array或Float32Array,或者2个Float64Array。

var buffer = new ArrayBuffer(20);
var int8s = new Int8Array(buffer); //创建一个新数组,使用整个缓冲器
var int16s = new Int16Array(buffer, 9); //只使用从字节9开始的缓冲器
var uint16s = new Uint16Array(buffer, 9, 10); //只使用从字节9到字节10的缓冲器

  能够指定缓冲器中可用的字节段,意味着能在同一个缓冲器中保存不同类型的数值,如下面的代码就是在缓冲器的开头保存8位整数,而在其他字节中保存16位整数:

var buffer = new ArrayBuffer(30); //缓冲器中有30个字节
var int8s = new Int8Array(buffer, 0, 10); //前面10个字节存储10个8位整数
var int16s = new Int16Array(buffer, 10, 10); //后面还有20个字节,2个字节存储1个16位整数,所以只能存储10个

  另外,每个视图构造函数都有一个名为

  BYTES_PER_ELEMENT

  表示类型化数组的每个元素需要多少字节:

console.log(Float64Array.BYTES_PER_ELEMENT) //8

  这样就可以利用这个属性来辅助初始化:

var buffer = new ArrayBuffer(20);
var int8s = new Int8Array(buffer, 0, 10 * Int8Array.BYTES_PER_ELEMENT);
var int16s = new Int16Array(buffer, int8s.byteOffset + int8s.byteLength, (10 / Int16Array.BYTES_PER_ELEMENT));

  另外,还可以不用首先创建ArrayBuffer对象,只要传入希望数组保存的元素数,相应的构造函数就可以自动创建一个包含足够字节数的ArrayBuffer对象:

var int16s = new Int16Array(10); //创建一个数组保存10个16位整数(10字节)
var int32s = new Int32Array(1); //创建一个数组保存1个32位整数(4字节)

  另外还可以把常规数组转换为类型化视图:

var int8s = new Int8Array([1,2,3,4]);
var view = new DataView(int8s.buffer);
console.log(int8s.toString()); //1234
console.log(view.byteLength); //4

  对类型化视图的迭代:

for (var i = 0; i < int8s.length; i++) {
    console.log(int8s[i]);
};

  也可以使用方括号语法为类型化视图的元素赋值:

var uint16s = new Uint16Array(10);
uint16s[0] = 65537;
console.log(uint16s[0]); //1

  另外可以通过

  subarray() 方法基于底层数组缓冲器的子集创建一个新视图,接收两个参数:开始元素的索引,可选的结束元素的索引:

  如:

var uint16s = new Uint16Array(10),
    sub = uint16s.subarray(2, 5);


  WebGL上下文


  目前,在支持的浏览器中,WebGL的名字叫做“experimental-webgl”,这是因为WebGL规范仍然未制定完成。制定完成后,这个上下文的名字就会变成简单的“webgl”。如果浏览器不支持WebGL,那么取得该上下文时会返回null。


var drawing = document.getElementById("drawing");
if (drawing.getContext) {
    var gl = drawing.getContext("experimental-webgl");
    if (gl) {
        //[...]
    }
}


  通过给getContext()传递第二个参数,可以为WebGL上下文设置一些选项。这个参数本身是一个对象,可以包含下列属性:

* `alpha`:值为true,表示为上下文创建一个Alpha通道缓冲区;默认值为true;
* `depth`:值为true,表示可以使用16位深缓冲区;默认值为true;
* `stencil`:值为true,表示可以使用8位模板缓冲区;默认值为false;
* `antialias`:值为true,表示将使用默认机制执行抗锯齿操作;默认值为true。
* `premultipliedAlpha`:值为true,表示绘图缓冲区有预乘Alpha值;默认为true;
* `preserveDrawingBuffer`:值为true;表示在绘图完成后保留绘图缓冲区;默认值为false。

  传递这个选项对象的方式如下:

var drawing = document.getElementById("drawing");
if (drawing.getContext) {
    var gl = drawing.getContext("experimental-webgl", {
        alpha: false
    });
    if (gl) {
        //[...]
    }
}

  大多数情况下不用开启,因为可能影响到性能,而且默认值一般都能满足我们需求。

  如果getContext()无法创建WebGL上下文,浏览器可能会报错。所以应该把它封装到try-catch块中:

var drawing = document.getElementById("drawing");
if (drawing.getContext) {
    try {
        var gl = drawing.getContext("experimental-webgl");
    } catch (e) {}
    if (gl) {
        //[...]
    }
}


  常量


  在WebGL中,保存在上下文对象中的这些常量都没有GL_前缀。


  方法命名


  方法名的后缀会包含参数个数(1到4),和接收的数据类型(f为浮点数,i为整数),如:gl.uniform4f()意味着要接收4个浮点数;另外还有很多方法接收数组参数而非一个个单独的参数,这样的方法中名字包含字母v,如:gl.uniform3iv()可以接收一个包含3个值的整数数组。


  准备绘图


  在实际操作WebGL上下文之前,一般都要使用某种实色清除canvas元素,为绘图做好准备。为此,首先必须使用:

  clearColor() 方法来指定要使用的颜色值,这个方法接收4个参数:红、绿、蓝和透明度。每个参数必须是一个0到1之间的数值,表示每种分量在最终颜色中的强度。

  如:

var drawing = document.getElementById("drawing");
if (drawing.getContext) {
    try {
        var gl = drawing.getContext("experimental-webgl");
    } catch (e) {}
    if (gl) {
        gl.clearColor(0,0,0,1); //把清理缓冲区的值设置为黑色
        gl.clear(gl.COLOR_BUFFER_BIT); //调用clear方法,传入参数gl.COLOR_BUFFER_BIT告诉WebGL使用之前定义的颜色来填充相应区域。
    }
}

  视口与坐标


  开始绘图之前,通常要先定义WebGL的视口(viewport)。默认情况下,视口可以使用整个canavs区域。要改变视口大小,可以调用

  viewport() 方法并传入4个参数:(视口相对于canvas元素的)x、y坐标、宽度和高度。

  视口坐标的原点(0,0)在canvas元素的左下角,x轴和y轴的正方向分别是向右和向上,可以定义为(width-1,height-1)。

  如:

var drawing = document.getElementById("drawing");
if (drawing.getContext) {
    try {
        var gl = drawing.getContext("experimental-webgl");
    } catch (e) {}
    if (gl) {
        gl.clearColor(0, 0, 0, 1);
        gl.clear(gl.COLOR_BUFFER_BIT);
        // gl.viewport(0, 0, drawing.width / 2, drawing.height / 2); //视口在画布的左下角四分之一区域
        gl.viewport(drawing.width / 2, 0, drawing.width / 2, drawing.height / 2); //视口在画布的右下角四分之一区域
    }
}

  视口内部的坐标系与定义视口的坐标系也不一样。在视口内部,坐标原点(0,0)是视口的中心点,因此视口左下角坐标为(-1,-1),而右上角坐标为(1,1)。


  缓冲区


  顶点信息保存在JavaScript的类型化数组中,使用之前必须转换到WebGL的缓冲区。要创建缓冲区,可以调用

  •   gl.createBuffer() ,然后使用
  •   gl.bindBuffer() 绑定到WebGL上下文。这两步做完以后,就可以用数据来填充缓冲区了。

  如:

var drawing = document.getElementById("drawing");
    if (drawing.getContext) {
        try {
            var gl = drawing.getContext("experimental-webgl");
        } catch (e) {}
        if (gl) {
            gl.clearColor(0, 0, 0, 1);
            gl.clear(gl.COLOR_BUFFER_BIT);
            gl.viewport(drawing.width / 2, 0, drawing.width / 2, drawing.height / 2);
            var buffer = gl.createBuffer(); //创建缓冲区
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer); //绑定到上下文
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0.5, 1]), gl.STATIC_DRAW); //使用Float32Array中的数据初始化buffer
        }
    }


  •   gl.bufferData()

  最后一个参数主要有:

  •   gl.STATIC_DRAW :数据只加载一次,在多次绘图中使用;
  •   gl.STREAM_DRAW :数据只加载一次,在几次绘图中使用;
  •   gl.DYNAMIC_DRAW :数据动态改变,在多次绘图中使用;

  一般来说 gl.STATIC_DRAW 够用了;

  在包含缓冲区的页面重载之前,缓冲区始终保留在内存中。如果你不想要某个缓冲区了,可以直接调用

  •   gl.deleteBuffer() 释放内存。

  错误


  JavaScript与WebGL之间的一个最大区别在于,WebGL操作一般不会抛出错误。为了知道是否有错误发生,必须在调用某个可能出错的方法后,手工调用

  •   gl.getError() 方法。这个方法返回一个表示错误类型的常量。

  可能的错误常量如下:

  •   gl.NO_ERROR :上一次操作没有发生错误(值为0)。
  •   gl.INVALID_ENUM :应该给方法传入WebGL常量,但却传错了参数。
  •   gl.INVALID_VALUE :在需要无符号数的地方传入了负值。
  •   gl.INVALID_OPERATION :在当前状态下不能完成操作。
  •   gl.OUT_OF_MEMORY :没有足够的内存完成操作。
  •   gl.CONTEXT_LOST_WEBGL :由于外部事件(如设备断电)干扰丢失了当前WebGL的上下文。

  如果发生了多个错误,需要反复调用gl.getError()直到返回gl.NO_ERROR:

var errorCode = gl.getError();
while (errorCode) {
    console.log(errorCode);
    errorCode = gl.getError();
}

  着色器


  着色器(shader)是OpenGL 中的另一个概念。WebGL中有两种着色器:定点着色器和片段(或像素)着色器。顶点着色器用于将3D顶点转换为需要渲染的2D点。片段着色器用于准确计算要绘制的每个像素的颜色。WebGL的着色器是使用GLSL(OpenGL Shading Language,OpenGL着色器)写的,GLSL是一种与C和JavaScript完全不同的语言。


  编写着色器


  GLSL是一种类C语言,专门用于编写OpenGL着色器。因为WebGL是OpenGL ES 2.0的实现,所以OpenGL中使用的着色器可以直接在WebGL中使用。

  每个着色器都有一个

  •   main() 方法,该方法在绘图期间会重复执行。

  为着色器传递数据的方式有两种:

  •   Attribute 和 Uniform 。通过Attribute可以向顶点着色器传入顶点信息,通过Uniform可以向任何着色器传入常量值。

  Attribute和Uniform在main()方法外部定义,分别使用关键字attribute和uniform。

  如Attribute顶点着色器:

void main() {
    gl_Position = vec4(aVertexPosition, 0.0, 1.0);
}

  又如Uniform片段着色器:

void main() {
    gl_FragColor = uColor;
}

  编写着色器程序


  浏览器不能理解GLSL程序,因此必须准备好字符串形式的GLSL程序,以便编译并链接到着色器程序。


  为着色器传入值


  前面定义的着色器必须接收一个值才能工作。为了给着色器传入这个值,必须先找到要接收这个值的变量。


  调试着色器和程序


  与着色器的其他操作一样,着色器操作也可能会失败,而且也是静默失败。如果你想找到着色器或程序执行中是否发生了错误,必须亲自询问WebGL上下文。


  绘图


  WebGL只能绘制三种形状:点、线和三角。其他所有形状都是由这三种基本形状合成之后,再绘制到三维空间中的。执行绘图操作要调用gl.drawArrays()或gl.drawElements()方法,前者用于数组缓冲区,后置用于元素数组缓冲区。


  纹理


  WebGL的纹理可以使用DOM中的图像。要创建一个新纹理,可以调用gl.createTexture(),然后再将一副图像绑定到该纹理。如果图像尚未加载到内存中,可能需要创建一个Image对象的实例,以便动态加载图像。图像加载完成之前,纹理不会初始化,因此,必须在load事件触发后才能设置纹理。


  读取像素


  与2D上下文类似,通过WebGL上下文也能读取像素值。读取像素值的方法readPixels()与OpenGL中的同名方法只有一点不同,即最后一个参数必须是类型化数组。像素信息是从帧缓冲区读取的,然后保存在类型化数组中。readPixels()方法的参数有:x、y、宽度、高度、图像格式、数据类型和类型化数组。前4个参数指定读取哪个区域中的像素。图像格式参数几乎总是gl.RGBA。数据类型用于指定保存在类型化数组中的数据类型,但有以下限制。

  •   如果类型是gl.UNSIGNED_BYTE,则类型化数组必须是Unit8Array。
  •   如果类型是gl.UNSIGNED_SHORT_5_6_5、gl.UNSIGNED_SHORT_4_4_4_4、或gl.UNSIGNED_SHORT_5_5_5_1,则类型化数组必须是Unit16Array。


  Firefox4+和Chrome都实现了WebGL API。Safari5.1也实现了WebGL,但默认是禁用的。


原文链接:http://hao.jser.com/archive/9334/?utm_source=tuicool&utm_medium=referral

来源作者:SEGMENTFAULT

更多

鲜花

握手

雷人

路过

鸡蛋

相关阅读

最新评论

HTML5中国微信

小黑屋|关于我们|HTML5论坛|友情链接|手机版|HTML5中国 ( 京ICP备11006447号 京公网安备:11010802018489号  

GMT+8, 2017-1-20 19:59

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

返回顶部