Какие аргументы обычно принимает фрагментный шейдер?

Пытаюсь разобраться в фрагментных шейдерах на примере HLSL. Работают только два варианта:
  • 4 июня 2026 г. 17:00
Ответы на вопрос 3
Коротко и по делу — что обычно приходит в фрагментный (pixel) шейдер и в каких вариантах это писать в HLSL.

1) Откуда приходят аргументы
- Всё, что приходит в PS — это либо значения, переданные из вершинного шейдера (VS -> PS), либо системные значения/ресурсы (текстуры, константные буферы и т.п.).  
- Связывание происходит через семантики (semantics): TEXCOORD0, COLOR0, SV_POSITION и т.д. Имя параметра может быть любым, важна семантика и тип.

2) Часто используемые семантики (входы PS)
- TEXCOORDn — любые интерполируемые данные (uv, normal в преобразованном виде, т.п.).  
- COLORn — цвет, обычно как float4.  
- SV_Position — позиция в экране (post-projection).  
- SV_IsFrontFace — бул, видимость грани (для двусторонней отрисовки).  
- SV_PrimitiveID, SV_SampleIndex, SV_RenderTargetArrayIndex — для специальных случаев.  
- sample/centroid/nointerpolation + noperspective — уточняют интерполяцию (см. ниже).

3) Часто используемые ресурсы (непосредственно доступные в PS)
- Texture2D, Texture2DArray, TextureCube и т.п.  
- SamplerState / SamplerComparisonState.  
- cbuffer (константные буферы).  
Обычно их объявляют как глобальные переменные или как параметры функции шейдера.

4) Что возвращает PS
- Обычно float4 : SV_Target — цвет пикселя.  
- Можно возвращать struct с несколькими таргетами (SV_Target0, SV_Target1, …) или вместе с глубиной (SV_Depth).

5) Форматы функций (примеры)
- Через struct (обычно предпочтительный и читаемый способ):

  struct PSInput {
      float4 pos   : SV_POSITION;
      float2 uv    : TEXCOORD0;
      float3 norm  : TEXCOORD1;
      float4 col   : COLOR0;
  };

  float4 PSMain(PSInput IN) : SV_Target {
      // использовать IN.uv, IN.norm и т.д.
  }

- Через отдельные параметры:

  float4 PSMain(float2 uv : TEXCOORD0, float4 col : COLOR0) : SV_Target {
      // ...
  }

- Пример с ресурсами:

  Texture2D    tex0 : register(t0);
  SamplerState samp : register(s0);

  float4 PSMain(PSInput IN) : SV_Target {
      float4 texcol = tex0.Sample(samp, IN.uv);
      return texcol * IN.col;
  }

6) Интерполяция / модификаторы
- nointerpolation — без интерполяции (например для flat shading).  
- centroid — предотвращает артефакты при мультисэмплинге, если значение вычислено на краю.  
- sample — передаёт значение per-sample (для мультисэмплинга).  
- noperspective — линейная интерполяция в экране (без коррекции перспективы).
Пример: float3 normal : NORMAL; или nointerpolation float flatVal : TEXCOORD0;

7) Частые ошибки/подводные камни
- Семантика должна совпадать между VS и PS (или хотя бы семантика/регистры должны позволять интерполяцию).  
- SV_POSITION в VS должен быть float4; если он есть в VS, то в PS он тоже может быть (чтобы знать экранную позицию пикселя).  
- Типы должны быть совместимы (например, VS выдаёт float3 как TEXCOORD0, PS не должен ожидать float4 с той же семантикой).  
- Если используете SV_Position в PS — это экранная позиция (в пикселях) и обычно float4, w не нормализован.

8) Примеры "рабочих вариантов", которые вы, возможно, видели
- Вариант 1: PS принимает struct с SV_POSITION и UV — работает.  
- Вариант 2: PS принимает только float2 uv : TEXCOORD0 — тоже работает.  
Оба корректны — это просто разные формы объявления входа.

Если нужно, могу разобрать конкретный код, который у вас не работает: пришлите сигнатуры VS и PS и какие семантики вы используете — покажу, где ошибка и как привести всё в соответствие.
Фрагментный шейдер получает ровно то, что отдаёт вершинный. В SDL3's 2D рендерере это COLOR0 и TEXCOORD0: <br/> <br/> <pre><code>struct PSInput {
    float4 v_color : COLOR0;
    float2 v_uv   : TEXCOORD0;
};</code></pre> <br/> <br/> TEXCOORD0 работает — это uv. Цвет принимай как COLOR0 (именно с нулём). Позиция в fragment shader только через SV_POSITION, но там уже пиксельные координаты, не 0-1. p.s. произвольные атрибуты SDL не пробрасывает, для этого нужен свой vertex shader через GPU API.
Почему пишешь SV_TARGET, а не SV_TARGET0 ?
Похожие вопросы