opengl-es之使用 2D 纹理解决方法的 WebGL/three.js 中的 3D 纹理

xiaohuochai 阅读:71 2025-06-02 22:19:02 评论:0

我想为我在 WebGL 中渲染的对象使用一些 3D 纹理。我目前正在片段着色器中使用以下方法,如 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 到目前为止还没有找到可行的解决方案。

请您参考如下方法:

看起来相对简单。如果您还想向下移动图像,则必须计算一个 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);
canvas {  
    border: 1px solid black; 
    margin: 2px; 
}
<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>


标签:WEB
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号