ПрограммированиеФорумГрафика

Загрузка obj-файла и группы сглаживания

#0
13:46, 22 янв 2025

Приветствую всех!

В общем-то формат obj вроде довольно простой и уже много раз обсуждавшийся, но...

Есть два вопроса.

В моих случаях файл obj может быть двух типов.
Тип 1

# comment
mtllib <имя файла с материалами>.mtl
o <object_name>
v 0.074919 0.000000 -0.491512
...
vt 0.113550 0.000000 
...
vn 0.168605 0.000000 -0.475087
...
usemtl <материал 1>
f 2/1/1 4/2/1 3/3/1
..
usemtl <материал 2>
f 7/4/2 5/5/2 6/6/2
...
usemtl <материал N>
f 7/4/2 5/5/2 6/6/2
...

В этом случае формируем для каждой части со своим материалом свой vao
(с тремя vbo).  В цикле от 0 до N-1 выводим эти N частей, переключаясь:
glBindVertexArray(obj->vao[part]);

Тип 2

mtllib <имя файла с материалами>.mtl
o  <object_name_1>
g  <object_name_1>
v 0.08650934 0.37876752 -0.07471959
...
vt 0.25000000 1.00000000
...
vn -0.37411056 -0.92066462 0.11143584
s 1
usemtl <материал 1>
f 1/1/1 2/2/2 3/3/3
s 2
f 2/7/7 1/6/6 4/4/4
f 1/6/6 5/5/5 4/4/4
s 3
f 6/8/8 7/9/9 4/11/11
f 7/9/9 2/10/10 4/11/11
s 4
f 7/12/12 6/13/13 3/14/14
s 5
f 3/17/17 6/16/16 4/15/15
s 6
f 3/20/20 4/19/19 5/18/18
s 7
f 3/23/23 5/22/22 8/21/21
s 8
f 1/26/26 8/25/25 5/24/24
s 9
f 3/29/29 8/28/28 1/27/27
s 10
f 2/30/30 7/31/31 3/32/32
...
o  <object_name_2>
g  <object_name_2>
v 0.01237990 0.14437500 0.00435934
...
vt 1.00000000 0.38524590
...
vn 0.94225726 -0.04540766 0.33179723
...
s 1
usemtl <материал 2>
f 11/35/35 10/34/34 9/33/33
s 2
f 9/36/36 12/37/37 11/38/38
s 3
f 9/39/39 13/40/40 12/41/41
s 4
f 13/44/44 14/43/43 12/42/42
s 5
f 10/47/47 14/46/46 13/45/45
s 6
f 14/50/50 11/49/49 12/48/48
s 7
f 13/51/51 9/52/52 10/53/53
s 8
f 10/56/56 11/55/55 14/54/54
...
o  <object_name_N>
g  <object_name_N>
v 0.016464990 0.575700 0.63435934
...

Здесь надо формировать свой vao (с тремя vbo) для каждого o-объекта.
Верно?
Это первый вопрос.

Второй вопрос, что делать с этими s-ками?
Как использовать?
Описания которые находил оч. скудные, не понятно как из этих s-ок извлекать полезную информацию.

Извините, но толкового объяснения на форуме не нашел, поэтому задаю вопрос.
Заранее благодарен за содержательные ответы по существу.

#1
20:43, 22 янв 2025

s — это smooth group. Влияет на вычисление нормалей в вершинах. По идее они не задаются тогда в файле через vn. Smooth группа задается для треугольника.

Визуально с ними проще разобраться в 3DS Max-е. Смысл такой, что если треугольники с общей вершиной имеют общий бит в smooth группах, то эта вершина имеет усредненную нормаль. Треугольники "гладко" интерполируются и не видно ребро.

youtube: smooth groups.

почему 32 кнопки для определения smooth groups в 3DS Max-е? Потому что изначально использовалось 32-х битное целое число.

if ((s1 & s2) == true) {
  // усредненная общая нормаль в общих вершинах
}
else {
  // Нормали не усредняются (берется нормаль треугольника или усреднение с другими треугольниками, если есть общий бит)
}

Если позиция вершины общая для двух треугольников, но у них нет пересечения по битам в smooth группах, то такие вершины нужно дублировать. Они будут различаться нормалями.

