{"id":2308,"date":"2020-09-10T14:31:35","date_gmt":"2020-09-10T06:31:35","guid":{"rendered":"http:\/\/blog.coolcoding.cn\/?p=2308"},"modified":"2020-09-10T14:31:35","modified_gmt":"2020-09-10T06:31:35","slug":"writing-shader-code-for-the-universal-rp","status":"publish","type":"post","link":"https:\/\/blog.coolcoding.cn\/?p=2308","title":{"rendered":"Writing Shader Code for the Universal RP"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">HLSL<\/h1>\n\n\n\n<p>The actual shader code is written in&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/High-Level_Shading_Language\">HLSL<\/a>&nbsp;(High level shading language) inside each ShaderLab Pass.<\/p>\n\n\n\n<p>Sometimes you\u2019ll also see it referred to as \u201ccg\u201d as well. Both Cg and HLSL (DX9-style at least) are treated basically as the same language, but cg is deprecated and has been for several years. Even so, in built-in pipeline shaders you\u2019ll commonly still see code between CGPROGRAM and ENDCG (and CGINCLUDE). These tags automatically include some of the Builtin-Include files, like HLSLSupport.cginc and UnityShaderVariables.cginc.<\/p>\n\n\n\n<p>However, If you use these CG tags in URP they will cause conflicts with the URP ShaderLibrary as many of the variables and functions will be defined twice. URP should use&nbsp;<strong>HLSLPROGRAM<\/strong>\/<strong>HLSLINCLUDE<\/strong>&nbsp;and&nbsp;<strong>ENDHLSL<\/strong>&nbsp;instead as they don\u2019t include these files.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">SCALAR VARIABLES<\/h5>\n\n\n\n<p>Variables in HLSL commonly consist of these scalar data types :<\/p>\n\n\n\n<ul><li>bool \u2013 true or false.<\/li><li>float \u2013 32 bit floating point number. Generally used for world space positions, texture coordinates, or scalar computations involving complex functions such as trigonometry or power\/exponentiation.<\/li><li>half \u2013 16 bit floating point number.&nbsp; Generally used for short vectors, directions, object space positions, colours.<\/li><li>double \u2013 64 bit floating point number. Cannot be used as inputs\/outputs, see note&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/direct3dhlsl\/dx-graphics-hlsl-scalar\">here<\/a>.<\/li><li>fixed \u2013 Used in built-in shaders only, not supported in URP, use half instead.<\/li><li>real \u2013 Used in URP only? I think this just defaults to half (assuming they are supported on the platform), unless the shader specifies \u201c#define&nbsp;PREFER_HALF&nbsp;0\u2033, then it will use float precision.<\/li><li>int \u2013 32 bit signed integer<\/li><li>uint \u2013 32 bit unsigned integer (except GLES2, where this isn\u2019t supported, and is defined as an int instead).<\/li><\/ul>\n\n\n\n<h5 class=\"wp-block-heading\">VECTOR<\/h5>\n\n\n\n<p>A vector can be produced by appending an integer from 1 to 4 to one of these scalar data types. For example :<\/p>\n\n\n\n<ul><li>float4 \u2013 (A vector containing 4 floats)<\/li><li>half3<\/li><li>int2, etc<\/li><\/ul>\n\n\n\n<p>In order to get one of the components of a vector, we can use .x, .y, .z, or .w (as well as .r, .g, .b, .a instead, which might make more sense for colours). We can also obtain another vector by using these multiple times, even rearranged. This is refereed to as\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/direct3dhlsl\/dx9-graphics-reference-asm-ps-registers-modifiers-source-register-swizzling\">swizzling<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float3 vector = float3(1, 2, 3);\nfloat3 a = vector.xyz;  \/\/ (1, 2, 3), same as vector.rgb\nfloat3 b = vector3.zyx; \/\/ (3, 2, 1), vector.bgr\nfloat3 c = vector.xxx;  \/\/ (1, 1, 1), vector.rrr\nfloat2 d = vector.zy;   \/\/ (3, 2), vector.bg\nfloat4 e = vector.xxzz; \/\/ (1, 1, 3, 3), vector.rrbb\nfloat f = vector.y;     \/\/ 2, vector.g\n \n\/\/ Note that something like \"vector.rx\" is not allowed.\n\/\/ Use vector.rr or vector.xx instead.<\/code><\/pre>\n\n\n\n<h5 class=\"wp-block-heading\">MATRIX<\/h5>\n\n\n\n<p>A matrix can be produced by appending two integers between 1 and 4 to the scalar, separated by an \u201cx\u201d. The first integer is the number of rows, while the second is the number of columns in the matrix :<\/p>\n\n\n\n<ul><li>float4x4 \u2013 4 rows, 4 columns<\/li><li>int4x3 \u2013 4 rows, 3 columns<\/li><li>half2x1 \u2013 2 rows, 1 column<\/li><li>float1x4 \u2013 1 row, 4 columns<\/li><\/ul>\n\n\n\n<p>A vector of one of the rows in the matrix can be obtained via [index], and obtaining one of the components of that vector can also be obtained via [index] again.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float3x3 matrix = {0,1,2,\n                   3,4,5,\n                   6,7,8};\nfloat3 row0 = matrix[0]; \/\/ (0, 1, 2)\nfloat3 row1 = matrix[1]; \/\/ (3, 4, 5)\nfloat3 row2 = matrix[2]; \/\/ (6, 7, 8)\nfloat row1column2 = matrix[1][2]; \/\/ 5\n\/\/ Note we could also do\nfloat row1column2 = matrix[1].z;<\/code><\/pre>\n\n\n\n<p> Matrices are commonly used for transformation between different coordinate spaces. To do this we need to do matrix multiplication, which can be done using the\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/direct3dhlsl\/dx-graphics-hlsl-mul\">mul<\/a>\u00a0function (rather than the * operator which won\u2019t work for a matrix and vector type). For example, transforming from Object space to World space is achieved by : <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mul(GetObjectToWorldMatrix(), float4(positionOS, 1.0)).xyz;\n\/\/ Where GetObjectToWorldMatrix() returns \"UNITY_MATRIX_M\"\n\/\/ Which is the model matrix that Unity passes in for the object<\/code><\/pre>\n\n\n\n<p>This is what the&nbsp;<a href=\"https:\/\/github.com\/Unity-Technologies\/Graphics\/blob\/756d775870f7bbc98d907ae0907f37f21861ca86\/com.unity.render-pipelines.core\/ShaderLibrary\/SpaceTransforms.hlsl#L55\">TransformObjectToWorld<\/a>&nbsp;function does. Note that the order of the matrix and vector is important in the mul(x, y) function. If the first input is a vector, it treats it as a row vector (1 row, n columns), while in the second input it treats it as a column vector (n rows, 1 column). For example, a float3 in the first mul slot, is the same as a float1x3. However in the second slot, it would be the same as a float3x1.<\/p>\n\n\n\n<p>The inputs must also have the same number of columns in the first input as rows in the second, and the dimensions of the result can vary depending on the inputs. The result will have as many rows in the first input and columns as the second input, so for mul(float4x4, float4 (second input, so column vector, float4x1)), the result is a float4x1, aka a float4.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">ARRAYS<\/h5>\n\n\n\n<p>An array can be specified in a shader, although they aren\u2019t supported in the Shaderlab Properties or material inspector and must be set from a C# script. The size of the array must be specified in the shader, and it should remain constant to prevent issues. If you don\u2019t know the size the array needs to be, you need to set a maximum and pass in the array padding with 0s. You can specify another float as a length for when you need to loop over the array, like the example\u00a0<a href=\"https:\/\/www.alanzucconi.com\/2016\/10\/24\/arrays-shaders-unity-5-4\/\">here<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float _Array[10]; \/\/ Float array\nfloat4 _Array[10]; \/\/ Vector array\nfloat4x4 _Array[10]; \/\/ Matrix array<\/code><\/pre>\n\n\n\n<p>When setting the float array, use&nbsp;<a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/Material.SetFloatArray.html\">material.SetFloatArray<\/a>&nbsp;or&nbsp;<a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/Shader.SetGlobalFloatArray.html\">Shader.SetGlobalFloatArray<\/a>. There is also SetVectorArray and SetMatrixArray and their global versions too.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">OTHER TYPES<\/h5>\n\n\n\n<p>HLSL also includes other types such as Textures and Samplers, which can be defined using the following macros in URP :<\/p>\n\n\n\n<p><code>TEXTURE2D(textureName);<\/code><\/p>\n\n\n\n<p><code>SAMPLER(sampler_textureName);<\/code><\/p>\n\n\n\n<p> There is also Buffers, though I\u2019ve never actually used them so am not that familiar with how they are used. They are set from C# using\u00a0<a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/Material.SetBuffer.html\">material.SetBuffer<\/a>\u00a0or\u00a0<a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/Shader.SetGlobalBuffer.html\">Shader.SetGlobalBuffer<\/a>. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#ifdef SHADER_API_D3D11\nStructuredBuffer&lt;float3> buffer;\n#endif\n\/\/ I think this is only supported in Direct3D 11?\n\/\/ and also require #pragma target 4.5 or higher?\n\/\/ see https:\/\/docs.unity3d.com\/Manual\/SL-ShaderCompileTargets.html<\/code><\/pre>\n\n\n\n<p>You may also want to look into other parts of HLSL such as&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/direct3dhlsl\/dx-graphics-hlsl-flow-control\">flow control<\/a>&nbsp;(if, for, while, etc), But the syntax is basically the same as C# if you are familiar with that. You can also find a list of all operators supported by HLSL&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/direct3dhlsl\/dx-graphics-hlsl-operators\">here<\/a>.&nbsp;<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">FUNCTIONS<\/h5>\n\n\n\n<p>Declaring functions in HLSL is fairly similar to C#. Here\u2019s an example :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float3 example(float3 a, float3 b){\n    return a * b;\n}<\/code><\/pre>\n\n\n\n<p>Where float3 is the return type, example is the function name and inside the brackets are the parameters passed into the function. In the case of no return type, void is used. You can also specify output parameters using \u201cout\u201d before the parameter type, or \u201cinout\u201d if you want it to be an input that you can edit and pass back out.<\/p>\n\n\n\n<p>You may also see \u201cinline\u201d before the function return type. This is the default modifier and is the only modifier a function can actually have, so it\u2019s not important to specify it. It means that the compiler will generate a copy of the function for each call. This is done to reduce the overhead of calling the function.<\/p>\n\n\n\n<p>You may also see functions like :<\/p>\n\n\n\n<p> #define EXAMPLE(x, y) ((x) * (y)) <\/p>\n\n\n\n<p> This is called a\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/direct3dhlsl\/dx-graphics-hlsl-appendix-pre-define-2\">macro<\/a>. Macros are handled before compiling the shader and when used are replaced with the definition with the parameters substituted. For example : <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float f = EXAMPLE(3, 5);\nfloat3 a = float3(1,1,1);\nfloat3 f2 = EXAMPLE(a, float3(0,1,0));\n \n\/\/ becomes :\nfloat f = ((3) * (5));\nfloat a = float(1,1,1);\nfloat3 f2 = ((a) * (float3(0,1,0)));\n\/\/ then the shader is compiled.\n \n\/\/ Note that the macro has () around x and y.\n\/\/ This is because we could do :\nfloat b = EXAMPLE(1+2, 3+4);\n\/\/ becomes :\nfloat b = ((1+2) * (3+4)); \/\/ 3 * 7, so 21\n\/\/ If those () wasn't included, it would instead be :\nfloat b = (1+2*3+4)\n\/\/ which equals 11 due to * taking precedence over +<\/code><\/pre>\n\n\n\n<p> They can also do some things that functions can\u2019t do. For example : <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)\n \n\/\/ Usage :\nOUT.uv = TRANSFORM_TEX(IN.uv, _MainTex)\n \n\/\/ becomes :\nOUT.uv = (IN.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw);<\/code><\/pre>\n\n\n\n<p> The \u201c##\u201d operator is a special case where macros can be useful. It allows us to concatenate the name and _ST parts, resulting in _MainTex_ST for this usage input. If the ## part was left out, it would just produce \u201cname_ST\u201d, resulting in an error since that hasn\u2019t be defined. (Of course, _MainTex_ST still also needs to be defined, but that\u2019s the intended behaviour, as appending _ST to the texture name is how Unity handles the tiling and offset values for a texture). <\/p>\n\n\n\n<h5 class=\"wp-block-heading\">BEGINNING THE SHADER<\/h5>\n\n\n\n<p>Shaders commonly consist of two stages, a&nbsp;<strong>vertex<\/strong>&nbsp;shader and&nbsp;<strong>fragment<\/strong>&nbsp;shader. In short, a vertex shader runs for each vertex in the mesh, while fragment runs for every pixel that will end up on the screen. Some fragments can be discarded, so don\u2019t become actual pixels, e.g. In alpha clip\/cutout shaders &amp; stencil shaders. There are also additional shaders such as hull\/domain (for tessellation) and geometry shaders, but I won\u2019t be going over them here \u2013 as far as I can tell they work the same way as in the built-in pipeline.<\/p>\n\n\n\n<p>In the Shaderlab example, we had a HLSLINCLUDE which automatically includes the code in every Pass inside the Subshader. Using this isn\u2019t required, but is helpful as we can ensure the&nbsp;UnityPerMaterial CBUFFER is the same for every pass \u2013 which is required to make the shader compatible with the SRP Batcher. This CBUFFER needs to include all of the exposed properties (same as in the Shaderlab Properties block). It cannot include other variables that aren\u2019t exposed, and textures don\u2019t need to be included.<\/p>\n\n\n\n<p>Note : While variables don\u2019t have to be exposed to set them via the C# material.SetColor\/SetFloat\/SetVector etc, if multiple material instances have different values, this can produce glitchy behaviour as the SRP Batcher will still batch them together when on screen.\u00a0If you have variables that aren\u2019t exposed \u2013 always set them using Shader.SetGlobalColor\/Float\/Vector etc, so that they remain constant for all material instances. If they need to be different per material, expose them via the Shaderlab Properties block and add them to the CBUFFER instead.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>HLSLINCLUDE\n    #include \"Packages\/com.unity.render-pipelines.universal\/ShaderLibrary\/Core.hlsl\"\n \n    CBUFFER_START(UnityPerMaterial)\n    float4 _BaseMap_ST;\n    float4 _BaseColor;\n    \/\/float4 _ExampleDir;\n    \/\/float _ExampleFloat;\n    CBUFFER_END\nENDHLSL<\/code><\/pre>\n\n\n\n<p>We are also including Core.hlsl from the URP ShaderLibrary using the #include as shown above. This is the URP-equivalent of the built-in pipeline UnityCG.cginc.&nbsp;Core.hlsl (and other ShaderLibrary files it automatically includes) contain a bunch of useful functions and macros (which are similar to functions, but are handled before the shader is compiled).<\/p>\n\n\n\n<p>One of the first things we need to do in our HLSLPROGRAM is specify the vertex and fragment shaders. We\u2019ll put names for each of our functions here, commonly \u201cvert\u201d and \u201cfrag\u201d is used, but they can be anything.<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>HLSLPROGRAM\n \n#pragma vertex vert\n#pragma fragment frag\n \n...\n \nENDHLSL<\/code><\/pre>\n\n\n\n<p>Before we define these functions, we\u2019ll need some structs, which I\u2019ll be going over in the next section.<\/p>\n\n\n\n<p>There are also some ShaderLibrary files which aren\u2019t included, that we might want to include, such as Lighting.hlsl. For now this example is an Unlit shader so we won\u2019t be needing that, but in a later section we\u2019ll go over a Lit example too.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>HLSL The actual shader code is written in&nbsp;HLSL&#038;nbs [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/2308"}],"collection":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2308"}],"version-history":[{"count":1,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/2308\/revisions"}],"predecessor-version":[{"id":2309,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/2308\/revisions\/2309"}],"wp:attachment":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2308"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2308"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2308"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}