Надо уточнить, что именно вы хотите получить: «перевести векторы нормалей в 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 и т.д.),
я напишу точный корректный код/формулы и подскажу, как сделать закруглённые углы правильно.