Hello,
I’m trying to tile different textures on a mesh using a custom shader and a Texture2DArray.
Everything is working, except a very small seam line appearing completely randomly between different tiles (and not between identically textured tile). It seems that I should use screen-space derivatives. This would correct the UV discontinuity which causes the GPU to use an wrong mipmap level at the edge of the tile.
Here is a basic tiling exemple : a 2 columns * 5 rows mesh with 3 identical tiles at the center of each column, and “top” and “bottow” tile at each end. You can see the lines appear between the normal and the top/bottom textures only, as well as below/above this top/bottow textures. The lines appears very randomly, most of the time everything is smooth.
Random lines
I’m creating the 3-elements texture2DArray from 3 128*128 textures, with mipmapping.
Here is my very basic shader, I simply look up a float from a data texture red channel and tile the appropriate texture2DArray element using scaled UVs. And obviously I don’t know what to put as derivativeUVs in the fragment shader.
To sample the Tex2DArray, I’m using a UNITY_SAMPLE_TEX2DARRAY_GRAD() function provided by forum user bgolus on this thread.
Shader "MyShader/TestShader"
{
Properties
{
_DataTex("Data Texture", 2D) = "white" {}
_Tex2DArray("Tex2DArray", 2DArray) = "red" {}
}
SubShader
{
Pass
{
Tags{ "RenderType" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
LOD 200
ZWrite Off
CGPROGRAM
#pragma exclude_renderers d3d9
#pragma exclude_renderers d3d11_9x
#pragma vertex vert
#pragma fragment frag
#pragma target 3.5
#include "UnityCG.cginc"
// from https://forum.unity3d.com/threads/texture2d-array-mipmap-troubles.416799/
#if defined(SHADER_API_D3D11) || defined(SHADER_API_XBOXONE) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE)
#define UNITY_SAMPLE_TEX2DARRAY_GRAD(tex,coord,dx,dy) tex.SampleGrad (sampler##tex,coord,dx,dy)
#else
#if defined(UNITY_COMPILER_HLSL2GLSL) || defined(SHADER_TARGET_SURFACE_ANALYSIS)
#define UNITY_SAMPLE_TEX2DARRAY_GRAD(tex,coord,dx,dy) tex2DArray(tex,coord,dx,dy)
#endif
#endif
// custom data
sampler2D _DataTex;
UNITY_DECLARE_TEX2DARRAY(_Tex2DArray);
struct appdata
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 position : SV_POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
};
// Vertex Shader
v2f vert(appdata input)
{
v2f output;
output.position = mul(UNITY_MATRIX_MVP, input.vertex);
output.uv = input.uv;
output.color = input.color;
return output;
}
// Fragment Shader
float4 frag(v2f input) : Color
{
// Tiling on a 2*5 grid
float2 scaledUVs = float2(input.uv.x * 2, input.uv.y * 5);
// Getting the data texture data
float data = tex2D(_DataTex, input.uv).r;
float2 derivativeUVs = scaledUVs; // ??? This is where I'm lost
// Top part
if (data < 0.2)
{
float4 fulltex = UNITY_SAMPLE_TEX2DARRAY_GRAD(_Tex2DArray, float3(scaledUVs, 2), ddx(derivativeUVs), ddy(derivativeUVs));
return fulltex;
}
// Middle part
if (data > 0.2 && data < 0.8 )
{
float4 fulltex = UNITY_SAMPLE_TEX2DARRAY_GRAD(_Tex2DArray, float3(scaledUVs, 1), ddx(derivativeUVs), ddy(derivativeUVs));
return fulltex;
}
// Bottom part
if (data > 0.8)
{
float4 fulltex = UNITY_SAMPLE_TEX2DARRAY_GRAD(_Tex2DArray, float3(scaledUVs, 0), ddx(derivativeUVs), ddy(derivativeUVs));
return fulltex;
}
return float4(0, 1, 0, 1);
}
ENDCG
}
}
FallBack "Diffuse"
}
Any help to understand this ddx/ddy magic (or any alternate, elegant solution to get rid of the lines) would be greatly appreciated.