Создать 2D текстуру из существующих текстур

У меня есть приложение, в котором я загружаю серию медицинских изображений (jpg), создаю текстурный объект для каждого изображения и отображаю эти текстуры на трехмерных плоскостях. Количество изображений зависит от разрешения сканера КТ, но мой прототип должен работать до 512.

В качестве дополнительной задачи я хочу использовать эти текстуры для выполнения объемного рендеринга на отдельном трехмерном холсте.

Все алгоритмы, которые решают проблему отсутствия 3D-текстур в WebGL, имеют "молчаливое" условие, что текстурный атлас в формате изображения уже существует.

В моем случае, однако, у меня нет такого атласа.

  1. Предполагая, что аппаратное обеспечение поддерживает итоговый размер этого 2D-текстурного атласа, как я могу адаптировать уже загруженные текстуры в одну 2D-текстуру и передать ее в шейдер?

  2. Я подумал о том, чтобы объединить данные каждого изображения в один массив и создать для него THREE.DataTexture. Но я не смог найти способ прочитать данные изображения из текстуры, которая его использует. Есть ли другой способ извлечь данные из загруженного изображения?

1 ответ

Решение

Самый простой способ - это загрузить ваши текстуры в двухмерный холст для создания атласа.

Давайте предположим, что у нас есть функция, которая загружает 512 текстур, и мы хотим поместить их в сетку 32 на 16

var width = 128;  // just assuming this is the size of a single texture
var height = 128; 
var across = 32;
var down = 16;

asyncLoad512Images(useLoaded512Images);

function useLoaded512Images(arrayOfImages) {
  var canvas = document.createElement("canvas");
  canvas.width = width * across;
  canvas.height = height * down;
  var ctx = canvas.getContext("2d");

  // draw all the textures into the canvas
  arrayOfImagess.forEach(function(image, ndx) {
    var x = ndx % across;
    var y = Math.floor(ndx / across);

    ctx.drawImage(image, x * width, y * height);
  }

  // now make a texture from canvas.
  var atlasTex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, atlasTex);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,
                canvas);
}

Некоторые оптимизации: Вы можете изменить код, чтобы создать холст в начале и, когда каждое изображение загружается, нарисовать его на 2d холсте в правильном месте. Преимущество будет в том, что браузеру не нужно будет хранить все 512 изображений в памяти. Он может отказаться от каждого сразу после того, как вы его нарисовали.

var width = 128;  // just assuming this is the size of a single texture
var height = 128; 
var across = 32;
var down = 16;
var numImages = 32 * 16;
var numImagesDownloaded = 0;

// make a canvas to hold all the slices
var canvas = document.createElement("canvas");
canvas.width = width * across;
canvas.height = height * down;
var ctx = canvas.getContext("2d");

// assume the images are named image-x.png
for (var ii = 0; ii < numImages; ++ii) {
  loadImage(ii);
}

function loadImage(num) {
  var img = new Image();
  img.onload = putImageInCanvas(img, num);
  img.src = "image-" + num + ".png";
}

function putImageInCanvas(img, num) {
  var x = num % across;
  var y = Math.floor(num / across);

  ctx.drawImage(img, x * width, y * height);

  ++numImagesDownloaded;
  if (numImagesDownloaded === numImages) {
    // now make a texture from canvas.
    var atlasTex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, atlasTex);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,
                  canvas);
    ....
  }
}

В качестве альтернативы вы можете превратить каждое изображение в текстуру и использовать текстуру, прикрепленную к кадровому буферу, для рендеринга текстуры изображения в текстуру атласа. Это больше работы. Вам нужно сделать простую 2d пару шейдеров, а затем визуализировать каждую текстуру изображения в атласную текстуру в правильном месте.

Единственная причина сделать это, если у текстур есть 4 канала данных вместо 3 или меньше, так как невозможно использовать все 4 канала с 2d canvas, так как 2d canvas всегда использует предварительно умноженную альфа.

Рисование текстуры в текстуру аналогично рисованию периода. Смотрите любой пример, который рисует в текстуру.

Короткая версия в Three.js есть,

сделать цель рендеринга

rtTexture = new THREE.WebGLRenderTarget( 
      width * across, height * down, { 
        minFilter: THREE.LinearFilter, 
        magFilter: THREE.NearestFilter, 
        format: THREE.RGBFormat,
        depthBuffer: false,
        stencilBuffer: false,
    } ); 
 rtTexture.generateMipmaps = false;

Установите плоскость и материал для рендеринга, поместите его в сцену. Для каждой текстуры изображения установите материал для использования этой текстуры изображения и настройте любые другие параметры, чтобы нарисовать квад, в котором вы хотите, чтобы он был нарисован в текстуре атласа. Я предполагаю, что орфографическая камера сделает это проще всего. Затем вызовите рендер с целью рендеринга.

renderer.autoClear = false;
renderer.render( sceneRTT, cameraRTT, rtTexture, false );

Это будет оказывать rtTexture,

Когда вы закончите rtTexture это ваша текстура атласа. Просто используйте текстуру как любую текстуру. Присвойте это материалу.

Другие вопросы по тегам