如何将源纹理中的特定 mip 贴图级别复制到目标纹理中的特定 mip 贴图级别?

问题描述 投票:0回答:1

我将许多 2D 纹理合并到 WebGL2 ( Three.js ) 中的数组纹理中,以便通过多次绘制渲染模型。这些单独的纹理经常变化并且使用 mipmap。因为不可能只为数组纹理中的单个图层生成 mipmap,所以我想为 2d 目标生成 mipmap,然后将整个 mipmap 链复制到给定纹理的特定图层中。

但是,通过 WebGL API 来看,似乎无法从使用

texStorage2D
创建的纹理绑定特定的 mipmap 级别,并将其复制到 3d 纹理中图层的特定 mipmap 级别。这发生在 Three.js 中,但以下是正在完成的粗略复制命令:

_gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, image.width );
_gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, image.height );
_gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, minX );
_gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, minY );

gl.bindTexture( _gl.TEXTURE_2D, srcTextureHandle, _gl.TEXTURE0 );
gl.copyTexSubImage3D( _gl.TEXTURE_2D_ARRAY, level, dstX, dstY, dstZ, minX, minY, width, height );

我发现可以在

copyTexSubImage3D
的参数中设置目标 mip 级别,但我没有找到一种方法来指定要复制绑定纹理的 mip 级别。

我看到 OpenGLES3.2 规范中有一个 glCopyImageSubData 函数,允许指定源和目标 mip 级别,但浏览器中没有等效的函数。这在 WebGL2 中可能吗?

javascript three.js webgl webgl2
1个回答
0
投票

不可能只为数组纹理中的单个图层生成 mipmap

这是可能的。你只需要自己做而不是打电话

gl.generateMipmap

通过抽奖

canvas { border: 1px solid black; }
<canvas id="c"></canvas>
<script type="module">
import 'https://greggman.github.io/webgl-lint/webgl-lint.js';
import * as twgl from 'https://twgljs.org/dist/5.x/twgl-full.module.js';

const lerp = (a, b, t) => a + (b - a) * t;

const vsGenMipmap = `#version 300 es
out vec2 v_uv;

void main() {
  vec2 pos[3];
  pos[0] = vec2(-1.0, -1.0);
  pos[1] = vec2(-1.0,  3.0);
  pos[2] = vec2( 3.0, -1.0);

  vec2 xy = pos[gl_VertexID];
  gl_Position = vec4(xy, 0.0, 1.0);
  v_uv = xy * vec2(0.5, -0.5) + vec2(0.5);
}
`;

const fsGenMipmap = `#version 300 es
precision highp float;
uniform highp sampler2DArray tex;
uniform uint layer;
in vec2 v_uv;
out vec4 fragColor;
void main() {
  fragColor = texture(tex, vec3(v_uv, layer));
}
`;

const vsView = `#version 300 es
uniform vec2 pos;
void main() {
  gl_PointSize = 32.0;
  gl_Position = vec4(pos, 0, 1);
}
`;
const fsView = `#version 300 es
precision highp float;
uniform highp sampler2DArray tex;
uniform uint layer;
uniform uint mipLevel;
out vec4 fragColor;
void main() {
  fragColor = textureLod(tex, vec3(gl_PointCoord.xy, layer), float(mipLevel));
}
`;

/** @type HTMLCanvasElement */
const el = document.getElementById('c');
const gl = el.getContext('webgl2');
const genMipMapProgramInfo = twgl.createProgramInfo(gl, [vsGenMipmap, fsGenMipmap]);
const viewProgramInfo = twgl.createProgramInfo(gl, [vsView, fsView]);

const r = [255, 0, 0, 255];
const g = [0, 255, 0, 255];
const b = [0, 0, 255, 255];
const y = [255, 255, 0, 255];
const c = [0, 255, 255, 255];
const m = [255, 0, 255, 255];
const _ = [0, 0, 0, 255];
const w = [255, 255, 255, 255];

