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#WukBtThe 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;
}