使用2D纹理解决方法的WebGL / three.js中的3D纹理?

时间:2014-04-11 07:38:45

标签: opengl-es three.js webgl fragment-shader 3d-texture

我想将一些3D纹理用于我在WebGL中渲染的对象。我正在片段着色器中使用以下方法,如WebGL and OpenGL Differences所示:

// tex is a texture with each slice of the cube placed horizontally across the texture.
// texCoord is a 3d texture coord
// size is the size if the cube in pixels.

vec4 sampleAs3DTexture(sampler2D tex, vec3 texCoord, float size) {
   float sliceSize = 1.0 / size;                         // space of 1 slice
   float slicePixelSize = sliceSize / size;              // space of 1 pixel
   float sliceInnerSize = slicePixelSize * (size - 1.0); // space of size pixels
   float zSlice0 = min(floor(texCoord.z * size), size - 1.0);
   float zSlice1 = min(zSlice0 + 1.0, size - 1.0);
   float xOffset = slicePixelSize * 0.5 + texCoord.x * sliceInnerSize;
   float s0 = xOffset + (zSlice0 * sliceSize);
   float s1 = xOffset + (zSlice1 * sliceSize);
   vec4 slice0Color = texture2D(tex, vec2(s0, texCoord.y));
   vec4 slice1Color = texture2D(tex, vec2(s1, texCoord.y));
   float zOffset = mod(texCoord.z * size, 1.0);
   return mix(slice0Color, slice1Color, zOffset);
}

问题是我可以使用的最大3D纹理是64x64x64(因为最大2D纹理宽度是4096 = 64 * 64)。如果可能的话,我想尝试使用更大的纹理,所以我想看看是否有人建议使用更高分辨率的3D纹理和类似的解决方法。据推测,我应该能够组织2D纹理,使得我可以水平和垂直排列3D切片,但到目前为止,我的google-fu还没有找到可行的解决方案。

1 个答案:

答案 0 :(得分:6)

似乎比较直接。如果您想要向下移动图像,则必须计算v纹理坐标,为切片选择正确的。要做到这一点,你需要知道纹理中有多少行以及每行有多少个切片

// tex is a texture with each slice of the cube placed in grid in a texture.
// texCoord is a 3d texture coord
// size is the size if the cube in pixels.
// slicesPerRow is how many slices there are across the texture
// numRows is the number of rows of slices

vec2 computeSliceOffset(float slice, float slicesPerRow, vec2 sliceSize) {
  return sliceSize * vec2(mod(slice, slicesPerRow), 
                          floor(slice / slicesPerRow));
}

vec4 sampleAs3DTexture(
    sampler2D tex, vec3 texCoord, float size, float numRows, float slicesPerRow) {
  float slice   = texCoord.z * size;
  float sliceZ  = floor(slice);                         // slice we need
  float zOffset = fract(slice);                         // dist between slices

  vec2 sliceSize = vec2(1.0 / slicesPerRow,             // u space of 1 slice
                        1.0 / numRows);                 // v space of 1 slice

  vec2 slice0Offset = computeSliceOffset(sliceZ, slicesPerRow, sliceSize);
  vec2 slice1Offset = computeSliceOffset(sliceZ + 1.0, slicesPerRow, sliceSize);

  vec2 slicePixelSize = sliceSize / size;               // space of 1 pixel
  vec2 sliceInnerSize = slicePixelSize * (size - 1.0);  // space of size pixels

  vec2 uv = slicePixelSize * 0.5 + texCoord.xy * sliceInnerSize;
  vec4 slice0Color = texture2D(tex, slice0Offset + uv);
  vec4 slice1Color = texture2D(tex, slice1Offset + uv);
  return mix(slice0Color, slice1Color, zOffset);
  return slice0Color;
}

这是一个片段



var canvas = document.getElementById("c");
var gl = canvas.getContext("webgl");
var program = twgl.createProgramFromScripts(
    gl, ["vshader", "fshader"], ["a_position"]);
gl.useProgram(program);

var sizeLoc = gl.getUniformLocation(program, "u_size");
var numRowsLoc = gl.getUniformLocation(program, "u_numRows");
var slicesPerRowLoc = gl.getUniformLocation(program, "u_slicesPerRow");

// make sphere triangles
var numDivisionsAround = 32;
var numDivisionsDown = 16;
var verts = [];
for (var v = 0; v < numDivisionsDown; ++v) {
  var v0 = Math.sin((v + 0) / numDivisionsDown * Math.PI);
  var v1 = Math.sin((v + 1) / numDivisionsDown * Math.PI);
  var y0 = Math.cos((v + 0) / numDivisionsDown * Math.PI);
  var y1 = Math.cos((v + 1) / numDivisionsDown * Math.PI);
  for (var h = 0; h < numDivisionsAround; ++h) {
    var a0 = (h + 0) * Math.PI * 2 / numDivisionsAround;
    var a1 = (h + 1) * Math.PI * 2 / numDivisionsAround;
    var x00 = Math.sin(a0) * v0;
    var x10 = Math.sin(a1) * v0;
    var x01 = Math.sin(a0) * v1;
    var x11 = Math.sin(a1) * v1;
    var z00 = Math.cos(a0) * v0;
    var z10 = Math.cos(a1) * v0;
    var z01 = Math.cos(a0) * v1;
    var z11 = Math.cos(a1) * v1;
    verts.push(
      x00, y0, z00, 
      x10, y0, z10, 
      x01, y1, z01,
      
      x01, y1, z01, 
      x10, y0, z10, 
      x11, y1, z11);
  }
}


var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);

// Make 3D texture
var size = 8;
var slicesPerRow = 4;
var numRows = Math.floor((size + slicesPerRow - 1) / slicesPerRow); 
var pixels = new Uint8Array(size * slicesPerRow * size * numRows * 4);
var pixelsAcross = slicesPerRow * size;
for (var slice = 0; slice < size; ++slice) {
    var row = Math.floor(slice / slicesPerRow);
    var xOff = slice % slicesPerRow * size;
    var yOff = row * size;    
    for (var y = 0; y < size; ++y) {
        for (var x = 0; x < size; ++x) {
            var offset = ((yOff + y) * pixelsAcross + xOff + x) * 4;
            pixels[offset + 0] = x / size * 255;
            pixels[offset + 1] = y / size * 255;
            pixels[offset + 2] = slice / size * 255;
            pixels[offset + 3] = 255;
        }
    }
}
// put this in a 2d canvas for debugging
var c = document.createElement("canvas");
c.width = size * slicesPerRow;
c.height = size * numRows;
document.body.appendChild(c);
var ctx = c.getContext("2d");
var id = ctx.getImageData(0, 0, c.width, c.height);
var numBytes = c.width * c.height * 4;
for (var ii = 0; ii < numBytes; ++ii) {
    id.data[ii] = pixels[ii];
}
ctx.putImageData(id, 0, 0);

var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size * slicesPerRow, numRows * size, 0,
              gl.RGBA, gl.UNSIGNED_BYTE, pixels);

var log = console.log.bind(console);
log("size        : " + size);
log("numRows     : " + numRows);
log("slicesPerRow: " + slicesPerRow);

gl.uniform1f(sizeLoc, size);
gl.uniform1f(numRowsLoc, numRows);
gl.uniform1f(slicesPerRowLoc, slicesPerRow);

// draw circle
gl.enable(gl.DEPTH_TEST);
gl.drawArrays(gl.TRIANGLES, 0, verts.length / 3);
&#13;
canvas { 
    border: 1px solid black;
    margin: 2px;
}
&#13;
<script src="https://twgljs.org/dist/3.x/twgl.min.js"></script>
<script id="vshader" type="whatever">
    attribute vec4 a_position;
    varying vec3 v_texcoord;
    void main() {
      gl_Position = a_position;
      v_texcoord = a_position.xyz * 0.5 + 0.5;
    }    
</script>
<script id="fshader" type="whatever">
    precision mediump float;
    
    // tex is a texture with each slice of the cube placed in grid in a texture.
    // texCoord is a 3d texture coord
    // size is the size if the cube in pixels.
    // slicesPerRow is how many slices there are across the texture
    // numRows is the number of rows of slices

    vec2 computeSliceOffset(float slice, float slicesPerRow, vec2 sliceSize) {
      return sliceSize * vec2(mod(slice, slicesPerRow), 
                              floor(slice / slicesPerRow));
    }
    
    vec4 sampleAs3DTexture(
        sampler2D tex, vec3 texCoord, float size, float numRows, float slicesPerRow) {
      float slice   = texCoord.z * size;
      float sliceZ  = floor(slice);                         // slice we need
      float zOffset = fract(slice);                         // dist between slices
    
      vec2 sliceSize = vec2(1.0 / slicesPerRow,             // u space of 1 slice
                            1.0 / numRows);                 // v space of 1 slice
    
      vec2 slice0Offset = computeSliceOffset(sliceZ, slicesPerRow, sliceSize);
      vec2 slice1Offset = computeSliceOffset(sliceZ + 1.0, slicesPerRow, sliceSize);
    
      vec2 slicePixelSize = sliceSize / size;               // space of 1 pixel
      vec2 sliceInnerSize = slicePixelSize * (size - 1.0);  // space of size pixels

      vec2 uv = slicePixelSize * 0.5 + texCoord.xy * sliceInnerSize;
      vec4 slice0Color = texture2D(tex, slice0Offset + uv);
      vec4 slice1Color = texture2D(tex, slice1Offset + uv);
      return mix(slice0Color, slice1Color, zOffset);
      return slice0Color;
    }
    
    varying vec3 v_texcoord;
    
    uniform float u_size;
    uniform float u_numRows;
    uniform float u_slicesPerRow;    
    uniform sampler2D u_texture;
    
    void main() {
         gl_FragColor = sampleAs3DTexture(
             u_texture, v_texcoord, u_size, u_numRows, u_slicesPerRow);
    }
</script>
<canvas id="c" width="400" height="400"></canvas>
&#13;
&#13;
&#13;