Last active
October 3, 2024 16:58
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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)] mattersWhenSettingInEditorFromScript ("Description for inspector.", 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 ____ _BLABLA // Will Build only used variations. (Use this if you don't plan to change this from script) | |
//Management from script | |
// From script you can set this keywords | |
if (isEnabled) | |
material.EnableKeyword("_BLABLABLA"); | |
else | |
material.DisableKeyword("_BLABLABLA"); | |
// To make sure Unity Editor is showing it correctly: | |
material.SetFloat("mattersWhenSettingInEditorFromScript", isEnabled ? 1 : 0); | |
// You can also set it globally. But 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); | |
// 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is great, thanks for sharing. 👍