0
\$\begingroup\$

I am trying to create a shader that allows to tile a specific region of a texture. This seem to be a common need however I can't seem to find any information about how to get this effect done.

The closest I have found is the following article made by Renaud Bedard which detail a little bit how he got it to work. : http://theinstructionlimit.com/wrap-spritesheet-atlas

I have tried to implement his process within a complete shader without any success.

float3x3 Matrices_Texture;

texture ColorTexture : DIFFUSE <
    string ResourceName = "default_color.dds";
    string UIName =  "Diffuse Texture";
    string ResourceType = "2D";
>;

sampler2D ColorSampler = sampler_state {
    Texture = <ColorTexture>;
    FILTER = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};  

struct vertexInput {
    float4 Position : POSITION0;
    float2 TextureCoordinates   : TEXCOORD0;
};

struct vertexOutput {
    float4 Position : POSITION0;
    float2 TextureCoordinates   : TEXCOORD0;
    float2 UV       : TEXCOORD1;
    float2 UVScale      : TEXCOORD2;
};


vertexOutput mainVS(vertexInput input) 
{
    vertexOutput output;
    output.TextureCoordinates = mul(float3(input.TextureCoordinates, 1), Matrices_Texture).xy;
    output.UV = Matrices_Texture[2].xy;
    output.UVScale = float2(Matrices_Texture[0][0], Matrices_Texture[1][1]);
    output.Position = input.Position;
    return output;
}

float4 mainPS(vertexOutput input)  : COLOR0
{
    float2 tc = input.TextureCoordinates;
    tc = frac((tc - input.UV) / input.UVScale) * input.UVScale + input.UV;
    float4 sample = tex2D(ColorSampler, tc);
    return sample;
}

technique technique0 {
    pass p0 {
        CullMode = None;
        VertexShader = compile vs_3_0 mainVS();
        PixelShader = compile ps_3_0 mainPS();
    }
}

Note that I have zero experience with HLSL, I tried my best to use the correct types and sementic but I still can't seem to get anywhere with this. If someone could provide additionnal information, it would be greatly appreciated.

Currently, it looks like it is tiling in the preview of the material however when applied to an object, no texture is being displayed. (Testing inside FX Composer 2.5)

enter image description here

\$\endgroup\$
  • \$\begingroup\$ In what specific way is your current shader not successful? Are you getting errors, or unexpected output? Tell us about these problems and you'll often get better solutions, faster. \$\endgroup\$ – DMGregory Nov 21 '18 at 2:43
  • \$\begingroup\$ @DMGregory Thanks for the feedback, I have added a screenshot of the current "preview". I am starting to believe that it might actually work when displaying 2d texture but might simply not appear in a 3d world hence why it doesn't look like it is working in the preview but does display something in the thumbnail. \$\endgroup\$ – Max Nov 21 '18 at 3:41
1
\$\begingroup\$

Here's a version of that HLSL FX file that does what you want in FX Composer :

float4x4 WorldViewProj : WorldViewProjection;

texture ColorTexture;
sampler2D ColorSampler = sampler_state {
    Texture = <ColorTexture>;
    FILTER = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};  

float2 CellCount;
float2 CellIndex;
float2 TCRepeat;

struct VertexInput 
{
    float4 Position : POSITION0;
    float2 TextureCoordinates : TEXCOORD0;
};

struct VertexOutput {
    float4 Position : POSITION0;
    float2 TextureCoordinates   : TEXCOORD0;
};

VertexOutput mainVS(VertexInput input) 
{
    VertexOutput output;

    float2 cellSize = 1.0f / CellCount;
    float2 cellOffset = cellSize * CellIndex;

    output.TextureCoordinates = (input.TextureCoordinates * cellSize + cellOffset) * TCRepeat;
    output.Position = mul(input.Position, WorldViewProj);
    return output;
}

float4 mainPS(VertexOutput input)  : COLOR0
{
    float2 cellSize = 1.0f / CellCount;
    float2 cellOffset = cellSize * CellIndex;   

    float2 tc = input.TextureCoordinates;
    tc = frac((tc - cellOffset) / cellSize) * cellSize + cellOffset;
    float4 sample = tex2D(ColorSampler, tc);
    return sample;
}

technique technique0 {
    pass p0 {
        CullMode = None;
        VertexShader = compile vs_3_0 mainVS();
        PixelShader = compile ps_3_0 mainPS();
    }
}

The shader running in FX Composer

The main issue with the original version is that you need to multiply the object-space vertex with the World-View-Projection matrix.

Also, I find that if you're working with a spritesheet, it's more intuitive to think in terms of cell count and cell index than providing a 3x3 texture matrix, so the version I linked here reflects that.

The "TCRepeat" parameter would be the tiling of your texture coordinates in your mesh, or you could leave it there for convenience. It represents the tiling rate of your plane.

\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.