Seams visible in shader

this is the problem I’m facing, the blue and cyan borders are the neighbouring tiles in the texture atlas i am using.

I am wondering how should i tackle this? I’ve tried all the settings for the texture including turning off the mipmaps and setting the filter mode to point and wrap mode to repeat. I’ve also read on a forum that this could be caused by the way directx 11 shaders sample textures is different to the way that openGL do. Some people say i should sample the texture at the center of each pixel but i have no idea how to do this?

It looks like texture coordinates on your terrain are wrong and you’re sampling from neighboring tiles. But we can’t help you without knowing how you generate those texture coordinates.
Also, DirectX 11 and OpenGL sample textures in the same way. DirectX 9 was little different.
You should use 4x4 texture with point filter for max efficiency and sample pixel centers. To sample pixel center you have to add half pixel offset to you texture coordinate. For example, texture coordinates of the center of the first pixel are [0.5 / width; 0.5 / height].
So, if you have a 4x4 texture:
1st pixel (0, 0) = [0.5 / 4; 0.5 / 4]
3rd pixel (2, 0) = [2.5 / 4; 0.5 / 4]
5th pixel (0, 1) = [0.5 / 4; 1.5 / 4]
etc

Edit: To get the green color from your first tile, the texture coordinates must be in range from 0.5/64 to 15.5/64. Anything bigger will sample color from neighboring tile

I’m using a modified surface shader, this is the code

Shader"Custom/shd_world2"
{
Properties
 {
 _Color ("Color", Color) = (1,1,1,1)
 _MainTex ("Albedo(RGB)", 2D) = "white" {}
 _AtlasCount ("Numberofsubtexturesintheatlas", float) = 1
 }
SubShader
 {
Tags { "RenderType"="Opaque" }
 LOD 200

CGPROGRAM

//PhysicallybasedStandardlightingmodel,andenableshadowsonalllight types
#pragmasurfacesurfStandardfullforwardshadows

//Useshadermodel3.0target,togetnicerlooking lighting
#pragmatarget3.0

sampler2D _MainTex;
float _AtlasCount;
fixed4 _Color;

struct Input
 {
float2 uv_MainTex;
float4color : COLOR;
float2texcoord : TEXCOORD0;
 };

void surf (Input IN, inout SurfaceOutputStandard o)
 {
half _tileSize = 1 / _AtlasCount;
float2 newUVs = float2(
 (fmod(IN.uv_MainTex.x/4,_tileSize)+(_tileSize)*IN.color.r),
fmod(IN.uv_MainTex.y/4,_tileSize)+(_tileSize)*IN.color.g);

fixed4 c = tex2D (_MainTex, newUVs ) * _Color/IN.texcoord.x;
 o.Albedo = c.rgb;
 o.Alpha = c.a;
 }
ENDCG
 }
FallBack"Diffuse"
}

where do i actually offest the sampling in such shaders ??

It would help to know how you create that geometry. What is stored in vertices. How the uvs look like. But ok, it looks to me that you store the palette index in color component of every vertex? Is that so? If you have the palette index stored in color then you don’t really need texture coordinates in vertices. You can compute the uvs from palette index only. Like this ( assuming _AtlasCount = 4 and IN.color is in range from 0 to 3)

float2 newUVs = float2(_tileSize * IN.color.r + (8.0/64)), _tileSize * IN.color.g + (8.0/64));

That is with 8 pixel offset. It doesn’t have to be 8 pixels, every tile is 16x16 pixels, you just have to sample somewhere inside that region.
But I’m not really sure I understand what you’re trying to do. Post more details about geometry if it doesn’t help

Hello!

