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
- For each vertex,
Vertreads the mesh data fromVertexInputand writes the vertex toVertexOutput. - 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.
- For each pixel,
Fragreads the interpolated values fromFragmentInputand 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.