It is currently Sat Aug 22, 2020 3:36 am


All times are UTC




Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: Texture atlases, bleeding, mipmaps & filtering
PostPosted: Tue Mar 01, 2011 12:14 pm 

Joined: Tue Feb 22, 2011 10:21 pm
Posts: 12
Hi fellow developers!

I have an issue regarding texture atlases and "texture bleeding" using mipmaps and anisotropic (or linear) filtering.

I know that this is not directly related to PolyVox, but I thought that since there are a few people on this forum that are developing a "cube game" using PolyVox seemed acceptable.
By the way, why not creating a forum category for stuff like this ? Or more directly with integrating PolyVox with other libraries ?

So, I'm trying to put some textures on my Ogre3D cubes (generated from PolyVox) using an atlas.
I'm using the method described here : http://www.ogre3d.org/forums/viewtopic.php?f=4&t=61602
I read this topic also : viewtopic.php?f=2&t=77 (very interresting, and thanks to AndiNo for the texture, it's very useful for debugging)

When using point filtering and no mipmapping it works but it's not very beautiful...
Attachment:
point.png
point.png [ 127.16 KiB | Viewed 22377 times ]


When using linear filtering or mipmaps :
Attachment:
bilinear.png
bilinear.png [ 221.91 KiB | Viewed 22377 times ]

Attachment:
mipmaps.png
mipmaps.png [ 167.19 KiB | Viewed 22377 times ]


Note : the light green zone represents one cube size.

Here is my material file :
Code:
vertex_program CubeVP cg
{
    source Cube.cg
    entry_point CubeVP
    profiles vs_3_0 vs_2_x vs_2_0 vs_1_1 vp40 vp30 vp20 arbvp1

    default_params
    {
        param_named_auto world world_matrix
        param_named_auto viewProj viewproj_matrix
    }
}


fragment_program CubeFP cg
{
    source Cube.cg
    entry_point CubeFP
    profiles ps_3_x ps_3_0 ps_2_x ps_2_0 ps_1_4 ps_1_3 ps_1_2 ps_1_1 fp40 fp30 fp20 arbfp1

    default_params
    {
    }
}

material Cube
{
    technique
    {               
        pass
        {                   
            // Vertex program reference
            vertex_program_ref CubeVP
            {
            }

            // Fragment program
            fragment_program_ref CubeFP
            {
            }   

            texture_unit
            {
            texture atlas20.dds 2d
            filtering linear linear point
            max_anisotropy 1
            tex_address_mode wrap
            }       
        }
    }
}

My shader file :
Code:
void CubeVP(
   float4 inPosition         : POSITION,
   float4 inColor              : COLOR,

   out float4 outClipPosition   : POSITION,
   out float4 outWorldPosition   : TEXCOORD0,
   out float4 outColor           : TEXCOORD1,

   uniform float4x4 world,
   uniform float4x4 viewProj)
{   
   //Compute the world space position
   outWorldPosition = mul(world, inPosition);   

   //Compute the clip space position
   outClipPosition = mul(viewProj, outWorldPosition);

   //Copy the color
   outColor = inColor;
}

void CubeFP(
   float4 inPosition      : POSITION,
   float4 inWorldPosition   : TEXCOORD0,
   float4 inColor         : TEXCOORD1,

   uniform sampler2D texture   : TEXUNIT0,

   out float4 result      : COLOR)

   //Calculate the normals
   float3 worldNormal = cross(ddy(inWorldPosition.xyz), ddx(inWorldPosition.xyz));
   worldNormal = normalize(worldNormal);

   float2 uv;
   if(worldNormal.x > 0.5)
      uv = inWorldPosition.yz;

   if(worldNormal.x < -0.5)
      uv = inWorldPosition.yz;

   if(worldNormal.y > 0.5)//Down
      uv = inWorldPosition.xz;

   if(worldNormal.y < -0.5)//Up
      uv = inWorldPosition.xz;

   if(worldNormal.z > 0.5)
      uv = inWorldPosition.xy;

   if(worldNormal.z < -0.5)
      uv = inWorldPosition.xy;

   uv = frac(uv / 2 - 0.5);
   uv = uv / 4;

   result = tex2D(texture, uv);

}

