Shadow Mapping

  1. Renders the entire scene’s depth from the point of the view of the camera. This data corresponds to a fragment’s z coordinates in clip space.
  2. Render the entire scene’s shadow map of the light from the point of the view of the light source. Light acts as a camera.
    1. the depth values tell us how far a ray of light traveled before it hit something.
    2. multiple renders if shadow cascades is active.
  3. Compare depth measurements from both points of view. The point is lit if both view points can see the point.
    1. Unity uses ScreenSpaceShadows shader to samples both depth textures, compares the values, and renders the final shadow value to a screen space shadow map.

URP Cascade: https://zhuanlan.zhihu.com/p/378451316

Water

Harry Alisavaski’s take on water shader: https://halisavakis.com/my-take-on-shaders-stylized-water-shader/

Alan Zucconi: https://www.alanzucconi.com/2019/10/08/journey-sand-shader-4/

screenPos.w: https://forum.unity.com/threads/what-is-screenpos-w.616003/

There are three parts in this water shader: color by depth, specular by normals, and fresnel.

Color by depth

Interpolating color by depth, or the difference between the eye depth and the view space depth, we can create the fog like effect in the water. With multiple lerp, we can even create foam, intersection, and fog color.

float sceneRawDepth = SampleSceneDepth(screenPos.xy / screenPos.w);
float sceneEyeDepth = LinearEyeDepth(sceneRawDepth, _ZBufferParams)

float diff = saturate( (sceneEyeDepth - screenPos.w) / _Threshold);

It worth to notice that a sine wave created the ripple effect in the intersection. By adding an offset in the foam area, the sine wave extended visually. By adding a foam texture value to the wave, the wave has foam shapes.

Specular by normal

$$ I = (N \cdot H)^{power} * \text{strength} $$

It worth to notice that unpack normal on different platforms is different too. UnpackNormal usually handles it better than hand written method. It is same as BlendNormal.

Fresnel

This part changes the transparency in the foam area. Without uniform normal values, there will be more details, but I think the water looks good without it too.

float fresnel = pow(1.0 - saturate(normal, viewDir), _FresnelPower);

float alpha = lerp( lerp(Alpha * fresnel, 1.0, foam), _FogColor.a, fogDiff);

Caustic

FLOGELZ, Caustic effect, Twitter: https://twitter.com/flogelz/status/1165251296720576512?s=20 Alan Zucconi: https://www.alanzucconi.com/2019/09/13/believable-caustics-reflections/

A hacky way to add caustic effect. It samples the same texture twice and blends them by min().

Shader

ZTest

https://blog.csdn.net/puppet_master/article/details/53900568

Offset

Offset [factor] [units]

Factor takes z-slope in screen space. Negative number means pulling closer to the camera. If we only have set factor to 1 or -1, it will avoid z-fighting when the camera is NOT perpendicular to the screen, because it has very very little contribution to the z-slope. The equation is:

$$ \text{finalOffset} = r \times \text{factor} + d \times \text{units} $$

r is the z-slope of the geometry, and d is the minimum constant units for the depth offset. d’s value depends on the devices.

WorldDir vs. WorldNormal in Unity Shader

WorldDir is applying the rotation from the transform matrix. WorldNormal is applying the rotation from the inverse transpose matrix.

Why?

Answer:

  1. http://web.archive.org/web/20120228095346/http://www.arcsynthesis.org/gltut/Illumination/Tut09 Normal Transformation.html
  2. https://forum.unity.com/threads/difference-between-unityobjecttoworlddir-and-unityobjecttoworldnormal.435302/

keyword

REQUIRES_WORLD_SPACE_POS_INTERPOLATOR and REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR https://forum.unity.com/threads/shadow-cascades-weird-since-7-2-0.828453/ URP defines this keyword when the calculation requires world space position. It does NOT mean the shader can calculate world space position in vertex or fragment. It means if the shader requires it. Same to the shadow coord.

In URP’s Lit shader:

Parallax effect defines REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR. It requires tangent space view direction.

Normal Map defines REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR. It requires world space tangent to calculate tanToWorld matrix.

Receiving shadows and disabling cascade defines REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR. So it requires shadowCoord to get shadow value from shadow maps. Using cascade calls upon TransformWorldToShadowCoord to get shadowCoord.

vertex animation

rotate around y-axis https://forum.unity.com/threads/rotating-mesh-in-vertex-shader.501709/

Variable, input, and methods

unity_SpecCube0 contains data for the active reflection probe. UNITY_SAMPLE_TEXCUBE samples a cube map.

Decode HDR

In DecodeHDR and DecodeHDREnvironment functions:

X and y are in the decodedInstructions.

RGBM = RGB + Magnitude. The final RGB values is :

$$ xM^{y} \cdot \text{RGB} $$

C#

protected access modifier is similar to private, but any subclass has access too.

internal access modifier limit the access in the same assembly

Fractal Brownian Motion

$$ y = A \cdot \sin{(x \cdot \text{frequency})} $$

Adding up different sine waves together to create noise. Different sine waves surely can create some random effect, but Perlin Noise is a better way.

Fractal Brownian Motion: increment the frequencies in regular steps (lacunarity) and decrease the amplitude (gains) of the noise (octaves), we can obtain a finer granularity in the noise and get more fine details.

const int octaves = 1; // add more details, also more self-similarity are added
float lacunarity = 2.0;
float gain = 0.5;

float amplitude = 0.5;
float frequency = 1.0;

for (int i=0; i<octaves; ++i)
{
	y += amplitude * noise(frequency * x);
	frequency *= lacunarity;
	amplitude *= gain;
}

Turbulence. Essentially an fBm, but constructed the absolute value of a signed noise to crate sharp valleys

for (int i=0; i<OCTAVES; ++i)
{
	value += amplitude * abs(snoise(st));
	st *= 2.0;
	amplitude *= .5;
}

Ridge. The sharp valleys are turned upside down to create sharp ridges instead.

n = abs(n); // create creases
n = offset - n; // invert so creases are at top
n = n*n; // sharpen creases

Reading Material:

Normal Map:

Dithered Transparency

Depth Srot

Dissolve Shader

Malioffline Compiler

Light Wrap