八、WebGL入门,纹理贴图

本节我们来讲WebGL里面很重要的纹理

纹理贴图在游戏开发中十分常见,但是纹理不单单只是一张贴图那么简单。

在WebGL中,纹理有多种,一维纹理:就是一条线;二维纹理:一张二维图片;二维纹理数组:由多张二维图片组成;还有三维纹理,立体纹理,本节主要介绍二维纹理


在早期的OpenGL中,只支持图片大小为2^n的纹理,但现在支持,但是最好用大小为2^n的纹理,这样效率会比较高

先看看效果:

八、WebGL入门,纹理贴图_第1张图片\

            var jsArrayData =
                [
                0,      0,      0,      0.0,    0.0,
                400,    0,      0,      1.0,    0.0,
                400,    400,    0,      1.0,    1.0,


                0,      0,      0,      0.0,    0.0,
                400,    400,    0,      1.0,    1.0,
                0,      400,    0,      0.0,    1.0,
                ];


void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, TexImageSource? source)

第一个参数target:指定是什么类型的纹理(2选1),

第二个参数level:是纹理的级别,0是基本文件级别,之后再详细说,

第三个参数internalforma:t是纹理像素在显卡内存中存储的格式,GL_RGBA表示一个像素由4个分量(r,g,b,a)组成,

第四个参数format:是指我们传入的图片像素的存储格式,

第五个参数type:是像素每个分量的存储类型,webgl.UNSIGNED_BYTE表示r,g,b,a的存储类型是unsigned byte,1个字节,范围是[0,255],

最后一个参数source:是图片文件


void texParameteri(GLenum target, GLenum pname, GLint param) 是设置纹理参数用的,

第一个参数与前面一致,

第二个参数pname:是纹理的参数:只能是下列四个:

GL_TEXTURE_MIN_FILTER:指定纹理图片缩小时用到的算法

GL_TEXTURE_MAG_FILTER:指定纹理图片放大时用到的算法 

GL_TEXTURE_WRAP_S :纹理包装算法,在s方向 

GL_TEXTURE_WRAP_T :纹理包装算法,在t方向

第三个参数param:是第二个参数的值(value),eg:  webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.NEAREST):表示纹理放大时用的算法是webgl.NEAREST,最近点采样,param有多个,具体的下面在介绍。

            webgl.bindTexture(webgl.TEXTURE_2D, null);
这句代码的作用是将纹理target绑定一个空纹理,这样就不会影响webgl的纹理状态机,不会向下影响,否则一直会使用上面绑定的texture纹理


在shader里面也要有相应的改动,代码如下

	
	


vs中:attribute vec2 inUV:表示纹理的坐标,是一个二维坐标,每个顶点都应该指定一个UV(ST)坐标,UV(ST)的范围是[0,1]:

八、WebGL入门,纹理贴图_第2张图片

inUV的类型一定是attribute,是通过顶点带进来的,然后这个变量要传给fs,因为最终取纹理图片像素绘制给矩形是在fs进行的,所以要加varying vec2 outUV.

在fs中 uniform sampler2D texture 就是我们加载的纹理图片,varying vec2 outUV是接受从vs传过来的纹理UV坐标

gl_FragColor = texture2D(texture, vec2(outUV.x, outUV.y)):这句代码是将纹理图片(outUV.x,outUV.y)坐标位置的像素取出来赋值给gl_FragColor,用于随后的着色


下面就是为shader里面新添加的变量,attribute vec2 inUV和uniform sampler2D texture 赋值了,在function init()添加下面代码,

            uniformTexture = webgl.getUniformLocation(programObject, "texture");
            attrUV = webgl.getAttribLocation(programObject, "inUV");

为了使用我们加载进来的纹理图片,我们还需要在function init()里添加其他代码:

            webgl.activeTexture(webgl.TEXTURE0);//激活level=0,第0阶段的纹理
            webgl.bindTexture(webgl.TEXTURE_2D, textureHandle);//使用textureHandle指向的纹理
            webgl.uniform1i(uniformTexture, 0);//为shader fs里的sampler2D texture 赋值,0表示:texture取第0阶段的纹理

其中void activeTexture(GLenum texture) 参数texture取值为下面所列,共32个,说明WebGL可以在一个三角形面上最多贴32张纹理,这32张纹理就是32个阶段,或32个寄存器,默认激活第0阶段的纹理

/* TextureUnit */

    const GLenum TEXTURE0                       = 0x84C0;
    const GLenum TEXTURE1                       = 0x84C1;
    const GLenum TEXTURE2                       = 0x84C2;
    const GLenum TEXTURE3                       = 0x84C3;
    const GLenum TEXTURE4                       = 0x84C4;
    const GLenum TEXTURE5                       = 0x84C5;
    const GLenum TEXTURE6                       = 0x84C6;
    const GLenum TEXTURE7                       = 0x84C7;
    const GLenum TEXTURE8                       = 0x84C8;
    const GLenum TEXTURE9                       = 0x84C9;
    const GLenum TEXTURE10                      = 0x84CA;
    const GLenum TEXTURE11                      = 0x84CB;
    const GLenum TEXTURE12                      = 0x84CC;
    const GLenum TEXTURE13                      = 0x84CD;
    const GLenum TEXTURE14                      = 0x84CE;
    const GLenum TEXTURE15                      = 0x84CF;
    const GLenum TEXTURE16                      = 0x84D0;
    const GLenum TEXTURE17                      = 0x84D1;
    const GLenum TEXTURE18                      = 0x84D2;
    const GLenum TEXTURE19                      = 0x84D3;
    const GLenum TEXTURE20                      = 0x84D4;
    const GLenum TEXTURE21                      = 0x84D5;
    const GLenum TEXTURE22                      = 0x84D6;
    const GLenum TEXTURE23                      = 0x84D7;
    const GLenum TEXTURE24                      = 0x84D8;
    const GLenum TEXTURE25                      = 0x84D9;
    const GLenum TEXTURE26                      = 0x84DA;
    const GLenum TEXTURE27                      = 0x84DB;
    const GLenum TEXTURE28                      = 0x84DC;
    const GLenum TEXTURE29                      = 0x84DD;
    const GLenum TEXTURE30                      = 0x84DE;
    const GLenum TEXTURE31                      = 0x84DF;