My atlas :
http://www.jmgr.info/atlas20.dds

So I'm trying to use only the "middle" of the texture and repeating it so that the "bleeding" disappears.
But there my knowledge of shaders is too limited so after several hours of fruitless efforts I'm asking you :)

Do you have any idea how I could achieve this ?


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Texture atlases, bleeding, mipmaps & filtering
PostPosted: Tue Mar 01, 2011 6:30 pm 

Joined: Sat Sep 18, 2010 9:45 pm
Posts: 189
Okay I sort of have this working. By that I mean when I zoom in I still see a slight border, i.e. it's still buggy. Example:

http://imgur.com/a/jGbd1#WukBt

The first image you can see see a border around it because I think my texture access is still off by one. I just don't have time to fix it right now.

Anyway, here is what I got... There are several things you need to be aware of:

1) The images of the atlas start in the middle. Therefore, you have to shift each by 0.25 in atlas local coordinate.
2) Since every image is in the middle, the width of each image is only 0.5. (This is assuming all "images" are uniform size; if they are non-uniform, you have to store extra information per image).
3) If you are generating per texture texture coordinates from the world position, you have to be make sure they align. Note: You may not have to do this if don't generate tex. coords from world position. I will get to this in a min.

Here are the shader code (it's all Cg): WARNING THis will not work as-is for GLSL since the texture origin are different.

The vertex program:

When I say image, I mean the actual image. A texture atlas is just the entire texture atlas.

* Here I do a TEX_WIDTH / ATLAS_WIDTH. It's really just 1.0 / 16.0 assuming 16 texture per row. This
is the fractional scale into texture atlas space.

* uv1 (texture coordinate 0) is the texture block ID. The index of a block is from 0 to 256. To get it into
the texture coordinates into the range [0, 1] I divide by 256.0. Then in the vertex shader I get it back
into the range of [0, 256]. There may be other ways to do this it's just how I'm currently hacking it.

* from the texture block id, I then compute texture atlas row and block x,y. It's just a 1D to 2D array index thing.

* I output the "worldPos" of the cube, which really is the center of a cube. Also, it's not really world coordinate. It's a local coordinate in "chunk space". So if my chunk is 32 x 32 x 256 (i.e. x in [0, 32], y in [0, 256], z in [0, 32]), "worldPos" will range in this.

Code:
#define ATLAS_WIDTH 4096.0
#define TEX_WIDTH 256.0
#define eOffset TEX_WIDTH / ATLAS_WIDTH

VS_OUT main_vp(
          float4 pos : POSITION,
          float4 uv1 : TEXCOORD0,
#if !TEXTURE_ATLAS
          float3 normal : NORMAL,
#endif
          uniform float4x4 worldViewProj
          )
{

  VS_OUT outp;
  outp.pos = mul(worldViewProj, pos);
#if TEXTURE_ATLAS
  outp.worldPos = pos + float4(0.5f, 0.5f, 0.5f, 0.5f);//mul(worldMat, pos);
  outp.textureAtlasOffset = float4(0.0f, 0.0f, 0.0f, 0.0f);
 
  float idx = uv1.x * 256.0 -1;
  float blocky = floor(idx / 16.0);
  float blockx = (idx - blocky * 16.0);
 
  outp.textureAtlasOffset = float4(blockx + 0.25f, blocky + 0.25f, 0, 0) * eOffset ;
#else
  outp.worldPos = float4(normal, 0.0);
  outp.textureAtlasOffset = uv1;
#endif

  return outp;
}


The pixel shader:

*I compute per image UV for each image that is in the range of [0, 1] from the interpolated worldPos. Basically, from the vertex shader, textureAtlasOffset is the origin of each image in texture atlas coordinates (remember we offset by 0.25 to get to the middle of the position; I should rename it as image origin). Next, we compute the actual texture atlas uv by adding per image uv to textureAtlasOffset (origin). Also remember that the actual image is in the middle, therefore scale by 0.5.

*Make sure you texture image "align" properly. This because how the per image UV is computed from worldPos.

*Lastly, this is just how I'm hacking it. There may be other ways to do this so to avoid some of this.

Code:
--SNIPPED--

#if TEXTURE_ATLAS

 float3 normal = cross(ddy(In.worldPos.xyz),ddx(In.worldPos.xyz));
 normal = normalize(normal);
#else
 float3 normal = In.worldPos.xyz;
#endif
 float3 n = float3(-normal.z, -normal.x, normal.y);

--snipped SH lighting stuff--

#if TEXTURE_ATLAS
  float2 uv0 = float2(1.0, 1.0);
  if(normal.x > 0.5)
    {
      uv0 = frac(float2(-In.worldPos.z, -In.worldPos.y));
    }
 
  if(normal.x < -0.5)
    {
      uv0 = frac(float2(-In.worldPos.z, In.worldPos.y));
      }
  //top
 
  if(normal.y > 0.5)
    {
      uv0 = frac(In.worldPos.xz);
    }
 
  //bottom
  if(normal.y < -0.5)
    {
      uv0 = frac(float2(-In.worldPos.x, In.worldPos.z));
    }
 
  if(normal.z > 0.5)
    {
      uv0 = frac(float2(In.worldPos.x, -In.worldPos.y));
      //uv0 = float2(In.worldPos.x, -In.worldPos.y);
    }
  if(normal.z < -0.5)
    {
      uv0 = frac(float2(-In.worldPos.x,-In.worldPos.y));
    }
 
  In.textureAtlasOffset += float4(uv0 * 0.5, 0.0, 0.0) * eOffset; /// 2.0);

#endif
  float4 texDiffuse = tex2D(diffuseMap, In.textureAtlasOffset.xy);
  float nightMulti;

  nightMulti = 1.0;
  if(uLightY < 0.0)
    nightMulti = lerp(0.05, 0.1, -uLightY);
 
  float4 vColor = float4(texDiffuse.xyz * diffuseColor.xyz * nightMulti,texDiffuse.w);
  //float4 vColor = float4(diffuseColor.xyz * nightMulti, 1.0);
  return vColor;
}