const layers = [
  new Uint8Array([
    r, r, g, g, r, r, b, b,
    r, r, g, g, r, r, b, b,
    g, g, r, g, b, b, r, r,
    g, g, r, g, b, b, r, r,
    r, r, g, g, r, r, b, b,
    r, r, g, g, r, r, b, b,
    g, g, r, g, b, b, r, r,
    g, g, r, g, b, b, r, r,
  ].flat()),
  new Uint8Array([
    y, y, c, c, y, y, m, m,
    y, y, c, c, y, y, m, m,
    c, c, y, c, m, m, y, y,
    c, c, y, c, m, m, y, y,
    y, y, c, c, y, y, m, m,
    y, y, c, c, y, y, m, m,
    c, c, y, c, m, m, y, y,
    c, c, y, c, m, m, y, y,
  ].flat()),
  new Uint8Array([
    w, w, _, _, w, w, _, _,
    w, w, _, _, w, w, _, _,
    _, _, w, _, _, _, w, w,
    _, _, w, _, _, _, w, w,
    w, w, _, _, w, w, _, _,
    w, w, _, _, w, w, _, _,
    _, _, w, _, _, _, w, w,
    _, _, w, _, _, _, w, w,
  ].flat()),
];

const width = 8;
const height = 8;
const numMipLevels = 4;
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D_ARRAY, tex);
gl.texStorage3D(gl.TEXTURE_2D_ARRAY, numMipLevels, gl.RGBA8, width, height, layers.length);
layers.forEach((data, layer) => {
  gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, layer, width, height, 1, gl.RGBA, gl.UNSIGNED_BYTE, data);
});


gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);

{
  gl.useProgram(genMipMapProgramInfo.program);
  const fb = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  for (let mipLevel = 1; mipLevel < numMipLevels; ++mipLevel) {
    const mipWidth = Math.max(1, width >> mipLevel);
    const mipHeight = Math.max(1, height >> mipLevel);
    gl.viewport(0, 0, mipWidth, mipHeight);
    for (let layer = 0; layer < layers.length; ++layer) {
      gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex, mipLevel, layer);
      gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAX_LEVEL, mipLevel - 1);
      gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_BASE_LEVEL, mipLevel - 1);
      gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAX_LOD, mipLevel - 1);
      twgl.setUniforms(genMipMapProgramInfo, {
        layer,
      });
      gl.drawArrays(gl.TRIANGLES, 0, 3);
    }
  }
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}

gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAX_LEVEL, 1000);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_BASE_LEVEL, 0);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAX_LOD, 1000);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);

gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(viewProgramInfo.program);
for (let layer = 0; layer < layers.length; ++layer) {
  const l = layer / (layers.length - 1);
  for (let mipLevel = 0; mipLevel < numMipLevels; ++mipLevel) {
    const m = mipLevel / (numMipLevels - 1);
    const pos = [
      lerp(-0.6, 0.6, l),
      lerp(-0.75, 0.75, m),
    ];
    twgl.setUniforms(viewProgramInfo, {
      pos,
      layer,
      mipLevel,
    });
    gl.drawArrays(gl.POINTS, 0, 1);
  }
}

</script>

通过 blitFramebuffer

canvas { border: 1px solid black; }
<canvas id="c"></canvas>
<script type="module">
import 'https://greggman.github.io/webgl-lint/webgl-lint.js';
import * as twgl from 'https://twgljs.org/dist/5.x/twgl-full.module.js';

const lerp = (a, b, t) => a + (b - a) * t;
const getMipSize = (size, mipLevel) => size.map(s => Math.max(1, s >> mipLevel));

const vsGenMipmap = `#version 300 es
out vec2 v_uv;

void main() {
  vec2 pos[3];
  pos[0] = vec2(-1.0, -1.0);
  pos[1] = vec2(-1.0,  3.0);
  pos[2] = vec2( 3.0, -1.0);

  vec2 xy = pos[gl_VertexID];
  gl_Position = vec4(xy, 0.0, 1.0);
  v_uv = xy * vec2(0.5, -0.5) + vec2(0.5);
}
`;

const fsGenMipmap = `#version 300 es
precision highp float;
uniform highp sampler2DArray tex;
uniform uint layer;
in vec2 v_uv;
out vec4 fragColor;
void main() {
  fragColor = texture(tex, vec3(v_uv, layer));
}
`;

const vsView = `#version 300 es
uniform vec2 pos;
void main() {
  gl_PointSize = 32.0;
  gl_Position = vec4(pos, 0, 1);
}
`;
const fsView = `#version 300 es
precision highp float;
uniform highp sampler2DArray tex;
uniform uint layer;
uniform uint mipLevel;
out vec4 fragColor;
void main() {
  fragColor = textureLod(tex, vec3(gl_PointCoord.xy, layer), float(mipLevel));
}
`;

