Your First HLSL Shader
Write a small shader that puts a texture on a shape and tints it, and learn what each part does.
Imagine painting a paper model. First you decide where each corner goes (the vertex function). Then you decide what colour each spot on the surface is (the fragment function). The shader is your painting instructions; the material is the actual paint and pictures you picked.
This page is for URP (a "Universal 3D" project). Old tutorials using CGPROGRAM or surface shaders are for the Built-in pipeline and won't work here.
Words to know
| Word | Meaning |
|---|---|
| Shader | A tiny program that runs on the graphics card (GPU) to draw things. |
| HLSL | The language shaders are written in. Looks a lot like C#. |
| Vertex | A corner of a mesh. |
| Fragment | Roughly, one pixel of the shape on screen. |
| UV | An address on a texture: (0,0) is bottom-left, (1,1) is top-right. |
1. Create it
- In the Project window: Create > Shader > Unlit Shader, name it
SimpleTexture, and replace everything inside with the code below. - Create > Material, then pick Simple/Texture from its Shader dropdown.
- Give the material a texture and a tint, and drag it onto a Cube.
2. The shader
Shader "Simple/Texture"
{
Properties // what shows up in the material Inspector
{
_BaseMap ("Texture", 2D) = "white" {}
_BaseColor ("Tint", Color) = (1, 1, 1, 1)
}
SubShader
{
Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" }
Pass
{
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment Frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// Data coming IN from the mesh
struct Attributes
{
float4 positionOS : POSITION; // corner position
float2 uv : TEXCOORD0; // texture address
};
// Data passed from Vert to Frag
struct Varyings
{
float4 positionCS : SV_POSITION; // where on screen
float2 uv : TEXCOORD0;
};
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
CBUFFER_START(UnityPerMaterial) // the material's numbers
float4 _BaseMap_ST; // tiling and offset
half4 _BaseColor;
CBUFFER_END
// Runs for every corner: work out where it goes on screen
Varyings Vert(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
return output;
}
// Runs for every pixel: work out its colour
half4 Frag(Varyings input) : SV_Target
{
half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv);
return texColor * _BaseColor;
}
ENDHLSL
}
}
}
3. What each part does
| Part | In plain words |
|---|---|
Properties | The sliders and slots in the material Inspector. Names must match the variables further down. |
Tags | Tell Unity "this is a solid object for URP". |
Attributes | What each corner of the mesh gives you. |
Varyings | What Vert hands to Frag. The GPU blends these smoothly between corners. |
CBUFFER block | Groups the material's values so URP can draw faster (SRP Batcher). Put every non-texture property here. |
TransformObjectToHClip | Converts a corner from "on the model" to "on the screen". |
SAMPLE_TEXTURE2D | Reads the texture's colour at a UV address. |
HLSL in two minutes
float= a number.float2,float3,float4= 2, 3 or 4 numbers together (like Vector2/3/4).half= a less precise, cheaper number. Fine for colours.- Pick parts with
.x .y .z .wor.r .g .b .a.color.rgbis the first three. - Handy functions:
lerp(a, b, t)blend,saturate(x)clamp to 0–1,sin(x)wave,dot,normalize. _Time.y= seconds since the game started.
4. Experiments
Change the last line of Frag and save; the Scene view updates instantly.
| Replace with | You'll see |
|---|---|
return half4(input.uv, 0, 1); | The UVs as colours: red across, green up. |
return texColor * _BaseColor * (0.75 + 0.25 * sin(_Time.y * 3)); | A gentle pulse. |
return half4(1 - texColor.rgb, 1); | Inverted colours. |
What happens at runtime
- Unity sends the mesh and the material's values to the GPU.
Vertruns for every corner and places it on screen.- The GPU works out which pixels each triangle covers, blending the UVs between the corners.
Fragruns for every covered pixel and returns its colour.
Common Mistakes
- Pink material: there's a compile error. Click the shader asset and read the errors in the Inspector.
- A property named differently in
Propertiesand theCBUFFER, so the Inspector value is ignored. - Returning alpha below 1 and expecting see-through. This shader is opaque; transparency needs extra settings.
- Expecting lighting or shadows. This is an unlit shader. Lighting needs much more code, or use Shader Graph.
Shader Graph (Create > Shader Graph) builds the same thing by connecting boxes, with lighting included. Many games never write HLSL by hand.
See also
This page is a simplified, AI-assisted interpretation. For the complete and authoritative reference, see the official documentation.