Skip to content
HY Devlog
Go back

The Definition Describes a Pixel Shader, Not a Shader

I clipped this while applying a toon shader. In the post on lilToon’s parameters I checked the Inspector items one by one, and while handling them “what exactly is a shader” kept sitting behind it. Knowing the parameters and knowing where those values go are different things.

The original is a 2024 post, bundled briefly into four sections: definition, transcription, structure, materials.

This post reads differently from the other clippings. It quotes the National Institute of Korean Language’s loanword transcription rules directly when addressing the spelling question, and links Unity’s official docs for the material explanation. It’s a post that brings sources.

So there’s little that snags. But one sentence of the definition conflicts with what comes two sections later.

A shader is, in computer graphics, a program that defines and processes the color, shading and lighting effects output to the screen.

A shader is broadly composed of two parts, a vertex shader and a pixel shader.

Read the two together and it says the vertex shader processes color and lighting. It doesn’t. The definition describes only the latter of the two.

Table of Contents

Table of Contents

The Transcription Argument Is Accurate

What’s well done first. The spelling question, which is also this post’s title.

According to the loanword transcription rules published by the National Institute of Korean Language, word-final [ʃ] is written ‘시’, [ʃ] before a consonant as ‘슈’, and [ʃ] before a vowel as ‘샤’, ‘섀’, ‘셔’, ‘셰’, ‘쇼’, ‘슈’ or ‘시’ depending on the vowel that follows, which makes 셰이더 the correct transcription.

Look the rule up and it’s exactly that. This is the sentence the Institute itself quotes when answering the same question.

Word-final [ʃ] is written ‘시’ (e.g. flash → 플래시)

[ʃ] before a consonant as ‘슈’ (e.g. shrub → 슈러브)

[ʃ] before a vowel is written ‘샤’, ‘섀’, ‘셔’, ‘셰’, ‘쇼’, ‘슈’ or ‘시’ depending on the vowel that follows (e.g. shark → 샤크, shopping → 쇼핑)

shader is /ˈʃeɪdər/. The [ʃ] sits before a vowel and the vowel that follows is [eɪ], so ‘셰’ is what the third line’s list selects. ‘셰’ + ‘이’ = 셰이.

Line up other words that came out of the same rule and the consistency shows.

EnglishPosition of [ʃ]Transcription
flashWord-final플래시
shrubBefore a consonant슈러브
sharkBefore a vowel [ɑː]샤크
shoppingBefore a vowel [ɒ]쇼핑
shakeBefore a vowel [eɪ]셰이크
shaderBefore a vowel [eɪ]셰이더

It’s the same reason nobody writes a milkshake as ‘밀크쉐이크’. shake and shader have the same first syllable.

And the post doesn’t stop at the rule. It goes on to check which way the official docs and recently published books are settling. Looking at the norm and the actual usage together is a rare virtue in a post like this.

Material Isn’t a “Similar Reason” to Shader

A parenthetical line in the materials section.

(Material is also written 머티리얼, 메터리얼 and so on, but for a reason similar to shader I’ll write 머티리얼)

It isn’t a similar reason. The reason 셰이더 is 셰이더 is the [ʃ] rule, and material has no [ʃ] sound. It’s /məˈtɪəriəl/. The rule quoted in the previous section has nowhere to apply in this word.

Where material’s transcription splits is somewhere else. The first syllable is [mə], and the competing ‘메터리얼’ is the result of reading the spelling (ma-te-) rather than the pronunciation. The loanword rules transcribe by pronunciation, not spelling, so the reason the two split has nothing to do with [ʃ].

셰이더 vs 쉐이더머티리얼 vs 메터리얼
The sound that splits[ʃ][ə]
The rule that appliesThe fricative [ʃ] itemVowel transcription
Cause of the mixingThe customary ‘쉐’Reading the spelling

The conclusion is the same — 머티리얼 is correct. It just isn’t correct for the same reason. Because this is a post that quoted the rule precisely, that parenthetical reads as a spot that leaned on its own precision. Whether 머티리얼 is listed in the official examples glossary I couldn’t confirm.

The Definition Describes a Pixel Shader

The conflict from the opening. The definition section and the structure section, set down again.

A shader is … a program that defines and processes the color, shading and lighting effects output to the screen. It decides what value to give each pixel on screen …