Top
Offline Profile  
Reply with quote  
 Post subject: Re: Texture atlases, bleeding, mipmaps & filtering
PostPosted: Tue Mar 01, 2011 8:13 pm 
Developer
User avatar

Joined: Sun May 04, 2008 6:35 pm
Posts: 1827
I'm not using texture atlases myself so I don't have any sample code to give you, but you might want to look at this thread:

http://www.ogre3d.org/forums/viewtopic.php?f=4&t=61602

I think it might solve the bleeding problem completly (I'm not certain) but at the expense of doubling your texture resolution.


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Texture atlases, bleeding, mipmaps & filtering
PostPosted: Tue Mar 01, 2011 8:30 pm 

Joined: Sat Sep 18, 2010 9:45 pm
Posts: 189
yes that's what he is working off of. He is having issues with the texture addressing.


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Texture atlases, bleeding, mipmaps & filtering
PostPosted: Tue Mar 01, 2011 9:14 pm 
Developer
User avatar

Joined: Sun May 04, 2008 6:35 pm
Posts: 1827
Ah, sorry, I should have read that more carefully.


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Texture atlases, bleeding, mipmaps & filtering
PostPosted: Tue Mar 01, 2011 11:38 pm 

Joined: Tue Feb 22, 2011 10:21 pm
Posts: 12
It works perfectly, thanks beyzend!

Here is my shader code :
Code:
#define ATLAS_WIDTH 1024.0
#define TEXTURE_WIDTH 256.0
#define RATIO TEXTURE_WIDTH / ATLAS_WIDTH

void CubeVP(
   float4 position         : POSITION,
   float4 tile              : COLOR,

   out float4 clipPosition      : POSITION,
   out float4 worldPosition   : TEXCOORD0,
   out float4 textureAtlasOffset   : TEXCOORD1,

   uniform float4x4 world,
   uniform float4x4 viewProj)
{   
   worldPosition = mul(world, position);   
   clipPosition = mul(viewProj, worldPosition);

   float tileX = ((tile.r != 0.0) ? (1.0 / tile.r) : 0.0);
   float tileY = ((tile.g != 0.0) ? (1.0 / tile.g) : 0.0);

   textureAtlasOffset = float4(tileX + 0.25f, tileY + 0.25f, 0, 0) * RATIO;
}

void CubeFP(
   float4 position         : POSITION,
   float4 worldPosition      : TEXCOORD0,
   float4 textureAtlasOffset   : TEXCOORD1,

   uniform sampler2D texture   : TEXUNIT0,

   out float4 result      : COLOR)

   float3 worldNormal = cross(ddy(worldPosition.xyz),ddx(worldPosition.xyz));
   worldNormal = normalize(worldNormal);

   worldPosition -= 0.5;

   float2 uv = float2(1.0, 1.0);

   if(worldNormal.x > 0.5)
      uv = frac(float2(-worldPosition.z, -worldPosition.y));

   if(worldNormal.x < -0.5)
      uv = frac(float2(-worldPosition.z, worldPosition.y));

   if(worldNormal.y > 0.5)
      uv = frac(worldPosition.xz);

   if(worldNormal.y < -0.5)
      uv = frac(float2(-worldPosition.x, worldPosition.z));

   if(worldNormal.z > 0.5)
      uv = frac(float2(worldPosition.x, -worldPosition.y));

   if(worldNormal.z < -0.5)
      uv = frac(float2(-worldPosition.x,-worldPosition.y));

   textureAtlasOffset += float4(uv * 0.5, 0.0, 0.0) * RATIO;

   result = tex2D(texture, textureAtlasOffset.xy);
}


