基于纹理坐标导数计算Mip Level的实现问题排查
OpenGL/WebGL 纹理Mip Level计算异常问题
我尝试基于纹理坐标导数,以与采样器相同的方式计算纹理Mip Level。参考Stack Overflow、Reddit、Khronos社区等多个资料,得到的计算公式如下:
vec2 dx_vtc = dFdx(texture_coordinate); vec2 dy_vtc = dFdy(texture_coordinate); float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); mip_level = 0.5 * log2(delta_max_sqr);
基于此我编写了测试程序,包含两个Shader:
- 第一个Shader使用纹理坐标采样含7级Mipmap的纹理(各级为纯色),采用
NEAREST_MIPMAP_NEAREST过滤,纹理坐标范围变化时Mip Level切换正常; - 第二个Shader通过上述公式计算Mip Level,再从对应颜色表中选取颜色(颜色与纹理Mip级一致),但始终显示红色(对应Mip Level 0)。
第二个Shader代码如下:
#version 300 es precision highp float; in vec2 v_texCoord; out vec4 outColor; const vec4 colors[8] = vec4[8]( vec4( 1, 0, 0, 1), // 0: red vec4( 1, 1, 0, 1), // 1: yellow vec4( 0, 1, 0, 1), // 2: green vec4( 0, 1, 1, 1), // 3: cyan vec4( 0, 0, 1, 1), // 4: blue vec4( 1, 0, 1, 1), // 5: magenta vec4(0.5, 0.5, 0.5, 1), // 6: gray vec4( 1, 1, 1, 1));// 7: white void main() { vec2 dx = dFdx(v_texCoord); vec2 dy = dFdy(v_texCoord); float deltaMaxSq = max(dot(dx, dx), dot(dy, dy)); float mipLevel = 0.5 * log2(deltaMaxSq); outColor = colors[int(mipLevel)]; }
我已完成多项调试:确认纹理坐标正确、颜色表索引有效、尝试可视化Mip Level均无效。请问问题出在哪里?
完整测试代码
CSS部分
html, body { margin: 0; font-family: monospace; height: 100%; } canvas { display: block; width: 100%; height: 100%; }
HTML与JS部分
<canvas id="c"></canvas> <script type="module"> import * as twgl from 'https://twgljs.org/dist/5.x/twgl-full.module.js'; const vs = `#version 300 es uniform mat4 u_worldViewProjection; uniform mat3 u_texMat; out vec2 v_texCoord; const vec2 position[6] = vec2[6]( vec2(0, 0), vec2(1, 0), vec2(0, 1), vec2(0, 1), vec2(1, 0), vec2(1, 1)); void main() { vec2 p = position[gl_VertexID]; v_texCoord = (u_texMat * vec3(p, 1)).xy; gl_Position = u_worldViewProjection * vec4(p, 0, 1); } `; const fsTex = `#version 300 es precision highp float; in vec2 v_texCoord; uniform sampler2D u_tex; out vec4 outColor; void main() { outColor = texture(u_tex, v_texCoord); } `; const fsMipLevel = `#version 300 es precision highp float; in vec2 v_texCoord; out vec4 outColor; const vec4 colors[8] = vec4[8]( vec4( 1, 0, 0, 1), // 0: red vec4( 1, 1, 0, 1), // 1: yellow vec4( 0, 1, 0, 1), // 2: green vec4( 0, 1, 1, 1), // 3: cyan vec4( 0, 0, 1, 1), // 4: blue vec4( 1, 0, 1, 1), // 5: magenta vec4(0.5, 0.5, 0.5, 1), // 6: gray vec4( 1, 1, 1, 1));// 7: white void main() { vec2 dx = dFdx(v_texCoord); vec2 dy = dFdy(v_texCoord); float deltaMaxSq = max(dot(dx, dx), dot(dy, dy)); float mipLevel = 0.5 * log2(deltaMaxSq); // mipLevel = mod(gl_FragCoord.x / 16.0, 8.0); // comment in to test we can use the colors outColor = colors[int(mipLevel)]; // outColor = vec4(mipLevel / 7.0, 0, 0, 1); // comment in to visualize another way // outColor = vec4(fract(v_texCoord), 0, 1); // comment in to visualize texcoord } `; const colors = [ '#F00', '#FF0', '#0F0', '#0FF', '#00F', '#F0F', '#888', '#FFF', ]; function createMips(colors) { const ctx = document.createElement('canvas').getContext('2d'); const numMips = colors.length; return colors.map((color, i) => { const size = 2 ** (numMips - i - 1); ctx.canvas.width = size; ctx.canvas.height = size; ctx.fillStyle = color; ctx.fillRect(0, 0, size, size); return ctx.getImageData(0, 0, size, size); }); } function main() { const m4 = twgl.m4; const gl = document.getElementById("c").getContext("webgl2"); if (!gl) { alert("Sorry, this example requires WebGL 2.0"); // eslint-disable-line return; } const texProgramInfo = twgl.createProgramInfo(gl, [vs, fsTex]); const mipProgramInfo = twgl.createProgramInfo(gl, [vs, fsMipLevel]); const texImage = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texImage); const data = createMips(colors); data.forEach(({width, height, data}, level) => { gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); }); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST); const lerp = (a, b, t) => a + (b - a) * t; function render(time) { time *= 0.001; twgl.resizeCanvasToDisplaySize(gl.canvas); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); gl.clearColor(0.3, 0.3, 0.3, 1); gl.clear(gl.COLOR_BUFFER_BIT); const uniforms = {}; const s = lerp(1, 128, Math.sin(time) * 0.5 + 0.5); uniforms.u_texMat = [ s, 0, 0, 0, s, 0, 0, 0, 1, ]; uniforms.u_worldViewProjection = m4.translation([-1.01, -0.5, 0]); gl.useProgram(texProgramInfo.program); twgl.setUniforms(texProgramInfo, uniforms); gl.drawArrays(gl.TRIANGLES, 0, 6); uniforms.u_worldViewProjection = m4.translation([0.01, -0.5, 0]); gl.useProgram(mipProgramInfo.program); twgl.setUniforms(mipProgramInfo, uniforms); gl.drawArrays(gl.TRIANGLES, 0, 6); requestAnimationFrame(render); } requestAnimationFrame(render); } main(); </script>
内容的提问来源于stack exchange,提问作者user128511
相关产品推荐
相关产品推荐

