1
votes

I am currently working on a multi-textured terrain and I have problems with the Sample function of Texture2DArray.

In my example, I use a Texture2DArray to store a set of different terrain texture, e.g. grass, sand, asphalt, etc. Each of my vertices stores a texture coordinate (UV coordinate) and an index of the texture I want to use. So, if my index is 0, I use the first texture. If the index is 1, I use the second texture, and so on. This works fine, as long as my index is a natural number (0, 1, ..). However, it fails, if the index is a real number (like 1.5f).

In order to look for the problem, I reduced my entire pixel shader to this:

Texture2DArray DiffuseTextures : register(t0);
Texture2DArray NormalTextures : register(t1);
Texture2DArray EmissiveTextures : register(t2);
Texture2DArray SpecularTextures : register(t3);
SamplerState Sampler : register(s0);

struct PS_IN
{
    float4 pos : SV_POSITION;
    float3 nor : NORMAL;
    float3 tan : TANGENT;
    float3 bin : BINORMAL;
    float4 col : COLOR;
    float4 TextureIndices : COLOR1;
    float4 tra : COLOR2;
    float2 TextureUV : TEXCOORD0;
};

float4 PS(PS_IN input) : SV_Target
{
    float4 texCol = DiffuseTextures.Sample(Sampler, float3(input.TextureUV, input.TextureIndices.r));

    return texCol;
}

The following image shows the result of a sample scene on the left side. As you can see, there is a hard border between the used textures. There is no form of interpolation.

In order to check my texture indices, I changed my pixel shader from above by returning the texture indices as a color:

return float4(input.TextureIndices.r, input.TextureIndices.r, input.TextureIndices.r, 1.0f);

The result can be seen on the right side of the image. The texture indices are correct, since they range in the interval [0, 1] and you can clearly see the interpolation at the border of the area. However, my sampled texture does not show any form of interpolation.

Example

Since my pixel shader is pretty simple, I wonder what causes this behaviour? Is there any setting in DirextX responsible for this?

I use DirectX 11, pixel shader ps_5_0 (I also tested with ps_4_0) and I use DDS textures (BC3 compression).

Edit

This is the sampler I am using:

SharpDX.Direct3D11.SamplerStateDescription samplerStateDescription = new SharpDX.Direct3D11.SamplerStateDescription()
{
    AddressU = SharpDX.Direct3D11.TextureAddressMode.Wrap,
    AddressV = SharpDX.Direct3D11.TextureAddressMode.Wrap,
    AddressW = SharpDX.Direct3D11.TextureAddressMode.Wrap,
    Filter = SharpDX.Direct3D11.Filter.MinMagMipLinear
};

SharpDX.Direct3D11.SamplerState samplerState = new SharpDX.Direct3D11.SamplerState(_device, samplerStateDescription);

_deviceContext.PixelShader.SetSampler(0, samplerState);

Solution

I made a function using the code presented by catflier for getting a texture color:

float4 GetTextureColor(Texture2DArray textureArray, float2 textureUV, float textureIndex)
{
    float tid = textureIndex;
    int id = (int)tid;
    float l = frac(tid);

    float4 texCol1 = textureArray.Sample(Sampler, float3(textureUV, id));
    float4 texCol2 = textureArray.Sample(Sampler, float3(textureUV, id + 1));

    return lerp(texCol1, texCol2, l);
}

This way, I can get the desired texture color for all texture types (diffuse, specular, emissive, ...) with a simple function call:

float4 texCol = GetTextureColor(DiffuseTextures, input.TextureUV, input.TextureIndices.r);
float4 bumpMap = GetTextureColor(NormalTextures, input.TextureUV, input.TextureIndices.g);
float4 emiCol = GetTextureColor(EmissiveTextures, input.TextureUV, input.TextureIndices.b);
float4 speCol = GetTextureColor(SpecularTextures, input.TextureUV, input.TextureIndices.a);

The result is as smooth as I wanted it to be: :-)

Solution

1

1 Answers

1
votes

Texture arrays do not sample across slices, so technically, this is expected result.

If you want to interpolate between slices (eg: 1.5f gives you "half" of second texture and "half" of third texture), you can use a Texture3d instead, which allows this (but will cost some more as it will perform trilinear filtering)

Otherwise, you can perform your sampling that way :

float4 PS(PS_IN input) : SV_Target
{
    float tid = input.TextureIndices.r;
    int id = (int)tid; 
    float l = frac(tid); //lerp amount

    float4 texCol1 = DiffuseTextures.Sample(Sampler, float3(input.TextureUV,id));
    float4 texCol2 = DiffuseTextures.Sample(Sampler, float3(input.TextureUV,id+1));
return lerp(texCol1,texCol2, l);

}

Please note that this technique is quite more flexible, since you can also provide non adjacent slices as input (so you can lerp between slice 2 and 23 for example), and eventually use a different blend mode by changing lerp by some other function.