The vertices in my geometry have the following information on them:

  • COLOR: i write in it which sub-texture i want from the atlas( 6th texture would mean R is 0,25 and G is 0,25 )

  • UV: will tell how many times the selected texture should be tiled in the current triangle( if top left corner is 0,0 and the bottom right is ( 3,3 ) the texture would get tiled 3 times in both directions

I generate the geometry by looping trough a 3D array of voxels to check which are currently visible. Once i found those that should be visible I loop trough them again and try to expand them in X,Y,Z to find the maximum space they can occupy without changing the actual geometry. Once that is complete for each remaining voxel i find which faces from it are visible and i write them directly to the gameObjects meshfilter mesh. this is how the geometry looks with the wire shader

Your example only offsets the actual location of the texture, how could i change the sampling location of the individual pixel?

Yeah, forget that code snippet. I didn’t understand your problem.

Ok, that’s better. I think I understand now. The uvs tell you how many times you want to repeat one atlas tile on given polygon. And colors identifies specific tile in the atlas.
You can try following

  • Make sure the texture has no compression
  • Use secondary UV set instead of COLOR. Color channel has only 8bit precision. You can’t store values like 0.5 or 0.25 precisely in color channels. I think that’s your problem

Also I don’t think the way you compute uvs is correct. Or at least it doesn’t work for me

float2 newUVs = float2((fmod(IN.uv_MainTex.x / 4, _tileSize) + (_tileSize)*IN.color.r),fmod(IN.uv_MainTex.y / 4, _tileSize) + (_tileSize)*IN.color.g);

Following snippet works

float _AtlasCount = 4.0;
float _tileSize = 1.0 / _AtlasCount;
newUVs = float2(frac(IN.uv_MainTex.x) * _tileSize + IN.color.r, frac(IN.uv_MainTex.y) * _tileSize + IN.color.g);

Hey Michal_

I followed your advice and changed the color for the UV2 and replaced the code with the one you provided, unfortunately there is some bug in the shaders which prevented me to directly use uv2_MainTex in the shader itself so i had to make a second texture, and apply it to that. It works now and the mistake seems to be gone for the most part except for some random dots of the neighbouring sub-texture

the shader now looks like this.

Shader"Custom/shd_world2"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo(RGB)", 2D) = "white" {}
_MainTex2 ("Unityderp", 2D) = "white" {}
_AtlasCount ("Numberofsubtexturesintheatlas", float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM

//PhysicallybasedStandardlightingmodel,andenableshadowsonalllight types
#pragmasurfacesurfStandardfullforwardshadows

//Useshadermodel3.0target,togetnicerlooking lighting
#pragmatarget3.0

sampler2D _MainTex;
float _AtlasCount;
fixed4 _Color;

struct Input
{
float2 uv_MainTex;
float2 uv2_MainTex2;
float2texcoord : TEXCOORD0;
};

void surf (Input IN, inout SurfaceOutputStandard o)
{
float _tileSize = 1.0 / _AtlasCount;
float2 newUVs = float2(frac(IN.uv_MainTex.x) * _tileSize + IN.uv2_MainTex2.x, frac(IN.uv_MainTex.y) * _tileSize + IN.uv2_MainTex2.y);

fixed4 c = tex2D (_MainTex, newUVs ) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack"Diffuse"
}

Looks like there are still some inaccuracies. You can try to clamp the fractional part to stay away from the tile edges.

float2 newUVs = float2(clamp(frac(IN.uv_MainTex.x), 0.02, 0.98) * _tileSize + IN.uv2_MainTex2.x, clamp(frac(IN.uv_MainTex.y), 0.02, 0.98) * _tileSize + IN.uv2_MainTex2.y);

Tried it, if i use the values you provided it still shows the dots, only when i use extremes like 0.9f and 0.1f they disappear, but even then I’m convinced i can see them occasionally

Edit

It seems only the voxels that have a texture tile multiple times on the Y coordinate seem to have this effect

Edit2

I’m so sorry, for some reason the texture settings didn’t save and the texture had mip-map generation field ticked, i removed the mip-maps and now it works as it should!

No problem, that happens all the time. Glad you made it working.