A shader is broadly composed of two parts, a vertex shader and a pixel shader.

The definition says “decides what value to give each pixel on screen,” and the vertex shader description two sections later reads like this.

computes vertex positions and passes the texture coordinates corresponding to each vertex

No color, no shading, no lighting. Positions and coordinates. The definition describes the latter of the two.

Unity’s current docs scope it to a far narrower single sentence.

A program that runs on the GPU

That’s all of it. What it computes doesn’t enter the definition. What enters that slot is which stage it runs at, and each stage does something different.

ShaderWhat it takesWhat it puts out
VertexOne vertexA transformed position + attributes to interpolate
Fragment / pixelInterpolated attributesOne candidate color
ComputeArbitrary buffersArbitrary buffers

The last row breaks the definition most clearly. A compute shader outputs nothing to the screen. The post sets compute shaders aside as “advanced material for later,” and that one is a counterexample to “a program that processes the color output to the screen.”

“Composed of two parts” also diverges from Unity’s terminology. The docs describe a Shader object as a container.

A Shader object is a Unity-specific way of working with shader programs; it is a wrapper for shader programs and other information.

It lets you define multiple shader programs in the same file, and tell Unity how to use them.

Not “one made of two” but “one that holds many.” That’s why there can be several Passes, and why SubShaders can diverge by hardware.

SubShaders let you separate your Shader object into parts that are compatible with different hardware, render pipelines, and runtime settings.

Using pixel and fragment as the same word is another place to go a layer deeper, and the post on the graphics pipeline already covered it — a fragment is a pixel candidate, and several fragments can appear for one pixel slot. So it isn’t “computes the final color of each pixel” but puts out a candidate color, and the next stage decides the final pixel.

Post-Processing Has a Vertex Shader Too

The last sentence of the pixel shader section.

Unlike the vertex shader, because it computes with color values, it’s also used in post-process effects and in 2D environments.

“Unlike the vertex shader” is wrong. Post-processing and 2D have vertex shaders too. They can’t not.

A valid usage rule in the Vulkan specification pins it down.

If the pipeline requires pre-rasterization shader state the stage member of one element of pStages must be VK_SHADER_STAGE_VERTEX_BIT or VK_SHADER_STAGE_MESH_BIT_EXT

A pipeline that rasterizes must have either a vertex shader or a mesh shader. You cannot create a graphics pipeline with neither. (The mesh shader side is that new path from the earlier post.)

So what does post-processing’s vertex shader do. It transforms four or fewer vertices of one screen-covering triangle (or quad). It’s invisible because it does almost nothing, not because it isn’t there.

2D is the same. A sprite is a quad, and a quad is four vertices. Something has to take those four to screen coordinates.

Vertex countWhat the vertex shader does
3D characterTens of thousandsSkinning, transform, passing normals
2D sprite4Transform, passing UVs
Post-processing3–4Generating fullscreen coordinates

What the post meant was probably “the case where the pixel shader is the center of the work.” That’s right. When writing a post-process shader, what you touch is almost all on the fragment side. But write it as “unlike the vertex shader” and it reads as saying there isn’t one. And believing that, there’s no explanation for why a Vert function is sitting there when you write a post-process shader in Shader Graph or HLSL.

The Unity Doc It Cites Is Version 4.6

The materials section cites Unity’s official docs. Bringing the source is this post’s strength, and the version that link points at is the problem.

https://docs.unity3d.com/460/Documentation/Manual/Materials.html

The 460 in the URL is Unity 4.6. A 2014 edition, cited by a 2024 post. That page’s sentences read like this.

There is a close relationship between Materials and Shaders in Unity. Shaders contain code that defines what kind of properties and assets to use. Materials allow you to adjust properties and assign assets.

A Shader is implemented through a Material

What the post rendered as “a shader contains code that defines the properties and kinds to use / a material lets you adjust those properties and assign textures, colors and so on” is this sentence. The translation is accurate. The current docs just draw the line differently.

Material — An asset that defines how a surface should be rendered.

A material contains a reference to a Shader object. If that Shader object defines material properties, then the material can also contain data such as colors or references to textures.

The difference is direction. The 4.6 edition says “A Shader is implemented through a Material” — it reads as a shader being realized by way of a material. The current edition says “A material contains a reference to a Shader object” — the material points at the shader.

