webGL图片纹理

基础概念
  1. 坐标定义:
    纹理坐标系范围是(0,1),左下角为(0,0),右上角为(1,1);
    webGL几何坐标系:x(-1, 1) y(-1, 1)

  2. gl.createTexture() —— 创建并初始化一个WebGLTexture对象,即创建一个纹理;

  3. gl.bindTexture(target, texture)
    将给定的texture绑定到目标,其中texture参数为创建的纹理对象,target为绑定点,类型为GLenum,支持的值包括gl.TEXTURE_2D(二维纹理)、gl.TEXTURE_CUBE_MAP(立方体映射纹理),使用webgl 2时可以支持gl.TEXTURE_3D(三维纹理)、gl.TEXTURE_2D_ARRAY(二维纹理数组);

  4. WebGLRenderingContext.texImage2D()
    用于指定二维纹理图像,把已加载的图形数据写到纹理;

    // WebGL1:
    void gl.texImage2D(target, level, internalformat, width, height, border, format, type, ArrayBufferView? pixels);
    void gl.texImage2D(target, level, internalformat, format, type, ImageData? pixels);
    void gl.texImage2D(target, level, internalformat, format, type, HTMLImageElement? pixels);
    void gl.texImage2D(target, level, internalformat, format, type, HTMLCanvasElement? pixels);
    void gl.texImage2D(target, level, internalformat, format, type, HTMLVideoElement? pixels);
    void gl.texImage2D(target, level, internalformat, format, type, ImageBitmap? pixels);
    

    其中target为指定纹理绑定对象,支持gl.TEXTURE_2Dgl.TEXTURE_CUBE_MAP_POSITIVE_Xgl.TEXTURE_CUBE_MAP_NEGATIVE_Xgl.TEXTURE_CUBE_MAP_POSITIVE_Ygl.TEXTURE_CUBE_MAP_NEGATIVE_Ygl.TEXTURE_CUBE_MAP_POSITIVE_Zgl.TEXTURE_CUBE_MAP_NEGATIVE_Z

    level:0为基本图像等级,n为第n即金字塔简化级;

    internalformat:指定纹理图像中的颜色组成,支持RGBARGBALPHA等;

    format:webgl1中与internalformat保持一致,webgl2参考文献3

    type:指定纹理像素的数据类型 —— gl.UNSIGNED_BYTEgl.UNSIGNED_SHORT_5_6_5(5bits红、6bits绿、5bits蓝)等

    pixels:纹理像素源,取以下对象之一:ArrayBufferView(type为gl.UNSIGNED_BYTE时必须使用Uint8Array)、ImageData、HTMLImageElement、HTMLCanvasElement、HTMLVideoElement、ImageBitmap;

  5. WebGLRenderingContext.pixelStorei(pname, params)
    图像预处理函数,参数pname指定预处理函数名,params指定预处理方式的参数。pname可选值包括gl.PACK_ALIGNMENTgl.UNPACK_ALIGNMENTgl.UNPACK_FLIP_Y_WEBGL(为true图像对称轴上下翻转)、 gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL(其他颜色通道乘以alpha通道);

    由于webgl纹理中坐标原点位于左下方,而图片信息依赖的坐标原点时左上角;因此通常需要沿着Y轴进行翻转处理

  6. gl.activeTexture(texture)
    激活指定的纹理,texture取值gl.TEXTUREI,其中的 I 在 0 到 gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1 范围内,gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS是一个常量

  7. WebGLRenderingContext.texParameter[fi]()
    用于设置纹理参数, gl.TEXTURE_MAG_FILTER纹理放大滤波器(可选值gl.LINEAR(默认值)、gl.NEAREST);

    void gl.texParameterf(GLenum target, GLenum pname, GLfloat param);
    void gl.texParameteri(GLenum target, GLenum pname, GLint param);
    
    gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
    
  8. WebGLRenderingContext.uniform[1234][fi][v]()
    指定uniform变量的值;location指定要修改的uniform的位置;

    void gl.uniform1f(location, v0);
    void gl.uniform1fv(location, value);
    void gl.uniform1i(location, v0);
    void gl.uniform1iv(location, value);
    
    void gl.uniform2f(location, v0, v1);
    void gl.uniform2fv(location, value);
    void gl.uniform2i(location, v0, v1);
    void gl.uniform2iv(location, value);
    
