Как правильно преобразовать локальные векторы нормалей в UV‑координаты текстуры?

Пытаюсь нарисовать прямоугольник с закруглёнными углами — без текстуры всё работает. <br/> <br/>
  • 20 июня 2026 г. 10:01
Ответы на вопрос 2
Надо уточнить, что именно вы хотите получить: «перевести векторы нормалей в UV» можно по‑разному, в зависимости от цели. Коротко о типичных задачах и правильных подходах; затем — что обычно нужно для рисования прямоугольника с закруглёнными углами.

1) Если у вас 2D‑прямоугольник и вы хотите наложить текстуру на него
- Не используйте нормали! Для текстурирования прямоугольника берут локальные позиции вершин и нормируют их в диапазон [0,1].
  Пример (если rect.xmin, rect.ymin — левый‑нижний, rect.size — размер):
  uv = (pos.xy - rect.min) / rect.size
  Если прямоугольник центрирован в (0,0) и половины размеров = halfSize:
  uv = (pos.xy / (2.0 * halfSize)) + 0.5

Это самый простой и правильный способ для вашего случая (рисование закруглённых углов — см. ниже).

2) Если вы действительно хотите получить UV из 3D‑нормали (напр. для environment mapping)
- Сферическая (парнонионная) проекция:
  u = 0.5 + atan2(n.z, n.x) / (2π)
  v = 0.5 - asin(n.y) / π
- Цилиндрическая:
  u = 0.5 + atan2(n.z, n.x) / (2π)
  v = n.y * 0.5 + 0.5
- Простая «плоская» проекция по нормали (часто для визуализации нормалей):
  uv = n.xy * 0.5 + 0.5

Важно: нормаль должна быть в той системе координат, для которой вы делаете проекцию (object/world/tangent). Для нормал‑карт сначала переводят значения из [0,1] в [-1,1] и умножают на TBN, чтобы получить нормаль в нужном пространстве.

3) Если вы используете нормали из normal map (tangent space) и хотите получить «мировые» нормали
- Соберите матрицу TBN (tangent, bitangent, normal) и умножьте:
  N_world = normalize(mat3(T, B, N) * (sample * 2.0 - 1.0))

4) Как правильно рисовать прямоугольник с закруглёнными углами (рекомендация)
- Для маски закруглённого прямоугольника проще всего работать с локальными координатами (не с нормалями) и использовать signed distance function (SDF). Пример GLSL‑функции (Inigo Quilez):
  float sdRoundRect(vec2 p, vec2 b, float r) {
    // p — позиция в локальных координатах (центр в (0,0))
    // b — половина размеров прямоугольника (half‑extents)
    // r — радиус закругления
    vec2 d = abs(p) - b;
    return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0) - r;
  }
  Использование: p возьмите как (uv * rect_size) - halfSize, или просто из локальной позиции фрагмента. По значению sdf можно вычислить сглаженную маску: alpha = smoothstep(thin, thick, -sdf).

5) Частые ошибки
- Попытка использовать нормаль как UV на плоском прямоугольнике — выдаст одинаковую нормаль на всём плоскости (нормаль = (0,0,1)), значит текстура будет однообразной.
- Неправильные пространства (нормаль в world, а проекция ожидает object).
- Забыл перевод normal map из [0,1] в [-1,1] и применение TBN.

Если вы пришлёте:
- ваш шейдерный код (или фрагмент), где вы пытаетесь преобразовать нормали в UV,
- в каком пространстве у вас позиции/нормали (object/world/tangent),
- какой результат ожидаете (маска, environment map, normal map и т.д.),

я напишу точный корректный код/формулы и подскажу, как сделать закруглённые углы правильно.
UV нужно считать из реальной позиции вершины в прямоугольнике, а не из нормалей дуги. Замени во всех 4 квадрантах: <br/> <br/> <pre><code>const float u = (px - rect.x) / rect.w;
const float v = (py - rect.y) / rect.h;</code></pre> <br/> <br/> Нормали дуги описывают направление от центра угла, не положение пикселя в текстуре. Эллипс работал потому что там нормали покрывают весь круг и центрированы в 0,0 — поэтому формула (n+1)*0.5 давала правильный результат. Тут же каждый угол живёт в своём квадранте.
Похожие вопросы