Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@quizcanners
Last active April 16, 2024 15:48
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save quizcanners/0da1cbad4b1e2187af73f6ab52a6dabb to your computer and use it in GitHub Desktop.
Save quizcanners/0da1cbad4b1e2187af73f6ab52a6dabb to your computer and use it in GitHub Desktop.
To keep snippets of code for Shaders. Just some stuff that I often use but also often forget because brain=poo
//https://neginfinity.bitbucket.io/ - a blog where I found how to do shadows for raymarched/raytraced primitives.
//https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
//https://github.com/TwoTailsGames/Unity-Built-in-Shaders/blob/master/CGIncludes/UnityCG.cginc - Most interesting stuff ;)
//https://github.com/TwoTailsGames/Unity-Built-in-Shaders/tree/master/CGIncludes
//https://docs.unity3d.com/Manual/SL-Shader.html
//http://developer.download.nvidia.com/CgTutorial/cg_tutorial_appendix_e.html
//https://unity3d.com/how-to/shader-profiling-and-optimization-tips
//https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
//http://www.deepskycolors.com/archive/2010/04/21/formulas-for-Photoshop-blending-modes.html
//http://www.iquilezles.org/blog/
//https://www.youtube.com/channel/UCcAlTqd9zID6aNX3TzwxJXg
// Material
mat = new Material(shader);
mat.hideFlags = HideFlags.HideAndDontSave;
// Set blend mode to invert destination colors.
mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusDstColor);
mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
// Turn off backface culling, depth writes, depth test.
mat.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
mat.SetInt("_ZWrite", 0);
mat.SetInt("_ZTest", (int)UnityEngine.Rendering.CompareFunction.Always);
//Properties
[PerRendererData][NoScaleOffset]_MainTex("Albedo", 2D) = "white" {}
// Image UI elements throw error in Build if Material(It's Shader) doesn't have _MainTex, so keep it here even if not used.
// [PerRendererData] - For UI, to hide it from material inspector;
// [NoScaleOffset] - To Hide scale and offset editing window - usefult if they are not utilized in shader
// Alternative Default Values: "white", "black", "gray", "bump", "red"
[HDR]_Color("Color", Color) = (1,1,1,1)
_SomeSlider("Reflectiveness or something", Range(0,1)) = 0
[HideInInspector]_ProjTexPos("Screen Space Position", Vector) = (0,0,0,0)
// [HideInInspector] - no need to show this in inspector
_Test("Any value", float) = 1
// Blend Modes:
Blend SrcAlpha OneMinusSrcAlpha // Traditional transparency
Blend One One // Additive
Blend OneMinusDstColor One // Soft Additive
// MULTICOMPILE & SHADER FEATURE
shader_feature_local // Same as shader_feature, but only for current shader (if Set from Properties)
multi_compile_local
// Use _local when planning to use any of the following
[KeywordEnum(None, Regular, Combined)] BUMP ("Bump Map", Float) = 0
#pragma shader_feature_local ___ BUMP_NONE BUMP_REGULAR BUMP_COMBINED
//Toggle
[Toggle(_BLABLA)] thisDoesntMatter ("Some Bla Bla BLa", Float) = 0
#pragma multi_compile_local ____ _BLABLA // Will Build all variations. (For changing via script) (___ = Also compile a version without any defines )
#pragma shader_feature_local _FEATUREA _FEATUREB // Will Build only used variations. (To configure materials in Editor only)
//Management
// From script you can set this keywords
renderer.material.EnableKeyword("_BLABLABLA");
// or globally (If Shader has it in it's properties, it will ignore the global value)
Shader.EnableKeyword("_BLABLABLA")
// Defines
#define USE_FEATURE_X // Must be before the include
#include "Assets/FeatureCode.cginc"
// Inside Feature Code
#if defined(USE_FEATURE_X)
// Feature code
#endif
// Unity's main Build-In parameters
_WorldSpaceCameraPos
_WorldSpaceLightPos0 - Main Directional Light Direction
_ProjectionParams w - clip length, can be used for fog
// PRecision & PErformance
#pragma fragmentoption ARB_precision_hint_fastest // or ARB_precision_hint_nicest
// On some devices makes no difference. Nicest - if shader depends on precise calculations
//*****COMMON OPERATORS/FUNCTIONS*****
fwidth(x), ddx(x), ddy(x) // The only operation that lets you get info on pixels "Next Door". It tells difference of pixels in this block (https://www.programmersought.com/article/71564489650/).
y = saturate(x); // Clamp x between 0 and 1
floor(); // return smallest int part
x % 1 // get fraction part
log(Y)/log(X); // LOGxY (LOGxY = Z | means that X to the power of Z equals Y | Log base X of Y is the power you need to raise X to get Y)
atan(x,y) // returns an angle in [-Pi +Pi] range)
smoothstep (a, b , t); // Basically allows us to remap t from ab space to 01 space but with smoothing;
// Returns 0 if t<a<b; 1 if t>b>a, interpolates in-between; Will reverse if b<a;
x*x*(3.0 - (2.0*x)); // To apply smoothing when 0<=x<=1. (When applied to the function below will produce smoothstep).
sharpstep(a, b, x) => saturate((x - a)/(b - a)); // Remap X to [A,B] sharply.
float cheapstep(float x) { // https://www.shadertoy.com/view/4ldSD2
x = 1.0 - x*x; // MAD
x = 1.0 - x*x; // MAD
return x;
}
step(a, x) // returns 1 if x>a, 0 - otherwise
normal = normalize(cross(ddy(worldPos),ddx(worldPos)));
_MainTex_TexelSize // x,y=1.0/Size | zw=width,length
_MainTex_ST // x,y = Tiling, z,w = Offset
_ScreenParams // x is the width of the camera’s target texture in pixels
//, y is the height of the camera’s target texture in pixels, z is 1.0 + 1.0/width and w is 1.0 + 1.0/height.
// Object center
float4 objectOrigin = mul(_Object2World, float4(0.0,0.0,0.0,1.0) );
// Billboard Shader
Use "DisableBatching" = "True" or GPUInstancing
float3x3 m = UNITY_MATRIX_M;
float objectScale = length(float3( m[0][0], m[1][0], m[2][0]));
o.pos = mul(UNITY_MATRIX_P,
mul(UNITY_MATRIX_MV, float4(0.0, 0.0, 0.0, 1.0))
+ float4(v.vertex.x, v.vertex.y, 0.0, 0.0)
* float4(objectScale, objectScale, 1.0, 1.0)
);
//*****COOL MATH*****
// Circle from Uv
float2 uv = (i.texcoord - 0.5);
uv *= uv;
float circle = smoothstep(0.25-softness, 0.25, uv.x + uv.y);
// Nice sharp point light with smooth falloff
sizeCoefficient/ distanceFromCenter;
// Follaff in two direction
1 / (abs(CENTER - position)*SHARPNESS + 0.01);
// Gyroid
float sdGyroid(float3 pos, float scale, float thickness, float bias) {
pos *= scale;
return abs(dot(sin(pos), cos(pos.zxy))+bias)/scale - thickness;
}
// Optimisation when converting formulas
float3 vec = float3(x,x,x);
dot(vec,vec) = x * x * 3;
// Using smoothstep with ddx, ddy, fwidth
const SMOOTHNESS = 5; // usually can be left constant after configured
float offset = fwidth(uv); // To see how big the image is on the screen (Bigger => Sharper transition)
float pixelSmoothBox = smoothstep(1, 1 - offset * SMOOTHNESS , distanceToEdge);
// Vertex Position Offset in World Space
float4 worldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1));
// modify World Position
v.vertex = mul(unity_WorldToObject, float4(worldPos.xyz, v.vertex.w));
o.pos = UnityObjectToClipPos(v.vertex); // don't forget
// View Direction
float3 viewDir : TEXCOORD0; //v2f
o.viewDir.xyz = WorldSpaceViewDir(v.vertex); // vert
i.viewDir.xyz = normalize(i.viewDir.xyz); //frag
// World Normal
o.normal.xyz = normalize(UnityObjectToWorldNormal(v.normal)); // vert
// World Position
float3 wpos : TEXCOORD3;
o.wpos = mul(unity_ObjectToWorld, v.vertex).xyz;
// ------- To Modify In World Space:
v.vertex += mul(unity_WorldToObject, offx,offy,offz, 0);
// Distance to camera
float toCamera = length(_WorldSpaceCameraPos - i.worldPos.xyz) - _ProjectionParams.y;
// Screen Position
float4 screenPos : TEXCOORD1; // v2f (TEXCOORD can be 0,1,2, etc - the obly rule is to avoid duplication)
o.screenPos = ComputeScreenPos(o.pos); // vert
float2 screenUV = i.screenPos.xy / i.screenPos.w; // frag (Returns in 01 range (if on screen))
// Reflect:
float dotprod = max(0, dot(worldNormal, i.viewDir.xyz));
float3 reflected = normalize(i.viewDir.xyz - 2 * (dotprod)*worldNormal);
// Random ()Hash
float hash11(float p) {
p = fract(p * .1031);
p *= p + 33.33;
p *= p + p;
return fract(p);
}
// Distance to a line:
inline float DistToLine(float3 pos, float3 a, float3 b) {
float3 pa = pos - a;
float3 ba = b - a;
float t = saturate(dot(pa, ba)/dot(ba,ba));
return length(pa - ba * t);
}
// Rotation (Pivot in center)
float2 Rot(float2 uv, float angle) {
float si = sin(angle);
float co = cos(angle);
return float2(co * uv.x - si * uv.y, si * uv.x + co * uv.y);
}
// Rotate By Quaternion
float3 RotateVec(in float3 vec, in float4 q)
{
float3 crossA = cross(q.xyz, vec) + q.w * vec;
vec += 2 * cross(q.xyz, crossA);
return vec;
}
// Get Rotation from DDX DDY
float2 rotation = normalize(ddx(uv));
// Get Angle:
const float pii = 3.14159265359;
const float pi2 = pii * 2;
float2 uv = i.texcoord.xy - 0.5;
float angle = (atan2(uv.x, uv.y) + pii) / pi2
// Clock Effect (how to wrap around 0)
float clock = 0.1f/min(
abs(time - pixelAngle),
pi2 - abs(time - pixelAngle));
// Get Angle in 01 space
// Option A:
float2 uv = IN.texcoord.xy - 0.5;
const float PI2 = 3.14159265359 *2;
float pixel_angle = atan2(uv.x, uv.y)/ PI2 + 0.5;
float pixel_distance = length(uv)* 2;
float2 uv2 = float2(pixel_angle, pixel_distance);
// Option B: (Dont't remember but I think this helped me avoid some artifacs in some case)
const float PI2 = 3.14159265359 * 2;
float angle = atan2(-uv.x, -uv.y)+0.001;
angle = saturate(max(angle,
PI2 + min(0, angle)
- max(0, angle*999999)
)/ PI2);
// Cos/Sin to 01 space
float lerp = (_CosTime.z + 1) * 0.5; // Time.x,y,z,w - from slowest to fastest
// Matrixes
// Rotate vector
float3 wrldNormal = mul(unity_ObjectToWorld, float4(objNormal, 0));
// Rotate point
float3 wrldPos = mul(unity_ObjectToWorld, float4(objPos, 1));
//*****SAMPLING*****
// To Apply Offset
//#define TRANSFORM_TEX(uv,_NameTex) (uv.xy * _NameTex_ST.xy + _NameTex_ST.zw)
uv = uv*_MainTex_ST.xy + _MainTex_ST.zw; // _MainTex_ST needs to be defined in the variables section (not the Parameters section)
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); // Tile Offset needs to be declared (the line above)
// To do POINT Sampling (undo bilinear filtering):
float4 _MainTex_TexelSize; // Texture Size (zw = width,heigth ; xy = 1/width, 1/height)
float2 pointUV = (floor(uv * _MainTex_TexelSize.zw) + 0.5) * _MainTex_TexelSize.xy;
// Smooth Pixelation Sampling (Looks really cool)
float2 perfTex = (floor(IN.texcoord.xy * _MainTex_TexelSize.zw) + 0.5) * _MainTex_TexelSize.xy;
float2 off = (IN.texcoord.xy - perfTex);
float wigth = length(fwidth(IN.texcoord.xy));
float size = 0.01 / wigth;
off = off * saturate((abs(off) * _MainTex_TexelSize.zw) * size * 2 - (size - 1));
perfTex += off;
// To Also get the border:
float2 diff = (abs(off) * _MainTex_TexelSize.zw);
edge = saturate((diff * 2 - 1)*sharpness*0.1 + 1); // In some usages the are between pixels contains other pixels, then border needs to be less sharp to fully cover that area.
float border = max(edge.x, edge.y);
// To Get MipLevel
_MainTex_TexelSize.zw *= modifier; // Optional
float2 px = _MainTex_TexelSize.z * ddx(uv);
float2 py = _MainTex_TexelSize.w * ddy(uv);
return (max(0, 0.5 * log2(max(dot(px, px), dot(py, py)))));
// Lerp between two transparent textures
col.rgb = lerp(col1.rgb * col1.a, col2.rgb * col2.a, transition);
col.a = lerp(col1.a, col2.a, transition);
col.rgb /= col.a + 0.001;
// Parallax
//v2f:
float3 tangentViewDir : TEXCOORD5; // 5 or whichever is free
//vert:
float3x3 objectToTangent = float3x3(
v.tangent.xyz,
cross(v.normal, v.tangent.xyz) * v.tangent.w,
v.normal
);
o.tangentViewDir = mul(objectToTangent, ObjSpaceViewDir(v.vertex));
//frag
i.tangentViewDir = normalize(i.tangentViewDir);
i.tangentViewDir.xy /= (i.tangentViewDir.z + 0.42);
// or ..... /= (abs(o.tangentViewDir.z) + 0.42); to work on both sides of a plane
uv -= i.tangentViewDir.xy * height;
// Optionally multiply by Fresnel
//********** SCREEN SPACE EFFECTS
// Tile a square to fill the screen:
//f2v
float2 screenParams : TEXCOORD1;
float4 screenPos : TEXCOORD2;
//vert:
o.screenParams = float2(max(_ScreenParams.x / _ScreenParams.y, 1), max(1, _ScreenParams.y / _ScreenParams.x));
o.screenPos = ComputeScreenPos(o.pos);
//frag:
i.screenPos.xy /= i.screenPos.w;
float2 fitToScreen = i.screenPos.xy * i.screenParams;
float4 col = tex2D(_MainTex, fitToScreen * tilsesCount);
// Fit Sprite Sampling to Fill Screen Without stretching the texture:
// In script (Could also be done in vert function):
float screenAspect = ((float)Screen.width) / Screen.height;
float texAspect = ((float)bgTex.width) / bgTex.height;
Vector2 aspectCorrection = Vector2.one;
if (screenAspect > texAspect)
aspectCorrection.y = (texAspect / screenAspect);
else
aspectCorrection.x = (screenAspect / texAspect);
// In Shader
float2 uv = (screenUV.xy - 0.5)*aspectCorrection.xy + 0.5;
// **** FOR LIGHT/COLOR SHADING
// Tangent Transformation:
float3 tspace0 : TEXCOORD3;
float3 tspace1 : TEXCOORD4;
float3 tspace2 : TEXCOORD5;
// ...... vert
float3 wTangent = UnityObjectToWorldDir(v.tangent.xyz);
float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
float3 wBitangent = cross(wNormal, wTangent) * tangentSign;
o.tspace0 = half3(wTangent.x, wBitangent.x, wNormal.x);
o.tspace1 = half3(wTangent.y, wBitangent.y, wNormal.y);
o.tspace2 = half3(wTangent.z, wBitangent.z, wNormal.z);
// ..... frag
float3 tnormal = UnpackNormal(tex2D(_BumpMap, TRANSFORM_TEX(i.texcoord, _BumpMap)));
worldNormal.x = dot(i.tspace0, tnormal);
worldNormal.y = dot(i.tspace1, tnormal);
worldNormal.z = dot(i.tspace2, tnormal);
// Shadow
SHADOW_COORDS(2)
TRANSFER_SHADOW(o);
float shadow = SHADOW_ATTENUATION(i);
// Color Bleed (https://www.quizcanners.com/single-post/2018/04/02/Color-Bleeding-in-Shader)
float3 mix = col.gbr + col.brg;
col.rgb += mix * mix*amount; // amount = 0.02
//Get proximity to culling edge (to apply fog as far as possible, for example)
float dist01 = 1- saturate((_ProjectionParams.z - length(o.worldPos.xyz - _WorldSpaceCameraPos.xyz)) / _ProjectionParams.z);
// World Position from Depth Texture
// Vert:
o.screenPos = ComputeScreenPos(o.pos);
o.viewDir.xyz = WorldSpaceViewDir(v.vertex);
// Frag
float3 viewDir = normalize(i.viewDir.xyz);
float2 screenUv = i.screenPos.xy / i.screenPos.w;
float4 ray = mul(unity_CameraInvProjection, float4(screenUv * 2.0 - 1.0, 1.0, 1.0));
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenUV);
float depth01 = Linear01Depth(depth);
float worldSpaceDistanceToCamera = length((ray.xyz / ray.w) * depth01);
float truePosition = trueDist * viewDir - _WorldSpaceCameraPos;
// Using Depth
// vert:
o.screenPos = ComputeScreenPos(o.pos);
COMPUTE_EYEDEPTH(o.screenPos.z);
// frag:
float2 screenUV = i.screenPos.xy / i.screenPos.w;
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenUV);
float sceneZ = LinearEyeDepth(UNITY_SAMPLE_DEPTH(depth));
// Solve contact A
float partZ = i.screenPos.z;
float fade = smoothstep(0,1, _Softness (sceneZ - partZ));
// Variant:
float distance = -mul(UNITY_MATRIX_V, float4(newPos,1)).z;
float fade = saturate(distance - partZ);
// Decal (Uses camera depth)
float2 screenUv = i.screenPos.xy / i.screenPos.w;
float3 ray = normalize(i.worldPos - _WorldSpaceCameraPos);
float orthoToPresp = dot(ray, -UNITY_MATRIX_V[2].xyz);
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenPos);
depth = Linear01Depth(depth) * _ProjectionParams.z / orthoToPresp; // Getting distance from camera
float3 projectedPos = _WorldSpaceCameraPos + ray * depth;
float3 cubeSpacePos = mul(unity_WorldToObject, float4(projectedPos,1)).xyz; // xz + 0.5 could be used for UV
// Normal vector for Decal
float3 normal = normalize(cross(ddy(cubeSpacePos), ddx(cubeSpacePos))); // From Target Surface
float3 textureNormal = normalize(mul(float3(bumpMap.r, 0.5, bumpMap.g), unity_WorldToObject)); // From Bump Map
// Sample Shadow at Random position
float GetShadowAttenuation(float3 worldPos)
{
#if defined(SHADOWS_CUBE)
{
unityShadowCoord3 shadowCoord = worldPos - _LightPositionRange.xyz;
float result = UnitySampleShadowmap(shadowCoord);
return result;
}
#elif defined(SHADOWS_SCREEN)
{
#ifdef UNITY_NO_SCREENSPACE_SHADOWS
unityShadowCoord4 shadowCoord = mul(unity_WorldToShadow[0], worldPos);
#else
unityShadowCoord4 shadowCoord = ComputeScreenPos(mul(UNITY_MATRIX_VP, float4(worldPos, 1.0)));
#endif
float result = unitySampleShadow(shadowCoord);
return result;
}
#elif defined(SHADOWS_DEPTH) && defined(SPOT)
{
unityShadowCoord4 shadowCoord = mul(unity_WorldToShadow[0], float4(worldPos, 1.0));
float result = UnitySampleShadowmap(shadowCoord);
return result;
}
#else
return 1.0;
#endif
}
// To Output shadow from Shadow Pass
float CalculateShadowDepth(float3 worldPos)
{
float4 projPos = mul(UNITY_MATRIX_VP, float4(worldPos, 1));
projPos = UnityApplyLinearShadowBias(projPos);
return projPos.z / projPos.w;
}
// To output depth from fragment function
float CalculateFragmentDepth(float3 worldPos)
{
float4 depthVec = mul(UNITY_MATRIX_VP, float4(worldPos, 1.0));
return depthVec.z / depthVec.w;
}
@MostHated
Copy link

This is great, thanks for sharing. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment