Volumes Of Fun http://www.volumesoffun.com/phpBB3/ |
|
Texture atlases, bleeding, mipmaps & filtering http://www.volumesoffun.com/phpBB3/viewtopic.php?f=2&t=152 |
Page 1 of 3 |
Author: | Jmgr [ Tue Mar 01, 2011 12:14 pm ] |
Post subject: | Texture atlases, bleeding, mipmaps & filtering |
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 [ 127.16 KiB | Viewed 22379 times ] When using linear filtering or mipmaps : Attachment: ![]() bilinear.png [ 221.91 KiB | Viewed 22379 times ] Attachment: ![]() mipmaps.png [ 167.19 KiB | Viewed 22379 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 ? |
Author: | beyzend [ Tue Mar 01, 2011 6:30 pm ] |
Post subject: | Re: Texture atlases, bleeding, mipmaps & filtering |
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; } |
Author: | David Williams [ Tue Mar 01, 2011 8:13 pm ] |
Post subject: | Re: Texture atlases, bleeding, mipmaps & filtering |
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. |
Author: | beyzend [ Tue Mar 01, 2011 8:30 pm ] |
Post subject: | Re: Texture atlases, bleeding, mipmaps & filtering |
yes that's what he is working off of. He is having issues with the texture addressing. |
Author: | David Williams [ Tue Mar 01, 2011 9:14 pm ] |
Post subject: | Re: Texture atlases, bleeding, mipmaps & filtering |
Ah, sorry, I should have read that more carefully. |
Author: | Jmgr [ Tue Mar 01, 2011 11:38 pm ] |
Post subject: | Re: Texture atlases, bleeding, mipmaps & filtering |
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 ![]() |
Author: | Jmgr [ Wed Mar 02, 2011 7:15 am ] |
Post subject: | Re: Texture atlases, bleeding, mipmaps & filtering |
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 ![]() |
Author: | beyzend [ Wed Mar 02, 2011 6:01 pm ] |
Post subject: | Re: Texture atlases, bleeding, mipmaps & filtering |
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. |
Author: | Jmgr [ Wed Mar 02, 2011 7:22 pm ] |
Post subject: | Re: Texture atlases, bleeding, mipmaps & filtering |
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... |
Author: | beyzend [ Thu Mar 03, 2011 9:50 pm ] |
Post subject: | Re: Texture atlases, bleeding, mipmaps & filtering |
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? |
Page 1 of 3 | All times are UTC |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |