Revision Difference
Getting_rid_of_Tex2D_macros#561590
<cat>Material.ShaderReference</cat>
<title>Getting rid of Tex2D macros</title>
# Getting rid of Texture2D/Sample macros
<note>This is a probably little bit too detailed article for beginner people. If this article is too dumb then feel free to edit or nuke it.</note>
In foreseeable future, some macros like all variations of `CreateTexture2D` might become obsolete. In this article you'll see a simple example how can you use Texture2D and SamplerState in your shader code to avoid using `CreateTexture2D` / `Tex2DS` macros.
## CreateTexture2D to Texture2D, Tex2DS to Sample()
Currently, most shaders use `CreateTexture2D` or `CreateTexture2DWithoutSampler` macros to create a new texture variable, and then `Tex2DS` to sample it inside the main pixel shader function. This is how code usually looks like:
```cs
// This is an EXAMPLE for creating & sampling Texture2D using macros.
// It's recommended to avoid using such macros as they might break in future.
PS
{
#define CUSTOM_MATERIAL_INPUTS
// Create an input for our Color map, this will show up in material editor.
CreateInputTexture2D( Color, Srgb, 8, "", "_color", "Material,10/10", Default3( 1.0, 1.0, 1.0 ) );
// Create a Texture2D variable called "g_tColor" from "Color" input box using CreateTexture2DWithoutSampler macro
CreateTexture2DWithoutSampler( g_tColor ) < Channel( RGB, Box( Color ), Srgb ); OutputFormat( BC7 ); SrgbRead( true ); >;
{...}
// Entry point for pixel shader
float4 MainPs( PixelInput i ) : SV_Target0
{
// Sample g_tColor texture and store it in a local variable
float3 l_tColor = Tex2DS( g_tColor, g_sAniso, i.vTextureCoords.xy ).rgb;
{...}
}
}
```
Instead of using `CreateTexture2D`, you can simply create a new Texture2D variable using native HLSL features and add the annotation like in previous example:
```cs
PS
{
#define CUSTOM_MATERIAL_INPUTS
// Create an input for our Color map, this will show up in material editor. We still need this.
CreateInputTexture2D( Color, Srgb, 8, "", "_color", "Material,10/10", Default3( 1.0, 1.0, 1.0 ) );
// Create a Texture2D with required annotation, but now without any macros. Does the same thing as CreateTexture2DWithoutSampler.
Texture2D g_tColor < Channel( RGB, Box( Color ), Srgb ); OutputFormat( BC7 ); SrgbRead( true ); >;
{...}
}
```
And then instead of sampling your new Texture2D using `Tex2D`/`Tex2DS` macro, simply use `.Sample( SamplerState, UV )`. In this code example, we will use built-in sampler state `g_sAniso`, so you won't need to create a new sampler.
```cs
float4 MainPs( PixelInput i ) : SV_Target0
{
// Sample g_tColor texture and store it in a local variable, but now without using any macros.
float3 l_tColor = g_tColor.Sample( g_sAniso, v.TextureCoords.xy );
{...}
}
```
⤶
If you're looking for more details about how sampler states work, check the documentation for [Sampler States in s&box docs](https://docs.facepunch.com/s/sbox-dev/doc/sampler-states-LlJc7ymJkq). If you can't find a built-in sampler that meets your requirements, you can create your own. For example, we need a sampler with `AddressU` and `AddressV` set to `CLAMP`, and point filtering enabled. Before the `MainPs` block, declare a new ShaderState variable and set up sampler states in the annotation:
⤶
In this code example, we're using `g_sAniso` as a sampler. This is one of built-in samplers that are always available, in most cases that's all what you will need for your shader. If you're looking for a full list of pre-defined samplers, or more details about how sampler states work, check the documentation for [Sampler States in s&box docs](https://docs.facepunch.com/s/sbox-dev/doc/sampler-states-LlJc7ymJkq).
⤶
If you can't find a built-in sampler that meets your requirements, you can create your own. For example, we need a sampler with `AddressU` and `AddressV` set to `CLAMP`, and point filtering enabled. Before the `MainPs` block, declare a new ShaderState variable and set up sampler states in the annotation: ⤶
```cs
SamplerState MyNewSampler < Filter( POINT ); AddressU( CLAMP ); AddressV( CLAMP ); >;
```
In `MainPs` block, you can now sample your Texture2D object using a new sampler like this:
```cs
float3 l_tColor = g_tColor.Sample( MyNewSampler, v.TextureCoords.xy );
```
## Other examples
If you are using Texture2D arrays, approach to replace macros with native HLSL code will be almost identical. Instead of `CreateTexture2DArray` macro, declare a new object `Texture2DArray name`. To sample a Texture2DArray object, do `.Sample( sampler, uv.xyz )`.
If you're working with Texture2DMS, `CreateTexture2DMS` macro will be just `Texture2DMS<float> name`. Don't forget about including annotations to your Texture2D declarations.
## But why?
Most IDEs do not understand these macros, and they can make things more confusing for beginners. These macros exist in engine's code since very early days of Source 2 and apparently were required back when it had to compile stuff for DX9, DX11 and OpenGL.
Instead of fiddling with `CreateTexture2DWithoutSampler`, `Tex2DS` and other macros, you can just use `<object>.Sample( sampler, uv )` function for all types of Texture2Ds, and IDEs will understand it. s&box has a bunch of built-in samplers so in most cases you won't need to create a new sampler state for every shader, but even if you'll need to do this, [it's very easy](https://docs.facepunch.com/s/sbox-dev/doc/sampler-states-LlJc7ymJkq).