0

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

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

所有解决 WebGL 中缺少 3D 纹理的算法都有一个“静默”的先决条件,即图像格式的纹理图集已经存在。

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

  1. 假设硬件支持这个 2D 纹理图集的最终大小,我如何将已经加载的纹理定制为一个 2D 纹理并将其提供给着色器?

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

4

1 回答 1

1

最简单的方法可能是将纹理加载到 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 画布始终使用预乘 alpha,因此无法将所有 4 个通道与 2d 画布一起使用。

将纹理绘制成纹理与绘制周期相同。查看任何绘制到纹理中的示例

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就是您的地图集纹理。只需像使用任何纹理一样使用纹理。将其分配给材质。

于 2015-06-16T08:20:46.907 回答