Unity 4.6 (the post’s citation)Current docs
Definition of a materialThe thing that lets you adjust propertiesAn asset defining how a surface is rendered
Their relationshipA shader is implemented through a materialA material references a shader
Direction of the referenceAmbiguousMaterial → shader, one way
Property data“you can assign”Only if the shader defines properties

The last two rows get used in practice. That the reference is one-way is what makes “one shader, many materials” possible. Change the shader and every material referencing it changes; change a material and only that one changes. That’s what the post’s recipe analogy points at, and the 4.6 sentence doesn’t show the direction.

A shader is the recipe for making the dish, and a material decides the ingredients and tools (textures, values) used when running the recipe.

The analogy is good. Add one layer and it follows that changing the recipe changes every dish made from it.

Where and Why You’d Use It

One Shader, Many Materials

The shape of actually using that one-way reference. Say you’re building an enemy hit flash. One shader, only the color differs.

Assets/
  Shaders/
    Flashable.shader          ← one shader
  Materials/
    Enemy_Grunt.mat           ← _FlashColor = white
    Enemy_Archer.mat          ← _FlashColor = yellow
    Enemy_Brute.mat           ← _FlashColor = red

Fix the shader’s flash logic and all three change. Want only the color changed and you touch one material. The docs’ “A material contains a reference to a Shader object” guarantees that result.

When changing values in code, don’t use the name as a string.

using UnityEngine;

[RequireComponent(typeof(Renderer))]
public class HitFlash : MonoBehaviour
{
    private const float FLASH_DURATION = 0.12f;

    // Cache the ID instead of the string. Don't re-hash on every call.
    private static readonly int FLASH_AMOUNT_ID = Shader.PropertyToID("_FlashAmount");

    [Header("Flash")]
    [SerializeField, Range(0f, 1f), Tooltip("Flash strength at the moment of a hit")]
    private float _flashAmount = 1f;

    private Renderer _renderer;
    private float _remaining;

    private void Awake()
    {
        _renderer = GetComponent<Renderer>();
    }

    public void Flash()
    {
        _remaining = FLASH_DURATION;
    }

    private void Update()
    {
        if (_remaining <= 0f)
        {
            return;
        }

        _remaining -= Time.deltaTime;
        float t = Mathf.Clamp01(_remaining / FLASH_DURATION);

        // Renderer.material makes a material unique to this renderer. See the note below.
        _renderer.material.SetFloat(FLASH_AMOUNT_ID, _flashAmount * t);
    }
}

The reason to use Shader.PropertyToID is simple. SetFloat("_FlashAmount", ...) works too, but turning that string into an integer ID happens on every call. Get the ID once as a static readonly and that work disappears.

Renderer.material Clones the Material

There’s a note attached to the code above. What happens the moment you read _renderer.material is written in the docs.

Modifying material will change the material for this object only. If the material is used by any other renderers, this will clone the shared material and start using it from now on.

This function automatically instantiates the materials and makes them unique to this renderer. It is your responsibility to destroy the materials when the game object is being destroyed.

A clone appears, and cleaning it up is my responsibility. With 100 enemies you get 100 materials. And different materials split batching — you’d be raising the draw calls to 100 to add one flash.

The post on physics materials had the same trap on Collider.material. There too it clones the moment you read it. Unity’s material properties share the same nature across different kinds.

The reflexive answer here is MaterialPropertyBlock. The docs’ stated purpose is exactly this case.

draw multiple objects with the same material, but slightly different properties. For example, if you want to slightly change the color of each mesh drawn.

using UnityEngine;

[RequireComponent(typeof(Renderer))]
public class HitFlashNoClone : MonoBehaviour
{
    private const float FLASH_DURATION = 0.12f;

    private static readonly int FLASH_AMOUNT_ID = Shader.PropertyToID("_FlashAmount");

    [Header("Flash")]
    [SerializeField, Range(0f, 1f)] private float _flashAmount = 1f;

    private Renderer _renderer;
    private MaterialPropertyBlock _properties;
    private float _remaining;

    private void Awake()
    {
        _renderer = GetComponent<Renderer>();

        // Docs: create one block and reuse it. Don't new it every time.
        _properties = new MaterialPropertyBlock();
    }

    public void Flash()
    {
        _remaining = FLASH_DURATION;
    }

