← Назад к вопросам

Как определить вектор отраженного света от нормали?

2.0 Middle🔥 151 комментариев
#C# и ООП

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Определение вектора отраженного света от нормали в Unity

Это фундаментальная задача в компьютерной графике, лежащая в основе освещения, шейдеров и рендеринга. В контексте Unity, вычисление вектора отражения критически важно для реализации динамического освещения, физически корректных материалов (например, металлов, стекла) и специальных эффектов (бликов, зеркальных отражений).

Формула отражения вектора

В основе лежит классическая формула отражения вектора относительно нормали. Если у нас есть:

  • Вектор направления света L (направленный от поверхности к источнику света, это важно!)
  • Нормаль поверхности N (единичный вектор, перпендикулярный поверхности)

Тогда отраженный вектор R вычисляется по формуле:

R = 2 * dot(N, L) * N - L;

Или, более явно через компоненты скалярного произведения:

float dotProduct = Vector3.Dot(N, L);
R = 2 * dotProduct * N - L;

Ключевые условия: Оба вектора N и L должны быть нормализованы (их длина равна 1). Вектор L должен быть направлен к поверхности (от источника света). В Unity и многих графических API часто используется именно такое направление для удобства вычислений.

Практическая реализация в Unity/C#

В Unity есть несколько способов получить этот вектор:

1. Использование стандартной математики Vector3

// Предполагаем, что lightDirection направлен ОТ источника К поверхности (стандартный подход в освещении)
Vector3 CalculateReflectionVector(Vector3 incidentLightDir, Vector3 surfaceNormal) {
    // Нормализуем векторы для гарантии корректности формулы
    Vector3 L = incidentLightDir.normalized;
    Vector3 N = surfaceNormal.normalized;

    // Скалярное произведение (проекция L на N)
    float dot = Vector3.Dot(N, L);

    // Формула отражения
    Vector3 reflectedVector = 2f * dot * N - L;

    return reflectedVector.normalized; // Часто возвращают нормализованный результат
}

2. Использование метода Vector3.Reflect

Unity предоставляет встроенный оптимизированный метод в структуре Vector3, который делает то же самое:

Vector3 reflectedLightDir = Vector3.Reflect(lightDirection, normal);

Это наиболее простой и рекомендуемый способ в повседневной разработке на Unity. Метод автоматически выполняет нормализацию и вычисления по формуле выше.

3. Пример использования в шейдере (HLSL/Cg)

В шейдерных программах вычисление происходит аналогично:

// В шейдерном коде (HLSL/Cg)
float3 ReflectLight(float3 lightDir, float3 normal) {
    float dotNL = dot(normal, lightDir);
    return 2.0 * dotNL * normal - lightDir;
}

// Или использование встроенной функции reflect
float3 reflectedDir = reflect(-lightDir, normal); 
// Примечание: часто lightDir в шейдерах направлен от поверхности к свету, поэтому нужен минус

Контексты применения в Unity

  • Расчет освещения в шейдерах: Для моделирования зеркального компонента (Specular) по модели Фонга или Блинна-Фонга. Отраженный вектор сравнивается с вектором к наблюдателю (viewDir) для расчета блика.
  • Физика и трассировка: Для простой трассировки лучей в реальном времени (например, для псевдо-зеркал, рефлективных снарядов).
  • Визуальные эффекты: Для расчета направления и интенсивности световых бликов на динамических объектах.
  • Скриптовые вычисления: При работе с Raycast или физическими свойствами материалов в скриптах.

Важные нюансы и проверки

  1. Направление векторов: Убедитесь, что понимаете направление L. В шейдерах _WorldSpaceLightPos0 часто дает вектор к свету, в то время как формула может требовать вектор от света. Это решается инверсией (-L).
  2. Нормализация: Неправильная длина векторов (особенно нормали) — самая частая причина ошибок. Вектор нормали должен быть единичным.
  3. Скалярное произведение: Значение dot(N, L) может быть отрицательным (если свет «сзади» поверхности). Это корректно и приведет к правильному расчету, но интенсивность отраженного света в таком случае обычно игнорируется (не освещает поверхность).

Таким образом, в Unity для определения вектора отраженного света от нормали наиболее практичным и эффективным методом является использование встроенной функции Vector3.Reflect(). Она абстрагирует математические детали, обеспечивает оптимальную производительность и является стандартом в скриптовом коде игры или редактора.