### Shader Texture Property Default Value Example (TXT) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Shows the syntax for declaring a texture property in a shader file (`.shader` or `.txt`) and setting a default value using a string keyword like "white". ```txt _MainTex ("My Texture", 2D) = "white"{} ``` -------------------------------- ### Examples Using Specific Default Texture Values (TXT) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Demonstrates how to use specific default texture property values like "unity_Lightmap" and "unity_SpecCube0" for scenarios requiring overrides for lightmaps or reflection probes in shader property declarations. ```txt _LightmapOverride ("Lightmap", 2D) = "unity_Lightmap"{} _ReflectionProbeOverride ("Reflection", CUBE) = "unity_SpecCube0"{} ``` -------------------------------- ### Full Fragment Shader Example (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/gpu-instancing.md A complete fragment shader demonstrating how to retrieve the world positions of the first three instances using `UNITY_SETUP_INSTANCE_ID` within a loop and then using these positions to color the current fragment. Includes the necessary `UNITY_INSTANCING_ENABLED` guard and restores the original instance context. ```glsl float4 frag (v2f i) : SV_Target { #if defined(UNITY_INSTANCING_ENABLED) v2f dummy; float3 cube_positions[3]; for (int idx = 0; idx < 3; idx++) // get positions of 3 first renderers { dummy.instanceID = idx; UNITY_SETUP_INSTANCE_ID(dummy); float3 wpos = float3(unity_ObjectToWorld[0][3], unity_ObjectToWorld[1][3], unity_ObjectToWorld[2][3]); cube_positions[idx] = wpos; } UNITY_SETUP_INSTANCE_ID(i); return float4(cube_positions[floor(i.uv.x * 3)], 1); #else return 0; #endif } ``` -------------------------------- ### Retrieve Instance World Positions in Fragment Shader (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/gpu-instancing.md Iterates through a fixed number of instance IDs (0 to 2 in this example). For each ID, it uses `UNITY_SETUP_INSTANCE_ID` with a dummy struct to temporarily set the context to that instance, retrieves its world position from the last column of the `unity_ObjectToWorld` matrix, and stores it in an array. ```glsl float3 cube_positions[3]; for (int idx = 0; idx < 3; idx++) { // make unity_ObjectToWorld point to renderer given by idx dummy.instanceID = idx; UNITY_SETUP_INSTANCE_ID(dummy); // get world space position of renderer and store it float3 wpos = float3(unity_ObjectToWorld[0][3], unity_ObjectToWorld[1][3], unity_ObjectToWorld[2][3]); cube_positions[idx] = wpos; } ``` -------------------------------- ### Accessing Point Light Position (GLSL/CG) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Retrieve the world space position of the i'th point light (where i is 0-3) using built-in Unity arrays. This is often used when abusing point lights to get object positions. ```GLSL float3 light_pos = float3(unity_4LightPosX0[i], unity_4LightPosY0[i], unity_4LightPosZ0[i]); ``` -------------------------------- ### Adjust Ray Origin Based on VFACE in Fragment Shader (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/raymarching.md Implements logic within the fragment shader to adjust the ray origin based on the `VFACE` semantic. If the fragment is front-facing (indicating the camera is likely outside), the ray origin is moved to the interpolated vertex position to start the ray from the object's surface. ```glsl float4 frag (v2f i, float facing : VFACE) : SV_Target { float ray_origin = ...; if (facing > 0) // if front face, move ray origin to vertex pos ray_origin = i.vert_position; } ``` -------------------------------- ### Define Vertex-to-Fragment Struct for Raymarching (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/raymarching.md Defines the `v2f` struct used to pass necessary data from the vertex shader to the fragment shader for raymarching. It includes fields for the ray origin and the vertex position, which are used to calculate the ray direction per pixel. ```glsl struct v2f { float3 ray_origin : TEXCOORD1; float3 vert_position : TEXCOORD2; }; ``` -------------------------------- ### Accessing Point Light Alpha Value (GLSL/CG) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Get the alpha component of the i'th light's color. This can be used to store extra data, such as an identifier, when the light color is set to black. ```GLSL unity_LightColor[i].a ``` -------------------------------- ### Calculate World Space Ray Origin/Position in Vertex Shader (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/raymarching.md Provides the vertex shader logic for setting up raymarching when calculations are performed in world space. It assigns the camera's world position (`_WorldSpaceCameraPos`) as the ray origin and transforms the vertex position to world space. ```glsl v2f vert (appdata v) { v2f o; ... initialize other fields o.ray_origin = _WorldSpaceCameraPos; o.vert_position = mul(unity_ObjectToWorld, v.vertex); return o; } ``` -------------------------------- ### Calculate Ray Direction in Fragment Shader (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/raymarching.md Shows the initial fragment shader logic to calculate the ray direction for each pixel. It uses the interpolated ray origin and vertex position passed from the vertex shader to compute a normalized direction vector. ```glsl float4 frag (v2f i) : SV_Target { float3 ray_origin = i.ray_origin; float3 ray_direction = normalize(i.vert_position - i.ray_origin); ... do the raymarching } ``` -------------------------------- ### Calculate Object Space Ray Origin/Position in Vertex Shader (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/raymarching.md Provides the vertex shader logic for setting up raymarching when calculations are performed in object space. It transforms the camera's world position into object space for the ray origin and uses the original vertex position. ```glsl v2f vert (appdata v) { v2f o; ... initialize other fields o.ray_origin = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1)); o.vert_position = v.vertex; return o; } ``` -------------------------------- ### Checking If Texture Exists (GLSL/CG) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Define a function that checks if a Texture2D property has been assigned a texture by attempting to get its dimensions. A width greater than 16 is used as a heuristic for existence. The texture must be declared as Texture2D, not sampler2D. ```GLSL Texture2D _MyTexture; bool TextureExists() { int width, height; _MyTexture.GetDimensions(width, height); return width > 16; } ``` -------------------------------- ### Write Depth Value Using SV_Depth in Fragment Shader (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/raymarching.md Calculates and outputs the depth value for the fragment using the `SV_Depth` semantic. It takes the world-space hit position found during raymarching, transforms it to clip space, and calculates the depth as `clip_pos.z / clip_pos.w`. ```glsl // However you calculate hit_position, make sure it is in world space float3 hit_position = ray_origin + ray_distance * ray_direction; // Output depth float4 clip_pos = mul(UNITY_MATRIX_VP, float4(hit_position, 1.0)); depth = clip_pos.z / clip_pos.w; ``` -------------------------------- ### Disable Culling in SubShader (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/raymarching.md Sets the `Cull` state to `Off` within the SubShader block. This is a common technique used in raymarched shaders to ensure that the object is rendered correctly even when the camera is inside it, preventing faces from being culled. ```glsl SubShader { Cull Off ... } ``` -------------------------------- ### Add VFACE Semantic to Fragment Shader Signature (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/raymarching.md Modifies the fragment shader signature to accept the `VFACE` semantic. This semantic indicates whether the current fragment belongs to a front-facing or back-facing primitive, which is useful for handling ray origins when the camera is inside the object. ```glsl float4 frag (v2f i, float facing : VFACE) : SV_Target { ... } ``` -------------------------------- ### Add SV_Depth Semantic to Fragment Shader Signature (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/raymarching.md Modifies the fragment shader signature to include an output variable with the `SV_Depth` semantic. This allows the fragment shader to explicitly write depth values to the depth buffer, ensuring the raymarched object interacts correctly with other geometry in the scene. ```glsl float4 frag (v2f i, out float depth : SV_Depth) : SV_Target { ... } ``` -------------------------------- ### Detecting VRChat Scenarios (Legacy GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Provides GLSL functions to detect various VRChat camera and rendering scenarios (VR, desktop, hand camera, panorama, mirror). Notes that some are obsolete and recommends using VRChat's builtin globals. ```glsl bool isVR() { #if UNITY_SINGLE_PASS_STEREO return true; #else return false; #endif } bool isVRHandCamera() { return !isVR() && abs(UNITY_MATRIX_V[0].y) > 0.0000005; } bool isDesktop() { return !isVR() && abs(UNITY_MATRIX_V[0].y) < 0.0000005; } bool isVRHandCameraPreview() { return isVRHandCamera() && _ScreenParams.y == 720; } bool isVRHandCameraPicture() { return isVRHandCamera() && _ScreenParams.y == 1080; } bool isPanorama() { return unity_CameraProjection[1][1] == 1 && _ScreenParams.x == 1075 && _ScreenParams.y == 1025; } bool isInMirror() { return unity_CameraProjection[2][0] != 0.f || unity_CameraProjection[2][1] != 0.f; } ``` -------------------------------- ### Using Inline Sampler States (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Demonstrates how to use inline sampler states in GLSL shaders (`SamplerState` with keywords in name) to control texture sampling settings (filtering, wrapping) for textures without accessible importer settings, like GrabPass results. ```glsl Texture2D _MainTex; SamplerState my_point_clamp_sampler; // ... half4 color = _MainTex.Sample(my_point_clamp_sampler, uv); ``` -------------------------------- ### Setting LightMode Tag for Light Sampling (ShaderLab) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Set the LightMode tag in the shader's Tags block to 'ForwardBase' or similar to enable sampling light data within the shader. ```ShaderLab Tags { "LightMode"="FowardBase" } ... ``` -------------------------------- ### Detecting VRChat Scenarios (Alternative GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Offers an alternative set of GLSL functions for detecting VR, desktop, and eye (left/right) based on `USING_STEREO_MATRICES` and VRChat specific uniforms, potentially useful in mirror scenarios. ```glsl uniform float _VRChatMirrorMode; uniform float3 _VRChatMirrorCameraPos; bool IsVR() { #if defined(USING_STEREO_MATRICES) return true; #else return _VRChatMirrorMode == 1; #endif } bool IsRightEye() { #if defined(USING_STEREO_MATRICES) return unity_StereoEyeIndex == 1; #else return _VRChatMirrorMode == 1 && mul(unity_WorldToCamera, float4(_VRChatMirrorCameraPos, 1)).x < 0; #endif } bool IsLeftEye() { return !IsRightEye(); } bool IsDesktop() { return !IsVR(); } ``` -------------------------------- ### Visualizing UV Unwrap in Clipspace with GLSL Shader Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Provides a simple Unity shader written in CG/HLSL that visualizes the UV coordinates of a mesh directly in clip space. This is a useful debugging tool for checking the UV layout of models. ```glsl Shader "UVUnwrap" { SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; v2f vert (float2 uv : TEXCOORD0) { v2f o; o.vertex = float4(float2(1,-1)*(uv*2-1),0,1); o.uv = uv; return o; } float4 frag (v2f i) : SV_Target { return float4(i.uv, 0, 1); } ENDCG } } } ``` -------------------------------- ### Restore Current Instance Context (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/gpu-instancing.md Calls `UNITY_SETUP_INSTANCE_ID` with the fragment shader's original input struct (`i`). This restores the `unity_ObjectToWorld` matrix and other instance-specific data back to the context of the instance currently being rendered by the fragment shader, which is necessary after iterating through other instances. ```glsl UNITY_SETUP_INSTANCE_ID(i); ``` -------------------------------- ### Enable GPU Instancing Pragma (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/gpu-instancing.md Adds the necessary pragma to the shader to enable GPU instancing compilation. This allows the shader to handle multiple instances of the same mesh efficiently by enabling multi-compile variants for instancing. ```glsl #pragma multi_compile_instancing ``` -------------------------------- ### List of Usable Default Texture Property Values (Text) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Provides a list of string keywords that can be used as default values for texture properties in shader declarations, including common colors, ramps, and Unity/VRChat specific textures. ```glsl red gray grey linearGray linearGrey grayscaleRamp greyscaleRamp bump blackCube lightmap unity_Lightmap unity_LightmapInd unity_ShadowMask unity_DynamicLightmap unity_DynamicDirectionality unity_DynamicNormal unity_DitherMask _DitherMaskLOD _DitherMaskLOD2D unity_RandomRotation16 unity_NHxRoughness unity_SpecCube0 unity_SpecCube1 unity_ProbeVolumeSH ``` -------------------------------- ### Cheap Wireframe Effect (GLSL/HLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md A fragment shader snippet that creates a simple wireframe effect by checking the fractional part of the screen position, often leveraging MSAA. ```GLSL float4 frag (centroid float4 p : SV_POSITION) : SV_Target { return any(frac(p).xy != 0.5); } ``` -------------------------------- ### Aliasing CBuffer Arrays in GLSL Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Demonstrates how to use the `packoffset` directive within a cbuffer in GLSL (HLSL) to alias multiple smaller arrays to a single larger array, bypassing Unity's 1023-element limit for `Material.SetVectorArray`. This allows accessing a larger contiguous block of memory in the shader. ```glsl cbuffer ProgramBuffer { float4 _Program[1023*4] : packoffset(c0); float4 _Program0[1023] : packoffset(c0); float4 _Program1[1023] : packoffset(c1023); float4 _Program2[1023] : packoffset(c2046); float4 _Program3[1023] : packoffset(c3069); }; ``` -------------------------------- ### Accessing Global GrabPass Texture (GLSL/CG) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Declare a sampler2D property with the exact name used in a global GrabPass. This allows the shader to sample the texture captured by that GrabPass. ```GLSL ... sampler2D _MyGlobalTexture; ... ``` -------------------------------- ### Distinguish Renderer by Scale Property (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/gpu-instancing.md Demonstrates a method to identify a specific renderer instance based on a custom property, such as its scale. It checks if the X component of the scale (derived from `unity_ObjectToWorld[0][0]`) matches a specific value (0.4) and renders it differently, allowing for unique handling of certain instances. ```glsl if (unity_ObjectToWorld[0][0].x == 0.4) return 0; ``` -------------------------------- ### GLSL Modulo Macro (GLSL/CG/HLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Define a macro for a modulo operation that behaves like GLSL's `mod` function, providing more predictable results with negative numbers compared to HLSL's `fmod`. ```GLSL #define glsl_mod(x,y) (((x)-(y)*floor((x)/(y)))) ``` -------------------------------- ### Using forcecase Attribute on Switch in GLSL Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Demonstrates applying the `[forcecase]` attribute to a switch statement in GLSL (HLSL). This attribute forces the compiler to generate a jump table for the switch, which can improve performance in certain scenarios compared to the default compiler behavior. ```glsl [forcecase] switch (myValue) { ... } ``` -------------------------------- ### Setting up MRT Target Buffers in Unity C# Source: https://github.com/pema99/shader-knowledge/blob/main/mrt.md This C# script demonstrates how to use `Camera.SetTargetBuffers` to configure a Unity camera to render to multiple RenderTextures simultaneously. It requires a Camera component and references to the target RenderTextures. The script sets the color buffers of multiple textures and a single depth buffer from one of the textures as the camera's render targets. ```csharp using UnityEngine; public class SetMRT : MonoBehaviour { public RenderTexture RT0, RT1, RT2, RT3; void Start() { GetComponent().SetTargetBuffers( new RenderBuffer[] { RT0.colorBuffer, RT1.colorBuffer, RT2.colorBuffer, RT3.colorBuffer }, RT0.depthBuffer); } } ``` -------------------------------- ### Transfer Instance ID in Vertex Shader (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/gpu-instancing.md Uses `UNITY_SETUP_INSTANCE_ID` to initialize the instance ID for the current vertex based on the input struct and `UNITY_TRANSFER_INSTANCE_ID` to pass the instance ID from the vertex shader input (`v`) to the fragment shader output (`o`). This ensures the fragment shader receives the correct instance index. ```glsl v2f vert(appdata v) { v2f o; UNITY_SETUP_INSTANCE_ID(v); UNITY_TRANSFER_INSTANCE_ID(v, o); ... } ``` -------------------------------- ### Adding Empty ForwardAdd Pass (ShaderLab/CG) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Include an empty pass with the 'ForwardAdd' LightMode tag. This prevents pixel lights from being included in the `unity_4LightPos` arrays if the main pass is ForwardBase, ensuring the arrays only contain vertex lights. ```ShaderLab ... main pass goes above here Pass { Tags { "LightMode"="ForwardAdd" } CGPROGRAM #pragma vertex empty #pragma fragment empty void empty(){} ENDCG } ``` -------------------------------- ### Mapping Spherical Harmonics to Shader Uniforms - C# Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md A C# function that converts Spherical Harmonics L2 coefficients from a `SphericalHarmonicsL2` object into a `Vector4` array formatted for use with standard Unity shader uniforms (`unity_SHAr`, `unity_SHAg`, etc.). It requires an output array of size 7. ```C# // outCoeffs must be size 7 private void SHToShaderCoefficients(ref SphericalHarmonicsL2 sh, ref Vector4[] outCoeffs) { // outCoeffs will have this order: // [0] = unity_SHAr // [1] = unity_SHAg // [2] = unity_SHAb // [3] = unity_SHBr // [4] = unity_SHBg // [5] = unity_SHBb // [6] = unity_SHC for (int i = 0; i < 3; i++) { outCoeffs[i] = new Vector4( sh[i, 3], sh[i, 1], sh[i, 2], sh[i, 0] - sh[i, 6] ); outCoeffs[i + 3] = new Vector4( sh[i, 4], sh[i, 5], sh[i, 6] * 3.0f, sh[i, 7] ); } outCoeffs[6] = new Vector4( sh[0, 8], sh[1, 8], sh[2, 8], 1.0f ); } ``` -------------------------------- ### Defining Named GrabPass (ShaderLab) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Define a GrabPass with a specific name. The texture captured by this pass will be available globally to any shader by referencing this name. ```ShaderLab GrabPass { "_MyGlobalTexture" } ``` -------------------------------- ### Defining IntRange Slider Property (ShaderLab) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Define an integer slider property in the ShaderLab Properties block using the `[IntRange]` attribute. ```ShaderLab Properties { [IntRange] _MyIntSlider ("My Int Slider", Range(0, 10)) = 0 } ``` -------------------------------- ### Encoding/Decoding UInt in FP16 GrabPass GLSL Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Provides GLSL functions for packing and unpacking a 32-bit unsigned integer into three half-precision floats. This technique is useful for storing integer data in FP16 textures like those used by Unity's GrabPass in HDR contexts, although it notes a limitation where the last 2 bits are lost. ```glsl // Packing/unpacking routines for saving integers to R16G16B16A16_FLOAT textures // Heavily based off of https://github.com/apitrace/dxsdk/blob/master/Include/d3dx_dxgiformatconvert.inl // For some reason the last 2 bits get stomped so we'll only allow uint14 for now :( float uint14ToFloat(uint input) { precise float output = (f16tof32((input & 0x00003fff))); return output; } uint floatToUint14(precise float input) { uint output = (f32tof16(input)) & 0x00003fff; return output; } // Encodes a 32 bit uint into 3 half precision floats float3 uintToHalf3(uint input) { precise float3 output = float3(uint14ToFloat(input), uint14ToFloat(input >> 14), uint14ToFloat((input >> 28) & 0x0000000f)); return output; } uint half3ToUint(precise float3 input) { return floatToUint14(input.x) | (floatToUint14(input.y) << 14) | ((floatToUint14(input.z) & 0x0000000f) << 28); } ``` -------------------------------- ### Guarding Code in Surface Shaders (GLSL/CG) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Wrap code that should not run during surface shader analysis (like GetDimensions) within an #ifndef SHADER_TARGET_SURFACE_ANALYSIS block. ```GLSL #ifndef SHADER_TARGET_SURFACE_ANALYSIS // call Texture2D.GetDimensions here #endif ``` -------------------------------- ### Setting Aliased CBuffer Arrays in CSharp Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Shows how to set data for the aliased cbuffer arrays from C# using Unity's `Material.SetVectorArray` method. Each call targets one of the smaller aliased arrays, collectively filling the larger underlying memory block. ```csharp myMaterial.SetVectorArray("_Program0", arrayPart0); myMaterial.SetVectorArray("_Program1", arrayPart1); myMaterial.SetVectorArray("_Program2", arrayPart2); myMaterial.SetVectorArray("_Program3", arrayPart3); ``` -------------------------------- ### Accessing Instance Data Directly in Unity GLSL Shader Source: https://github.com/pema99/shader-knowledge/blob/main/gpu-instancing.md This GLSL snippet illustrates how to directly access built-in instance data arrays in Unity shaders. It shows how to retrieve the object-to-world matrix for a specific instance by indexing into unity_Builtins0Array using a combination of unity_BaseInstanceID and a per-renderer ID (like the one set via Renderer.sortingOrder). ```GLSL unity_Builtins0Array[unity_BaseInstanceID + rendererID].unity_ObjectToWorldArray ``` -------------------------------- ### Color Fragment Based on Retrieved Position (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/gpu-instancing.md Uses the retrieved world positions to determine the fragment color. It samples the `cube_positions` array based on the U coordinate of the texture coordinates (`i.uv.x`), effectively creating color bands corresponding to the positions of the first three instances for visualization. ```glsl return float4(cube_positions[floor(i.uv.x * 3)], 1); ``` -------------------------------- ### Inverting a Matrix - GLSL Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Provides a GLSL function to calculate the inverse of a 4x4 matrix. It uses the transpose of the input matrix and calculates cofactors to determine the inverse, finally scaling by the reciprocal of the determinant. ```GLSL float4x4 inverse(float4x4 mat) { float4x4 M=transpose(mat); float m01xy=M[0].x*M[1].y-M[0].y*M[1].x; float m01xz=M[0].x*M[1].z-M[0].z*M[1].x; float m01xw=M[0].x*M[1].w-M[0].w*M[1].x; float m01yz=M[0].y*M[1].z-M[0].z*M[1].y; float m01yw=M[0].y*M[1].w-M[0].w*M[1].y; float m01zw=M[0].z*M[1].w-M[0].w*M[1].z; float m23xy=M[2].x*M[3].y-M[2].y*M[3].x; float m23xz=M[2].x*M[3].z-M[2].z*M[3].x; float m23xw=M[2].x*M[3].w-M[2].w*M[3].x; float m23yz=M[2].y*M[3].z-M[2].z*M[3].y; float m23yw=M[2].y*M[3].w-M[2].w*M[3].y; float m23zw=M[2].z*M[3].w-M[2].w*M[3].z; float4 adjM0,adjM1,adjM2,adjM3; adjM0.x=+dot(M[1].yzw,float3(m23zw,-m23yw,m23yz)); adjM0.y=-dot(M[0].yzw,float3(m23zw,-m23yw,m23yz)); adjM0.z=+dot(M[3].yzw,float3(m01zw,-m01yw,m01yz)); adjM0.w=-dot(M[2].yzw,float3(m01zw,-m01yw,m01yz)); adjM1.x=-dot(M[1].xzw,float3(m23zw,-m23xw,m23xz)); adjM1.y=+dot(M[0].xzw,float3(m23zw,-m23xw,m23xz)); adjM1.z=-dot(M[3].xzw,float3(m01zw,-m01xw,m01xz)); adjM1.w=+dot(M[2].xzw,float3(m01zw,-m01xw,m01xz)); adjM2.x=+dot(M[1].xyw,float3(m23yw,-m23xw,m23xy)); adjM2.y=-dot(M[0].xyw,float3(m23yw,-m23xw,m23xy)); adjM2.z=+dot(M[3].xyw,float3(m01yw,-m01xw,m01xy)); adjM2.w=-dot(M[2].xyw,float3(m01yw,-m01xw,m01xy)); adjM3.x=-dot(M[1].xyz,float3(m23yz,-m23xz,m23xy)); adjM3.y=+dot(M[0].xyz,float3(m23yz,-m23xz,m23xy)); adjM3.z=-dot(M[3].xyz,float3(m01yz,-m01xz,m01xy)); adjM3.w=+dot(M[2].xyz,float3(m01yz,-m01xz,m01xy)); float invDet=rcp(dot(M[0].xyzw,float4(adjM0.x,adjM1.x,adjM2.x,adjM3.x))); return transpose(float4x4(adjM0*invDet,adjM1*invDet,adjM2*invDet,adjM3*invDet)); } ``` -------------------------------- ### Shading by Instance ID in Unity GLSL Shader Source: https://github.com/pema99/shader-knowledge/blob/main/gpu-instancing.md This GLSL fragment shader snippet demonstrates how to use the instance ID (i.instanceID) within a Unity shader. It returns a grayscale color based on the instance ID, allowing visual verification that the instance IDs are stable and unique for each renderer when using techniques like setting Renderer.sortingOrder. ```GLSL float4 frag (v2f i) : SV_Target { #if defined(UNITY_INSTANCING_ENABLED) return i.instanceID / 3.0; #else return 0; #endif } ``` -------------------------------- ### Depth-Only Pass for Transparent Shaders (ShaderLab) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Add a pass before the main transparent pass that writes to the depth buffer (ZWrite On) but not color (ColorMask 0). This helps resolve draw order issues by ensuring objects correctly occlude others in the depth buffer. ```ShaderLab Pass { ZWrite On ColorMask 0 } ``` -------------------------------- ### Controlling Render States with Properties (ShaderLab) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Use properties defined in the ShaderLab block to dynamically control fixed-function rendering states like Culling and ZWrite within the SubShader block. Uses `[Enum]` and `[ToggleUI]` attributes. ```ShaderLab Properties { [Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull", Float) = 0 [ToggleUI] _ZWrite ("ZWrite", Float) = 0 } SubShader { Cull [_Cull] ZWrite [_ZWrite] } ``` -------------------------------- ### Setting MRT Render Targets in Unity C# Source: https://github.com/pema99/shader-knowledge/blob/main/mrt.md Demonstrates how to use the Camera.SetTargetBuffers method in a C# script to assign multiple RenderTextures as color buffers and a single RenderTexture's depth buffer to a camera, enabling MRT rendering for simulating multiple camera loops. ```C# using UnityEngine; public class SetMRT : MonoBehaviour { public Camera LoopCamera; public Camera DBCamera; public RenderTexture RT0, RT1, RT2, RT0_DB, RT1_DB, RT2_DB; void Start() { LoopCamera.SetTargetBuffers( new RenderBuffer[] { RT0.colorBuffer, RT1.colorBuffer, RT2.colorBuffer }, RT0.depthBuffer); DBCamera.SetTargetBuffers( new RenderBuffer[] { RT0_DB.colorBuffer, RT1_DB.colorBuffer, RT2_DB.depthBuffer }, RT0_DB.depthBuffer); } } ``` -------------------------------- ### Declare Instance ID in Shader Structs (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/gpu-instancing.md Includes the `UNITY_VERTEX_INPUT_INSTANCE_ID` macro in the vertex (`appdata`) and fragment (`v2f`) input structs. This macro declares a variable (typically `instanceID`) to hold the instance index for each vertex/fragment, which is essential for accessing instance-specific data. ```glsl struct appdata { ... UNITY_VERTEX_INPUT_INSTANCE_ID // add this }; struct v2f { ... UNITY_VERTEX_INPUT_INSTANCE_ID // add this }; ``` -------------------------------- ### Skipping Shader Optimizations with Pragma in GLSL Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Shows how to use the `#pragma skip_optimizations` directive in a shader to disable compiler optimizations for a specific graphics API (e.g., `d3d11`). This can significantly speed up shader compilation during development iteration but should be removed for production builds as it reduces runtime performance. ```glsl #pragma skip_optimizations d3d11 ``` -------------------------------- ### Setting Renderer Sorting Order in Unity C# Source: https://github.com/pema99/shader-knowledge/blob/main/gpu-instancing.md This Unity C# script, designed to run in edit mode, sets the sortingOrder property of the attached Renderer component. It uses a public sortingOrder variable, settable in the inspector, to assign a unique, stable ID to each renderer instance, which can then be used for instancing logic. ```C# using UnityEngine; [ExecuteInEditMode] public class SetSortingOrder : MonoBehaviour { public int sortingOrder; void OnEnable() { GetComponent().sortingOrder = sortingOrder; } } ``` -------------------------------- ### Defining Custom Enum Property (ShaderLab) Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Manually define an enum dropdown property in the ShaderLab Properties block using the `[Enum]` attribute, specifying custom name-value pairs for the dropdown options. ```ShaderLab Properties { [Enum(One,1,SrcAlpha,5)] _Blend2 ("Blend mode subset", Float) = 1 } ``` -------------------------------- ### Implementing Geometry Instancing in HLSL Shader Source: https://github.com/pema99/shader-knowledge/blob/main/geometry-shaders.md This HLSL geometry shader snippet illustrates how to enable geometry instancing using the `[instance(n)]` attribute and access the instance index via the `SV_GSInstanceID` semantic. This allows the geometry function to be invoked multiple times (up to 32) for each input primitive, each with a unique `instanceID`. This technique is recommended over increasing `maxvertexcount` for better performance when generating large amounts of geometry. ```hlsl #pragma target 5.0 #pragma geometry geom ... [instance(8)] [maxvertexcount(12)] void geom(triangle v2f IN[3], inout TriangleStream tristream, uint instanceID : SV_GSInstanceID) { ... } ``` -------------------------------- ### Main MRT Loop Shader Fragment Function (HLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/mrt.md Implements the core logic of the main camera loop shader's fragment stage. It samples the input textures, applies a simple fractional operation with different offsets to simulate changing values over time, and outputs the results to multiple render targets (SV_Target0, SV_Target1, SV_Target2) simultaneously. ```HLSL struct fragout { float4 color0 : SV_Target0; float4 color1 : SV_Target1; float4 color2 : SV_Target2; }; fragout frag (v2f i) { fragout res; res.color0 = frac(tex2D(_RT0, i.uv) + 0.01); res.color1 = frac(tex2D(_RT1, i.uv) + 0.03); res.color2 = frac(tex2D(_RT2, i.uv) + 0.05); return res; } ``` -------------------------------- ### Shader Fragment Function Modification for Camera Loop Source: https://github.com/pema99/shader-knowledge/blob/main/camera-loops.md This line replaces the standard return statement in a ShaderLab fragment function. It reads the previous frame's color (`col`), adds a small value (0.01), and takes the fractional part. This creates a simple stateful effect where the color brightens slightly each frame, wrapping around when it reaches 1.0. ```ShaderLab return frac(col + 0.01); ``` -------------------------------- ### Main MRT Loop Shader Properties and Samplers (HLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/mrt.md Defines the necessary texture properties and sampler variables within the shader's Properties block and CGPROGRAM section. These are used to reference the input textures from the previous frame's output, which serve as the state for each simulated camera loop. ```HLSL Properties { _RT0("RT0", 2D) = "white"{} _RT1("RT1", 2D) = "white"{} _RT2("RT2", 2D) = "white"{} } SubShader { Pass { CGPROGRAM ... sampler2D _RT0; sampler2D _RT1; sampler2D _RT2; ... ENDPROGRAM } } ``` -------------------------------- ### Double Buffering Shader Fragment Function (HLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/mrt.md Provides the fragment function for the double buffering shader. This function performs a simple copy operation, sampling the input textures (_RT0, _RT1, _RT2) and outputting their values directly to the corresponding render targets (SV_Target0, SV_Target1, SV_Target2) to facilitate the double buffering mechanism. ```HLSL struct fragout { float4 color0 : SV_Target0; float4 color1 : SV_Target1; float4 color2 : SV_Target2; }; fragout frag (v2f i) { fragout res; res.color0 = tex2D(_RT0, i.uv); res.color1 = tex2D(_RT1, i.uv); res.color2 = tex2D(_RT2, i.uv); return res; } ``` -------------------------------- ### Generating Degenerate Mesh in Unity C# Source: https://github.com/pema99/shader-knowledge/blob/main/geometry-shaders.md This C# script, intended for use in the Unity Editor, creates a mesh asset with a single vertex and a large number of triangles (defined by `size * size * 3`). This degenerate mesh is useful as input for geometry shaders in VRChat to generate a large amount of geometry efficiently. The script saves the mesh as an asset in the project. ```csharp // idea by Nave, original: https://pastebin.com/Q43UPHf4 #if UNITY_EDITOR using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; public class CreateParticlesMesh : MonoBehaviour { [MenuItem("GameObject/Create Paricles Mesh")] static void DoIt() { int size = 1024; // Change this value to what you want var mesh = new Mesh(); mesh.vertices = new Vector3[] { new Vector3(0, 0, 0) }; mesh.triangles = new int[size * size * 3]; mesh.bounds = new Bounds(new Vector3(0, 0, 0), new Vector3(1, 1, 1)); string path = "Assets/" + size + "x" + size + ".asset"; AssetDatabase.CreateAsset(mesh, path); EditorGUIUtility.PingObject(mesh); } } #endif ``` -------------------------------- ### Declaring Globally Overridable Texture Property - C# Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Demonstrates how to declare a texture property in a C# shader properties block that can be globally overridden using `Shader.SetGlobalTexture`. This requires omitting a default texture value in the declaration. ```C# Properties { _Udon_GlobalTexture("Texture", 2D) = "" {} } ``` -------------------------------- ### GLSL Shader Fragment Function for MRT Source: https://github.com/pema99/shader-knowledge/blob/main/mrt.md This GLSL shader code shows the structure of a fragment function designed for Multiple Render Targets. It defines a struct `fragout` where each field is associated with a specific render target using the `SV_TargetN` semantic. The fragment function then writes different color values to each field of this struct, directing output to the corresponding RenderTextures set up for MRT. ```glsl #pragma fragment frag ... struct fragout { float4 color0 : SV_Target0; float4 color1 : SV_Target1; float4 color2 : SV_Target2; float4 color3 : SV_Target3; }; fragout frag (v2f i) { fragout res; res.color0 = float4(1, 0, 0, 1); res.color1 = float4(0, 1, 0, 1); res.color2 = float4(0, 0, 1, 1); res.color3 = float4(1, 0, 1, 1); return res; } ``` -------------------------------- ### Preventing CBuffer Alias Optimization in GLSL Source: https://github.com/pema99/shader-knowledge/blob/main/tips-and-tricks.md Provides a hack to prevent the Unity shader compiler from optimizing out the aliased cbuffer arrays that might appear unused. This is achieved by including them in a calculation within a conditional branch that is never executed. ```glsl // Hack to prevent unity from deleting aliased cbuffer. Branch will never be hit if (uv.x < 0) someValuedUsedInFurtherCalculation = _Program0[0] + _Program1[0] + _Program2[0] + _Program3[0]; ``` -------------------------------- ### Reading RenderTexture Pixels into UdonSharp (C#) Source: https://github.com/pema99/shader-knowledge/blob/main/shader-data-to-udon.md This C# UdonSharp script demonstrates how to read pixel data from a Texture2D, which is assumed to be populated by a camera rendering to a RenderTexture, within the `OnPostRender` callback. It reads the pixels into the Texture2D using `ReadPixels`, applies the changes, and then retrieves the pixel data as a `Color` array using `GetPixels` for use within Udon. The script must be attached to the camera GameObject that renders to the target RenderTexture. ```C# using UnityEngine; using UdonSharp; public class ReadTexture : UdonSharpBehaviour { public Texture2D tex; void OnPostRender() { // Read the pixels. tex.ReadPixels(new Rect(0, 0, 256, 256), 0, 0, false); tex.Apply(); // Get the pixels into UDON. Color[] pixels = tex.GetPixels(0, 0, 256, 256); // ... do something with the pixel data } } ``` -------------------------------- ### Reconstructing World Position from Depth (GLSL) Source: https://github.com/pema99/shader-knowledge/blob/main/depth-texture.md This GLSL shader snippet demonstrates how to use the _CameraDepthTexture to reconstruct the world space position of each fragment. It includes a matrix inversion function and vertex/fragment shaders to perform the transformation from clip space depth to world space. ```GLSL struct v2f { float4 vertex : SV_Position; float4 clipPos : TEXCOORD0; nointerpolation float4x4 inverseVP : IVP; }; UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture); float4x4 inverse(float4x4 mat) { float4x4 M=transpose(mat); float m01xy=M[0].x*M[1].y-M[0].y*M[1].x; float m01xz=M[0].x*M[1].z-M[0].z*M[1].x; float m01xw=M[0].x*M[1].w-M[0].w*M[1].x; float m01yz=M[0].y*M[1].z-M[0].z*M[1].y; float m01yw=M[0].y*M[1].w-M[0].w*M[1].y; float m01zw=M[0].z*M[1].w-M[0].w*M[1].z; float m23xy=M[2].x*M[3].y-M[2].y*M[3].x; float m23xz=M[2].x*M[3].z-M[2].z*M[3].x; float m23xw=M[2].x*M[3].w-M[2].w*M[3].x; float m23yz=M[2].y*M[3].z-M[2].z*M[3].y; float m23yw=M[2].y*M[3].w-M[2].w*M[3].y; float m23zw=M[2].z*M[3].w-M[2].w*M[3].z; float4 adjM0,adjM1,adjM2,adjM3; adjM0.x=+dot(M[1].yzw,float3(m23zw,-m23yw,m23yz)); adjM0.y=-dot(M[0].yzw,float3(m23zw,-m23yw,m23yz)); adjM0.z=+dot(M[3].yzw,float3(m01zw,-m01yw,m01yz)); adjM0.w=-dot(M[2].yzw,float3(m01zw,-m01yw,m01yz)); adjM1.x=-dot(M[1].xzw,float3(m23zw,-m23xw,m23xz)); adjM1.y=+dot(M[0].xzw,float3(m23zw,-m23xw,m23xz)); adjM1.z=-dot(M[3].xzw,float3(m01zw,-m01xw,m01xz)); adjM1.w=+dot(M[2].xzw,float3(m01zw,-m01xw,m01xz)); adjM2.x=+dot(M[1].xyw,float3(m23yw,-m23xw,m23xy)); adjM2.y=-dot(M[0].xyw,float3(m23yw,-m23xw,m23xy)); adjM2.z=+dot(M[3].xyw,float3(m01yw,-m01xw,m01xy)); adjM2.w=-dot(M[2].xyw,float3(m01yw,-m01xw,m01xy)); adjM3.x=-dot(M[1].xyz,float3(m23yz,-m23xz,m23xy)); adjM3.y=+dot(M[0].xyz,float3(m23yz,-m23xz,m23xy)); adjM3.z=+dot(M[3].xyz,float3(m01yz,-m01xz,m01xy)); adjM3.w=+dot(M[2].xyz,float3(m01yz,-m01xz,m01xy)); float invDet=rcp(dot(M[0].xyzw,float4(adjM0.x,adjM1.x,adjM2.x,adjM3.x))); return transpose(float4x4(adjM0*invDet,adjM1*invDet,adjM2*invDet,adjM3*invDet)); } v2f vert (float4 vertex : POSITION, float2 uv : TEXCOORD0) { v2f o; o.vertex = float4(float2(1,-1)*(uv*2-1),1,1); o.clipPos = o.vertex; o.inverseVP = inverse(UNITY_MATRIX_VP); return o; } float4 frag (v2f i) : SV_Target { float4 clipPos = i.clipPos / i.clipPos.w; clipPos.z = tex2Dproj(_CameraDepthTexture, ComputeScreenPos(clipPos)); float4 homWorldPos = mul(i.inverseVP, clipPos); float3 wpos = homWorldPos.xyz / homWorldPos.w; // world space fragment position return float4(wpos, 1.0f); } ```