S&box Wiki

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).