WebGL 多重纹理的使用介绍
作者:H_World
引言
基于上篇的内容,纹理中最小的单位是纹素,若干纹素组成一个纹理单元,每个纹理单元用一个number值来管理,那么我们来看看它是如何工作的。
激活
在使用纹理单元之前,得需要激活纹理单元
activeTexture()
参数:
- 指定准备激活的纹理单元:webgl.TEXTURE0、webgl.TEXTURE1...最后的数字表示纹理单元的编号
绑定
这一步,需要告诉WebGL系统纹理对象使用的是何种类型的纹理,在对该对象的操作之前,需要先绑定该对象,这和之前的缓冲区对象的数据写操作类似。
bindTexture(webgl.TEXTURE_2D, texture0)
参数:
- webgl.TEXTURE_2D: 二维纹理;webgl.TEXTURE_CUBE_MAP: 立方体纹理
- texture0:需要绑定指定的纹理对象
该方法开启纹理对象,以及将纹理对象texture0绑定到纹理单元webgl.TEXTURE0上,之后通过操作纹理单元去操作纹理对象。
配置
这儿主要是对纹理对象具体的信息操作了,例如纹理单元类型、尺寸、是否裁剪、纹理的展示方式(放大或缩小)等,下面我们再一一分析。
texParameteri(target, pname, param)
参数:
- target: webgl.TEXTURE_2D或webgl.TEXTURE_CUBE_MAP
- pname: webgl.TEXTURE_MAG_FILTER纹理放大,例如16x16的纹理图像映射到32x32的像素空间中;webgl.TEXTURE_MIN_FILTER纹理缩小, 应用场景相反;webgl.TEXTURE_WRAP_S纹理水平轴(ST坐标系统)方向填充;webgl.TEXTURE_WRAP_T纹理T轴方向填充。
- param: webgl.LINEAR线性变换,使用距离像素中心最近的四个点,进行线性平均加权,然后作为新的颜色值; webgl.NEAREST最近的距离优先,新的像素值取中心像素的值;CLAMP_TO_EDGE: 设置纹理 S/T 轴方向上的边缘环绕方式为CLAMP_TO_EDGE,即在超出边缘的部分使用纹理边缘的像素颜色填充. webgl.REPEAT纹理重复补充;webgl.MIRRORED_REPEAT:纹理镜像对称式重复
会发现参数pname、param中的参数常量分类比较多,对应的分类是
- pname : TEXTURE_MAG_FILTER、TEXTURE_MIN_FILTER
- param: NEAREST、LINEAR
和
- pname : TEXTURE_WRAP_S、TEXTURE_WRAP_T
- param: REPEAT、MIRRORED_REPEAT、CLAMP_TO_EDGE
分配
纹理对象信息配置好后,接着可以将纹理图像分配给纹理对象
webgl.texImage2D(target, level, internalformat, format type, image)
参数:
- target: webgl.TEXTURE_2D或webgl.TEXTURE_CUBE_MAP
- level: 指定纹理缓冲区中要加载的纹理级别,一般为 0
- internalformat: 纹理对象内部数据格式,通常使用webgl.RGBA
- format: 纹理数据格式,必须与internalformat相同的值
- type: 纹理数据类型
- image: 纹理对象中image对象
其中的纹理数据类型有:
webgl.UNSIGNED_BYTE: 无符号整型,每个颜色分量占据1字节 webgl.UNSIGNED_SHORT_5_6_5:RGB webgl.UNSIGNED_SHORT_4_4_4:RGBA webgl.UNSIGNED_SHORT_5_5_5_1:RGBA
传递
经过上面一系列准备后,我们就可以把最终的纹理单元传递给片元着色器了,这儿就用到了方法 webgl.uniform1i(),上篇这个方法已经说过了,就不再说明了。
主要看看片元着色器中是怎么做的
uniform sampler2D texture; varying vec2 inUV; vec4 color1=texture2D(texture, vec2(inUV.x, 1.0 - inUV.y)); gl_FragColor=color1;
- 定义了一个可以在外部访问的一种特殊的、专用于纹理对象的数据类型sampler2D,就是对应的纹理单元类型webgl.TEXTURE_2D;如果是webgl.TEXTURE_CUBE_MAP呢,则是samplerCube.
- 通过uniform1i方法,将纹理对象传递到了片元着色器中的texture变量
- inUV接收顶点着色器中的顶点的纹理坐标,当然这里的纹理坐标是在外部设置的
- texture2D()在片元着色器中根据纹理坐标获取纹理图像上的纹素的颜色
- 把获取到的颜色赋值给片元着色器
经过对纹理的加载、设置、映射,剩下的就是绘画了。
webgl.drawArrays(webgl.TRIANGLE_STRIP, 0, 4)
好了,晦涩难懂的概念介绍完了,就把具体的代码贴出来吧!
效果
代码
着色器设置
const VSHADER_SOURCE = ` attribute vec2 a_position; attribute vec2 outUV; varying vec2 inUV; void main(void){ gl_Position=vec4(a_position, 0.0, 1.0); inUV=outUV; } ` const FSHADER_SOURCE = ` precision mediump float; uniform sampler2D texture; uniform sampler2D texture1; varying vec2 inUV; uniform float anim; void main(void){ vec4 color1=texture2D(texture, vec2(inUV.x, 1.0 - inUV.y)); vec4 color2=texture2D(texture1, vec2(inUV.x + anim, 1.0 - inUV.y)); gl_FragColor=color1+color2; } `
数据缓存区的设置
const initBuffer = async () => { let positions = [ 0.8, -0.8, -0.8, -0.8, 0.8, 0.8, -0.8, 0.8, ] let pointPosition = new Float32Array(positions) let aPsotion = webgl.getAttribLocation(webgl.program, "a_position") let triangleBuffer = webgl.createBuffer() webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer) webgl.bufferData(webgl.ARRAY_BUFFER, pointPosition, webgl.STATIC_DRAW) webgl.enableVertexAttribArray(aPsotion) webgl.vertexAttribPointer(aPsotion, 2, webgl.FLOAT, false, 0, 0) var texCoords = [ 1, 0, 0, 0, 1, 1, 0, 1, ] const attribOutUV = webgl.getAttribLocation(webgl.program, "outUV") let texCoordBuffer = webgl.createBuffer() webgl.bindBuffer(webgl.ARRAY_BUFFER, texCoordBuffer) webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(texCoords), webgl.STATIC_DRAW) webgl.enableVertexAttribArray(attribOutUV) webgl.vertexAttribPointer(attribOutUV, 2, webgl.FLOAT, false, 0, 0) uniformTexture = webgl.getUniformLocation(webgl.program, "texture") uniformTexture1 = webgl.getUniformLocation(webgl.program, "texture1") texture0 = await initTexture('/web-gl/landscape.png') texture1 = await initTexture("/web-gl/fog.png") }
主要是设置顶点坐标和纹理坐标的数据
纹理对象的初始化
const initTexture = (imageSrc: string): Promise<WebGLTexture> => { return new Promise((resolve, reject) => { let textureHandle = webgl.createTexture() const image = new Image() image.onload = function () { webgl.bindTexture(webgl.TEXTURE_2D, textureHandle) webgl.texParameteri( webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.CLAMP_TO_EDGE ) webgl.texParameteri( webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.CLAMP_TO_EDGE ) webgl.texParameteri( webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.LINEAR ) webgl.texImage2D( webgl.TEXTURE_2D, 0, webgl.RGBA, webgl.RGBA, webgl.UNSIGNED_BYTE, image ) resolve(textureHandle) } image.onerror = reject image.src = imageSrc }) }
这儿需要使用异步方法,因为需要在image.onload方法中返回设置信息数据后的纹理对象。 在 webgl 的纹理加载中,由于图片是异步加载的,因此我们需要使用 Promise 来处理加载完毕后的回调函数。
绘制和动态变化
const updateCount = () => { count = count + 0.001 if (count >= 1.14) { count = -1.0 } } const draw = () => { webgl.clearColor(0.0, 1.0, 1.0, 1.0) webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT) webgl.enable(webgl.DEPTH_TEST) //纹理变动 uniformAnim = webgl.getUniformLocation(webgl.program, "anim"); updateCount() webgl.uniform1f(uniformAnim, count); webgl.activeTexture(webgl.TEXTURE0) webgl.bindTexture(webgl.TEXTURE_2D, texture0) webgl.uniform1i(uniformTexture, 0) webgl.activeTexture(webgl.TEXTURE1) webgl.bindTexture(webgl.TEXTURE_2D, texture1) webgl.uniform1i(uniformTexture1, 1) webgl.drawArrays(webgl.TRIANGLE_STRIP, 0, 4) requestAnimationFrame(draw) }
具体的代码就是这些了。
小结一下
到目前为止,我们已经了解了顶点着色器、片元着色器、顶点缓冲区、矩阵绘制和变换、纹理图像、纹理叠加等,这样我们已经基本上对二维绘图掌握了,一个面我们了解了,之后我们可能就去看看多个面的物体和场景。
那么就让我们一起看看三维世界吧!
以上就是WebGL 多重纹理的详细内容,更多关于WebGL 多重纹理的资料请关注脚本之家其它相关文章!