What you can do Is supplying a buffer, such as Buffer<uint> that contains texture IDs.
You can then use the vertex shader's SV_InstanceID to index into this particular buffer and can write the Texture ID additionally to the UV Coordinates (float3 consisting of UV and Texture ID), the fragment shader does not need to be changed except for the new float3 and the texture array sampling. Rough example:
// Structured buffer containing texture IDs per instance
Buffer<uint> InstanceTextureIDs : register(t0);
// Texture2DArray for all instance textures
Texture2DArray textureArray : register(t1);
SamplerState samplerState : register(s0);
// Vertex input structure
struct VSInput
{
float3 position : POSITION;
float2 texcoord : TEXCOORD0;
uint instanceID : SV_InstanceID;
};
// Vertex to pixel shader output
struct PSInput
{
float4 position : SV_POSITION;
float3 texcoord3D : TEXCOORD0; // xy = UV, z = texture slice
};
// Vertex Shader
PSInput VSMain(VSInput input)
{
PSInput output;
//...//
// Fetch texture ID and pack it with UV into a float3
uint texID = InstanceTextureIDs[input.instanceID];
output.texcoord3D = float3(input.texcoord, texID);
return output;
}
float4 PSMain(PSInput input) : SV_TARGET
{
// Sample from the texture array using the packed float3
return textureArray.Sample(samplerState, input.texcoord3D);
}
```