    private void Update()
    {
        if (_remaining <= 0f)
        {
            return;
        }

        _remaining -= Time.deltaTime;
        float t = Mathf.Clamp01(_remaining / FLASH_DURATION);

        _properties.SetFloat(FLASH_AMOUNT_ID, _flashAmount * t);
        _renderer.SetPropertyBlock(_properties);
    }
}

material is never read, so no clone appears. But this answer depends on the pipeline. The same doc carries a warning.

this is not compatible with SRP Batcher. Using this in the Universal Render Pipeline (URP), High Definition Render Pipeline (HDRP) or a custom render pipeline based on the Scriptable Render Pipeline (SRP) will likely result in a drop in performance.

In URP the means used to avoid cloning is the slower one. And the reason is in the SRP Batcher’s premise. That doc’s advice runs the opposite way.

use as few shader variants as possible. You can still use as many different materials with the same shader as you want.

All material content now persists in GPU memory

What the SRP Batcher batches by is shader variants, not materials. So having several materials that use the same shader is fine — that’s the shape this batcher assumes. Per-material values live in a GPU-side constant buffer, and MaterialPropertyBlock breaks that premise.

PipelineHow to give per-object valuesWhat to avoid
Built-inMaterialPropertyBlockMultiplying materials by cloning via material
URP / HDRPSeparate materials (same shader)MaterialPropertyBlock

The answer inverts. In the post on render pipelines we saw that built-in is “not something you choose but what’s left when you assign nothing,” and whether that slot is empty changes even the direction of optimization here.

If you want three enemies to flash in different colors in URP, the three materials from the previous section are the answer as they stand. Rather than going to MaterialPropertyBlock to avoid cloning, don’t read material at all and make three materials as assets to plug in.

Where Not to Use It

Memorizing the definition as “a program that decides pixel colors.” This post’s definition has that shape, and it stops explaining when you meet a compute shader. Unity’s one line stays useful longer — “A program that runs on the GPU.” What it does is decided by which stage it plugs into.

Believing a post-process shader has no vertex shader. The Vulkan spec forbids it. That’s why there’s a Vert function in a Shader Graph fullscreen pass or an HLSL template, and having nothing to touch there isn’t the same as it not being there.

Reading Renderer.material and forgetting the cleanup. The docs say “It is your responsibility to destroy the materials.” If you need per-object values, look at the pipeline first — built-in means MaterialPropertyBlock, URP and HDRP mean separate materials as assets. If every object takes the same value, sharedMaterial.

Grabbing shader code without checking the render pipeline. The split seen in the post on the custom shaders doc. Shader code off the internet failing to even compile is usually the built-in versus URP difference, and knowing “what a shader is” and using a shader that runs in my project are different problems.

Wrapping Up

This is a post that brings sources. It hangs the transcription argument on the National Institute of Korean Language’s rules and the material explanation on Unity’s docs. The process by which shader’s [ʃ] before a vowel becomes ‘셰’ is right by the rule too — the same place shake becomes ‘셰이크’.

Three things snag. The definition describes only the pixel shader. “The color, shading and lighting output to the screen” isn’t what the vertex shader two sections later does, and the compute shader the post deferred doesn’t even output to the screen. Unity’s definition is one line — “A program that runs on the GPU.”

“Unlike the vertex shader, it’s used in post-processing and 2D” is wrong. The Vulkan spec requires a vertex shader or a mesh shader in a pipeline that rasterizes. Post-processing’s vertex shader does the small job of transforming three vertices, and small isn’t the same as absent.

The grounds for the material transcription differ from the shader’s. material has no [ʃ], so the previous section’s rule has nowhere to apply. The conclusion is the same and only the grounds differ, but because this post quoted the rule precisely, that parenthetical stands out more.

And the Unity doc it cites is version 4.6. The translation is accurate, but the current docs draw the line differently — they write the direction, that a material references a shader. That one direction explains “one shader, many materials,” and it’s the place the post’s recipe analogy was pointing at.


References

The starting point for this post was SuHong — 셰이더? 쉐이더? Shader 란 무엇인가 (2024-11-17). I re-checked the transcription argument against the National Institute of Korean Language’s rules, and compared the scope of the definition and structure explanations against the current Unity docs and the Vulkan specification.


Share this post:

Previous Post
There Is No `--parameters` Flag
Next Post
The Lite Version Doesn't Show Up in the Shader List