webgl2 方法解析: pixelStorei()

128 阅读3分钟

在WebGL2中,gl.pixelStorei() 是一个用于控制像素存储模式的函数,它允许你设置如何从图像(如PNG、JPEG等)或数据源中读取和解包像素数据。这个函数主要影响 gl.texImage2D()gl.texSubImage2D() 等操作像素数据的函数的行为。

常用参数:

以下是 gl.pixelStorei() 在WebGL2中常用的参数(第二个参数是整数值):

  1. gl.UNPACK_FLIP_Y_WEBGL

    • 设置为 1(true)时,图像会在上传时垂直翻转(Y轴反转),因为WebGL的纹理坐标原点在左下角,而图像的坐标原点通常在左上角。

    • 示例:

      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
      
  2. gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL

    • 设置为 1 时,图像的RGB值会预乘Alpha值(即 RGB = RGB * (A / 255)),适用于透明纹理的混合处理。
  3. gl.UNPACK_COLORSPACE_CONVERSION_WEBGL

    • 控制是否进行颜色空间转换(默认 gl.NONE,不转换)。
  4. gl.UNPACK_ALIGNMENT

    • 指定内存中像素数据的对齐方式(默认为4,即每行按4字节对齐)。如果图像数据每行的字节数不是4的倍数,可能需要调整为1、2、4等值以避免解析错误。

    • 示例:

      gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
      

使用示例:

以下是一个可直接运行的示例, 主要通过 翻转Y轴 来展示pixelStorei()方法的功能

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebGL2 纹理示例(翻转Y轴)</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; width: 600px; height: 400px; }
    </style>
</head>
<body>
    <canvas id="glCanvas"></canvas>
    <div>
      <button onclick="notYFlip()">不翻转</button>
      <button onclick="yFlip()">y轴翻转</button>
    </div>
​
    <script>
        // 编译着色器
        function createShader(gl, source, type) {
          const shader = gl.createShader(type);
          gl.shaderSource(shader, source);
          gl.compileShader(shader);
          if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
              console.error('着色器编译错误:', gl.getShaderInfoLog(shader));
              gl.deleteShader(shader);
              return null;
          }
          return shader;
        }
​
        function createProgram(vertexShader, fragmentShader) {
          // 创建着色器程序
          const program = gl.createProgram();
          gl.attachShader(program, vertexShader);
          gl.attachShader(program, fragmentShader);
          gl.linkProgram(program);
          if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
            console.error('程序链接错误:', gl.getProgramInfoLog(program));
          }
​
          return program;
          
        }
​
        function createTexture() {
          
          // 创建纹理
          const texture = gl.createTexture();
          gl.bindTexture(gl.TEXTURE_2D, texture);
​
          // 设置纹理参数(缩小/放大时使用线性过滤)
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
​
          gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 255, 255, 255]));
          
          return texture;
​
        }
​
        // 渲染函数
        function render() {
          gl.clearColor(0.0, 0.0, 0.0, 1.0);
          gl.clear(gl.COLOR_BUFFER_BIT);
​
          gl.useProgram(program);
​
          // 创建并绑定顶点缓冲区
          const vertexBuffer = gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
          gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
          // 获取属性位置
          const positionLoc = gl.getAttribLocation(program, 'aPosition');
          const texCoordLoc = gl.getAttribLocation(program, 'aTexCoord');
          // 启用属性
          gl.enableVertexAttribArray(positionLoc);
          gl.enableVertexAttribArray(texCoordLoc);
          // 设置属性指针
          const stride = 4 * Float32Array.BYTES_PER_ELEMENT; // 每个顶点4个float(x,y,s,t)
          gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, stride, 0);
          gl.vertexAttribPointer(texCoordLoc, 2, gl.FLOAT, false, stride, 2 * Float32Array.BYTES_PER_ELEMENT);
​
          // 创建并绑定索引缓冲区
          const indexBuffer = gl.createBuffer();
          gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
          gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
​
          gl.bindTexture(gl.TEXTURE_2D, texture);
          gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
        }
​
        function loadImg() {
​
          return new Promise((resolve, reject) => {
            // 加载图像
            const image = new Image();
            image.onload = function() {
​
              resolve(image);
            };
            image.src = './小狼狗.jpg'; // 使用一个示例图片(可替换为你的本地图片)
​
          });
        }
​
        async function notYFlip() {
​
          const image = await loadImg();
​
          // 绑定纹理
          gl.bindTexture(gl.TEXTURE_2D, texture);
​
          gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
​
          // 将图像数据上传到纹理
          gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
​
          // 渲染
          render();
        }
​
        async function yFlip() {
​
          const image = await loadImg();
          
          // 绑定纹理
          gl.bindTexture(gl.TEXTURE_2D, texture);
​
          gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
​
          // 将图像数据上传到纹理
          gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
​
          // 渲染
          render();
        }
​
        // 初始化WebGL2上下文
        const canvas = document.getElementById('glCanvas');
        const gl = canvas.getContext('webgl2');
​
        if (!gl) {
            alert('您的浏览器不支持WebGL2!');
            throw new Error('WebGL2 not supported');
        }
​
        // 顶点和纹理坐标数据(一个正方形)
        const vertices = new Float32Array([
            // 位置 (x, y)     纹理坐标 (s, t)
            -0.5, -0.5,        0.0, 1.0,  // 左下
             0.5, -0.5,        1.0, 1.0,  // 右下
             0.5,  0.5,        1.0, 0.0,  // 右上
            -0.5,  0.5,        0.0, 0.0   // 左上
        ]);
​
        // 索引数据(两个三角形组成正方形)
        const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
​
        // 顶点着色器
        const vsSource = `#version 300 es
            in vec2 aPosition;
            in vec2 aTexCoord;
            out vec2 vTexCoord;
            void main() {
                gl_Position = vec4(aPosition, 0.0, 1.0);
                vTexCoord = aTexCoord;
            }
        `;
​
        // 片段着色器
        const fsSource = `#version 300 es
            precision highp float;
            in vec2 vTexCoord;
            uniform sampler2D uTexture;
            out vec4 fragColor;
            void main() {
                fragColor = texture(uTexture, vTexCoord);
            }
        `;
​
        const vertexShader = createShader(gl, vsSource, gl.VERTEX_SHADER);
        const fragmentShader = createShader(gl, fsSource, gl.FRAGMENT_SHADER);
        const program = createProgram(vertexShader, fragmentShader);
        const texture = createTexture();
​
        notYFlip();
​
    </script>
</body>
</html>

注意事项:

  • 这些设置是全局状态,调用后会持续生效,直到再次修改。
  • WebGL2还保留了WebGL1的类似参数,并可能新增了一些扩展选项(如 gl.PACK_ROW_LENGTH 等)。