即使问题中的示例是 WebGL,也将其标记为 OpenGL,因为 OpenGL 领域专家应该能够回答这个问题。这里有一个 OpenGL 存储库
我正在尝试以与采样器基于纹理坐标导数相同的方式计算纹理 mip 级别。
我在这里和其他地方看到了很多答案。例如
所有这 3 个都说计算是
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);
鉴于此,我写了一个测试。在测试中我有 2 个着色器。
第一个着色器使用纹理坐标和采样纹理来绘制四边形。纹理有 7 个 mip 级别,每个级别都是不同的纯色。当我增加纹理坐标的范围时,我应该看到不同的 mip 被选择(使用 NEAREST_MIPMAP_NEAREST)。这有效?
第二个着色器通过使用上面的公式计算 mip 级别,然后使用该 mip 级别从表中选择颜色来绘制四边形,表中的颜色与纹理中的颜色相匹配。这不起作用?。我所看到的都是红色(第一种颜色)。
这是第二个着色器
#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 级别
mipLevel / 7.0
,因此当 mip 级别达到 7 时,四边形应该变得更亮。这是行不通的。
我究竟做错了什么?
html, body {
margin: 0;
font-family: monospace;
height: 100%;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
<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>
mipmap 级别还取决于纹理的大小。mipmap 级别是对应将多少(方形)纹理元素压缩到一个片段中的度量。在链接问题(如何在 GLSL 片段着色器纹理中访问自动 mipmap 级别?)的答案中,mipmap 级别是根据纹理坐标乘以纹理大小计算得出的:
您错过了代码中的那部分。所以公式应该是:
另外,当您查找颜色时,您需要对 mipmap 级别进行舍入:
outColor = colors[int(mipLevel)];