S&box Wiki

Revision Difference

AnatomyOfVFX#547555

<cat>Code.Shader</cat> <title>Anatomy of VFX Files</title> # What is a VFX File? The VFX file is a container for your shader program. Its main purpose is to eliminate the need for multiple shader files for each shader stage. The VFX file is a plain text file and it can be opened in any code/text editor and the underlying shader format is HLSL. The VFX is split into multiple different sections separated by a header and curly brackets. An example of the VFX format can be seen below: ``` //========================================================================================================================= // Optional //========================================================================================================================= HEADER { CompileTargets = ( IS_SM_50 && ( PC || VULKAN ) ); Description = "Template Shader for S&box"; } //========================================================================================================================= // Optional //========================================================================================================================= FEATURES { #include "common/features.hlsl" } //========================================================================================================================= COMMON { #include "common/shared.hlsl" } //========================================================================================================================= struct VertexInput { #include "common/vertexinput.hlsl" }; //========================================================================================================================= struct PixelInput { #include "common/pixelinput.hlsl" }; //========================================================================================================================= VS { #include "common/vertex.hlsl" // // Main // PixelInput MainVs( INSTANCED_SHADER_PARAMS( VertexInput i ) ) { PixelInput o = ProcessVertex( i ); // Add your vertex manipulation functions here return FinalizeVertex( o ); } } //========================================================================================================================= PS { #include "common/pixel.hlsl" // // Main // PixelOutput MainPs( PixelInput i ) { Material m = GatherMaterial( i ); /* m.Metalness = 1.3f; // Forces the object to be metalic */ return FinalizePixelMaterial( i, m ); } } ``` # HEADER The VFX `HEADER` section gives information about the header and sets the compile targets. The following options can be set: | Key | Value Type | Example | Explanation | |-----|------------|---------|-------------| | Version | Number | `Version = 1;` | Sets a version number for your shader, this is shown in the Material Editor | | DevShader | Boolean | `DevShader = true;` | If your shader is marked as a dev shader, you can reload your shader in the Material Editor | | CompileTargets | Expression | `CompileTargets = ( IS_SM_50 && ( PC || VULKAN ) );` | Sets the compile target for your shader, currently PC and D3D11 are the only options and this should not be changed! | | Description | String | `Description = "My shaders description";` | A description of your shader which is shown to the user in the material editor | | DebugInfo | Boolean | `DebugInfo = true;` | Ships debug information with your shader, this is only useful for developing shaders. Shipping shaders with this will drastically increase the file size | ``` HEADER { Description = "My shaders description!"; DevShader = true; Version = 1; CompileTargets = ( IS_SM_50 && ( PC || VULKAN ) ); } ``` # FEATURES Features allow you to add customization to your shaders in the form of using the VFX preprocessor(note that this is different from the HLSL preprocessor). Features allow you to specify conditions on attributes, sampler settings, or render states. Features MUST be defined within the `FEATURES` section and feature names MUST start with `F_`! Features have multiple different configurations and settings, you have regular checkboxes, radio buttons and rulesets to only allow one feature or the other. Features are defined as follows: `Feature(F_FEATURE_NAME, RANGE_MIN..RANGE_MAX, "Feature Category");` ## Checkbox ``` Feature(F_MY_FEATURE_NAME, 0..1, "This is a category"); Feature(F_MY_FEATURE_NAME2, 0..1, "This is a category"); Feature(F_MY_FEATURE_NAME_3, 0..1, "This is a category"); ``` `MY_FEATURE_NAME` is transformed into `My Feature Name`. All underscores are converted into spaces within the material editor. The range `0..1` indicates that this is a checkbox. These checkboxes will also share the same category. <upload src="653cb/8d9850202fbd29f.png" size="23270" name="2021-10-03_36-02-06f903ab-202e-4441-b29d-934b69d84180-v9q57xG2.png" /> ## Categories You can create another category by providing a different category name. ``` Feature(F_DO_SOMETHING, 0..1, "This is another category"); ``` <upload src="653cb/8d98502490cf769.png" size="12134" name="2021-10-03_36-59-f61684e9-5754-437b-a0e9-28aa881a5ec0-KDNvT2rL.png" /> ## Radio buttons Features also allow you to have radio buttons. A group of buttons that only allow one choice. This is done by specifying a range that is greater than `0..1` within the feature and providing the names of each radio button. It can be done as so: ``` Feature(F_RADIO_FEATURES, 0..3(0="Checkbox 1", 1="Checkbox 2", 2="Checkbox 3", 3="Checkbox 4"), "This is another category"); ``` ## Feature rules Feature rules allow you to limit the section of options. It works similar to radio buttons however it's a bit more fine control. Feature rules are defined as follows: `FeatureRule(RULE(F_FEATURE1, F_FEATURE2, ...), "A useful tooltip");` Below is a list of feature rules: | Rule | Arguments | Example | Info | |------|-----------|---------|------| | AllowN | F_FEATURE_0, F_FEATURE_N | `FeatureRule(Allow1(F_FEATURE1, F_FEATURE2, F_FEATURE3), "You can only pick one!");` | N is any number | ## Using features As features are only run through the VFX preprocessor, you can only use them in such things as within render states. An example of their usage can be used as below: ``` FEATURES { Feature(F_ENABLE_DEPTH, 0..1, "Settings!"); } PS { RenderState( DepthEnable, F_ENABLE_DEPTH ? true : false ); } ``` ## Example ``` FEATURES { Feature(F_MY_FEATURE_NAME, 0..1, "This is a category"); Feature(F_MY_FEATURE_NAME2, 0..1, "This is a category"); Feature(F_MY_FEATURE_NAME_3, 0..1, "This is a category"); Feature(F_DO_SOMETHING, 0..1, "This is another category"); Feature(F_RADIO_FEATURES, 0..3(0="Checkbox 1", 1="Checkbox 2", 2="Checkbox 3", 3="Checkbox 4"), "This is another category"); // Features MUST begin with F_ Feature(F_ENABLE_DEPTH, 0..1, "Settings!"); Feature(F_A_CHECKBOX_FEATURE, 0..3(0="Radio 1", 1="Radio 2", 2="Radio 3", 3="Radio 4"), "Category name"); Feature(F_ONLY_ALLOW_ONE_1, 0..1, "Feature rules"); Feature(F_ONLY_ALLOW_ONE_2, 0..1, "Feature rules"); Feature(F_ONLY_ALLOW_ONE_3, 0..1, "Feature rules"); FeatureRule(Allow1(F_ONLY_ALLOW_ONE_1, F_ONLY_ALLOW_ONE_2, F_ONLY_ALLOW_ONE_3), "Hover over hint"); } ``` <upload src="653cb/8d98500c7a33e2e.png" size="29587" name="2021-10-03_27-08-b859b709-0c90-48ed-9a8e-a8b30188ae55-D5UkXcfi.png" /> # Common The common section is any information that is shared between all shader stages. This can include anything from defines, includes functions, etc. An example can be seen below: ``` COMMON { #define SOME_GLOBAL_DEF } PS { #define LOCAL_DEF #ifdef SOME_GLOBAL_DEF // This is seen #endif #ifdef LOCAL_DEF // This is seen #endif } VS { #ifdef SOME_GLOBAL_DEF // This is seen #endif #ifdef LOCAL_DEF // This is not seen! #endif } ``` # Shader Inputs/Outputs The shader inputs and outputs are structs that act like sections in VFX. If you noticed in the sample file, they are defined in the same area as headers are. There are multiple different inputs and outputs which you can use: | Input/Output | Stage | Name | Alternate Name | |--------------|-------|------|----------------| | Input | Vertex | VertexInput | VS_INPUT | | Input | Pixel/Fragment | PixelInput | PS_INPUT | | Input | Geometry | GeometryInput | GS_INPUT | | Input | Geometry | HullInput | HS_INPUT | | Input | Domain | DomainInput | DS_INPUT | | Output | Vertex | VertexOutput | VS_OUTPUT | | Output | Pixel/Fragment | PixelOutput | PS_OUTPUT | | Output | Geometry | GeometryOutput | GS_OUTPUT | | Output | Geometry | HullOutput | HS_OUTPUT | | Output | Domain | DomainOutput | DS_OUTPUT | ## Example ``` struct VertexInput { float3 vPositionOs : POSITION < Semantic( PosXyz ); >; float4 vNormalOs : NORMAL < Semantic( OptionallyCompressedTangentFrame ); >; float2 vTexCoord : TEXCOORD0 < Semantic( LowPrecisionUv ); >; } // OR struct VS_INPUT { float3 vPositionOs : POSITION < Semantic( PosXyz ); >; float4 vNormalOs : NORMAL < Semantic( OptionallyCompressedTangentFrame ); >; float2 vTexCoord : TEXCOORD0 < Semantic( LowPrecisionUv ); >; } ``` # Shader Stages There is a section for each dedicated shader stage, the valid shader stages are as follows: | Stage | Name | Entry Point | |-------|------|-------------| | VS | Vertex Shader | MainVs | | PS | Pixel/Fragment Shader | MainPs | | GS | Geometry Shader | MainGs | | HS | Hull Shader | MainHs | | DS | Domain Shader | MainDs | | CS | Compute Shader | MainCs | A VFX file can have as many unique stages as you like. Typically you'll be using `PS` and `VS` in most shaders. ## Example ``` VS { #include "common/vertex.hlsl" // // Main // PixelInput MainVs( INSTANCED_SHADER_PARAMS( VertexInput i ) ) { PixelInput o = ProcessVertex( i ); // Add your vertex manipulation functions here return FinalizeVertex( o ); } } ```