/** @type HTMLCanvasElement */
const el = document.getElementById('c');
const gl = el.getContext('webgl2');
const genMipMapProgramInfo = twgl.createProgramInfo(gl, [vsGenMipmap, fsGenMipmap]);
const viewProgramInfo = twgl.createProgramInfo(gl, [vsView, fsView]);

const r = [255, 0, 0, 255];
const g = [0, 255, 0, 255];
const b = [0, 0, 255, 255];
const y = [255, 255, 0, 255];
const c = [0, 255, 255, 255];
const m = [255, 0, 255, 255];
const _ = [0, 0, 0, 255];
const w = [255, 255, 255, 255];

const layers = [
  new Uint8Array([
    r, r, g, g, r, r, b, b,
    r, r, g, g, r, r, b, b,
    g, g, r, g, b, b, r, r,
    g, g, r, g, b, b, r, r,
    r, r, g, g, r, r, b, b,
    r, r, g, g, r, r, b, b,
    g, g, r, g, b, b, r, r,
    g, g, r, g, b, b, r, r,
  ].flat()),
  new Uint8Array([
    y, y, c, c, y, y, m, m,
    y, y, c, c, y, y, m, m,
    c, c, y, c, m, m, y, y,
    c, c, y, c, m, m, y, y,
    y, y, c, c, y, y, m, m,
    y, y, c, c, y, y, m, m,
    c, c, y, c, m, m, y, y,
    c, c, y, c, m, m, y, y,
  ].flat()),
  new Uint8Array([
    w, w, _, _, w, w, _, _,
    w, w, _, _, w, w, _, _,
    _, _, w, _, _, _, w, w,
    _, _, w, _, _, _, w, w,
    w, w, _, _, w, w, _, _,
    w, w, _, _, w, w, _, _,
    _, _, w, _, _, _, w, w,
    _, _, w, _, _, _, w, w,
  ].flat()),
];

const width = 8;
const height = 8;
const numMipLevels = 4;
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D_ARRAY, tex);
gl.texStorage3D(gl.TEXTURE_2D_ARRAY, numMipLevels, gl.RGBA8, width, height, layers.length);
layers.forEach((data, layer) => {
  gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, layer, width, height, 1, gl.RGBA, gl.UNSIGNED_BYTE, data);
});


{
  gl.useProgram(genMipMapProgramInfo.program);
  const readFB = gl.createFramebuffer();
  const drawFB = gl.createFramebuffer();
  gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFB);
  gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawFB);
  for (let mipLevel = 1; mipLevel < numMipLevels; ++mipLevel) {
    const [readWidth, readHeight] = getMipSize([width, height], mipLevel - 1);
    const [drawWidth, drawHeight] = getMipSize([width, height], mipLevel);
    for (let layer = 0; layer < layers.length; ++layer) {
      gl.framebufferTextureLayer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex, mipLevel, layer);
      gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex, mipLevel - 1, layer);
      gl.blitFramebuffer(
        0, 0, readWidth, readHeight,
        0, 0, drawWidth, drawHeight,
        gl.COLOR_BUFFER_BIT,
        gl.LINEAR);
    }
  }
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}

gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAX_LEVEL, 1000);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_BASE_LEVEL, 0);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAX_LOD, 1000);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);

gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(viewProgramInfo.program);
for (let layer = 0; layer < layers.length; ++layer) {
  const l = layer / (layers.length - 1);
  for (let mipLevel = 0; mipLevel < numMipLevels; ++mipLevel) {
    const m = mipLevel / (numMipLevels - 1);
    const pos = [
      lerp(-0.6, 0.6, l),
      lerp(-0.75, 0.75, m),
    ];
    twgl.setUniforms(viewProgramInfo, {
      pos,
      layer,
      mipLevel,
    });
    gl.drawArrays(gl.POINTS, 0, 1);
  }
}

</script>

这些还应该建议如何从一层复制到另一层。通过

DRAW_FRAMEBUFFER
framebufferTextureLayer
中设置 mipmap/图层,然后进行 blit 或绘制。只需确保您正在读取的纹理是否相同,您的纹理参数设置不会导致反馈循环。

© www.soinside.com 2019 - 2024. All rights reserved.