webgl2 方法解析: texImage2D()

gl.texImage2D() 是 WebGL 中的一个核心函数,用于将图像数据上传到 2D 纹理对象中。它是最常用的纹理初始化和更新函数之一,支持多种数据源和格式,广泛用于创建纹理、更新纹理内容以及加载图像数据到 GPU。

函数定义

css 复制代码
gl.texImage2D(
  target,
  level,
  internalFormat,
  width,
  height,
  border,
  format,
  type,
  data
);
  • target:指定纹理绑定的目标。常见的值包括:

    • gl.TEXTURE_2D:普通 2D 纹理。
    • gl.TEXTURE_CUBE_MAP_POSITIVE_Xgl.TEXTURE_CUBE_MAP_NEGATIVE_X 等:立方体贴图的六个面。
  • level :指定 Mipmap 层级。level = 0 表示基本纹理,level > 0 表示 Mipmap 层级。

  • internalFormat:指定纹理的内部存储格式。常见的格式包括:

    • gl.RGBgl.RGBA:未压缩的 RGB 或 RGBA 格式。
    • gl.LUMINANCEgl.ALPHA:单通道格式。
    • 压缩格式(如 gl.COMPRESSED_RGBA_S3TC_DXT5_EXT)。
  • widthheight:纹理的宽度和高度,以像素为单位。

  • border:纹理的边框宽度。在 WebGL 中,这个值必须为 0。

  • format :指定图像数据的格式,例如 gl.RGBgl.RGBA 等。

  • type :指定图像数据的类型,例如 gl.UNSIGNED_BYTEgl.FLOAT 等。

  • data :包含图像数据的 ArrayBufferView(如 Uint8Array)、ImageDataHTMLImageElementHTMLCanvasElementHTMLVideoElement

示例代码

以下是一个使用 gl.texImage2D() 的示例,展示如何将图像数据上传到 2D 纹理中:

ini 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webglContext.texImage3D示例</title>
  <style>
    canvas {
      width: 800px;
      height: 600px;
      border: 1px solid black;
    }
  </style>
</head>
<body>
  <canvas id="glCanvas"></canvas>
  <img id="textureImage" src="./小狼狗.jpg" crossorigin="anonymous" style="display: none;">
​
  <script>
​
    // 获取 canvas 元素
    const canvas = document.getElementById('glCanvas');
    canvas.width = 800;
    canvas.height = 600;
    const gl = canvas.getContext('webgl2');
    if (!gl) {
      alert('获取渲染上下文失败! ');
      throw new Error('获取渲染上下文失败! ');
    }
​
​
    async function main() {
      try {
        const imgData = await loadImageAndGetData('./小狼狗.jpg');
        const texture = create2DTexture(imgData);
        renderScene(texture);
      } catch (error) {
        console.error('加载失败:', error);
      }
    }
​
    main();
​
    // 渲染场景
    function renderScene(texture) {
      // 清空画布
      gl.clearColor(0.0, 0.0, 0.0, 1.0);
      gl.clear(gl.COLOR_BUFFER_BIT);
      gl.viewport(0, 0, canvas.width, canvas.height);
​
      // 创建着色器
      const vertexShader = gl.createShader(gl.VERTEX_SHADER);
      const vertexSource = `#version 300 es
      in vec3 aPosition;
      in vec2 aTexCoord;
      out vec2 vTexCoord;
​
      void main() {
        gl_Position = vec4(aPosition, 1.0);
        vTexCoord = aTexCoord;
      }
      `;
      gl.shaderSource(vertexShader, vertexSource);
      gl.compileShader(vertexShader);
      //检查编译状态
      if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
        console.log('vertexshader 编译失败! ', gl.getShaderInfoLog(vertexShader));
        gl.deleteShader(vertexShader);
        return null;
      }
​
      const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
      const fragmentSource = `#version 300 es
      precision mediump float;
      in vec2 vTexCoord;
      uniform sampler2D uTexture;
      out vec4 fragColor;
​
      void main() {
        fragColor = texture(uTexture, vTexCoord);
        // fragColor = vec4(1.0, 0.0, 0.0, 1.0);
      }
      `;
      gl.shaderSource(fragmentShader, fragmentSource);
      gl.compileShader(fragmentShader);
      //检查编译状态
      if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
        console.log('fragmentShader 编译失败! ', gl.getShaderInfoLog(fragmentShader));
        gl.deleteShader(fragmentShader);
        return null;
      }
​
      const program = gl.createProgram();
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      gl.linkProgram(program);
      // 检查链接状态
      if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        console.error('program 链接失败! ');
        gl.deleteProgram(program);
        return null;
      }
      gl.useProgram(program);
​
      // 矩形数据
      const vertices = new Float32Array([
        -0.5, -0.5, 0.0, 
        0.5, -0.5, 0.0,
        0.5, 0.5, 0.0,
        -0.5, 0.5, 0.0,
      ]);
​
      const indices = new Uint16Array([
        0, 1, 2,
        2, 3, 0
      ]);
​
      const texCoord = new Float32Array([
        0.0, 1.0,
        1.0, 1.0,
        1.0, 0.0,
        0.0, 0.0
      ]);
​
      // 创建顶点缓冲区
      const vertexBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
      gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
      const positionLocation = gl.getAttribLocation(program, 'aPosition');
      gl.enableVertexAttribArray(positionLocation);
      gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
​
      // 索引缓冲区
      const indexBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
      gl.activeTexture(gl.TEXTURE0);
      gl.bindTexture(gl.TEXTURE_2D, texture);
      gl.uniform1i(gl.getUniformLocation(program, 'uTexture'), 0);
