WebGL 纹理完整教程:原理、场景 + 可直接运行 Demo一、WebGL 纹理核心概念1. 纹理是什么纹理就是一张图片,把像素数据贴到几何体表面(类似贴纸),WebGL 通过纹理单元、纹理对

WebGL 纹理完整教程:原理、场景 + 可直接运行 Demo一、WebGL 纹理核心概念1. 纹理是什么纹理就是一张图片,把像素数据贴到几何体表面(类似贴纸),WebGL 通过纹理单元、纹理对
WebGL 纹理完整教程原理、场景 可直接运行 Demo一、WebGL 纹理核心概念1. 纹理是什么纹理就是一张图片把像素数据贴到几何体表面类似贴纸WebGL 通过纹理单元、纹理对象、采样器、UV 坐标配合使用。 核心四要素纹理对象 (Texture)存储图片像素缓存纹理单元 (Texture Unit)GPU 并行纹理槽位默认至少 16 个gl.TEXTURE0 ~ gl.TEXTURE15UV 坐标贴图坐标范围[0,1](0,0) 图片左上角(1,1) 右下角纹理参数缩放过滤、环绕模式2. 关键纹理参数环绕模式图片超出 UV 0~1 时gl.REPEAT重复平铺默认gl.CLAMP_TO_EDGE拉伸边缘像素gl.MIRRORED_REPEAT镜像重复过滤模式缩放贴图时放大TEXTURE_MAG_FILTERgl.LINEAR线性模糊平滑gl.NEAREST像素块马赛克缩小TEXTURE_MIN_FILTER多配合 Mipmapgl.LINEAR_MIPMAP_LINEAR三线性画质最好gl.NEAREST硬像素二、纹理常见应用场景2D/3D 模型贴图人物皮肤、墙体、地板、道具外观UI 界面游戏按钮、图标、背景面板帧缓冲 FBO 渲染纹理离屏渲染、镜面反射、阴影贴图、后期滤镜模糊 / 调色数据纹理把数组数据存纹理GPU 并行计算GPGPU视频纹理播放视频贴到模型播放器、虚拟大屏文字纹理字体图集贴图渲染文字环境贴图天空盒、反射球环境光三、完整可运行 Demo贴图矩形 本地图片纹理功能绘制一个带图片纹理的全屏矩形包含完整纹理创建、图片异步加载、着色器 UV 采样逻辑 复制全部代码保存为.html直接浏览器打开即可。html预览!DOCTYPE html html langzh-CN head meta charsetUTF-8 titleWebGL Texture Demo/title style canvas { border: 1px solid #000; } /style /head body canvas idwebglCanvas width600 height400/canvas script // 1. 获取Canvas与WebGL上下文 const canvas document.getElementById(webglCanvas); const gl canvas.getContext(webgl) || canvas.getContext(experimental-webgl); if (!gl) alert(浏览器不支持WebGL); // 着色器代码 // 顶点着色器传递顶点坐标 UV贴图坐标 const vertexShaderSource attribute vec2 a_Position; attribute vec2 a_Uv; varying vec2 v_Uv; void main() { gl_Position vec4(a_Position, 0.0, 1.0); v_Uv a_Uv; } ; // 片元着色器纹理采样sampler2D读取纹理 const fragmentShaderSource precision mediump float; uniform sampler2D u_Texture; varying vec2 v_Uv; void main() { // 根据UV从纹理采样像素颜色 gl_FragColor texture2D(u_Texture, v_Uv); } ; // 工具函数编译着色器、链接程序 function createShader(gl, type, source) { 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(gl, vsSource, fsSource) { const vs createShader(gl, gl.VERTEX_SHADER, vsSource); const fs createShader(gl, gl.FRAGMENT_SHADER, fsSource); const program gl.createProgram(); gl.attachShader(program, vs); gl.attachShader(program, fs); gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.error(程序链接失败, gl.getProgramInfoLog(program)); return null; } gl.useProgram(program); return program; } // 初始化着色器程序 const program createProgram(gl, vertexShaderSource, fragmentShaderSource); // 顶点数据矩形 UV坐标 // 格式[x,y, u,v] 四个顶点两个三角组成矩形 const vertices new Float32Array([ -1.0, 1.0, 0.0, 0.0, // 左上 -1.0, -1.0, 0.0, 1.0, // 左下 1.0, 1.0, 1.0, 0.0, // 右上 1.0, -1.0, 1.0, 1.0 // 右下 ]); const buffer gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); // 绑定顶点属性 const a_Position gl.getAttribLocation(program, a_Position); const a_Uv gl.getAttribLocation(program, a_Uv); const stride 4 * Float32Array.BYTES_PER_ELEMENT; // 一组4个浮点数 // 位置每组前2个 gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, stride, 0); gl.enableVertexAttribArray(a_Position); // UV每组后2个偏移2个浮点数 gl.vertexAttribPointer(a_Uv, 2, gl.FLOAT, false, stride, 2 * Float32Array.BYTES_PER_ELEMENT); gl.enableVertexAttribArray(a_Uv); // 创建纹理对象核心逻辑 function createTexture(gl) { const texture gl.createTexture(); // 绑定到当前激活纹理单元 TEXTURE0 gl.bindTexture(gl.TEXTURE_2D, texture); // 设置纹理环绕模式 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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); // 临时填充1x1白色像素避免图片未加载时黑屏报错 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 loadTexture(gl, texture, imgUrl) { const img new Image(); img.crossOrigin anonymous; // 解决跨域图片报错 img.onload () { gl.bindTexture(gl.TEXTURE_2D, texture); // 上传图片像素到GPU纹理 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); render(); // 图片加载完成重绘 }; img.src imgUrl; } // 渲染函数 function render() { gl.clearColor(0.2, 0.2, 0.2, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); // 激活0号纹理单元 gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); // 把采样器u_Texture绑定到0号纹理单元 gl.uniform1i(gl.getUniformLocation(program, u_Texture), 0); // 绘制两个三角形共4顶点 gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); } // 启动流程 const texture createTexture(gl); // 替换成你自己的图片链接本地图片直接放同目录 loadTexture(gl, texture, https://picsum.photos/id/237/400/300); render(); /script /body /html四、关键 API 分步解析1. 创建纹理js运行const texture gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); // 绑定2D纹理目标2. 设置纹理参数必写否则缩放会黑块js运行// S水平方向T垂直方向环绕 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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);3. 上传图片数据到 GPUjs运行gl.texImage2D( gl.TEXTURE_2D, 0, // mip层级 gl.RGBA, // GPU内部存储格式 gl.RGBA, // 图片像素格式 gl.UNSIGNED_BYTE, // 像素数据类型 img // Image/Video/Canvas数据源 );4. 激活纹理单元 传入着色器js运行// 激活第0个纹理槽 gl.activeTexture(gl.TEXTURE0); // 绑定纹理对象到当前激活单元 gl.bindTexture(gl.TEXTURE_2D, texture); // 片元着色器sampler2D变量 纹理单元编号0 gl.uniform1i(u_TextureLoc, 0);5. 着色器采样glsluniform sampler2D u_Texture; // 纹理采样器 varying vec2 v_Uv; // 插值UV坐标 // 采样获取像素颜色 vec4 color texture2D(u_Texture, v_Uv);五、拓展常用纹理拓展用法1. Mipmap 优化远距离贴图图片尺寸是 2 的幂512/1024/2048时开启js运行gl.generateMipmap(gl.TEXTURE_2D); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);2. 多纹理叠加双纹理单元js运行// 纹理1绑定TEXTURE0 gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, tex1); gl.uniform1i(texLoc1, 0); // 纹理2绑定TEXTURE1 gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, tex2); gl.uniform1i(texLoc2, 1);片元着色器混合两张贴图glslvec4 col1 texture2D(u_Tex1, v_Uv); vec4 col2 texture2D(u_Tex2, v_Uv); gl_FragColor mix(col1, col2, 0.5);3. Video 视频纹理直接传入video标签到texImage2D渲染循环每帧更新纹理。4. FBO 渲染纹理离屏渲染创建纹理绑定帧缓冲把 3D 场景渲染到纹理再贴到模型实现镜面、特效。六、常见踩坑点图片跨域黑屏设置img.crossOrigin anonymous图片服务器允许跨域UV 超出范围拉伸异常修改TEXTURE_WRAP_S/T为CLAMP_TO_EDGE缩小贴图出现黑块没设置TEXTURE_MIN_FILTER过滤参数Mipmap 报错图片宽高必须是 2 的整数次幂图片加载前黑屏先填充 1x1 临时像素纹理