标签:webgl
注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,如果翻译有误,欢迎大家指正。
当然,做一些处理的话,不是2的阶乘的图片数据也是可以用的,但是基本上作为纹理使用的图像数据的大小必须是2的阶乘。
另外,看一下普通的网页就能感觉到,网页上的图片数据的读取是要花一点时间的,在进行纹理转换的话,必须是在图片读取完之后才行,这里需要做一些特殊的处理,如果对javascript不太熟悉的话可能会无从下手,这个后面会说。
那么,下面就开始说一说纹理使用的步骤吧。
纹理在WebGL中要使用纹理对象来处理,生成纹理对象需要使用createTexture函数。
>createTexture的使用例子
var tex = gl.createTexture();这个函数没有参数,只是单纯的返回一个纹理对象,经过上面的代码,变量tex就是一个空的纹理对象了。
大家想一想,之前在WebGL中使用缓存对象的时候也是需要进行和WebGL的绑定处理,比如使用VBO的时候,这次也和缓存一样,要进行绑定处理。要对纹理数据进行操作的时候,首先必须先进行绑定,然后使用操作纹理的一些函数,被绑定的纹理对象才能适用这些处理。
把纹理数据和WebGL进行绑定的函数是bindTexture。
>bindTexture的使用例子
gl.bindTexture(gl.TEXTURE_2D, tex);这个函数需要两个参数。第一个参数是纹理的种类,绘制2D的图像类型的话,通常使用gl.TEXTURE_2D作为参数。第二个参数就是要绑定的纹理对象,这里还是挺简单的吧。
>texImage2D的使用例子
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);看了上面的代码,一定会有”这是什么东东?“的感觉吧,这个函数一共接收六个参数,看起来复杂,其实挺简单的。
刚才也说了,使用texImage2D函数可以把图片数据接分配给纹理,但是网页中的图片的读取是要花一点时间的。
这里需要注意的是,texImage2D函数被调用的时候,必须是图片已经读取完了之后,如果在图片读取完之前调用texImage2D,则无法正确的将图片数据分配给纹理。。
这里需要在图片读取完后的事件中来调用。
具体流程,就是先用javascript来生成图片对象,图片对象有onload事件可以监测到图片读取完成。在这里把纹理相关的处理做完,最后给图片对象指定图片地址开始读取图片。
这里比较重要的一点是,在图片开始读取之前,要先添加onload事件,在事件中添加纹理相关的处理,这样,图片读取完成之后就可以自动进行纹理相关的这些处理了。
上面说的这些处理,写成函数的话,就是下面这样。
>纹理的生成函数
function create_texture(source){ // イメージオブジェクトの生成 var img = new Image(); // データのオンロードをトリガーにする img.onload = function(){ // テクスチャオブジェクトの生成 var tex = gl.createTexture(); // テクスチャをバインドする gl.bindTexture(gl.TEXTURE_2D, tex); // テクスチャへイメージを適用 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); // ミップマップを生成 gl.generateMipmap(gl.TEXTURE_2D); // テクスチャのバインドを無効化 gl.bindTexture(gl.TEXTURE_2D, null); // 生成したテクスチャをグローバル変数に代入 texture = tex; }; // イメージオブジェクトのソースを指定 img.src = source; }这个自定义函数create_texture接收一个图片对象的图片地址作为参数。函数中,首先生成图片对象,在图片数据读取之前,先添加了onload事件,在事件中,有纹理的生成,绑定,以及分配图片数据等处理。在函数中,texImage2D函数执行之后,应该会感觉有点奇怪吧,为了生成纹理映射,使用了generateMipmap函数。
执行了generateMipmap函数之后,就能生成纹理映射了,这个函数的参数和bindTexture一样,也是gl.TEXTURE_2D。
和VBO一样,WebGL中同一时间只能绑定一个纹理,所以最后要解除绑定。在最终生成纹理对象的时候,使用了全局变量,因为onload没有办法返回自己本身。上面的例子,变量texture必须是create_texture函数能够参考到的空间内。
onload的对应结束之后,最后给图片对象指定图片地址,因为已经加了onload事件,图片读取完成之后会自动调用onload事件,执行生成纹理的代码。
好了,已经知道了纹理对象的生成的方法了,那么重要的就是如何把纹理放到多边形中了。
要把纹理放到多边形中,在生成多边形的时候,就需要包含将纹理如何放到多边形中这样的信息。所以需要给顶点添加新的顶点属性。在前面的文章(九,顶点缓存的基础)中已经详细介绍了,向顶点中添加信息需要使用新的VBO。那么这一次新添加的VBO中需要保存顶点的纹理坐标。纹理坐标就是为了表示使用纹理的哪个坐标。纹理坐标使用的范围是0 ~ 1,而且有横竖两个方向。所以,表示纹理坐标的时候类似于(0.0, 0.0)这样,需要两个元素。(lufy:翻译的有点绕嘴,看后面的使用部分就明白了。)
而且,这里有些奇怪的地方,一般,图片数据的坐标系是以左上为原点来考虑的,如下图所示。
从左上角的原点,向右以及向下来对应X和Y的值的大小。
而WebGL中的纹理坐标系则是像下面这样。
坐标系上下颠倒了一下。就是说纹理坐标系中,左下是原点,纵方向上的数值越向上表示越大。但是,对照两个图看一下就知道了,指定纹理坐标的时候也不需要考虑太多。为什么呢?因为图片也是上下翻转的,所以和以左上角为原点的图片一样考虑就行,结果是一致的。
现在的阶段,只需要知道纹理空间上坐标系是上下翻转的就可以了。*以后使用纹理进行一些特殊的处理的时候才需要详细了解相关的知识。
这次没有任何光照效果,渲染的只是带有图像的多边形而已。虽然用圆环体也可以,但是还需要处理一些细节部分,所以还从多边形模型来开始介绍。
首先,准备模型的顶点数据,刚才也说了,为了保存纹理坐标,要添加新的顶点属性,而因为不需要光照效果,所以法线等情报这次也不使用。
>顶点数据的准备
// attributeLocationを配列に取得 var attLocation = new Array(); attLocation[0] = gl.getAttribLocation(prg, ‘position‘); attLocation[1] = gl.getAttribLocation(prg, ‘color‘); attLocation[2] = gl.getAttribLocation(prg, ‘textureCoord‘); // attributeの要素数を配列に格納 var attStride = new Array(); attStride[0] = 3; attStride[1] = 4; attStride[2] = 2; // 頂点の位置 var position = [ -1.0, 1.0, 0.0, 1.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ]; // 頂点色 var color = [ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 ]; // テクスチャ座標 var textureCoord = [ 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0 ]; // 頂点インデックス var index = [ 0, 1, 2, 3, 2, 1 ]; // VBOとIBOの生成 var vPosition = create_vbo(position); var vColor = create_vbo(color); var vTextureCoord = create_vbo(textureCoord); var VBOList = [vPosition, vColor, vTextureCoord]; var iIndex = create_ibo(index); // VBOとIBOの登録 set_attribute(VBOList, attLocation, attStride); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iIndex);定义了一个四个顶点的四边形。详细看一下保存顶点的位置的部分的话就明白了,是以四边形的中心为原点,顶点顺序和写字母Z的顺序一样,顶点颜色定义成了不透明的白色。
顶点数据是用一个数组来表示的,和之前一样生成VBO和IBO。这和之前所作的是完全一样的。这样,顶点相关的处理,也就是说着色器内用attribute变量处理的数据的准备都已经ok了。
这次因为不进行光照处理,所以准备好坐标变换矩阵就可以处理顶点了。逆矩阵和光源的位置等都是不需要的。
但是考虑一下uniform修饰符的变量的话,需要增加一个,uniform修饰符定义的变量是指全部顶点都进行一致处理的数据,所以,作为所有顶点都同样被使用的纹理数据,就必须使用uniform变量来传递数据了。
>uniform相关处理
// uniformLocationを配列に取得 var uniLocation = new Array(); uniLocation[0] = gl.getUniformLocation(prg, ‘mvpMatrix‘); uniLocation[1] = gl.getUniformLocation(prg, ‘texture‘);这次使用的uniform变量有两个,一个是为了处理坐标变换矩阵,另一个是为了提交纹理数据。
要将特定的纹理单位设置为有效使用的是activeTexture函数。
>纹理单位有效化
// 有効にするテクスチャユニットを指定 gl.activeTexture(gl.TEXTURE0);这里作为参数使用的gl.TEXTURE0常量,后面的0就是纹理单位的编号,如果将编号1的纹理设置为有效的话,就需要是gl.TEXTURE1。但是,没有什么特殊的理由的话,使用纹理单位应该按照从小到大的顺序来使用。
>>纹理单位的最大值(上限值) |
多个纹理同时使用的时候,纹理单位是必须用到的,这个最大的单位数是由运行环境决定的。因为运行WebGL的除了电脑,还有手机等,所以纹理单位能使用到多少个判断起来是非常费劲的。 |
因为是受到硬件的性能的制约,所以使用之前先判断一下,然后进行分别处理是可行的。查询执行环境的可使用最大纹理单位数使用getParameter函数。 |
下面是例子 |
gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); |
向getParameter函数中传入这个非常长的常量gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS就可以得到一个整数值,表示可以使用的最大文理单位数,如果返回值是10的话,那么可以使用的纹理单位就是gl.TEXTURE0 ~ gl.TEXTURE9。 |
将相应的纹理单位设置为有效之后,接着就需要将纹理和WebGL进行绑定处理了,这个在将图片数据转换成纹理数据的时候也做过了,所以简单如下。
>纹理和WebGL绑定
// テクスチャをバインドする gl.bindTexture(gl.TEXTURE_2D, texture);绑定了纹理情报要传送给着色器,所以需要用uniform变量来处理,这里还使用之前的uniformLocation,处理如下。
>将纹理数据传给着色器
// uniform変数にテクスチャを登録 gl.uniform1i(uniLocation[1], 0);这里需要注意的是,和向着色器中传入矩阵和向量不同,因为需要传入纹理单位的编号。uniform1i函数是向着色器中传入一个整数的时候使用的。第二个参数就是要向着色器中传入的整数0。也就是说,这里传入的整数是和之前有效化的纹理单位相一致的。
接着,是着色器的修改了,首先是顶点着色器。
>顶点着色器代码
attribute vec3 position; attribute vec4 color; attribute vec2 textureCoord; uniform mat4 mvpMatrix; varying vec4 vColor; varying vec2 vTextureCoord; void main(void){ vColor = color; vTextureCoord = textureCoord; gl_Position = mvpMatrix * vec4(position, 1.0); }顶点着色器中,顶点的位置,顶点的颜色,还有顶点的纹理坐标都是用attribute修饰符定义的。顶点的颜色和纹理坐标没有做任何处理,都是直接传给片段着色器的。
>片段着色器代码
precision mediump float; uniform sampler2D texture; varying vec4 vColor; varying vec2 vTextureCoord; void main(void){ vec4 smpColor = texture2D(texture, vTextureCoord); gl_FragColor = vColor * smpColor; }片段着色器使用uniform修饰符来接收纹理数据。注意这个sampler2D的变量类型,就是采样的意思,先把它当作纹理数据考虑就行了。
这次的例子,顶点着色器中用attribute定义的顶点的纹理坐标,在片段着色器中用varying变量来接收,然后在片段着色器一侧将这个纹理坐标,传入到texture2D函数中。
这样,用texture2D函数获取到纹理的颜色信息之后,再和顶点颜色(varying变量vColor)相乘,就得到最终的颜色了。
纹理的使用方法,用了双倍的时间来讲解了,应该理解了吧。
纹理周边的处理是非常冗长的,但是主要的就是纹理坐标,纹理对象,以及为了处理这些数据的着色器的对应,纹理中单位这个概念,使用合理的操作可以处理多个纹理等。虽然这次只是最基本的部分的封装,但是变更点非常的多,所以最后会贴出全部代码(lufy:我就不贴了,大家直接用浏览器查看就行了。),另外,最后给出了运行demo。
下次,介绍一下多个纹理的使用。四边形中使用纹理的demo
转载请注明:转自lufy_legend的博客http://blog.csdn.net/lufy_legend
标签:webgl
原文地址:http://blog.csdn.net/lufy_legend/article/details/40213293