载入2D纹理
WebGL中最常见的纹理格式就是2D纹理,2D纹理的最基本形式就是使用单幅图像作为纹理。为了把2D纹理应用于几何对象,首先需要载入纹理。就是将普通图像文件的纹理载入到纹理对象中,然后将这幅图像作为2D纹理的输入数据。图像文件可以是PNG、JPEG或GIF格式。
创建WebGLTexture对象
在WebGL中使用纹理的第一个步骤是为每个纹理创建一个WebGLTexture对象。创建纹理对象要使用下面的代码:
var texture = gl.createTexture()
WebGLTexture是个容器对象,它可以作为纹理的引用,通过它访问与此纹理有关的处理参数。
还有个方法可以显式地删除一个WebGLTexture对象。例如,如果要删除一个名为Texture的纹理对象,则可以使用以下的代码:
gl.deleteTexture(texture);
注意,当结束使用纹理时,并不需要调用gl.deleteTexture()方法。JavaScript垃圾收集在销毁WebGLTexture对象时会自动删除相应的纹理对象。这个方法只是给用户提供更灵活的控制权,控制何时销毁纹理对象。
绑定纹理
对新创建的纹理对象做任何操作之前,首先需要把它绑定为当前纹理对象。例如为了把一个名为texture的纹理对象绑定为一个2D纹理对象,要用下面的代码:
gl.bindTexture(gl.TEXTURE_2D, texture);
调用gl.bindTexture()可告诉WebGL,这就是从现在起需要操作的纹理对象。这个方法与WebGLTexture纹理对象的关系就如同gl.bindBuffer()方法WebGLBuffer缓冲对象的关系。
载入图像数据
绑定了纹理对象后,我们就可以把图像数据载入纹理对象中,即把纹理数据传到GPU(或GPU可以访问的内存)。把纹理数据上传给GPU要使用gl.texlmage2D()方法,我们等会详细介绍,这个方法可以接受各种不同格式的纹理数据,但是当纹理是普通图像文件(PNG、GIF或JPEG)时,则它通常可以接受一个HTML DOM类型Image对象。因此,在调用gl.texlmage2D()方法之前,需要把数据保存在一个image对象中。
一个Image对象是由HTML文档中的标记显式创建得到的元素。但是调用下面的函数也可以显创建一个image对象:
image = new Image();
新建的Image对象是一个空对象,还没有载入任何图像数据。为了把图像数据载入image对象中,需要把Image对象的src属性设置为需要载入图像的URL。只要把图像的URL赋给这个src属性,系统就会按异步方式载入这个图像。
为了知道图像何时载入结束,可以使用onload事件。当图像载入结束时会立刻引发这个事件。下面这个代码创建一个Image对象,并载入图像数据:
function setupTexture() {
myTexture = gl.createTexture();
myTexture.image = new Image();
myTexture.image.onload = function () {
textureFinishedLoading(myTexture)
}
myTexture.image.src = "webgl.gif";
}
载入图像且onload事件触发匿名函数时,调用textureFinishedLoading()函数把处理过程转入下个步骤。在下个步骤中把图像数据上传给GPU。
在用JavaScript代码把图像数据载入一个Image对象时,一个常用的方法是把onload事件处理程序赋给Image对象。当我们想把一个Image对象作为WebGL中某个纹理的输入数据时,就是使用这种方法。
注意:纹理大小必须是2的n次方。在学习如何载入图像数据时,必须知道图像可接受的大小。开发人员经常选择宽度和高度都是2的n次方的图像(即图像的宽度和高度为1、2、4、8、16、32、64、128等值)。采用这种方法的理由之一是老式的GPU只支持纹理的宽度和高度都是2的n次方。桌面OpenGL 2.0及之后的版本实际上可以支持非2的n次方(NPOT)的纹理。在OpenGL ES2.0和WebGL中,允许使用NPOT纹理,但是存在以下限制:
- 如果使用NPOT纹理,则不能使用Mip映射贴图。
- 唯一允许使用的纹理包装模式是gl.CLAMP_TO_EDGE。
将纹理上传到GPU
为了把纹理上传到GPU,需要调用gl.texImage2D()方法。这个方法有多个不同的版本,
它们各有不同的参数,具体用法取决于用作纹理的数据类型。主要的3个原型如下所示:
void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, HTMLImageElement image) /* May throw DOMException */
void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, HTMLCanvasElement canvas) /* May throw DOMException */
void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, HTMLVideoElement video) /* May throw DOMException */
对于第一个版本,纹理数据是一个HTML DOM类型的image对象。第二个版本接受一个HTML5画布元素作为纹理的输入数据。最后一个版本接受一个视频元素作为纹理的输入数据。
接受HTML Image对象的版本可能会是我们最经常使用的方法。为了将Image对象上传到GPU,要像下面的代码那样调用gl.texImage2D()方法:
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
第1个参数表示目标是一个2D纹理,第2个参数指定了Mip映射级别,这里必须把这个参数设置为0。
第3个参数表示内部格式,第4个参数表示格式,在WebGL中,这两个参数必须相同。在本例中,用gl.RGBA表示此纹理的每个纹素都有红、绿、蓝和alpha通道