Напишите HLSL для различных графиков APIs
Существуют различия в том, как графика рендеринга ведет себя между разными графиками APIs. Большую часть времени Unity Editor скрывает различия, но есть некоторые ситуации, когда Unity не может сделать это для вас.
Проверка обработки исключений с плавающей запятой
Настольные и мобильные платформы могут по-разному обрабатывать исключения во время операций с плавающей запятой, таких как деление на ноль. Исключения могут привести к NaN, нулю или другому значению. Проверьте свои шейдеры на различных платформах, на которые вы ориентируетесь.
Используйте последовательную семантику
Используйте следующую семантику для обеспечения работы шейдеров на всех платформах:
SV_POSITIONвместоPOSITIONдля позиции на выходе вершинного шейдера.SV_TargetвместоCOLORилиCOLOR0для выхода фрагментного шейдера.PSIZE, если вы отображаете меши как точки. Например, установитеPSIZEна 1. Это предотвращает некоторые платформы, такие как Metal от чтения размера точки как неопределенного.
Дополнительные сведения о семантике см. в Справочник по семантике High-Level Shader Language (HLSL).
Используйте const для констант
Не используйте const для значений, которые поступают из-за пределов шейдера или которые вы рассчитываете во время выполнения, так как это работает только в HLSL, а не в OpenGL Shading Language (GLSL). Используйте const только для констант времени компиляции.
Избегайте несоответствующих расположений буферов
Если вы используете стандартный тип HLSL cbuffer или макросы Unity CBUFFERSTART и CBUFFEREND, то float3 может стать float4, или float может стать float2.
Чтобы убедиться, что все графики APIs компилируются в буфер с одинаковым расположением данных, выполните следующие действия:
- Используйте
float4иfloat4x4вместоfloat3иfloat3x3, потому что переменныеfloat4имеют одинаковые размеры на всех графиках APIs, в то время как переменныеfloat3могут иметь разные размеры на некоторых графиках APIs. - Заявляйте переменные в порядке уменьшения размера, например,
float4, затемfloat2, затемfloat, так что все графики APIs структурируют данные одинаково.
Например:
cbuffer myConstantBuffer {
float4x4 matWorld;
float4 vObjectPosition; // Uses a float4 instead of a float3
float arrayIndex;
}
Примечание: Вы не можете добавлять структуры в постоянные буферы.
Обработка обратных координатных пространств
DirectX, Metal и Vulkan используют разные координатные системы, чем другие графики APIs. Чтобы убедиться, что шейдеры работают правильно на всех платформах, настройте свой код для обработки обратных текстур.
Чтобы проверить обратные текстуры, используйте предпроцессорные условия, такие как #if и #ifdef со встроенными макросами или методами Unity.
Проверка текстур перевернутого рендеринга
Если вы взяли образец текстуры рендеринга на (0,0), DirectX, Metal и Vulkan, то возвращается текстура в левом верхнем углу. Другие графики APIs возвращают текстуру в левом нижнем углу. Unity обрабатывает это внутренне для большинства текстур рендеринга, переворачивая текстуры рендеринга внутри, где это необходимо, так что используемые координаты возвращают одни и те же текстуры на всех платформах.
Чтобы проверить, использует ли текущая платформа обратные координаты для отображения текстур, используйте один из следующих методов:
-
Проверьте, определяет ли Unity
UNITY_UV_STARTS_AT_TOP, что означает, что графика API использует(0,0)для левого верхнего угла текстуры.Например, чтобы проверить на наличие обратных текстур рендеринга и затем перевернуть вертикальные ультрафиолетовые координаты:
#if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) uv.y = 1-uv.y; #endif Проверьте встроенную переменную
ProjectionParams.x. Если значение-1, то Unity перевернул текстуру рендеринга, так что вы должны также перевернуть вертикальные координаты UV.
Примечание: Если вы используете GrabPass во Встроенном Render Pipeline, Unity может не инвертировать текстуру. Для вычисления координат, используемых для выборки текстуры, используйте метод ComputeGrabScreenPos. Для получения дополнительной информации, см. Использование встроенных функций шейдера во Встроенном Render Pipeline.
Проверка на наличие обратного направления глубины
Направление глубины в пространстве клипа, буфер глубины и текстуры глубины различаются в зависимости от графики API, на которую вы направляетесь.
| Координатное пространство | координата Z в DirectX, Metal и Vulkan | координата Z в OpenGL ES и WebGL |
|---|---|---|
| Буфер глубины и текстуры глубины | 1.0 вблизи до 0.0 далеко |
0.0 вблизи до 1.0 далеко |
| Пространство клипа | 1.0 вблизи до 0.0 далеко |
-1.0 вблизи до 1.0 далеко |
Используйте следующее в пользовательском коде HLSL для обработки этих различий:
Используйте SystemInfo.usesReversedZBuffer, чтобы проверить, использует ли графика API 1.0 до 0.0 для глубины.
-
Проверьте, определяет ли Unity
UNITY_REVERSED_Z, что означает, что графика API использует от 1.0 до 0.0 для глубины, затем переверните значение z.Например, во Встроенном Render Pipeline:
float z = tex2D(_CameraDepthTexture, uv); #if defined(UNITY_REVERSED_Z) z = 1.0f - z; #endif
Возможно, вам также потребуется перевернуть значения глубины отклонения, если вы используете плагин для отображения кода.
Больше макросов, которые помогают с глубиной, см. в Shader методах во Встроенном Render Pipeline или Shader методах в Universal Render Pipeline (URP).
Компиляция на платформах DirectX
Если вы нацелены на DirectX графику API, компилятор более строгий, чем другие компиляторы. Проверьте следующее:
- Установите все компоненты вектора. Например, если вы объявляете
float4, убедитесь, что вы установили все четыре компонента. - Не используйте метод
tex2Dв вертикальном шейдере. Вместо этого используйте методtex2Dlodдля выборки конкретного уровня mip и используйте#pragma target 3.0для обеспечения совместимости. - Если вы используете вершинный шейдер с параметром
outв шейдере поверхности, инициализируйте выход с помощью макросаUNITY_INITIALIZE_OUTPUT.