​
      // 材质坐标
      const texBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, texBuffer);
      gl.bufferData(gl.ARRAY_BUFFER, texCoord, gl.STATIC_DRAW);
      const texPositionLocation = gl.getAttribLocation(program, 'aTexCoord');
      gl.enableVertexAttribArray(texPositionLocation);
      gl.vertexAttribPointer(texPositionLocation, 2, gl.FLOAT, false, 0, 0);
​
      // 绘制矩形
      gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
    }
​
    // 创建 3D 纹理
    function create2DTexture(img) {
      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.REPEAT);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
​
      // 上传图像数据到3D纹理
      gl.texImage2D(
        gl.TEXTURE_2D,  // 目标
        0,              // level, 指定详细级别。0 级是基本图像等级,n 级是第 n 个金字塔简化级。
        gl.RGBA,        // 内部格式
        img.width,      // 数据宽(像素个数)
        img.height,     // 数据高(像素个数 )
        0,              // 指定纹理的边框宽度。必须为 0。历史接口遗留
        gl.RGBA,        // 格式
        gl.UNSIGNED_BYTE, // 数据类型
        img.data,       // 数据
      );
​
      return texture;
    }
​
    // 加载本地的图像
    function loadImageAndGetData(url) {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.crossOrigin = 'Anonymous'; // 处理跨域问题
        
        img.onload = function() {
          const canvas = document.createElement('canvas');
          canvas.width = img.width;
          canvas.height = img.height;
          
          const ctx = canvas.getContext('2d');
          ctx.drawImage(img, 0, 0);
          
          // 获取像素数据
          const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
          const pixelData = imageData.data; // Uint8ClampedArray
          
          resolve({
            width: img.width,
            height: img.height,
            data: pixelData
          });
        };
        
        img.onerror = reject;
        img.src = url;
      });
    }
  </script>
</body>
</html> 

使用 HTML 图像加载纹理

gl.texImage2D() 也可以直接从 HTML 元素(如 <img><canvas><video>)加载纹理数据。以下是一个从 <img> 元素加载纹理的示例:

1. 创建 HTML 图像元素

ini 复制代码
<img id="textureImage" src="texture.jpg" crossorigin="anonymous">

2. 在 JavaScript 中加载纹理

ini 复制代码
const image = document.getElementById('textureImage');
image.onload = function() {
  const texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
​
  gl.texImage2D(
    gl.TEXTURE_2D,       // 目标
    0,                   // Mipmap 层级
    gl.RGBA,             // 内部格式
    gl.RGBA,             // 格式
    gl.UNSIGNED_BYTE,    // 类型
    image                // 数据(HTMLImageElement)
  );
​
  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.REPEAT);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
};

关键点解释

  1. 纹理初始化

    • gl.texImage2D() 可以用于初始化纹理,也可以用于更新整个纹理的内容。
    • 如果需要更新纹理的子区域,可以使用 gl.texSubImage2D()
  2. 内部格式与数据格式

    • internalFormat 指定了纹理在 GPU 中的存储格式。
    • formattype 指定了上传数据的格式和类型。
  3. Mipmap

    • 如果需要使用 Mipmap,可以在上传基本纹理后调用 gl.generateMipmap() 生成 Mipmap 层级。
  4. 数据源

    • data 参数可以是 ArrayBufferViewImageDataHTMLImageElementHTMLCanvasElementHTMLVideoElement

注意事项

  1. 纹理大小

    • WebGL 要求纹理的宽度和高度必须是 2 的幂(除非启用了 gl.getExtension('EXT_texture_filter_anisotropic'))。
  2. 性能优化

    • 如果需要频繁更新纹理内容,建议使用 gl.texSubImage2D() 更新纹理的子区域,而不是重新上传整个纹理。
相关推荐
xhload3d6 天前
WebGL/Canvas 内存泄露分析
低代码·3d·html5·webgl·数字孪生·可视化·软件开发·工业互联网·内存泄漏·轻量化·技术应用·hightopo
爱看书的小沐8 天前
【小沐杂货铺】基于Three.js渲染三维风力发电机(WebGL、vue、react、WindTurbine)
javascript·vue.js·webgl·three.js·opengl·风力发电机·windturbine
郝学胜-神的一滴8 天前
Three.js光照技术详解:为3D场景注入灵魂
开发语言·前端·javascript·3d·web3·webgl
linweidong9 天前
让低端机也能飞:Canvas/WebGL/Viz 分层、降级渲染与数据抽样策略
前端框架·webgl·canvas·前端动画·前端面经·css渲染·动画优化
CAD老兵11 天前
打造高性能二维图纸渲染引擎系列(一):Batched Geometry 助你轻松渲染百万实体
前端·webgl·three.js
CAD老兵11 天前
打造高性能二维图纸渲染引擎系列(三):高性能 CAD 文本渲染背后的隐藏工程
前端·webgl·three.js
CAD老兵11 天前
打造高性能二维图纸渲染引擎系列(二):创建结构化和可扩展的渲染场景
前端·webgl·three.js
王维志13 天前
使用Asp.Net WebApi(.net 8)托管Unity WebGL
unity·游戏引擎·webgl
Zuckjet_13 天前
第 7 篇:交互的乐趣 - 响应用户输入
前端·javascript·webgl
xhload3d14 天前
智慧钢厂高炉冶炼仿真分析 | 图扑数字孪生
3d·智慧城市·html5·webgl·数字孪生·可视化·热力图·智慧工厂·工业互联网·工业组态·高炉炼铁·数字工厂·高炉炉体·智慧高炉·高炉