So now my next goals are lighting & shadows. I plan to try Ambient Occlusion first with the AmbientOcclusionCalculator.

Beyzend, I saw that you have posted many interesting topics on the Ogre forum about this subject but I had no time to read them for know. I will probably come back with many questions afterwards :)


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Texture atlases, bleeding, mipmaps & filtering
PostPosted: Wed Mar 02, 2011 7:15 am 

Joined: Tue Feb 22, 2011 10:21 pm
Posts: 12
I forgot to add that it works only if mipmapping is disabled and if anisotropic filtering is set to 4 or less. It's not perfect but for me it's a huge step forward ;)


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Texture atlases, bleeding, mipmaps & filtering
PostPosted: Wed Mar 02, 2011 6:01 pm 

Joined: Sat Sep 18, 2010 9:45 pm
Posts: 189
what do you mean by that? Are you using Ogre? I didn't even now there was a way to turn off mip mapping, maybe you disabled it in the dds generation?

I think it works for me but I haven't looked to make sure it's actually "working" I mean it looks like it's there...damn.

Oh btw, when I used that tool from that Ogre link, I had some problems with the textures being in unexpected positions in the atlas. I had to write a custom python script to manually output the textures in the order I want. And also, the default dimensions from that tool are weird, the default value likes to strech on the width. It's weird, you just have to mess with it man.


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Texture atlases, bleeding, mipmaps & filtering
PostPosted: Wed Mar 02, 2011 7:22 pm 

Joined: Tue Feb 22, 2011 10:21 pm
Posts: 12
Quote:
what do you mean by that? Are you using Ogre? I didn't even now there was a way to turn off mip mapping, maybe you disabled it in the dds generation?

Yes I'm using Ogre 1.7. I'm deactivating mipmaps in the .material file like so :
Code:
filtering anisotropic anisotropic none

max_anisotropy 4

That allows anisotropic filtering but deactivates mipmaps. (the "none")

Quote:
Oh btw, when I used that tool from that Ogre link, I had some problems with the textures being in unexpected positions in the atlas. I had to write a custom python script to manually output the textures in the order I want. And also, the default dimensions from that tool are weird, the default value likes to strech on the width. It's weird, you just have to mess with it man.

Yes I too have such problems, the texture order seems to be dependent of the order of the input textures filenames.
For the resulting atlas not being size squared (square sized ?) I'm just adding dummy textures at the end...


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Texture atlases, bleeding, mipmaps & filtering
PostPosted: Thu Mar 03, 2011 9:50 pm 

Joined: Sat Sep 18, 2010 9:45 pm
Posts: 189
yeah that's right. I have that also it's was simply commented out (I have filtering anisotropic).

However, it does work for me. I tried it with different mipmap settings, plus max_anisotropic to 8 they are all working for me. Did you check that your dds does contain mipmaps?


Top
Offline Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2, 3  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Theme created StylerBB.net