PS: Насколько я помню, при усреднении нормали, берется не среднее число, а в пропорциях площадей треугольников.

PS2: Пока писал, вспомнил, что у меня ж есть статья
https://wat.gamedev.ru/articles/SmoothGroup

#2
22:35, 22 янв 2025

wat
> if ((s1 & s2) == true) {
>   // усредненная общая нормаль в общих вершинах
> }
> else {
>   // Нормали не усредняются (берется нормаль треугольника или усреднение с другими треугольниками, если есть общий бит)
> }
Рили так?
S1 & S2 может быть равно не нулю даже при разных s1,s2. Насколько помню, в 3дмаксе просто 32 различных доступных группы? Может просто s1, s2 на равенство надо сравнивать?
Или твоя теория верна, если s1, s2, это числа, у которых только один единичный бит равный номеру группы.
Думаю, 32 -это просто более чем достаточно для групп сглаживания. И сделали так для экономии памяти. 5 бит, вместо 4 байт.

#3
2:31, 23 янв 2025

wat
> if ((s1 & s2) == true) {
Вместо == true должно быть либо != 0, либо ничего, иначе получается проверка пересечения только младшего бита.

AMM1AK
> Может просто s1, s2 на равенство надо сравнивать?
Работа с группами сглаживания как с битовыми масками даёт гораздо больше гибкости в указании, что надо сглаживать, а что нет. Например, в случае

\  2   /
 \    /
  \  /
   \/
   |
1  |  3 
   |

можно сгладить рёбра 1-2 и 2-3, но не сглаживать ребро 1-3.

#4
2:52, 23 янв 2025

Dinosaur
> можно сгладить рёбра 1-2 и 2-3, но не сглаживать ребро 1-3.
Может сгладить 1-3, 2-3, но не 1-2? Исходя из двоичного представления всех этих чисел и побитового *

#5
11:45, 23 янв 2025

Цитирую англоязычный форум:

Если в OBJ-файле есть нормали для каждой вершины, вам не нужны части s. Но вы можете использовать их для вычисления нормалей вершин. Части s представляют собой группы сглаживания и должны интерпретироваться как 32-битные битовые поля. Таким образом, на самом деле существует 32 различных группы сглаживания, и каждая грань может быть частью более чем одной. Таким образом, все грани после строки «s 5» являются частью групп сглаживания 1 и 3 (установлены первый и третий биты). Когда две соседние грани входят в одну группу сглаживания, край между ними гладкий (вершины имеют общие нормали). Таким образом, вы можете восстановить необходимые нормали для каждой вершины.

Вопрос, как восстановить?

Кстати, просматривая различные obj-файлы, видел s > 32. s может быть не только int-ом.

#6
12:28, 23 янв 2025

Gyros
> Вопрос, как восстановить?
Нормаль вершины, это нормаль треугольника частью которого она является.

Вычисляем нормаль треугольника.
Передаём результат во все вершины треугольника.

При вычислении нормалей для модели, модель будет выглядеть полигональной, не сглаженной.
Чтобы было гладко, надо усреднить значения. Если несколько вершин различных треугольников имеют одинаковую позицию, суммируем нормали, а потом нормализуем вектор. (,потом этот нормализованный вектр передаём вершинам как новая нормаль)
Получится гладкая модель.

Как я понял про группы сглаживания, при вычислении гладкой нормали просто проверяем, треугольники имеющие общие вершины находятся в одной группе или нет. Если в одной то суммируем нормали т.д.

#7
21:50, 23 янв 2025

AMM1AK
> Может сгладить 1-3, 2-3, но не 1-2?
Может. С s1 == s2 подобные комбинации невозможны, а с (s1 & s2) != 0 — легко.

Gyros
> просматривая различные obj-файлы, видел s > 32. s может быть не только int-ом.
В приведённой тобой же цитате написано, что s — не номер группы, а битовая маска, поэтому для выхода за пределы 32-х бит s должно быть больше 4294967295.

#8
14:02, 25 янв 2025

Dinosaur
Благодарю!
Спасибо. Я ошибся с возможным кол-вом групп.

BADCOIQ
Спасибо!
Да, алгоритм примерно понятен. Но теперь думаю, что в своем загрузчике реализовывать его - это усложнять программу.
Потому что нужно запоминать индексы нормалей в группе (а они нумеруются не с начала файла, а с начала
объекта (o), кол-во треуг. в группе, потом вычислять новую нормаль, корректировать старые.

Например,

o object_11
v 0.03735606 0.44806757 -0.01797489
...
vn -0.78611780 -0.61783855 0.01715593
s 1
usemtl Pollen
f 15/57/57 16/58/58 17/59/59
s 2
f 18/62/62 16/61/61 15/60/60
s 3
f 18/65/65 15/64/64 19/63/63
s 4
f 18/68/68 19/67/67 20/66/66
s 5
f 18/71/71 20/70/70 21/69/69
s 6
f 18/74/74 21/73/73 22/72/72
s 7
f 22/75/75 21/76/76 17/77/77
s 8
f 21/78/78 20/79/79 17/80/80
s 9
f 20/81/81 19/82/82 17/83/83
s 10
f 18/86/86 22/85/85 16/84/84
s 11
f 19/87/87 15/88/88 17/89/89
s 12
f 16/90/90 22/91/91 17/92/92
o <другой объект>

Получается тут, если
s 1 - то ничего не делаю,
s 2 - тоже
s 3  - группы треуг. 1 и 2,  делаю:

1)
// вычисляю среднюю нормаль
new_nX = (normals[57].X+normals[62].X) /2; //делю на два т.к. два треуг. в группе
new_nY = (normals[58].Y+normals[61].Y) /2;
new_nZ = (normals[59].Z+normals[60].Z) /2;
2)
//  нормирую
new_n = normalize(new_nX, new_nY, new_nZ);