顶点缓冲区要加上纹理UV坐标:

            var jsArrayData =
                [
	            x       y       z       u       v
                    0,      0,      0,      0.0,    0.0,
                    400,    0,      0,      1.0,    0.0,
                    400,    400,    0,      1.0,    1.0,

                    0,      0,      0,      0.0,    0.0,
                    400,    400,    0,      1.0,    1.0,
                    0,      400,    0,      0.0,    1.0,
                ];

xyz与uv的对应关系如下:

八、WebGL入门,纹理贴图_第3张图片

function renderScene()也要相应的调整:

       function renderScene() {

            //! 设置重绘背景的颜色
            webgl.clearColor(0.0, 0.0, 0.0, 1.0);
            //! 执行绘制,即将背景清空成制定的颜色(clearColor)
            webgl.clear(webgl.COLOR_BUFFER_BIT);

            //! 指定绘制所使用的顶点数据 从 该缓冲区中获取
            webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);

            webgl.activeTexture(webgl.TEXTURE0);
            webgl.bindTexture(webgl.TEXTURE_2D, textureHandle);
            webgl.uniform1i(uniformTexture, 0);

            webgl.uniformMatrix4fv(uniformProj, false, projectMat);

            webgl.enableVertexAttribArray(v3PositionIndex);
            webgl.enableVertexAttribArray(attrUV);

            webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 4 * 5, 0);
            webgl.vertexAttribPointer(attrUV, 2, webgl.FLOAT, false, 4 * 5, 12);

            webgl.drawArrays(webgl.TRIANGLES, 0, 6);
        }


纹理的基本流程就显示完了,现在来 深入讲一下纹理的参数设置,

纹理其实是一系列的二维图片,不是一张。

假设我在游戏里面离一张图片很近,那么我看的就很清楚,而离远了就很模糊,其实这是有多张纹理,这样做为了提升性能,离近了用清晰的纹理,远处用模糊的纹理,纹理的这种处理是LOD,层次细节

void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, TexImageSource? source)现在我们仔细讲讲里面的参数,

这句代码的作用是讲图片里面的数据传给纹理对象

第二个参数是纹理的级别level,下面看一些图,你就明白是什么意思了:

八、WebGL入门,纹理贴图_第4张图片

最左边的是第0层,向右是1层,大小依次递减

void texParameteri(GLenum target, GLenum pname, GLint param) 是设置纹理参数用的,上面有介绍

第二个参数pname:是纹理的参数:只能是下列四个:

GL_TEXTURE_MIN_FILTER:指定纹理图片缩小时用到的算法

GL_TEXTURE_MAG_FILTER:指定纹理图片放大时用到的算法 

GL_TEXTURE_WRAP_S :纹理包装算法,在s方向 

GL_TEXTURE_WRAP_T :纹理包装算法,在t方向


第三个参数param:是第二个参数的值(value)


放大和缩小所用的算法只有两个 NEAREST和LINEAR,(即第三个参数param的值是webgl.NEAREST或webgl.LINEAR)分别是最近点采样和线性采样,前者效率高单效果不好,后者效率不高单效果比较好。

GL_TEXTURE_WRAP_S和GL_TEXTURE_WRAP_T是纹理包装,之前我们说过纹理坐标是[0,1],但是如果纹理坐标超过1该怎么办

它们的算法(param的值)有:REPEAT,CLAMP_TO_EDGE,MIRRORED_REPEAT,分别是重复绘制,用边沿的像素填充和镜像重复(反过来)


下面我演示下你们就会明白,我先使纹理坐标超过1

            var jsArrayData =
                [
                    0,      0,      0,      0.0,    0.0
                    400,    0,      0,      2.0,    0.0,
                    400,    400,    0,      2.0,    2.0,

                    0,      0,      0,      0.0,    0.0,
                    400,    400,    0,      2.0,    2.0,
                    0,      400,    0,      0.0,    2.0
                ];
纹理参数设置如下:
            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入门,纹理贴图_第5张图片

左边是使用CLAMP_TO_EDGE,纹理坐标超过1,右边是纹理坐标没超过1,两者对比可以发现左边纹理坐标超过1的部分是由边缘像素填充的

当设置为REPAET时

            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.REPAET);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.REPAET);
效果如下:

八、WebGL入门,纹理贴图_第6张图片

纹理坐标超过1的部分是重复绘制

当设置为MIRR0RED_REPAET时

            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.MIRR0RED_REPAET);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.MIRR0RED_REPAET);
效果如下:

八、WebGL入门,纹理贴图_第7张图片

纹理坐标超过1的部分是重复反过来绘制


好, 本节内容完

你可能感兴趣的:(WebGL,自学)