VFX for the item box from the Mario Kart games, implemented both in Shader Graph and in pure HLSL for Universal RP in Unity 2021.3.10f1
- MARIO KART Item Box tutorial by Jettelly
- URP Unlit Shader Structure
- Custom Lighting in Shader Graph
- ShaderLibrary public repo from Unity
- Pure HLSL Implemenation
- Shader Graph Implementation
- Particle System
- Animation
- Creating the Textures
- Creating the Mesh
- Mark the shader to cull front (only render inward looking faces)
- Add two
Texture2D
properties, one for the rainbow colors and the other for the grayscale shapes. - Add a
Velocity
property to control the velocity of the animation. - Multiply the
Time
variable by theVelocity
parameter, and add it to theRed
channel of theGrayscale
texture. - Use this value to offset the UVs along the X coordinate.
- Use frac() to ensure the value is between 0 and 1 for uvs.
- Use these new uvs to get the pixel color from the colors texture.
half4 frag(Varyings IN) : SV_Target
{
half4 grayscaleColor = tex2D(_Grayscale, IN.uv);
float timeScaled = _Time.y * _ColorVelocity;
float offset = grayscaleColor.r + timeScaled;
float2 offsetUVs = float2(frac(IN.uv.x + offset), IN.uv.y);
half4 rainbowColor = tex2D(_Colors, offsetUVs);
return rainbowColor;
}
- Add
Tags
to specify the shader will be transparent. ZWrite Off
for best practices.- Use
Blend One One
to make an additive blend mode with theFrame Buffer
.
Tags { "RenderType"="Transparent" }
Tags { "Queue"="Transparent" }
LOD 100
ZWrite Off
Blend One One
- Include the
Core.hlsl
to use stuff likeTransformObjectToHClip
which is included along with several other transformation functions. - Include
Lighting.hlsl
so thatGetMainLight()
works insideMainLight.hlsl
- We can't include
Lighting.hlsl
insideMainLight.hlsl
, becasue that file is also used in shader lab, where it gets compiled along other includes. And it would result in a redefinition of a lot of things.
- We can't include
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "./MainLight.hlsl"
#include "./BlinnPhongLighting.hlsl"
- Obtain the view dir in world space by substracting the world space position of the vertex to the
_WorldSpaceCameraPos
, which is a built-in shader variable. - Then normalize() it.
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
OUT.normal = TransformObjectToWorldNormal(IN.normal);
OUT.viewDir = normalize(_WorldSpaceCameraPos.xyz - TransformObjectToWorld(IN.positionOS));
- Calculate the
Fresnel
effect by doing the dot() between the view direction and the normal of the pixel.
// fresnel
half fresnelDot = dot(IN.normal, IN.viewDir);
fresnelDot = saturate(fresnelDot); // clamp to 0,1
half fresnel = max(0.0, 0.6 - fresnelDot); // fresnelDot is zero when normal is 90 deg angle from view dir
- Calculate the
BlinnPhong
lighting by doing the dot product between the refracted light dir along the normal, and the view dir.
// blinn phong
float3 lightDir = 0;
float3 lightColor = 0;
MainLight_half(lightDir, lightColor);
half specular = 0;
ComputeBlinnPhong_half(lightDir, IN.normal, IN.viewDir, specular);
void MainLight_half(out half3 Direction, out half3 Color)
{
Light light = GetMainLight();
Direction = light.direction;
Color = light.color;
}
void ComputeBlinnPhong_half(half3 lightDir, half3 normal, half3 viewDir, out half Specular)
{
half3 reflectedLightDir = reflect(lightDir, normal);
Specular = max(0, dot(-viewDir, reflectedLightDir)); // avoid negative values
}
- Add two
Texture2D
properties, one for the rainbow colors and the other for the grayscale shapes. - Add a
Velocity
property to control the velocity of the animation. - Multiply the
Time
variable by theVelocity
parameter, and add it to theRed
channel of theGrayscale
texture. - Use this value as an offset in the
Tiling and Offset
node used to determine the UVs of the sampler 2D for the rainbow colors. - Mark the shader to cull front (only render inward looking faces)
- Add a
Fresnel Effect
Node. - Define a
Custom Function
Node and make it use thehlsl
file with the function that grabs the main light. - Define another
Custom Function
Node to compute theBlinn Phong
lighting. - Implement the
.hlsl
code to compute the main light direction and the blinn phong lighting:- Reflect the view direction along the normal.
- Calculate the
dot
product between the inverted view direction, and the reflected light. - The stronger the dot product, the stronger the specular.
- Use the
Normal Vector
andView Direction
nodes, along with the calculatedMain Light
direction, to compute the basicBlinn Phong
lighting in the custom function. - Add the
Fresnel
and theBlinn Phong
together.
void MainLight_half(out half3 Direction, out half3 Color)
{
// https://blog.unity.com/technology/custom-lighting-in-shader-graph-expanding-your-graphs-in-2019
#if SHADERGRAPH_PREVIEW
Direction = half3(0.5, 0.5, 0);
Color = 1;
#else
Light light = GetMainLight();
Direction = light.direction;
Color = light.color;
#endif
}
void ComputeBlinnPhong_half(half3 lightDir, half3 normal, half3 viewDir, out half Specular)
{
half3 reflectedLightDir = reflect(lightDir, normal);
Specular = max(0, dot(-viewDir, reflectedLightDir)); // avoid negative values
}
- Create a
ShaderGraph
shader to just render the question mark texture. - Create a
Shuriken
particle system object.- Disable the
Shape
section to avoid particles changing size over time. - Set
Start Speed
to zero in the top. - Set
Rate Over Time
to zero in theEmission
section.- Create one burst with just one particle.
- Disable the
- Create an
Animation Controller
and an idleAnimation
. - Modify the curves so that the rotation is smooth and varies over time.