3)
// обновляю нормали вершин треугольников в группе сглаживания
normal[57] = new_n;
normal[58] = new_n;
normal[59] = new_n;
normal[62] = new_n;
normal[61] = new_n;
normal[60] = new_n;

Верно ли я представляю процесс?

Кстати, в программе SAP 3D сглаживание можно регулировать с пом. угла:

+ Показать

SAP 3D перелопатил мой obj-файл, убрал из него эти s-ки.

Вообще мне кажется лучше обрабатывать obj-модели мощной программой (хотя бы той же SAP 3D), а потом уже более упрощенную модель (без s-ок) загружать в своей программе. Ведь цели написать универсальный просмотрщик нет.

Как считаете?

#9
14:47, 25 янв 2025

Тут просто нужно определиться, важна скорость загрузки или объём данных. В первом случае - использовать уже посчитанные нормали в вершинах (с учетом групп сглаживания, углов, итд). Во втором - хранить только группу, но нормали в вершинах считать самостоятельно во время загрузки.

#10
10:17, 29 янв 2025

Моя моделька созданная в SAP 3D

+ Показать

корректно считывается и отображается.

Но после реэкспорта из линуксового meshlab-а, отображается вот-так:

+ Показать

Это из-за чего так?
Причем когда ее вертишь она резко затемняется/освещается по треугольным граням.
Граням не хватает нормалей?

Причем сам meshlab отображает свои реэкспортированные модели корректно.

#11
12:27, 29 янв 2025

Это из-за чего так?

Да нормали неправильные.
А вот почему, тебе придётся найти самому причину(их две).

#12
10:53, 30 янв 2025

ronniko
спасибо! Но какие вы имели в виду причины?

Оказалось, что я передавал в шейдер освещения по Фонгу

prog_set_uniform_float(shader, "Material.Shininess",  mtls->materials[index_mtl]->Ns);

нулевое значение из mtl-файла.

Как только там расставил значения 100 все стало нормально отображаться:

+ Показать

Видно, что meshlab сгладил нормали. И зря это сделал.
Мне предыдущий несглаженный вид больше нравится,
по крайней мере для этой модели с прямыми углами.

Проявился другой артефакт:

+ Показать

Некрасивый блик, его форма.

Он появляется из-за того, что я разбиваю встретившуюся многоугольную грань на веер треугольников
(triangle fan):

+ Показать

Хорошо бы ввести новую точку в центре и уже от нее делать веер.

Кто как думает?

ПрограммированиеФорумГрафика

Тема в архиве.