2

I am trying to use the 3D textures in WebGL, but since it's not currently nativelly supported, I have to use to tricks with a 2D image. It is composed of 64 images, each are 64px by 64px (for a total of 64x4096). I wanted to post a link but I dont have enough xp on SO (seriously?), still you can find the file url in the fiddle lower.

I used the solution by gman as well as my own (nearest neighbor). In both cases, I have a strange rendering issue with grey stripe artifacts when I use an oblique projection (capture).

The stripes/dashes seem to appear in between the slices when the projection is oblique, and depending on the camera position, they do not appear at the same frequency. This is why I am thinking about a rendering issue rather than a sampling issue.

Does anyone know how to get rid of these artifacts? Is there some built-in OpenGL ES thing to enable/disable?

Here is the shader code:

<!-- FRAGMENT SHADER -->
<script id="fragment_shader1_custom3" type="x-shader/x-fragment">

uniform sampler2D texture;
uniform float imageIndex;
varying vec4 worldCoord;
varying vec2 vUv;

// 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);
}


void main( void ) {
  // the position within the shader
  vec2 shaderPos = vUv;

  // if outside the texture cube, use a yellow color
  if(worldCoord.x < 0.0 || worldCoord.x >= 1.0 || worldCoord.y < 0.0 || worldCoord.y >= 1.0 || worldCoord.z < 0.0 || worldCoord.z >= 1.0){
    gl_FragColor = vec4(1.0, 1.0 , 0.0, 1.0);
  }else{

    vec3 chunkPosition = vec3(worldCoord.x, worldCoord.y, worldCoord.z);

    // 63.0 instead of 64.0 to avoid a edge situation
    gl_FragColor = sampleAs3DTexture(texture, chunkPosition, 63.0, 64.0, 1.0);

  }
}
</script> <!-- END FRAGMENT SHADER -->



<!-- VERTEX SHADER -->
<script id="vertexShader_custom1" type="x-shader/x-vertex">
varying vec2 vUv;
varying vec4 worldCoord;

void main()
{
  vUv = uv;
  vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
  gl_Position = projectionMatrix * mvPosition;
  worldCoord = modelMatrix * vec4( position, 1.0 );
}
</script> <!-- END VERTEX SHADER -->

Here is the fiddle.

Note that I use ThreeJS. Thanks for your help.

  • I manage to get rid of the artifacts. Works well in my case, but is not really a general fix ``` var texture = new THREE.TextureLoader().load( texFile ); texture.magFilter = THREE.NearestFilter; texture.minFilter = THREE.NearestFilter; ``` – Jonathan Lurie Oct 25 '16 at 19:00
  • Try to adjust the settings of your texture when you load it. You want to disable some features that work well for 2D texture but will be a problem for a 3D on. I would try first to set the .magFilter to THREE.NearestFilter. BTW the fiddle is not working because of cross site security. – Arnaud Oct 25 '16 at 19:38
  • Oh, I see you tried and it worked, even before I wrote my comment. good. – Arnaud Oct 25 '16 at 19:40
  • oh, that's the minFilter, not the magFilter that was faultly, you're right – Arnaud Oct 25 '16 at 19:59
  • @Arnaud Thanks! Btw I dont know about the cross origin, I found imgur was working for that, do you have any other recomendation/alternative for the future? – Jonathan Lurie Oct 26 '16 at 13:12
  • Sorry no idea how to fix the cross origin problem. I never used Imgur. In your fiddle, I pressed shift-ctrl-K to get the js console and there, one of the messages was: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://imgur.com/htmv5fE. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). I have not tried to look at what a CORS header is. – Arnaud Oct 26 '16 at 19:58

1 Answers1

1

As you found by yourself, the solution was to set texture.minFilter = THREE.NearestFilter after loading the texture with THREE.TextureLoader() in your js code (for this reason, please append the js code in your question, this may help a future user that would step on this question on stackoverflow).

Now an explanation of the stripes: when your pixel is close to one of the planes where you change slice, the variable sliceZ jumps. You get in the situation where the pixel covers more than one texel. Note that part of the pixel is at some v coordinate, and part of the pixel is at a v coordinate that is bigger by a quantity that corresponds to 64 pixels of the texture (to get to the image below)/ My guess is that the hidden machinery believes that your pixel covers a v-span of 64 texture pixels and thus takes the average value of those pixels (that has been pre-computed by the default mip-map setting in .minFilter).

Arnaud
  • 247
  • 1
  • 8