绘制纹理图片
  1. 创建WebGLRenderingContext对象:

    var canvas = document.getElementById("canvas");
    var gl = canvas.getContext('webgl');
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    
  2. 初始化shaders及program
    首先创建vertexShaderfragmentShader,然后创建一个WebGLProgram,最后将program与着色器关联;

    function initShaders(gl, vertexShaderSource, fragmentShaderSource) {
        var program = createProgram(gl, vertexShaderSource, fragmentShaderSource);
        if (!program) {
            console.log('Failed to create program');
            return false;
        }
    
        gl.useProgram(program);
        gl.program = program;
    
        return true;
    }
    
    function createProgram(gl, vertexShaderSource, fragmentShaderSource) {
        var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
        var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
        if (!vertexShader || !fragmentShader) {
            return null;
        }
    
        // create a program object
        var program = gl.createProgram();
        if (!program) {
            console.log('gl.createProgram failed');
            return null;
        }
    
        // attach  the shader objects
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);
    
        // link the program object
        gl.linkProgram(program);
    
        // check link status
        var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
        if (!linked) {
            var error = gl.getProgramInfoLog(program);
            console.log('Failed to link program: ' + error);
            gl.deleteProgram(program);
            gl.deleteShader(vertexShader);
            gl.deleteShader(fragmentShader);
            return null;
        }
    
        return program;
    }
    
    function loadShader(gl, type, source) {
        // create shader object
        var shader = gl.createShader(type);
        if (shader == null) {
            console.log('unable to create shader');
            return null;
        }
    
        // set shader source code
        gl.shaderSource(shader, source);
    
        // compile the shader
        gl.compileShader(shader);
    
        // check compile status
        var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
        if (!compiled) {
            var error = gl.getShaderInfoLog(shader);
            console.log('Failed to compile shader: ' + error);
            return null;
        }
    
        return shader;
    }
    
  3. 编写glsl定义两个着色器并定义顶点信息
    顶点信息使用Float32Array的强类型数组定义,其中前两位表示几何坐标系位置坐标,范围x(-1, 1)y(-1, 1);后两位表示纹理坐标系,范围x(0, 1)y(0, 1);具体的坐标值需要根据具体的渲染区域做相应的调整;

    vertexShader中多定义了两个变量——a_TextCoordv_TextCoorda_Positiona_TextCoord分别从帧缓存器中读取顶点位置坐标和纹理坐标,v_TextCoord则用于将纹理坐标信息传递给片元着色器,用于提取纹理颜色;

    在片元着色器中使用方法texture2D(u_Sampler, v_TextCoord)用取样器采集对应纹理坐标位置(实际上就是取原图形中的对应位置的颜色信息)的颜色信息;

    var vertices = new Float32Array([
        -1, 1, 0.0, 1.0, // 前 2 位是位置坐标, 后 2 位是纹理坐标
        -1, -1, 0.0, 0.0,
        1, 1, 1.0, 1.0,
        1, -1, 1.0, 0.0
    ]);
        // vertex shader
    var VERTEX_SHADER_SOURCE =
        'attribute vec4 a_Position;\n' +
        'attribute vec2 a_TexCoord;\n' +
        'varying vec2 v_TexCoord;\n' +
    
        'void main() {\n' +
        '   gl_Position = a_Position;\n' +
        '   v_TexCoord = a_TexCoord;\n' +
        '}\n';
    
    // fragment shader
    var FRAGMENT_SHADER_SOURCE =
        'precision mediump float;\n' +
        'varying vec2 v_TexCoord;\n' +
        'uniform sampler2D u_Sampler;\n' +
    
        'void main() {\n' +
        '   gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' +
        '}\n';
    
  4. 加载并绘制纹理
    相对于基本的几何图形绘制,纹理绘制需要在声明着色器时需要额外的变量;

    initVertexBuffers(gl, vertices);
    
    var image = new Image();
    image.src = './assets/drawn.jpg';
    image.onload = function () {
        loadTexture(image);
        draw();
    };
    
    function initVertexBuffers(gl, vertices) {
        var vertexBuffer = gl.createBuffer();
        if (!vertexBuffer) {
            console.log('Failed to create buffer object');
            return -1;
        }
    
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
    
        var FSIZE = vertices.BYTES_PER_ELEMENT;
        console.log('fsize:', FSIZE)
    
        var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
        gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 4 * FSIZE, 0);
        gl.enableVertexAttribArray(a_Position);
    
        var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
        gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, 4 * FSIZE, 2 * FSIZE);
        gl.enableVertexAttribArray(a_TexCoord);
    }
    
    function loadTexture(image) {
        var texture = gl.createTexture(); // 创建纹理
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 翻转 Y 轴
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texture); // 绑定纹理到帧缓存器
    
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); // 设置纹理参数
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); // 把加载的图形数据写到纹理
    
        var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
        gl.uniform1i(u_Sampler, 0);
    }
    
    function draw() {
        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    }
    
  5. 非2的幂的处理
    需要注意的是,在多数情况下,纹理的宽和高都必须是2的幂;上述代码中的图片是256*256的;

    1. 方案一:通过调整画布大小将图片纹理进行合理映射,存在明显拉伸时可以适当修正纹理坐标值;

    2. 方案二:必须使用非 2的幂的纹理——webGL原生支持,但是有一定的限制条件;

      这种非2的幂纹理不能用来生成多级渐进纹理,而且不能使用纹理重复(重复纹理寻址等)。使用重复纹理寻址的一个例子就是使用一张砖块的纹理来平铺满一面墙壁。

      多级渐进纹理和纹理坐标重复可以通过调用 texParameteri() 来禁用,当然首先你已经通过调用 bindTexture() 绑定过纹理了。这样虽然已经可以使用非2的幂纹理了,但是你将无法使用多级渐进纹理,纹理坐标包装,纹理坐标重复,而且无法控制设备如何处理你的纹理。

      使用以下参数可以使用非2的幂的图片

      	// gl.NEAREST is also allowed, instead of gl.LINEAR, as neither mipmap.
      	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
      	// Prevents s-coordinate wrapping (repeating).
      	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      	// Prevents t-coordinate wrapping (repeating).
      	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
      

在这里插入图片描述



参考文献
  1. Using textures in WebGL
  2. WebGLRenderingContext.bindTexture()
  3. WebGLRenderingContext.texImage2D()
  4. WebGLRenderingContext.texParameter[fi]
  5. 动画纹理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Neil-

你们的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值