从现有纹理创建2D纹理

时间:2015-06-15 16:00:00

标签: three.js texture-atlas

我有一个应用程序,我加载一系列医学图像(jpg),为每个图像创建纹理对象,并在3D平面上显示这些纹理。图像数量取决于CT扫描仪的分辨率,但我的原型最多可以使用512。

作为一项额外任务,我想使用这些纹理在单独的3D画布上执行体积渲染。

所有解决WebGL缺乏3D纹理的算法都具有静音"已经存在图像格式的纹理图集的先决条件。

但就我而言,我没有这样的地图集。

  1. 假设硬件支持这个2D纹理图集的最终尺寸,我怎么能将已经加载的纹理定制成一个单独的2D纹理并将其提供给着色器?

  2. 我考虑过将每个图像的数据合并为一个数组,然后创建THREE.DataTexture。但是我找不到从使用它的纹理中读取图像数据的方法。是否有其他方法来提取加载图像的数据?

1 个答案:

答案 0 :(得分:0)

最简单的方法可能是将纹理加载到2D画布中以构建地图集。

假设我们有一个下载512个纹理的函数,我们希望将它们放在32 x 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着色器,然后将每个图像纹理渲染到正确位置的atlas纹理。

这样做的唯一原因是,如果纹理有4个数据通道而不是3个或更少,因为没有办法使用2d画布的所有4个通道,因为2d画布总是使用预乘alpha。

将纹理绘制到纹理中与绘制周期相同。 See any example that draws into a texture

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;

设置要渲染的平面和材质,将其放入场景中。对于每个图像纹理,设置材质以使用该图像纹理并设置任何其他参数,以使其绘制四边形,以便将其绘制到图集纹理中。 I'm guessing an orthographic camera would make that easiest。然后使用渲染目标调用渲染。

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

这将呈现给rtTexture

当你完成rtTexture是你的地图集纹理。只需像任何纹理一样使用纹理。将其分配给材料。