Table of Contents

Input and output data

Vert and Frag read and write their data through fixed structures: IN and OUT. This page describes what each of them contains, and how data travels from the mesh to the screen.

The data flow

mesh ──► VertexInput ──► Vert ──► VertexOutput ──► (interpolation) ──► FragmentInput ──► Frag ──► output
  1. For each vertex, Vert reads the mesh data from VertexInput and writes the vertex to VertexOutput.
  2. The GPU builds triangles from the vertices, finds the pixels each triangle covers, and for every pixel blends the vertex values of the triangle, weighted by how close the pixel is to each vertex. This is interpolation.
  3. For each pixel, Frag reads the interpolated values from FragmentInput and writes the result to its output structure.

Interpolation is linear across a triangle. A value computed per vertex is cheaper than the same value computed per pixel, but coarser: details smaller than a triangle are lost. And an interpolated direction is no longer exactly unit length, so normalize it in Frag when precision matters.

Only the fields your code actually uses are passed between the stages, so unused ones cost nothing.

Coordinate spaces

Field names end with the space the value is in:

Suffix Space
OS Object space: relative to the object's pivot, before its Transform is applied, as stored in the mesh.
WS World space. On HDRP, world-space positions are relative to the camera (see below).
CS Clip space; in FragmentInput, the pixel's position on screen.
TS Tangent space: relative to the surface, as in a normal map.

ShaderApi has functions to convert between spaces, such as ShaderApi.TransformObjectToWorld.

VertexInput

The mesh vertex, the input of Vert.

Field Type Contents
positionOS float3 The position.
normalOS float3 The normal.
tangentOS float4 The tangent; w is the sign of the bitangent (1 or -1).
color float4 The vertex color.
uv0 … uv7 TEXCOORD The UV channels. On lightmapped meshes, uv1 also holds the baked lightmap UVs and uv2 the realtime ones.

A UV channel can be read as a vector of 2 to 4 components, depending on what the mesh stores in it. Read it into a variable of the type you need, for example float2 uv = IN.uv0;.

Don't use uv4 and uv5 to compute the vertex position: in the motion vectors pass, Unity replaces them with other data on skinned and Alembic meshes.

VertexOutput

The output of Vert. Before Vert runs, it's filled from VertexInput, so you only change what you need.

Field Type Contents
positionOS float3 The vertex position. Change it to deform the mesh.
normalOS float3 The vertex normal. Update it when you deform the mesh.
tangentOS float4 The vertex tangent.
uv0 … uv7 INTERP The UV channels, passed on to FragmentInput.
interp0 … interp3 INTERP Custom values passed on to FragmentInput.

Passing values to Frag

Position, normal and tangent reach Frag in world space automatically, and so do the mesh UVs. To pass anything else, such as the vertex color or a value computed per vertex, write it to a custom field in Vert and read the same field in Frag:

public override void Vert(VertexInput IN, ref VertexOutput OUT)
{
    OUT.interp0 = IN.color;
}

public override void Frag(FragmentInput IN, ref UnlitFragmentOutput OUT)
{
    float4 vertexColor = IN.interp0;
    OUT.albedo = (half3)vertexColor.rgb;
}

A custom field holds a scalar or a vector of up to 4 components; its size follows how you use it. Use the same type when writing and reading it.

Custom fields aren't available in every pass: they are 0 in the Meta pass, which the lightmapper uses, and on HDRP in passes with tessellation. Anything the surface's color depends on, such as the UVs for its textures, should come from the uv fields, which work in every pass.

Changing the UVs

To transform the UVs once per vertex instead of once per pixel, assign them in Vert:

OUT.uv0 = BaseMap.GetTransformedUV(IN.uv0);

A UV field that Vert assigns anywhere is no longer filled from the mesh; it starts at 0. If you assign it only under a condition, also assign it on the other code paths.

FragmentInput

The data interpolated for the pixel, the input of Frag in Lit and Unlit shaders.

Field Type Contents
positionCS float3 The pixel's position on screen: xy in pixels, z the depth.
positionWS float3 The position in world space.
normalWS float3 The interpolated vertex normal, in world space.
tangentWS float4 The interpolated vertex tangent, in world space; w is the bitangent sign.
uv0 … uv7 INTERP The UV channels. On HDRP, uv4 to uv7 require Unity 6000.3 or later.
interp0 … interp3 INTERP The custom values written in Vert.
Important

On HDRP, positionWS is relative to the camera: HDRP renders everything around the camera for precision. Pass it as is to the ShaderApi functions, which expect exactly that. For effects that must stay fixed in the world, such as world-space noise, convert it with ShaderApi.GetAbsolutePositionWS. On URP that function returns the position unchanged, so the same code works on both pipelines.

LitFragmentOutput

The surface a Lit shader's Frag describes. Set only the fields the surface needs; the others keep their defaults.

Field Default Contents
albedo 0 The base color.
alpha 1 The opacity, used by transparency and alpha clipping.
alphaClipThreshold 0.5 Pixels whose alpha is below it are discarded when alpha clipping is on.
normalWS vertex normal The surface normal in world space, for example from a normal map.
smoothness 0.5 From 0 (rough) to 1 (mirror-like).
metallic 0 From 0 (non-metal) to 1 (metal).
specularColor 0 The specular color, used instead of metallic in the specular workflow.
ambientOcclusion 1 How much ambient light reaches the surface.
emission 0 Light the surface emits.

HDRP adds fields for its features: bentNormalWS, tangentWS, anisotropy, iridescenceMask, iridescenceThickness, subsurfaceMask, transmissionMask, thickness, diffusionProfile, coatMask, ior, transmittanceColor, atDistance, distortion, distortionBlur and depthOffset. They are ignored on URP. See HDRP features.

UnlitFragmentOutput

Field Default Contents
albedo 0 The color.
alpha 1 The opacity.
alphaClipThreshold 0.5 The alpha clipping threshold.
emission 0 Additional emitted light, added to the color.
distortion, distortionBlur 0 HDRP only: distortion.

Fullscreen input and output

A Fullscreen shader has no vertex stage and uses its own structures.

FullscreenInput field Type Contents
uv float2 The pixel's position on screen, from 0 to 1.
positionCS float3 The pixel's position in pixels (xy).

FullscreenOutput has a single field, color (float4): the pixel's color. Its alpha is used only when the shader has a [Blend] attribute; otherwise the color replaces the image. See Fullscreen shaders.