Создать 2D текстуру из существующих текстур
У меня есть приложение, в котором я загружаю серию медицинских изображений (jpg), создаю текстурный объект для каждого изображения и отображаю эти текстуры на трехмерных плоскостях. Количество изображений зависит от разрешения сканера КТ, но мой прототип должен работать до 512.
В качестве дополнительной задачи я хочу использовать эти текстуры для выполнения объемного рендеринга на отдельном трехмерном холсте.
Все алгоритмы, которые решают проблему отсутствия 3D-текстур в WebGL, имеют "молчаливое" условие, что текстурный атлас в формате изображения уже существует.
В моем случае, однако, у меня нет такого атласа.
Предполагая, что аппаратное обеспечение поддерживает итоговый размер этого 2D-текстурного атласа, как я могу адаптировать уже загруженные текстуры в одну 2D-текстуру и передать ее в шейдер?
Я подумал о том, чтобы объединить данные каждого изображения в один массив и создать для него 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
это ваша текстура атласа. Просто используйте текстуру как любую текстуру. Присвойте это материалу.