Compare commits
10 Commits
2c1dee1628
...
lab2
| Author | SHA1 | Date | |
|---|---|---|---|
| 2792d2657d | |||
| 36b296254b | |||
| 6f3b5c3cc5 | |||
| d85c3a36a6 | |||
| cd907cbf75 | |||
| 960b54b168 | |||
| e3c991c7ea | |||
| 3e827942ea | |||
| ae234f0e2b | |||
| b01b5eb9c3 |
5
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
*
|
||||
!.gitignore
|
||||
!lab1
|
||||
!lab1
|
||||
!lab2
|
||||
!lab3
|
||||
!lab4
|
||||
BIN
lab1/img/ruler/lighting/1-world.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
lab1/img/ruler/lighting/2-points.png
Normal file
|
After Width: | Height: | Size: 501 KiB |
BIN
lab1/img/ruler/lighting/3-camera.png
Normal file
|
After Width: | Height: | Size: 1017 KiB |
BIN
lab1/img/ruler/modeling/1-add-cube.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
lab1/img/ruler/modeling/10-break.png
Normal file
|
After Width: | Height: | Size: 562 KiB |
BIN
lab1/img/ruler/modeling/11-merge.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
lab1/img/ruler/modeling/12-select.png
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
lab1/img/ruler/modeling/13-break-top.png
Normal file
|
After Width: | Height: | Size: 548 KiB |
BIN
lab1/img/ruler/modeling/14-fill.png
Normal file
|
After Width: | Height: | Size: 250 KiB |
BIN
lab1/img/ruler/modeling/15-scale-locale.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
lab1/img/ruler/modeling/16-cube.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
lab1/img/ruler/modeling/17-diff.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
lab1/img/ruler/modeling/18-final-diff.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
lab1/img/ruler/modeling/2-subdivision.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
lab1/img/ruler/modeling/3-edges.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
lab1/img/ruler/modeling/4-skew.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
lab1/img/ruler/modeling/5-arrows.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
lab1/img/ruler/modeling/6-bend.png
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
lab1/img/ruler/modeling/7-plane.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
lab1/img/ruler/modeling/8-reference.png
Normal file
|
After Width: | Height: | Size: 336 KiB |
BIN
lab1/img/ruler/modeling/9-remove-edge.png
Normal file
|
After Width: | Height: | Size: 386 KiB |
BIN
lab1/img/ruler/real/back-side.jpg
Normal file
|
After Width: | Height: | Size: 575 KiB |
BIN
lab1/img/ruler/real/back.jpg
Normal file
|
After Width: | Height: | Size: 511 KiB |
BIN
lab1/img/ruler/real/front.jpg
Normal file
|
After Width: | Height: | Size: 566 KiB |
BIN
lab1/img/ruler/real/side.jpg
Normal file
|
After Width: | Height: | Size: 551 KiB |
BIN
lab1/img/ruler/render/back-side.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
lab1/img/ruler/render/back.png
Normal file
|
After Width: | Height: | Size: 781 KiB |
BIN
lab1/img/ruler/render/front.png
Normal file
|
After Width: | Height: | Size: 801 KiB |
BIN
lab1/img/ruler/render/side.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
lab1/img/ruler/texturing/1-apply.png
Normal file
|
After Width: | Height: | Size: 273 KiB |
BIN
lab1/img/ruler/texturing/2-settings.png
Normal file
|
After Width: | Height: | Size: 490 KiB |
BIN
lab1/img/ruler/texturing/3-materials.png
Normal file
|
After Width: | Height: | Size: 391 KiB |
BIN
lab1/img/ruler/texturing/4-top-uv.png
Normal file
|
After Width: | Height: | Size: 610 KiB |
BIN
lab1/img/ruler/texturing/5-top-uv-tuning.png
Normal file
|
After Width: | Height: | Size: 699 KiB |
BIN
lab1/img/ruler/texturing/6-back-uv.png
Normal file
|
After Width: | Height: | Size: 422 KiB |
BIN
lab1/img/ruler/texturing/7-apply.png
Normal file
|
After Width: | Height: | Size: 227 KiB |
BIN
lab1/img/ruler/texturing/8-table.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
lab1/img/ruler/texturing/9-up.png
Normal file
|
After Width: | Height: | Size: 416 KiB |
577
lab1/report.tex
@@ -373,7 +373,48 @@
|
||||
|
||||
\textbf{Материал:} Зажим изготовлен из пластика и покрыта красной краской. Внутри ободков находятся кусочки проволоки, за счёт которых зажим запоминает приданную ему форму. Надпись со сроком годности нанесена белой краской.
|
||||
|
||||
\textbf{Особенности:} Объект имеет уникальную форму, которая была придана ему вручную специально для этой лабораторной работы. Также уникальной особенностью является надпись на объекте. В ней содержится срок годности и номер бригады -- <<09.03.25.2 - 2>>.
|
||||
\textbf{Особенности:} Объект имеет уникальную форму, которая была придана ему вручную специально для этой лабораторной работы. Также уникальной особенностью является надпись на объекте. В ней содержится срок годности и номер бригады -- <<09.03.25.2 - 1>>.
|
||||
|
||||
|
||||
\newpage
|
||||
\subsection{Объект моделирования №3}
|
||||
Объектом №3 является сломанная линейка (Рис.~\ref{fig:ruler/real/front}-\ref{fig:ruler/real/back-side}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.2\linewidth]{img/ruler/real/front.jpg}
|
||||
\caption{Объект моделирования №3, вид с лицевой стороны.}
|
||||
\label{fig:ruler/real/front}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.2\linewidth]{img/ruler/real/back.jpg}
|
||||
\caption{Объект моделирования №3, вид с обратной стороны.}
|
||||
\label{fig:ruler/real/back}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.6\linewidth]{img/ruler/real/side.jpg}
|
||||
\caption{Объект моделирования №3, вид сбоку на лицевую сторону.}
|
||||
\label{fig:ruler/real/side}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/ruler/real/back-side.jpg}
|
||||
\caption{Объект моделирования №3, вид сбоку на обратную сторону.}
|
||||
\label{fig:ruler/real/back-side}
|
||||
\end{figure}
|
||||
|
||||
\textbf{Форма:} Линейка имеет форму удлинённой плоской пластины с прямыми параллельными краями. Торец, на котором нанесены измерительные деления, скошен.
|
||||
|
||||
\textbf{Материал:} Линейка изготовлена из пластика и покрыта чёрной краской. Измерительные деления и отметки нанесены белой краской.
|
||||
|
||||
\textbf{Особенности:} Линейка имеет уникальный излом. Также она слегка погнута по всей длине. Видны следы продолжительной эксплуатации -- в некоторых местах стёрта краска, а в некоторых видны царапины.
|
||||
|
||||
|
||||
\newpage
|
||||
\section{Описание технологии разработки моделей}
|
||||
@@ -389,7 +430,7 @@
|
||||
|
||||
\subsubsection{Моделирование}
|
||||
|
||||
Нажимаем на цифру 7 на Num Pad, чтобы перейти на вид сверху. Затем нажимаем Shift + A и в разделе Image выбираем Reference. В открывшемся окне проводника выбираем изображение лицевой стороны покерной фишки и нажимаем Add~Image~(Рис.~\ref{fig:chip/modeling/1-add-reference}).
|
||||
Добавим изображение фишки в сцену, чтобы использовать его как визуальный ориентир. Нажимаем на цифру 7 на Num Pad (или выбираем "Top" в меню View - Viewpoint), чтобы перейти на вид сверху. Затем нажимаем сочетание клавиш Shift + A (либо открываем список Add в верхнем меню) и в разделе Image выбираем Reference. В открывшемся окне проводника выбираем изображение лицевой стороны покерной фишки и нажимаем Add~Image~(Рис.~\ref{fig:chip/modeling/1-add-reference}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -399,25 +440,25 @@
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
Нажимаем Shift + A и в разделе Mesh выбираем Cylinder (Рис.~\ref{fig:chip/modeling/2-add-cylinder}).
|
||||
Создадим цилиндрическую основу для будущей фишки. Нажимаем сочетание клавиш Shift + A (либо используем меню Add) и в разделе Mesh выбираем Cylinder (Рис.~\ref{fig:chip/modeling/2-add-cylinder}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/chip/modeling/2-add-cylinder.png}
|
||||
\caption{Добавление цилиндра.}
|
||||
\label{fig:chip/modeling/2-add-cylinder}
|
||||
\end{figure}
|
||||
\end{figure}
|
||||
|
||||
Нажимаем Alt + Z, чтобы перейти в режим прозрачности и видеть изображение фишки сквозь цилиндр.
|
||||
Активируем режим прозрачности для точного совмещения объектов. Нажимаем сочетание клавиш Alt + Z (или выбираем "X-Ray" в правом нижнем меню Viewport Shading), чтобы видеть изображение сквозь цилиндр.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/chip/modeling/3-alt-z.png}
|
||||
\caption{Режим прозрачности.}
|
||||
\label{fig:chip/modeling/3-alt-z}
|
||||
\end{figure}
|
||||
|
||||
Выбираем изображение, а затем с помощью масштабирования (клавиша S) и смещения (клавиша G) выравниваем центр цилиндра с центром фишки на изображении. Перемещаем курсор с зажатой клавишей Shift для более точного позиционирования. Результат на Рис.~\ref{fig:chip/modeling/4-positioning}.
|
||||
\end{figure}
|
||||
|
||||
Подгоним размеры и позицию изображения под геометрию цилиндра. Выбираем изображение, затем используем масштабирование (клавиша S) и перемещение (клавиша G), совмещая центр цилиндра с центром фишки на референсе. Для точной настройки перемещаем курсор с зажатой клавишей Shift. Результат на Рис.~\ref{fig:chip/modeling/4-positioning}.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -427,7 +468,7 @@
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
Нажимаем на цилиндр и переходим в режим редактирования (клавиша Tab). В режиме выбора граней (клавиша 3) и с зажатой клавишей Shift выбираем верхнюю и нижнюю грани цилиндра (Рис.~\ref{fig:chip/modeling/5-select-faces}).
|
||||
Подготовим грани для формирования ложбинки. Нажимаем на цилиндр и переходим в режим редактирования (клавиша Tab или кнопка Edit Mode в верхнем меню). В режиме выбора граней (клавиша 3 или иконка Face Select) с зажатой клавишей Shift выбираем верхнюю и нижнюю грани цилиндра (Рис.~\ref{fig:chip/modeling/5-select-faces}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -437,16 +478,16 @@
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
Возвращаемся на вид сверху (клавиша 7 на Num Pad). Нажимаем клавишу I, чтобы добавить внтреннюю грань. Выравниваем её по внешней стороне ложбинки на фишке, ориентируясь на изображения. При этом перемещаем курсор с зажатой клавишей Shift для более точного позиционирования.
|
||||
Создадим контур внешнего края ложбинки. Возвращаемся на вид сверху (клавиша 7 на Num Pad). Нажимаем клавишу I (или выбираем Inset Faces в меню Mesh > Faces), чтобы добавить внутреннюю грань. Выравниваем её по внешнему краю ложбинки, используя референс для точности позиционирования.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/chip/modeling/6-insert-faces.png}
|
||||
\caption{Добавление новых рёбер на верхней и нижней гранях цилиндра.}
|
||||
\label{fig:chip/modeling/6-insert-faces}
|
||||
\end{figure}
|
||||
\end{figure}
|
||||
|
||||
Снова нажимаем на клавишу I. Новую грань выравниваем по внутренней стороне ложбинки на фишке (Рис.~\ref{fig:img/chip/modeling/7-insert-inner-faces}).
|
||||
Сформируем внутренний контур ложбинки. Снова нажимаем клавишу I и создаём вторую грань, выравнивая её по внутреннему краю ложбинки (Рис.~\ref{fig:img/chip/modeling/7-insert-inner-faces}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -455,36 +496,37 @@
|
||||
\label{fig:img/chip/modeling/7-insert-inner-faces}
|
||||
\end{figure}
|
||||
|
||||
Изображение больше не понадобится и его можно скрыть. Возвращаемся в объектный режим (клавиша Tab), нажимаем на изображение, а затем нажимаем на клавишу H.
|
||||
Уберём референс из рабочей области для удобства. Возвращаемся в объектный режим (клавиша Tab), выбираем изображение и нажимаем клавишу H (или используем меню Object > Show/Hide > Hide Selected).
|
||||
|
||||
Нажимаем на цилиндр, а затем нажимаем на клавишу N, чтобы открыть боковое меню с размерами модели. В разделе Dimensions указываем реальные размеры изделия. В поле X и Y указываем значения 40mm, в поле Z указываем 2mm. Затем нажимаем точку на Num Pad, чтобы приблизиться к модели (Рис.~\ref{fig:chip/modeling/8-sizing}).
|
||||
Зададим реальные размеры для модели. Нажимаем на цилиндр, затем клавишу N (или открываем меню View > Sidebar). В разделе Dimensions указываем: X/Y = 40mm, Z = 2mm. Нажимаем точку на Num Pad (или кнопку Frame Selected в правом верхнем углу) для фокусировки на модели (Рис.~\ref{fig:chip/modeling/8-sizing}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.9\linewidth]{img/chip/modeling/8-sizing.png}
|
||||
\includegraphics[width=0.8\linewidth]{img/chip/modeling/8-sizing.png}
|
||||
\caption{Указание размеров фишки.}
|
||||
\label{fig:chip/modeling/8-sizing}
|
||||
\end{figure}
|
||||
|
||||
Нажимаем сочетание клавиш Ctrl + A и в появившемся меню выбираем вариант Scale (Рис.~\ref{fig:chip/modeling/9-scale}).
|
||||
Применим масштабирование для корректной работы с размерами. Нажимаем сочетание клавиш Ctrl + A (или выбираем Apply > Scale в меню Object) и выбираем Scale (Рис.~\ref{fig:chip/modeling/9-scale}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/chip/modeling/9-scale.png}
|
||||
\includegraphics[width=0.6\linewidth]{img/chip/modeling/9-scale.png}
|
||||
\caption{Применение функции Scale к модели.}
|
||||
\label{fig:chip/modeling/9-scale}
|
||||
\end{figure}
|
||||
|
||||
Переходим в режим редактирования (клавиша Tab). С зажатой клавишей Alt нажимаем на грань внутри ложбинки, чтобы выделить все грани ложбинки. Нажимаем на клавишу 1 на Num Pad, чтобы переключиться на вид сбоку. Затем нажимаем на клавишу E, чтобы создать углубление. В боковом меню (клавиша N) в разделе Median в поле Z указываем значение 0.6mm. Затем нажимаем сочетание клавиш Ctrl + 7 на Num Pad, чтобы переключиться на вид снизу. Аналогичным образом выделяем грани ложбинки, переключаемся на вид сбоку и создаём углубление. В боковом меню в разделе Median в поле Z указываем значение -0.6mm (Рис.~\ref{fig:chip/modeling/10-trench}).
|
||||
\newpage
|
||||
Создадим углубления для ложбинки. В режиме редактирования (клавиша Tab) с зажатой Alt выделяем грани ложбинки. Переключаемся на вид сбоку (клавиша 1 на Num Pad), нажимаем E (или Extrude в меню Mesh), задаём Z = 0.6mm в панели Median. Повторяем для нижней части: вид снизу (Ctrl + 7), выделение граней, углубление с Z = -0.6mm (Рис.~\ref{fig:chip/modeling/10-trench}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.9\linewidth]{img/chip/modeling/10-trench.png}
|
||||
\caption{Добавление ложбинки.}
|
||||
\caption{Формирование углублений ложбинки.}
|
||||
\label{fig:chip/modeling/10-trench}
|
||||
\end{figure}
|
||||
\end{figure}
|
||||
|
||||
На этом этапе можно отключить режим прозрачности (Alt + z). Переходим в объектный режим (клавиша Tab) и в разделе Modifiers добавляем модификатор Subdivision Surface из раздела Generate. В параметрах Levels Viewport и Render указываем значение 3. Нажимаем на цилиндр правой кнопкой мыши и выбираем пункт Shade Smooth (Рис.~\ref{fig:chip/modeling/11-shade-smooth}).
|
||||
Сгладим геометрию для реалистичного вида. Отключаем X-Ray (Alt + Z), в объектном режиме добавляем модификатор Subdivision Surface (меню Modifier Properties > Add Modifier). Устанавливаем Levels Viewport/Render = 3. Применяем Shade Smooth через контекстное меню объекта (Рис.~\ref{fig:chip/modeling/11-shade-smooth}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -493,7 +535,7 @@
|
||||
\label{fig:chip/modeling/11-shade-smooth}
|
||||
\end{figure}
|
||||
|
||||
На данном этапе углы модели выглядят слишком скруглёнными. Переходим в режим редактирования (клавиша Tab). Нажимаем сочетание клавиш Ctrl + R и создаём новые грани, почти в плотную к граням, на которых происходит перепад высот. Создаём такие же грани в ложбинке. В режиме выбора поверхностей (клавиша 3) нажимаем на центральную часть фишки и нажимаем на клавишу I, чтобы создать новую грань в центральной части фишки. Затем переключаемся на вид снизу (Ctrl + 7 на Num Pad) и проделываем те же операции (Рис.~\ref{fig:chip/modeling/12-new-edges}).
|
||||
Добавим контрольные рёбра для сохранения чётких граней. В режиме редактирования используем инструмент Loop Cut (сочетание клавиш Ctrl + R) возле перепадов высот. На верхней и нижней поверхностях создаём дополнительные грани инструментом Inset (клавиша I) (Рис.~\ref{fig:chip/modeling/12-new-edges}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -502,7 +544,7 @@
|
||||
\label{fig:chip/modeling/12-new-edges}
|
||||
\end{figure}
|
||||
|
||||
Теперь добавим подложку, на которой разместим текстуру стола. В боковом меню (клавиша N) в разделе Location в поле Z указываем значение 1mm, чтобы приподнять модель. Затем нажимаем Shift + A и в разделе Mesh выбираем Plane (Рис.~\ref{fig:chip/modeling/13-plane}). Затем нажимаем H, чтобы временно скрыть добавленную поверхность.
|
||||
Теперь добавим подложку, на которой разместим текстуру стола. В боковом меню (клавиша N) в разделе Location в поле Z указываем значение 1mm, чтобы приподнять модель. Затем нажимаем Shift + A (либо открываем список Add в верхнем меню) и в разделе Mesh выбираем Plane (Рис.~\ref{fig:chip/modeling/13-plane}). Затем нажимаем H, чтобы временно скрыть добавленную поверхность.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -511,27 +553,28 @@
|
||||
\label{fig:chip/modeling/13-plane}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
\subsubsection{Текстурирование}
|
||||
|
||||
Переходим в раздел UV Editing в верхнем меню программы. В правом окне переключаемся в объектный режим (клавиша Tab), нажимаем на модель и нажимаем на точку на Num Pad, чтобы приблизиться к ней (Рис.~\ref{fig:chip/texturing/1-start}).
|
||||
Приступим к наложению текстур на модель фишки для придания ей реалистичного внешнего вида. Переходим в раздел UV Editing через верхнее меню программы. В правом окне активируем объектный режим с помощью клавиши Tab (либо через выпадающее меню в левом нижнем углу окна). Выделяем модель и приближаемся к ней, нажав клавишу точки на Num Pad (Рис.~\ref{fig:chip/texturing/1-start}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/chip/texturing/1-start.png}
|
||||
\includegraphics[width=0.8\linewidth]{img/chip/texturing/1-start.png}
|
||||
\caption{Раздел UV Editing.}
|
||||
\label{fig:chip/texturing/1-start}
|
||||
\end{figure}
|
||||
|
||||
В разделе Render Properties в поле Render Engine выбираем EEVEE, ставим галочку на разделе Raytracing и в его подразделе Fast GI Approximation в поле Threshold указыем значение 1. После чего нажимаем сочетание клавиш Z + 2, чтобы переключиться в режим предпросмотра материалов.
|
||||
Настроим рендер-движок для корректного отображения материалов. В разделе Render Properties выбираем EEVEE в поле Render Engine. Активируем Raytracing, устанавливаем значение Threshold в 1 в подразделе Fast GI Approximation. Переключаемся в режим предпросмотра материалов с помощью сочетания клавиш Z + 2 (либо выбираем режим Material Preview через меню Viewport Shading в верхней панели) (Рис.~\ref{fig:chip/texturing/2-settings}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/chip/texturing/2-settings.png}
|
||||
\includegraphics[width=0.6\linewidth]{img/chip/texturing/2-settings.png}
|
||||
\caption{Настройка движка EEVEE для корректного отображения текстур.}
|
||||
\label{fig:chip/texturing/2-settings}
|
||||
\end{figure}
|
||||
|
||||
Открываем раздел Material Properties и добавляем новый материал, нажимая на кнопку New. В поле Base Color указываем значение \#9FAAB1FF. Затем нажимаем на + и снова на New, чтобы добавить ещё один материал. В разделе Base Color нажимаем на жёлтый кружок и выбираем пункт Image Texture (Рис.~\ref{fig:chip/texturing/3-image-texture}). В открывшемся окне проводника выбираем изображение фишки сверху. Изображение фишки снизу добавляем аналогичным образом.
|
||||
Создадим материалы для текстур фишки. Открываем раздел Material Properties и добавляем новый материал, нажимая на кнопку New. В поле Base Color указываем значение \#9FAAB1FF. Затем нажимаем на + и снова на New, чтобы добавить ещё один материал. В разделе Base Color нажимаем на жёлтый кружок и выбираем пункт Image Texture (Рис.~\ref{fig:chip/texturing/3-image-texture}). В открывшемся окне проводника выбираем изображение фишки сверху. Изображение фишки снизу добавляем аналогичным образом.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -540,7 +583,7 @@
|
||||
\label{fig:chip/texturing/3-image-texture}
|
||||
\end{figure}
|
||||
|
||||
Переходим в режим редактирования (клавиша Tab). Переходим на вид сверху (клавиш 7 на Num Pad). В режиме выбора поверхностей (клавиша 3), выделяем все видимые поверхности. В разделе с материалами выбираем материал с изображением фишки сверху и нажимаем на кнопку Assign (Рис.~\ref{fig:chip/texturing/4-assigning}).
|
||||
Выполним UV-развёртку для точного наложения текстур. Переходим в режим редактирования клавишей Tab (либо через меню Mode). Активируем вид сверху клавишей 7 на Num Pad (меню View → Top). Выделяем все видимые грани в режиме выбора поверхностей (клавиша 3) и назначаем материал с текстурой фишки кнопкой Assign (Рис.~\ref{fig:chip/texturing/4-assigning}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -549,7 +592,7 @@
|
||||
\label{fig:chip/texturing/4-assigning}
|
||||
\end{figure}
|
||||
|
||||
На панели слева распологаем проекцию поверхностей на текстуре таким образом, чтобы форма поверхности совпадала с изображением. Аналогичным образом накладываем материал с текстурой обратной стороны фишки (Рис.~\ref{fig:chip/texturing/5-uv-editing}).
|
||||
Отредактируем UV-координаты для точного совпадения с изображением. На панели слева распологаем проекцию поверхностей на текстуре таким образом, чтобы форма поверхности совпадала с изображением. Аналогичным образом накладываем материал с текстурой обратной стороны фишки (Рис.~\ref{fig:chip/texturing/5-uv-editing}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -558,7 +601,7 @@
|
||||
\label{fig:chip/texturing/5-uv-editing}
|
||||
\end{figure}
|
||||
|
||||
Теперь можно вернуть поверхность, добавленную на этапе моделирования (Alt~+~H). В разделе Material Properties добавляем новый материал и в поле Base Color указываем текстуру стола. С помощью клавишы S в левом окне можно подогнать размеры текстуры (Рис.~\ref{fig:chip/texturing/6-table}).
|
||||
Добавим текстуру для поверхности стола. Возвращаем скрытую ранее плоскость стола сочетанием клавиш Alt + H (меню Object → Show/Hide → Show Hidden). В разделе Material Properties добавляем новый материал и в поле Base Color указываем текстуру стола. С помощью клавишы S в левом окне можно подогнать размеры текстуры (Рис.~\ref{fig:chip/texturing/6-table}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -570,9 +613,9 @@
|
||||
\newpage
|
||||
\subsubsection{Освещение и камера}
|
||||
|
||||
Переходим в раздел Shading в верхнем меню программы. Нажимаем на модель и нажимаем на точку на Num Pad, чтобы приблизиться к ней. После чего используем сочетание клавиш Z + 8, чтобы перейти в режим предпросмотра результата.
|
||||
Настроим освещение сцены для финального рендеринга. Переходим в раздел Shading в верхнем меню программы. Нажимаем на модель и нажимаем на точку на Num Pad, чтобы приблизиться к ней. После чего используем сочетание клавиш Z + 8, чтобы перейти в режим предпросмотра результата.
|
||||
|
||||
Затем на панели редактирования материалов нужно переключиться на вкладку World. Нажать сочетание клавиш Shift + A и в разделе Texture выбрать Image Texture. Выход Color узла Image Texture необходимо подключить ко входу Color узла Background. В Image Texture указывается путь до HDRI текстуры окружения, которая поставляется вместе с Blender -- \texttt{C:\textbackslash Program Files\textbackslash Blender Foundation\textbackslash Blender 4.3\textbackslash 4.3\textbackslash datafiles\textbackslash studiolights\textbackslash world\textbackslash }. В параметре Strength узла Background указываем значение 0.200 (Рис.~\ref{fig:chip/lighting/1_start}).
|
||||
Создадим фоновое освещение через HDRI-карту. На панели редактирования материалов нужно переключиться на вкладку World. Нажать сочетание клавиш Shift + A и в разделе Texture выбрать Image Texture (меню Add → Texture → Image Texture). Выход Color узла Image Texture необходимо подключить ко входу Color узла Background. В Image Texture указывается путь до HDRI текстуры окружения, которая поставляется вместе с Blender -- \texttt{C:\textbackslash Program Files\textbackslash Blender Foundation\textbackslash Blender 4.3\textbackslash 4.3\textbackslash datafiles\textbackslash studiolights\textbackslash world\textbackslash }. В параметре Strength узла Background указываем значение 0.200 (Рис.~\ref{fig:chip/lighting/1_start}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -581,7 +624,7 @@
|
||||
\label{fig:chip/lighting/1_start}
|
||||
\end{figure}
|
||||
|
||||
Переключаемся на вид сбоку (клавиша 1 на Num Pad), нажимаем Shift + A и в разделе Light выбираем Point. С помощью клавиши G располагаем источник света слева сверху над моделью. Для этого в боковом меню (клавиша N) в разделе Location в поле X указываем -0.3m, в поле Z указываем 0.2m. В разделе Object Data Properties в поле Power устанавливаем значение 4.5W. Затем дублируем источник света с помощью сочетания клавиш Shift + D и располагаем с противоположной стороны. Источнику света слева задаём синеватый оттенок (\#C5FBFF) в поле Color раздела Object Data Properties (Рис.~\ref{fig:chip/lighting/2_points}).
|
||||
Добавим дополнительные источники света для создания теней как на исходных изображениях. Переключаемся на вид сбоку (клавиша 1 на Num Pad), нажимаем Shift + A (либо открываем список Add в верхнем меню) и в разделе Light выбираем Point. С помощью клавиши G располагаем источник света слева сверху над моделью. Для этого в боковом меню (клавиша N) в разделе Location в поле X указываем -0.3m, в поле Z указываем 0.2m. В разделе Object Data Properties в поле Power устанавливаем значение 4.5W. Затем дублируем источник света с помощью сочетания клавиш Shift + D и располагаем с противоположной стороны. Источнику света слева задаём синеватый оттенок (\#C5FBFF) в поле Color раздела Object Data Properties (Рис.~\ref{fig:chip/lighting/2_points}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -591,7 +634,7 @@
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
Теперь добавим камеру. Для этого нажимаем сочетание клавиш Shift + A и выбираем пункт Camera. Теперь располагаем её ровно над моделью, чтобы получить изображение вида сверху. В боковом меню (клавиша N) в разделе Location в Z указываем значение 0.15m, а в разделе Rotation в поле X укажем значение 0. В разделе Object Data Properties в подразделе Lens в поле Focal Length указываем значение 110mm. Нажимаем 0 на Num Pad, чтобы переключиться на вид с камеры (Рис.~\ref{fig:chip/lighting/3_camera}).
|
||||
Теперь добавим камеру, чтобы зафиксировать ракурс для рендеринга. Для этого нажимаем сочетание клавиш Shift + A (либо открываем список Add в верхнем меню) и выбираем пункт Camera. Теперь располагаем её ровно над моделью, чтобы получить изображение вида сверху. В боковом меню (клавиша N) в разделе Location в Z указываем значение 0.15m, а в разделе Rotation в поле X укажем значение 0. В разделе Object Data Properties в подразделе Lens в поле Focal Length указываем значение 110mm. Нажимаем 0 на Num Pad, чтобы переключиться на вид с камеры (Рис.~\ref{fig:chip/lighting/3_camera}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -684,7 +727,7 @@
|
||||
|
||||
\subsubsection{Моделирование}
|
||||
|
||||
Нажимаем на цифру 7 на Num Pad, чтобы перейти на вид сверху. Затем нажимаем Shift + A и в разделе Image выбираем Reference. В открывшемся окне проводника выбираем изображение зажима и нажимаем Add~Image~(Рис.~\ref{fig:clamp/modeling/1-add-reference}).
|
||||
Для точного воссоздания формы добавим референсное изображение. Нажимаем на цифру 7 на Num Pad (или выбираем вид сверху через меню View), чтобы перейти на вид сверху. Затем нажимаем сочетание клавиш Shift + A (либо открываем список Add в верхнем меню) и в разделе Image выбираем Reference. В открывшемся окне проводника выбираем изображение зажима и нажимаем Add~Image~(Рис.~\ref{fig:clamp/modeling/1-add-reference}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -693,7 +736,7 @@
|
||||
\label{fig:clamp/modeling/1-add-reference}
|
||||
\end{figure}
|
||||
|
||||
Нажимаем Shift + A и в разделе Mesh выбираем Plane (Рис.~\ref{fig:clamp/modeling/2-add-plane}).
|
||||
Создадим базовую плоскость для построения контура. Нажимаем сочетание клавиш Shift + A (либо открываем меню Add) и в разделе Mesh выбираем Plane (Рис.~\ref{fig:clamp/modeling/2-add-plane}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -702,7 +745,7 @@
|
||||
\label{fig:clamp/modeling/2-add-plane}
|
||||
\end{figure}
|
||||
|
||||
Переходим в режим редактирования (клавиша Tab) и в режиме выбора вершин (клавиша 1) с зажатой клавишей Shift выбираем три любых вершины добавленной плоскости. Нажимаем на клавишу X и в появившемся меню выбираем пункт Vertex, чтобы удалить эти вершины. Выделяем оставшуюся вершину и с помощью клавишы G переносим её в крайнюю точку ободка зажима. Затем нажимаем клавишу E, чтобы добавить новую точку. Таким образом делаем окантовку для всего зажима (Рис.~\ref{fig:clamp/modeling/3-border}).
|
||||
Приступим к формированию контура зажима по референсу. Переходим в режим редактирования (клавиша Tab) и в режиме выбора вершин (клавиша 1) с зажатой клавишей Shift выбираем три любых вершины добавленной плоскости. Нажимаем на клавишу X (либо используем опцию Delete в контекстном меню) и в появившемся меню выбираем пункт Vertex, чтобы удалить эти вершины. Выделяем оставшуюся вершину и с помощью клавишы G переносим её в крайнюю точку ободка зажима. Затем нажимаем клавишу E, чтобы добавить новую точку. Таким образом делаем окантовку для всего зажима (Рис.~\ref{fig:clamp/modeling/3-border}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -712,28 +755,27 @@
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
Переходим в объектный режим. Выделяем изображение и скрываем (клавиша H), оно больше не понадобится. Теперь выделяем полученную линию окантовки и в боковом меню (клавиша N) в разделе Dimensions в поле X указываем реальную ширину объекта -- 21mm. После чего значение поля X из раздела Scale копируем в поле Y того же раздела, чтобы сохранить пропорции при масштабировании (Рис.~\ref{fig:clamp/modeling/4-sizes}).
|
||||
Зададим реальные размеры модели. Переходим в объектный режим (клавиша Tab). Выделяем изображение и скрываем (клавиша H), оно больше не понадобится. Теперь выделяем полученную линию окантовки и в боковом меню (клавиша N) в разделе Dimensions в поле X указываем реальную ширину объекта -- 21mm. После чего значение поля X из раздела Scale копируем в поле Y того же раздела, чтобы сохранить пропорции при масштабировании (Рис.~\ref{fig:clamp/modeling/4-sizes}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/clamp/modeling/4-sizes.png}
|
||||
\includegraphics[width=0.8\linewidth]{img/clamp/modeling/4-sizes.png}
|
||||
\caption{Задание реальных размеров.}
|
||||
\label{fig:clamp/modeling/4-sizes}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
Переключаемся на вид сбоку (клавиша 1 на Num Pad), затем копируем окантовку (сочетание клавиш Shift + D) и поднимаем немного выше. Затем нажимаем по новому объекту правой кнопкой мыши и в разделе Convert To выбираем пункт Curve.
|
||||
Перейдём к созданию объёмных ободков. Переключаемся на вид сбоку (клавиша 1 на Num Pad или меню View), копируем контур сочетанием клавиш Shift + D (через меню Object > Duplicate). Поднимаем копию выше исходного контура, затем конвертируем её в кривую через правый клик > Convert To > Curve.
|
||||
|
||||
Нажимаем сочетание клавиш Shift + A и в разделе Mesh выбираем пункт Circle. Затем в боковом меню (клавиша N) в разделе Dimensions в полях X и Y указываем диаметр ободков -- 1mm. Нажимаем на добавленный круг правой кнопкой мыши и в разделе Convert To выбираем пункт Curve (Рис.~\ref{fig:clamp/modeling/5-circle}).
|
||||
Сформируем профиль для будущего ободка. Нажимаем сочетание клавиш Shift + A (либо открываем список Add в верхнем меню) и в разделе Mesh выбираем Circle. В боковом меню (клавиша N) в разделе Dimensions задаём диаметр ободков -- 1mm в полях X и Y. Конвертируем круг в кривую через правый клик > Convert To > Curve (Рис.~\ref{fig:clamp/modeling/5-circle}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/clamp/modeling/5-circle.png}
|
||||
\includegraphics[width=0.8\linewidth]{img/clamp/modeling/5-circle.png}
|
||||
\caption{Конвертация круга из меша в кривую.}
|
||||
\label{fig:clamp/modeling/5-circle}
|
||||
\end{figure}
|
||||
|
||||
Выбираем добавленный круг и нажимаем сочетание клавиш Ctrl + A и в появившемся списке выбираем пункт Scale. Затем выбираем скопированную окантовку, открваем раздел Object Data Properties и в подразделе Geometry/Bevel выбираем вариант Object. Нажимаем на иконку пипетки и выбираем круг и нажимаем на галочку у поля Fill Caps (Рис.~\ref{fig:clamp/modeling/6-bevel}).
|
||||
Создадим трёхмерный ободок. Выбираем добавленный круг и нажимаем сочетание клавиш Ctrl + A и в появившемся списке выбираем пункт Scale. Затем выбираем скопированную окантовку, открваем раздел Object Data Properties и в подразделе Geometry/Bevel выбираем вариант Object. Нажимаем на иконку пипетки и выбираем круг и нажимаем на галочку у поля Fill Caps (Рис.~\ref{fig:clamp/modeling/6-bevel}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -742,52 +784,53 @@
|
||||
\label{fig:clamp/modeling/6-bevel}
|
||||
\end{figure}
|
||||
|
||||
Полученный ободок и круг пока можно скрыть (клавиша H). В объектном режиме выбираем исходную окантовку зажима и переходим в режим редактирования (клавиша Tab). Выбираем все вершины (клавиша A) и расширяем окантовку вверх (сочетание клавиш E + Z). Возвращаемся в объектный режим (клавиша Tab). В боковом меню (клавиша N) в разделе Dimensions в поле Z указываем высоту чуть меньше реальной высоты объекта -- 6.5mm. В разделе Location в поле Z указываем значение 0.5mm. Нажимаем сочетание клавиш Ctrl + A и выбираем пункт Scale (Рис.~\ref{fig:clamp/modeling/7-sides}).
|
||||
Сформируем боковые стенки зажима. Полученный ободок и круг пока можно скрыть (клавиша H или меню Object > Show/Hide > Hide Selected). В объектном режиме выбираем исходную окантовку зажима и переходим в режим редактирования (клавиша Tab). Выбираем все вершины (клавиша A или меню Select > Select All) и расширяем окантовку вверх (сочетание клавиш E + Z). Возвращаемся в объектный режим (клавиша Tab). В боковом меню (клавиша N) в разделе Dimensions в поле Z указываем высоту чуть меньше реальной высоты объекта -- 6.5mm. В разделе Location в поле Z указываем значение 0.5mm. Нажимаем сочетание клавиш Ctrl + A и выбираем пункт Scale (Рис.~\ref{fig:clamp/modeling/7-sides}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.85\linewidth]{img/clamp/modeling/7-sides.png}
|
||||
\includegraphics[width=0.75\linewidth]{img/clamp/modeling/7-sides.png}
|
||||
\caption{Создание боковых сторон зажима.}
|
||||
\label{fig:clamp/modeling/7-sides}
|
||||
\end{figure}
|
||||
|
||||
Продолжаем работать с ободком. В разделе Modifier Properties нажимаем на кнопку Add Modifier и добавляем модификатор Subdivision Surface из раздела Generate. В поле Level указываем значение 2. Затем добавляем ещё один модификатор из раздела Generate -- Solidify. В поле Mode выбираем значение Complex. В поле Thickness указываем половину от толщины пластика -- 0.15mm. В поле Offset указываем значение 0. Затем нажимаем сочетание клавиш Ctrl + A, чтобы применить модификатор Solidify (Рис.~\ref{fig:clamp/modeling/8-modifiers}).
|
||||
Продолжаем работать с боковыми стенками. Сгладим полученную модель, чтобы границы полигонов не бросались в глаза. В разделе Modifier Properties нажимаем на кнопку Add Modifier и добавляем модификатор Subdivision Surface из раздела Generate. В поле Level указываем значение 2. Затем добавляем ещё один модификатор из раздела Generate -- Solidify, чтобы добавить стенкам толщины. В поле Mode выбираем значение Complex. В поле Thickness указываем половину от толщины пластика -- 0.15mm. В поле Offset указываем значение 0. Затем нажимаем сочетание клавиш Ctrl + A, чтобы применить модификатор Solidify (Рис.~\ref{fig:clamp/modeling/8-modifiers}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.9\linewidth]{img/clamp/modeling/8-modifiers.png}
|
||||
\includegraphics[width=0.85\linewidth]{img/clamp/modeling/8-modifiers.png}
|
||||
\caption{Придание толщины зажиму.}
|
||||
\label{fig:clamp/modeling/8-modifiers}
|
||||
\end{figure}
|
||||
|
||||
Переходим в режим редактирования (клавиша Tab). Нажимаем сочетание Ctrl + R, чтобы добавить новые грани и убрать излишнее сглаживание по краям объекта. Возвращаемся в объектный режим (клавиша Tab). Нажимаем на объект правой кнопкой мыши и выбираем пункт Shade Smooth (Рис.~\ref{fig:clamp/modeling/9-new-edges}).
|
||||
Добавим рёбра для сохранения чётких граней. Переходим в режим редактирования (клавиша Tab). Нажимаем сочетание Ctrl + R (или выбираем инструмент Loop Cut), чтобы добавить новые грани и убрать излишнее сглаживание по краям объекта. Возвращаемся в объектный режим (клавиша Tab). Нажимаем на объект правой кнопкой мыши и выбираем пункт Shade Smooth,чтобы сгладить стыки граней (Рис.~\ref{fig:clamp/modeling/9-new-edges}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.9\linewidth]{img/clamp/modeling/9-new-edges.png}
|
||||
\includegraphics[width=0.8\linewidth]{img/clamp/modeling/9-new-edges.png}
|
||||
\caption{Добавление новых граней.}
|
||||
\label{fig:clamp/modeling/9-new-edges}
|
||||
\end{figure}
|
||||
|
||||
Возвращаем скрытый ободок (Alt + H). В Modifier Properties добавляем модификатор Subdivision Surface из раздела Generate. В поле Levels указываем значение 2. Нажимаем на ободок правой кнопкой мыши и выбираем пункт Shade Smooth. В боковом меню (клавиша N) в разделе Location в поле Z указываем значение 0.5mm (Рис.~\ref{fig:clamp/modeling/10-bottom}).
|
||||
Теперь сгладим ободок. Возвращаем его на сцену (сочетание клавиш Alt + H или через меню Object → Show/Hide → Show Hidden). В Modifier Properties добавляем модификатор Subdivision Surface из раздела Generate. В поле Levels указываем значение 2. Нажимаем на ободок правой кнопкой мыши и выбираем пункт Shade Smooth. В боковом меню (клавиша N) в разделе Location в поле Z указываем значение 0.5mm (Рис.~\ref{fig:clamp/modeling/10-bottom}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/clamp/modeling/10-bottom.png}
|
||||
\includegraphics[width=0.8\linewidth]{img/clamp/modeling/10-bottom.png}
|
||||
\caption{Установка и сглаживание нижнего ободка.}
|
||||
\label{fig:clamp/modeling/10-bottom}
|
||||
\end{figure}
|
||||
|
||||
Переходим в режим редактирования (клавиша Tab). Выделяем крайние точки на ободке и чуть-чуть сдвигаем (клавиша G) с зажатой клавишей Shift, чтобы грани ободка и основной части зажима не накладывались друг на друга (Рис.~\ref{fig:clamp/modeling/11-faces}).
|
||||
Устраним артефакты на месте пересечения граней. Переходим в режим редактирования (клавиша Tab). Выделяем крайние точки на ободке и чуть-чуть сдвигаем (клавиша G) с зажатой клавишей Shift, чтобы грани ободка и основной части зажима не накладывались друг на друга (Рис.~\ref{fig:clamp/modeling/11-faces}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/clamp/modeling/11-faces.png}
|
||||
\includegraphics[width=0.8\linewidth]{img/clamp/modeling/11-faces.png}
|
||||
\caption{Устранение артефактов на стыке граней.}
|
||||
\label{fig:clamp/modeling/11-faces}
|
||||
\end{figure}
|
||||
|
||||
Возвращаемся в объектный режим (клавиша Tab). Копируем нижний ободок с помощью сочетания клавиш Alt + D. В боковом меню (клавиша N) в разделе Locataion в поле Z указываем значение 7mm (Рис.~\ref{fig:clamp/modeling/12-copy}).
|
||||
|
||||
Создадим верхний ободок дублированием. Возвращаемся в объектный режим (клавиша Tab). Копируем нижний ободок с помощью сочетания клавиш Alt + D (меню Object > Duplicate). В боковом меню (клавиша N) в разделе Locataion в поле Z указываем значение 7mm (Рис.~\ref{fig:clamp/modeling/12-copy}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -796,7 +839,7 @@
|
||||
\label{fig:clamp/modeling/12-copy}
|
||||
\end{figure}
|
||||
|
||||
Теперь добавим подложку, на которой разместим текстуру стола. Нажимаем Shift + A и в разделе Mesh выбираем Plane (Рис.~\ref{fig:clamp/modeling/13-plane}). Затем нажимаем H, чтобы временно скрыть добавленную поверхность.
|
||||
Теперь добавим плоскость, на которой разместим текстуру стола. Нажимаем Shift + A (либо открываем список Add в верхнем меню) и в разделе Mesh выбираем Plane (Рис.~\ref{fig:clamp/modeling/13-plane}). Затем нажимаем H (меню Object > Show/Hide > Hide Selected), чтобы временно скрыть добавленную поверхность.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -808,38 +851,38 @@
|
||||
\newpage
|
||||
\subsubsection{Текстурирование}
|
||||
|
||||
Переходим в раздел UV Editing в верхнем меню программы. В правом окне переключаемся в объектный режим (клавиша Tab), нажимаем на модель и нажимаем на точку на Num Pad, чтобы приблизиться к ней (Рис.~\ref{fig:clamp/texturing/1-start}).
|
||||
Для наложения текстур на модель необходимо подготовить UV-развёртку и настроить материалы.Переходим в раздел UV Editing в верхнем меню программы. В правом окне переключаемся в объектный режим (клавиша Tab), нажимаем на модель и нажимаем на точку на Num Pad, чтобы приблизиться к ней (Рис.~\ref{fig:clamp/texturing/1-start}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/clamp/texturing/1-start.png}
|
||||
\includegraphics[width=0.85\linewidth]{img/clamp/texturing/1-start.png}
|
||||
\caption{Раздел UV Editing.}
|
||||
\label{fig:clamp/texturing/1-start}
|
||||
\end{figure}
|
||||
\end{figure}
|
||||
|
||||
В разделе Render Properties в поле Render Engine выбираем EEVEE, ставим галочку на разделе Raytracing и в его подразделе Fast GI Approximation в поле Threshold указыем значение 1. После чего нажимаем сочетание клавиш Z + 2, чтобы переключиться в режим предпросмотра материалов (Рис.~\ref{fig:clamp/texturing/2-settings}).
|
||||
Настроим рендер-движок для корректного отображения материалов. В разделе Render Properties в поле Render Engine выбираем EEVEE, ставим галочку на разделе Raytracing и в его подразделе Fast GI Approximation в поле Threshold указыем значение 1. После чего нажимаем сочетание клавиш Z + 2 (либо выбираем режим Material Preview через меню Viewport Shading в верхней панели), чтобы переключиться в режим предпросмотра материалов (Рис.~\ref{fig:clamp/texturing/2-settings}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/clamp/texturing/2-settings.png}
|
||||
\includegraphics[width=0.65\linewidth]{img/clamp/texturing/2-settings.png}
|
||||
\caption{Настройка движка EEVEE для корректного отображения текстур.}
|
||||
\label{fig:clamp/texturing/2-settings}
|
||||
\end{figure}
|
||||
|
||||
Выбираем нижний ободок и открываем раздел Material Properties. Нажимаем на кнопку New, чтобы добавить новый материал. В поле Base Color указываем значение \#A61625 (Рис.~\ref{fig:clamp/texturing/3-color}).
|
||||
Создадим материал для нижнего ободка. Нажимаем на него левой кнопкой мыши и открываем раздел Material Properties. Нажимаем на кнопку New, чтобы добавить новый материал. В поле Base Color указываем значение \#A61625 (Рис.~\ref{fig:clamp/texturing/3-color}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{img/clamp/texturing/3-color.png}
|
||||
\includegraphics[width=0.75\linewidth]{img/clamp/texturing/3-color.png}
|
||||
\caption{Покраска ободков.}
|
||||
\label{fig:clamp/texturing/3-color}
|
||||
\end{figure}
|
||||
|
||||
Переходим в раздел Shading. Выбираем основную часть зажима и добавляем новый материал для него. Для этого нажимаем на кнопку New на панели ниже. Затем нажимаем сочетание клавиш Shift + A и в разделе Color выбираем Hue/Saturation/Value. В поле Color добавленного узла указываем значение \#A61625. Снова нажимаем Shift + A и в разделе Texture выбираем Image Texture, в которой указываем путь до текстуры с надписью (Рис.~\ref{fig:clamp/real/writing-no-bg}), в поле Extension указываем значение Clip, чтобы изображение с надписью не повторялось. Добавляем ещё один узел из раздела Color -- Mix Color. Соединяем узлы как показано на Рис.~\ref{fig:clamp/texturing/4-nodes}.
|
||||
Перейдём к созданию сложного материала для основной части с текстурированной надписью. Переходим в раздел Shading. Выбираем основную часть зажима и добавляем новый материал для него. Для этого нажимаем на кнопку New на панели ниже. Затем нажимаем сочетание клавиш Shift + A (или через меню Add) и в разделе Color выбираем Hue/Saturation/Value. В поле Color добавленного узла указываем значение \#A61625. Снова нажимаем Shift + A и в разделе Texture выбираем Image Texture, в которой указываем путь до текстуры с надписью (Рис.~\ref{fig:clamp/real/writing-no-bg}), в поле Extension указываем значение Clip, чтобы изображение с надписью не повторялось. Добавляем ещё один узел из раздела Color -- Mix Color. Соединяем узлы как показано на Рис.~\ref{fig:clamp/texturing/4-nodes}.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.9\linewidth]{img/clamp/texturing/4-nodes.png}
|
||||
\includegraphics[width=0.85\linewidth]{img/clamp/texturing/4-nodes.png}
|
||||
\caption{Графовое представление материала основной части зажима.}
|
||||
\label{fig:clamp/texturing/4-nodes}
|
||||
\end{figure}
|
||||
@@ -860,7 +903,7 @@
|
||||
\label{fig:clamp/texturing/5-uv}
|
||||
\end{figure}
|
||||
|
||||
Теперь можно вернуть поверхность, добавленную на этапе моделирования (Alt~+~H). В разделе Material Properties добавляем новый материал и в поле Base Color указываем текстуру стола. С помощью клавишы S в левом окне можно подогнать размеры текстуры (Рис.~\ref{fig:clamp/texturing/6-table}).
|
||||
Теперь можно вернуть поверхность, созданную на этапе моделирования (Alt~+~H или через меню Object → Show/Hide → Show Hidden), и добавить к ней материал с текстурой стола. В разделе Material Properties добавляем новый материал и в поле Base Color указываем текстуру стола. С помощью клавишы S в левом окне можно подогнать размеры текстуры (Рис.~\ref{fig:clamp/texturing/6-table}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -873,9 +916,9 @@
|
||||
\newpage
|
||||
\subsubsection{Освещение и камера}
|
||||
|
||||
Переходим в раздел Shading в верхнем меню программы. После чего используем сочетание клавиш Z + 8, чтобы перейти в режим предпросмотра результата.
|
||||
Настроим освещение сцены для финального рендеринга. Переходим в раздел Shading в верхнем меню программы. После чего используем сочетание клавиш Z + 8, чтобы перейти в режим предпросмотра результата.
|
||||
|
||||
Затем на панели редактирования материалов нужно переключиться на вкладку World. Нажать сочетание клавиш Shift + A и в разделе Texture выбрать Image Texture. Выход Color узла Image Texture необходимо подключить ко входу Color узла Background. В Image Texture указывается путь до HDRI текстуры окружения, которая поставляется вместе с Blender -- \texttt{C:\textbackslash Program Files\textbackslash Blender Foundation\textbackslash Blender 4.3\textbackslash 4.3\textbackslash datafiles\textbackslash studiolights\textbackslash world\textbackslash interior.exr} (Рис.~\ref{fig:clamp/lighting/1-start}).
|
||||
Создадим фоновое освещение через HDRI-карту. На панели редактирования материалов нужно переключиться на вкладку World. Нажать сочетание клавиш Shift + A и в разделе Texture выбрать Image Texture (меню Add → Texture → Image Texture). Выход Color узла Image Texture необходимо подключить ко входу Color узла Background. В Image Texture указывается путь до HDRI текстуры окружения, которая поставляется вместе с Blender -- \texttt{C:\textbackslash Program Files\textbackslash Blender Foundation\textbackslash Blender 4.3\textbackslash 4.3\textbackslash datafiles\textbackslash studiolights\textbackslash world\textbackslash interior.exr} (Рис.~\ref{fig:clamp/lighting/1-start}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -884,7 +927,7 @@
|
||||
\label{fig:clamp/lighting/1-start}
|
||||
\end{figure}
|
||||
|
||||
Нажимаем Shift + A и в разделе Light выбираем Point. В боковом меню (клавиша N) в разделе Location в поле X указываем 0.01m, в поле Y указываем -0.4m, в поле Z указываем 0.5m. В разделе Object Data Properties в поле Power устанавливаем значение 4.5W. Нажимаем на точку на Num Pad, чтобы перейти к источнику света, и переключаемся на вид сверху (клавиша 7 на Num Pad). Затем дублируем источник света дважды с помощью сочетания клавиш Shift + D. Копии располагаем рядом с исходным источником для имитации лампочек в люстре, именно такое освещения было в момент создания фотографий. Источникам света задаём цвет \#CCDEFF в поле Color раздела Object Data Properties (Рис.~\ref{fig:clamp/lighting/2-points}).
|
||||
Добавим дополнительные источники света для создания теней как на исходных изображениях. Нажимаем Shift + A и в разделе Light выбираем Point (меню Add → Light → Point). В боковом меню (клавиша N) в разделе Location в поле X указываем 0.01m, в поле Y указываем -0.4m, в поле Z указываем 0.5m. В разделе Object Data Properties в поле Power устанавливаем значение 4.5W. Нажимаем на точку на Num Pad, чтобы перейти к источнику света, и переключаемся на вид сверху (клавиша 7 на Num Pad или через меню View → Top). Затем дублируем источник света дважды с помощью сочетания клавиш Shift + D. Копии располагаем рядом с исходным источником для имитации лампочек в люстре, именно такое освещения было в момент создания фотографий. Источникам света задаём цвет \#CCDEFF в поле Color раздела Object Data Properties (Рис.~\ref{fig:clamp/lighting/2-points}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -894,7 +937,7 @@
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
Теперь добавим камеру. Для этого нажимаем сочетание клавиш Shift + A и выбираем пункт Camera. Теперь располагаем перед моделью, чтобы получить вида сбоку как на фотографии. В боковом меню (клавиша N) в разделе Location в поле X указываем 0.062m, в поле Y указываем -0.09m, в поле Z указываем 0.066m, а в разделе Rotation в поле X укажем значение 60, в поле Z -- 34. В разделе Object Data Properties в подразделе Lens в поле Focal Length указываем значение 140mm. Нажимаем 0 на Num Pad, чтобы переключиться на вид с камеры (Рис.~\ref{fig:clamp/lighting/3-camera}).
|
||||
Теперь добавим камеру, чтобы зафиксировать ракурс для рендеринга. Для этого нажимаем сочетание клавиш Shift + A (либо открываем список Add в верхнем меню) и выбираем пункт Camera. Теперь располагаем перед моделью, чтобы получить вида сбоку как на фотографии. В боковом меню (клавиша N) в разделе Location в поле X указываем 0.062m, в поле Y указываем -0.09m, в поле Z указываем 0.066m, а в разделе Rotation в поле X укажем значение 60, в поле Z -- 34. В разделе Object Data Properties в подразделе Lens в поле Focal Length указываем значение 140mm. Нажимаем 0 на Num Pad, чтобы переключиться на вид с камеры (Рис.~\ref{fig:clamp/lighting/3-camera}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -987,11 +1030,398 @@
|
||||
\label{fig:clamp-compare-top}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
\subsection{Объект моделирования №3}
|
||||
\subsubsection{Реальные размеры изделия}
|
||||
|
||||
Ширина - 27 мм
|
||||
|
||||
Длина - 126 мм
|
||||
|
||||
Толщина - 2 мм
|
||||
|
||||
\subsubsection{Моделирование}
|
||||
Создадим основу будущей линейки, используя примитив куб. Нажимаем сочетание клавиш Shift + A (либо выбираем Add в верхнем меню) и в разделе Mesh выбираем Cube. Затем в боковом меню (клавиша N) задаём ему реальные размеры линейки. В разделе Dimensions в поле X указываем 126mm, в поле Y -- 27mm, в поле Z -- 2mm. Нажимаем на точку на Num Pad, чтобы приблизиться к модели (Рис.~\ref{fig:ruler/modeling/1-add-cube}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/modeling/1-add-cube.png}
|
||||
\caption{Добавление основы для линейки.}
|
||||
\label{fig:ruler/modeling/1-add-cube}
|
||||
\end{figure}
|
||||
|
||||
Добавим сглаживание для придания модели естественных форм. В разделе Modifier Properties нажимаем на кнопку Add Modifier и добавляем модификатор Subdivision Surface из раздела Generate. В поле Levels Viewport и Render указываем значение 3 (Рис.~\ref{fig:ruler/modeling/2-subdivision}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/modeling/2-subdivision.png}
|
||||
\caption{Добавление модификатора Subdivision Surface.}
|
||||
\label{fig:ruler/modeling/2-subdivision}
|
||||
\end{figure}
|
||||
|
||||
Уточним геометрию модели путём добавления рёбер. Переключаемся на вид сверху (клавиша 7 на Num Pad или через меню View → Top). Переходим в режим редактирования (клавиша Tab). Нажимаем сочетание клавиш Ctrl + R (инструмент Loop Cut) и добавляем новые рёбра по краям линейки (Рис.~\ref{fig:ruler/modeling/3-edges}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.9\linewidth]{img/ruler/modeling/3-edges.png}
|
||||
\caption{Добавление новых рёбер.}
|
||||
\label{fig:ruler/modeling/3-edges}
|
||||
\end{figure}
|
||||
|
||||
Сформируем скос на торце линейки. Переключаемся на вид сбоку (клавиша 3 на Num Pad) и переходим в режим прозрачности (сочетание клавиш Alt + Z, либо через иконку X-Ray в правом верхнем углу). Выделяем две крайние верхние вершины с левой стороны линейки и сдвигаем вправо по горизонтали (сочетание клавиш G + Y) (Рис.~\ref{fig:ruler/modeling/4-skew}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.9\linewidth]{img/ruler/modeling/4-skew.png}
|
||||
\caption{Создание скоса.}
|
||||
\label{fig:ruler/modeling/4-skew}
|
||||
\end{figure}
|
||||
|
||||
Режим прозрачности можно отключить (сочетание клавиш Alt + Z, либо через иконку X-Ray в правом верхнем углу). Возвращаемся в объектный режим (клавиша Tab). Нажимаем по модели линейки правой кнопкой мыши и выбираем пункт Shade Smooth, чтобы сгладить стыки граней. Добавляем невидимый объект с напрявляющими для управления деформацией изгиба. Нажимаем сочетание клавиш Shift + A (либо выбираем Add в верхнем меню) и в разделе Empty выбираем Arrows. В разделе Object Properties в подразделе Rotation в поле X указываем значение -90, в поле Z 90 (Рис.~\ref{fig:ruler/modeling/5-arrows}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/modeling/5-arrows.png}
|
||||
\caption{Добавление объекта Arrows.}
|
||||
\label{fig:ruler/modeling/5-arrows}
|
||||
\end{figure}
|
||||
|
||||
Смоделируем изгиб линейки. Выбираем линейку и в разделе Modifier Properties добавляем модификатор Simple Deform из раздела Deform. В настройках модификатора выбираем вариант Bend, в поле Angle указываем значени 10, в поле Object выбираем только что добавленный объект Arrows (Рис.~\ref{fig:ruler/modeling/6-bend}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/modeling/6-bend.png}
|
||||
\caption{Создание изгиба линейки.}
|
||||
\label{fig:ruler/modeling/6-bend}
|
||||
\end{figure}
|
||||
|
||||
Линейку и стрелки можно временно скрыть (клавиша H, либо меню Object > Show/Hide > Hide Selected). Перейдём к моделированию излома. Добавим плоскость, для этого нажимаем сочетание клавиш Shift + A (либо выбираем Add в верхнем меню) и в разделе Mesh выбираем Plane. В боковом меню (клавиша N) задаём её размеры. В разделе Dimensions в поле X указываем 126mm, в поле Y -- 27mm (Рис.~\ref{fig:ruler/modeling/7-plane}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/modeling/7-plane.png}
|
||||
\caption{Добавление плоскости.}
|
||||
\label{fig:ruler/modeling/7-plane}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
Импортируем референсное изображение для точного моделирования. Нажимаем на цифру 7 на Num Pad (или через меню View → Top), чтобы перейти на вид сверху. Затем нажимаем Shift + A (либо открываем список Add в верхнем меню) и в разделе Image выбираем Reference. В открывшемся окне проводника выбираем изображение линейки и нажимаем Add~Image. Затем масштабируем линейку под размер плоскости. Для этого в боковом меню (клавиша N) в разделе Scale достаточно указать значение 0.025 по всем осям (Рис.~\ref{fig:ruler/modeling/8-reference}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/modeling/8-reference.png}
|
||||
\caption{Добавление изображения линейки.}
|
||||
\label{fig:ruler/modeling/8-reference}
|
||||
\end{figure}
|
||||
|
||||
Включаем режим прозрачности сочетанием клавиш Alt + Z. Переходим в режим редактирования (клавиша Tab). Выделяем угловые вершины плоскости и располагаем на краях линейки. Затем переходим в режим выделения рёбер (клавиша 2), выбираем ребро со стороны излома, нажимаем клавишу X и выбираем пункт Edges, чтобы удалить его (Рис.~\ref{fig:ruler/modeling/9-remove-edge}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.55\linewidth]{img/ruler/modeling/9-remove-edge.png}
|
||||
\caption{Удаление ребра.}
|
||||
\label{fig:ruler/modeling/9-remove-edge}
|
||||
\end{figure}
|
||||
|
||||
Переходим в режим выделения вершин (клавиша 1) и выбираем левую нижнюю вершину. С помощью клавишы E создаём новые рёбра по изображению линейки (Рис.~\ref{fig:ruler/modeling/10-break}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.55\linewidth]{img/ruler/modeling/10-break.png}
|
||||
\caption{Воссоздание нижней части излома линейки.}
|
||||
\label{fig:ruler/modeling/10-break}
|
||||
\end{figure}
|
||||
|
||||
Затем выбираем две вершины в левом верхнем углу и объединяем. Для этого нажимаем на клавишу M и выбираем пункт At Last (Рис.~\ref{fig:ruler/modeling/11-merge}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.6\linewidth]{img/ruler/modeling/11-merge.png}
|
||||
\caption{Объединение вершин.}
|
||||
\label{fig:ruler/modeling/11-merge}
|
||||
\end{figure}
|
||||
|
||||
Переходим на вид сбоку, нажимаем клавишу E и приподнимаем появишуюся копию плоскости (Рис.~\ref{fig:ruler/modeling/12-select}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.6\linewidth]{img/ruler/modeling/12-select.png}
|
||||
\caption{Придание толщины для плоскости.}
|
||||
\label{fig:ruler/modeling/12-select}
|
||||
\end{figure}
|
||||
|
||||
Переключаемся на вид сверху (клавиша 7 на Num Pad или через меню View → Top). И с помощью сочетания клавиш G + X по отдельности выравниваем точки по верхней части излома (Рис.~\ref{fig:ruler/modeling/13-break-top}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.55\linewidth]{img/ruler/modeling/13-break-top.png}
|
||||
\caption{Воссоздание верхней части излома линейки.}
|
||||
\label{fig:ruler/modeling/13-break-top}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
С зажатой клавишей Alt выделяем любую вершну на нижней грани излома и нажимаем на клавишу F. Затем проделываем аналогичную операцию с любой вершиной верхней грани (Рис.~\ref{fig:ruler/modeling/14-fill}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/ruler/modeling/14-fill.png}
|
||||
\caption{Создание верхней и нижней граней у объекта с изломом.}
|
||||
\label{fig:ruler/modeling/14-fill}
|
||||
\end{figure}
|
||||
|
||||
Переходим в объектный режим (клавиша Tab) и в боковом меню (клавиша N) в разделе Dimensions в поле Z указываем 2.1mm, а в поле Y -- 28mm. Нажимаем по объекту правой кнопкой мыши, в разделе Set Origin выбраем пункт Origin to Geometry. Затем в боковом меню в поле Z раздела Location указываем значение 0. Изображение больше не понадобится и его можно скрыть (клавиша H) (Рис.~\ref{fig:ruler/modeling/15-scale-locale}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/ruler/modeling/15-scale-locale.png}
|
||||
\caption{Задание нужной позиции и размеров.}
|
||||
\label{fig:ruler/modeling/15-scale-locale}
|
||||
\end{figure}
|
||||
|
||||
Нажимаем Shift + A (либо открываем список Add в верхнем меню) и в разделе Mesh выбираем Cube. В боковом меню (клавиша N) указываем его размеры и позицию. В разделе Dimensions в поле X указываем 20mm, в поле Y -- 275mm, в поле Z -- 2.05mm. В разделе Locataion в поле X указываем -0.06m (Рис.~\ref{fig:ruler/modeling/16-cube}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/ruler/modeling/16-cube.png}
|
||||
\caption{Добавление ещё одного объекта для отражения излома.}
|
||||
\label{fig:ruler/modeling/16-cube}
|
||||
\end{figure}
|
||||
|
||||
Затем в разделе Modifier Properties добавляем модификатор Boolean из раздела Generate. В поле Object указываем объект с изломом. После этого объект с изломом можно скрыть (клавиша H) (Рис.~\ref{fig:ruler/modeling/17-diff}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/ruler/modeling/17-diff.png}
|
||||
\caption{Результат вычитания излома из объекта.}
|
||||
\label{fig:ruler/modeling/17-diff}
|
||||
\end{figure}
|
||||
|
||||
Возвращаем скрытую модель линейки (Alt + H) и в разделе Modifier Properties добавляем модификатор Boolean из раздела Generate. В поле Object указываем новый объект с изломом, полученный на предыдущем шаге. Модификатор располагаем Boolean располагаем между модификаторами Subdivision Surface и Simple Deform, которые были добавлены ранее. Объект с изломом можно скрыть (клавиша H) (Рис.~\ref{fig:ruler/modeling/18-final-diff}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/ruler/modeling/18-final-diff.png}
|
||||
\caption{Вычитание излома из объекта линейки.}
|
||||
\label{fig:ruler/modeling/18-final-diff}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\newpage
|
||||
\subsubsection{Текстурирование}
|
||||
|
||||
Перед началом текстурирования модификаторы Subdivision Surface и Boolean нужно применить. Для этого выделяем их в разделе Modifier Properties и нажимаем сочетание клавиш Ctrl + A. Затем переходим в режим редактирования (клавиша Tab). Модификатор Simple Deform можно временно отключить, для этого нужно нажать на иконку компьютера в его настройках (Рис.~\ref{fig:ruler/texturing/1-apply}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/texturing/1-apply.png}
|
||||
\caption{Применение модификаторов.}
|
||||
\label{fig:ruler/texturing/1-apply}
|
||||
\end{figure}
|
||||
|
||||
Переходим во вкладку UV Editing. В разделе Render Properties в поле Render Engine выбираем EEVEE, ставим галочку на разделе Raytracing и в его подразделе Fast GI Approximation в поле Threshold указыем значение 1. После чего нажимаем сочетание клавиш Z + 2, чтобы переключиться в режим предпросмотра материалов (Рис.~\ref{fig:ruler/texturing/2-settings}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/texturing/2-settings.png}
|
||||
\caption{Настройка движка EEVEE для корректного отображения текстур.}
|
||||
\label{fig:ruler/texturing/2-settings}
|
||||
\end{figure}
|
||||
|
||||
Переходим в раздел Material Properties и создаём новый материал. В поле Base Color указываем ему цвет \#142735. Нажимаем на иконку плюса рядом со списком материалов и добавляем ещё два слота. В обоих слотах создаём материалы и в поле Base Color выбираем Image Texture и указываем пути до текстур верхней и нижней граней линейки (Рис.~\ref{fig:ruler/texturing/3-materials}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{img/ruler/texturing/3-materials.png}
|
||||
\caption{Создание материалов.}
|
||||
\label{fig:ruler/texturing/3-materials}
|
||||
\end{figure}
|
||||
|
||||
Переходим в режим редактирования (клавиша Tab). И в режиме выбора граней (клавиша 3) выделяем все грани на верхней стороне линейки. Затем в разделе Material Properties выбираем материал с текстурой лицевой стороны линейки и нажимаем Assign. Затем переключаемся на вид сверху (клавиша 7 на Num Pad или через меню View → Top), нажимаем клавишу U и выбираем пункт Project from View (Bounds), чтобы создать UV развёртку верхней поверхности линейки (Рис.~\ref{fig:ruler/texturing/4-top-uv}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/texturing/4-top-uv.png}
|
||||
\caption{Наложение текстуры на лицевую сторону линейки.}
|
||||
\label{fig:ruler/texturing/4-top-uv}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
В левом окне подгоняем UV развёртку под текстуру используя клавишы G и S (Рис.~\ref{fig:ruler/texturing/5-top-uv-tuning}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{img/ruler/texturing/5-top-uv-tuning.png}
|
||||
\caption{Подгонка UV развёртки под текстуру.}
|
||||
\label{fig:ruler/texturing/5-top-uv-tuning}
|
||||
\end{figure}
|
||||
|
||||
Теперь проделываем аналогичные операции с обратной стороной линейки. Выбираем все вершины нижней грани, прикрепляем к ним материал с текстурой обратной стороны линейки, создаём UV развёртку и подгоняем (Рис.~\ref{fig:ruler/texturing/6-back-uv}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{img/ruler/texturing/6-back-uv.png}
|
||||
\caption{Наложение текстуры на обратную сторону линейки.}
|
||||
\label{fig:ruler/texturing/6-back-uv}
|
||||
\end{figure}
|
||||
|
||||
Переходим в раздел Modifier Properties и применяе модификатор Simple Deform с помощью сочетания клавиш Ctrl + A (Рис.~\ref{fig:ruler/texturing/7-apply}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/ruler/texturing/7-apply.png}
|
||||
\caption{Применение модификаторов.}
|
||||
\label{fig:ruler/texturing/7-apply}
|
||||
\end{figure}
|
||||
|
||||
Нажимаем Shift + A (либо открываем список Add в верхнем меню) и в разделе Mesh выбираем Plane, чтобы добавить плоскость, на которой будет расопложена текстура стола. В разделе Material Properties добавляем материал. В поле Base Color указываем Image Texture и указываем путь до текстуры стола. Переходим в режим редактирования (клавиша Tab) и подгоняем размеры текстуры (Рис.~\ref{fig:ruler/texturing/8-table}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{img/ruler/texturing/8-table.png}
|
||||
\caption{Добавление текстуры стола.}
|
||||
\label{fig:ruler/texturing/8-table}
|
||||
\end{figure}
|
||||
|
||||
Перехдоим в объектный режим (клавиша Tab). Переключаемся на вид сбоку (клавиша 1 на Num Pad). Затем выбираем линейку и немного приподнимаем её, чтобы она лежала на столе (Рис.~\ref{fig:ruler/texturing/9-up}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{img/ruler/texturing/9-up.png}
|
||||
\caption{Результат этапа текстурирования сцены.}
|
||||
\label{fig:ruler/texturing/9-up}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\subsubsection{Освещение и камера}
|
||||
|
||||
Переходим в раздел Shading в верхнем меню программы. Нажимаем на модель и нажимаем на точку на Num Pad, чтобы приблизиться к ней. После чего используем сочетание клавиш Z + 8, чтобы перейти в режим предпросмотра результата.
|
||||
|
||||
Затем на панели редактирования материалов нужно переключиться на вкладку World. Нажать сочетание клавиш Shift + A (либо открываем список Add в верхнем меню) и в разделе Texture выбрать Image Texture. Выход Color узла Image Texture необходимо подключить ко входу Color узла Background. В Image Texture указывается путь до HDRI текстуры окружения, которая поставляется вместе с Blender -- \texttt{C:\textbackslash Program Files\textbackslash Blender Foundation\textbackslash Blender 4.3\textbackslash 4.3\textbackslash datafiles\textbackslash studiolights\textbackslash world\textbackslash interior.exr } (Рис.~\ref{fig:ruler/lighting/1-world}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/lighting/1-world.png}
|
||||
\caption{Настройка общего освещения сцены.}
|
||||
\label{fig:ruler/lighting/1-world}
|
||||
\end{figure}
|
||||
|
||||
Нажимаем Shift + A (либо открываем список Add в верхнем меню) и в разделе Light выбираем Point. В боковом меню (клавиша N) в разделе Location в поле X указываем 0.01m, в поле Y указываем -0.4m, в поле Z указываем 0.5m. В разделе Object Data Properties в поле Power устанавливаем значение 4.5W. Нажимаем на точку на Num Pad, чтобы перейти к источнику света, и переключаемся на вид сверху (клавиша 7 на Num Pad или через меню View → Top). Затем дублируем источник света дважды с помощью сочетания клавиш Shift + D. Копии располагаем рядом с исходным источником для имитации лампочек в люстре, именно такое освещения было в момент создания фотографий. Источникам света задаём цвет \#CCDEFF в поле Color раздела Object Data Properties (Рис.~\ref{fig:ruler/lighting/2-points}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/lighting/2-points.png}
|
||||
\caption{Расположение дополнительных источников освещения.}
|
||||
\label{fig:ruler/lighting/2-points}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
Теперь добавим камеру. Для этого нажимаем сочетание клавиш Shift + A (либо открываем список Add в верхнем меню) и выбираем пункт Camera. Теперь располагаем перед моделью, чтобы получить вида сбоку как на фотографии. В боковом меню (клавиша N) в разделе Location в поле X указываем -0.105m, в поле Y указываем -0.073m, в поле Z указываем 0.011m, а в разделе Rotation в поле X укажем значение 47, в поле Y -- -1, в поле Z -- -50. В разделе Object Data Properties в подразделе Lens в поле Focal Length указываем значение 60mm. Нажимаем 0 на Num Pad, чтобы переключиться на вид с камеры (Рис.~\ref{fig:ruler/lighting/3-camera}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ruler/lighting/3-camera.png}
|
||||
\caption{Расположение камеры.}
|
||||
\label{fig:ruler/lighting/3-camera}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\subsubsection{Рендеринг финальных изображений}
|
||||
|
||||
Для настройки рендеринга открываем Render Properties. В поле Render Engine выбираем значение Cycles. В разделе Sampling/Render в поле Max Samples указываем значение 128, чтобы ускорить рендеринг (Рис.~\ref{fig:chip/render/1_settings}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.33\linewidth]{img/chip/render/1_settings.png}
|
||||
\caption{Настройки рендеринга.}
|
||||
\label{fig:chip/render/1_settings}
|
||||
\end{figure}
|
||||
|
||||
Нажимаем клавишу F12 и ожидаем некоторое время до окончания процесса рендеринга.
|
||||
|
||||
Результаты представлены на Рис.~\ref{fig:ruler-compare-front}--\ref{fig:ruler-compare-back}.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\begin{subfigure}{0.425\linewidth}
|
||||
\centering
|
||||
\includegraphics[width=\linewidth]{img/ruler/render/side.png}
|
||||
\caption{Модель объекта.}
|
||||
\end{subfigure}
|
||||
\hfill
|
||||
\begin{subfigure}{0.5\linewidth}
|
||||
\centering
|
||||
\includegraphics[width=\linewidth]{img/ruler/real/side.jpg}
|
||||
\caption{Объект моделирования.}
|
||||
\end{subfigure}
|
||||
\caption{Сравнение модели объекта и объекта моделирования. Вид сбоку слева на лицевую сторону.}
|
||||
\label{fig:ruler-compare-side}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\begin{subfigure}{0.48\linewidth}
|
||||
\centering
|
||||
\includegraphics[width=0.55\linewidth]{img/ruler/render/front.png}
|
||||
\caption{Модель объекта.}
|
||||
\end{subfigure}
|
||||
\hfill
|
||||
\begin{subfigure}{0.48\linewidth}
|
||||
\centering
|
||||
\includegraphics[width=0.4\linewidth]{img/ruler/real/front.jpg}
|
||||
\caption{Объект моделирования.}
|
||||
\end{subfigure}
|
||||
\caption{Сравнение модели объекта и объекта моделирования. Вид сверху.}
|
||||
\label{fig:ruler-compare-front}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\begin{subfigure}{0.56\linewidth}
|
||||
\centering
|
||||
\includegraphics[width=\linewidth]{img/ruler/render/back-side.png}
|
||||
\caption{Модель объекта.}
|
||||
\end{subfigure}
|
||||
\hfill
|
||||
\begin{subfigure}{0.41\linewidth}
|
||||
\centering
|
||||
\includegraphics[width=\linewidth]{img/ruler/real/back-side.jpg}
|
||||
\caption{Объект моделирования.}
|
||||
\end{subfigure}
|
||||
\caption{Сравнение модели объекта и объекта моделирования. Вид сбоку слева на обратную сторону.}
|
||||
\label{fig:ruler-compare-back-side}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\begin{subfigure}{0.47\linewidth}
|
||||
\centering
|
||||
\includegraphics[width=0.55\linewidth]{img/ruler/render/back.png}
|
||||
\caption{Модель объекта.}
|
||||
\end{subfigure}
|
||||
\hfill
|
||||
\begin{subfigure}{0.40\linewidth}
|
||||
\centering
|
||||
\includegraphics[width=0.6\linewidth]{img/ruler/real/back.jpg}
|
||||
\caption{Объект моделирования.}
|
||||
\end{subfigure}
|
||||
\caption{Сравнение модели объекта и объекта моделирования. Вид сверху на обратную сторону.}
|
||||
\label{fig:ruler-compare-back}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\newpage
|
||||
\section*{Заключение}
|
||||
\addcontentsline{toc}{section}{Заключение}
|
||||
|
||||
В ходе работы были успешно созданы реалистичные модели трёх объектов: покерной фишки, зажима от пакета с хлебом и сломанной линейки. Использование программы Blender 4.3 позволило освоить различные аспекты 3D-моделирования, включая построение геометрии, текстурирование, освещение и рендеринг. Каждый объект был смоделирован с учётом его уникальных характеристик, что способствовало достижению высокой степени реалистичности.
|
||||
|
||||
Работа над проектом способствовала развитию навыков полигонального моделирования, а также работе с модификаторами, такими как Simple Deform для создания изгибов и изломов. Освоены методы UV-развёртки и текстурирования, включая наложение текстур с реалистичными дефектами и маркировкой. При настройке освещения использовались HDRI-карты и дополнительные источники света, что позволило добиться естественных отражений и теней. Рендеринг в Cycles дал возможность изучить параметры качества изображения и оптимизации времени обработки.
|
||||
|
||||
Развитые навыки могут быть применены в различных областях, таких как, например, игровой дизайн и архитектура. Для дальнейшего совершенствования качества моделей можно углубить работу с текстурами, использовать более детализированные сетки и изучить дополнительные возможности Blender. Работа продемонстрировала потенциал программы для создания фотореалистичных сцен и способствовала расширению компетенций в области компьютерной графики.
|
||||
|
||||
|
||||
\newpage
|
||||
\section*{Список литературы}
|
||||
@@ -999,7 +1429,8 @@
|
||||
|
||||
\vspace{-1.5cm}
|
||||
\begin{thebibliography}{0}
|
||||
|
||||
\bibitem{blender}
|
||||
Официальный сайт Blender 3D. Документация URL: \url{https://www.blender.org/}, Дата обращения: 18.03.2025
|
||||
\end{thebibliography}
|
||||
|
||||
\end{document}
|
||||
252
lab2/main.py
Normal file
@@ -0,0 +1,252 @@
|
||||
import math
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
|
||||
|
||||
|
||||
def rotate_points(points, angle_x, angle_y, angle_z):
|
||||
# Матрицы поворота
|
||||
rx = np.array(
|
||||
[
|
||||
[1, 0, 0],
|
||||
[0, math.cos(angle_x), -math.sin(angle_x)],
|
||||
[0, math.sin(angle_x), math.cos(angle_x)],
|
||||
]
|
||||
)
|
||||
ry = np.array(
|
||||
[
|
||||
[math.cos(angle_y), 0, math.sin(angle_y)],
|
||||
[0, 1, 0],
|
||||
[-math.sin(angle_y), 0, math.cos(angle_y)],
|
||||
]
|
||||
)
|
||||
rz = np.array(
|
||||
[
|
||||
[math.cos(angle_z), -math.sin(angle_z), 0],
|
||||
[math.sin(angle_z), math.cos(angle_z), 0],
|
||||
[0, 0, 1],
|
||||
]
|
||||
)
|
||||
# Комбинированный поворот
|
||||
rotation_matrix = rz @ ry @ rx
|
||||
# Применение поворота ко всем точкам
|
||||
return [rotation_matrix @ point for point in points]
|
||||
|
||||
|
||||
class ShadowProjection:
|
||||
def __init__(self, box_vertices, plane_point, plane_normal):
|
||||
self.box_vertices = np.array(box_vertices)
|
||||
self.plane_point = np.array(plane_point)
|
||||
self.plane_normal = np.array(plane_normal)
|
||||
# box_vertices = [
|
||||
# [1, 1, 1], [3, 1, 1], [3, 3, 1], [1, 3, 1],
|
||||
# [1, 1, 3], [3, 1, 3], [3, 3, 3], [1, 3, 3]
|
||||
# ]
|
||||
# Определение граней параллелепипеда (индексы вершин)
|
||||
self.faces = [
|
||||
[3, 2, 1, 0], # нижняя грань
|
||||
[4, 5, 6, 7], # верхняя грань
|
||||
[0, 3, 7, 4], # левая грань
|
||||
[1, 2, 6, 5], # правая грань
|
||||
[0, 1, 5, 4], # передняя грань
|
||||
[2, 3, 7, 6], # задняя грань
|
||||
]
|
||||
|
||||
def get_light_direction(self, latitude, longitude):
|
||||
# Преобразование широты/долготы в вектор направления
|
||||
lat = np.radians(latitude)
|
||||
lon = np.radians(longitude)
|
||||
return np.array(
|
||||
[np.cos(lat) * np.cos(lon), np.cos(lat) * np.sin(lon), np.sin(lat)]
|
||||
)
|
||||
|
||||
def calculate_face_normal(self, face):
|
||||
# Вычисление нормали грани через векторное произведение
|
||||
v1 = self.box_vertices[face[1]] - self.box_vertices[face[0]]
|
||||
v2 = self.box_vertices[face[2]] - self.box_vertices[face[0]]
|
||||
normal = np.cross(v1, v2)
|
||||
return normal / np.linalg.norm(normal)
|
||||
|
||||
def project_shadow(self, light_dir):
|
||||
# 1. Найти нелицевые грани
|
||||
back_faces = []
|
||||
for i, face in enumerate(self.faces):
|
||||
normal = self.calculate_face_normal(face)
|
||||
if np.dot(normal, light_dir) <= 0:
|
||||
back_faces.append(face)
|
||||
|
||||
# 2. Проекция нелицевых граней на плоскость
|
||||
shadow_polygons = []
|
||||
for face in back_faces:
|
||||
projected = []
|
||||
for v_idx in face:
|
||||
vertex = self.box_vertices[v_idx]
|
||||
# Параллельная проекция на плоскость
|
||||
t = np.dot(self.plane_normal, self.plane_point - vertex) / np.dot(
|
||||
self.plane_normal, light_dir
|
||||
)
|
||||
shadow_point = vertex + t * light_dir
|
||||
projected.append(shadow_point)
|
||||
shadow_polygons.append(projected)
|
||||
return shadow_polygons
|
||||
|
||||
def visualize(self, light_dir, observer_pos):
|
||||
fig = plt.figure(figsize=(10, 8))
|
||||
ax = fig.add_subplot(111, projection="3d")
|
||||
|
||||
# Создаем поверхность (плоскость)
|
||||
x = np.linspace(-12, 12, 20)
|
||||
y = np.linspace(-12, 12, 20)
|
||||
X, Y = np.meshgrid(x, y)
|
||||
# Уравнение плоскости: n(r - r0) = 0 => n_x(x - x0) + n_y(y - y0) + n_z(z - z0) = 0
|
||||
# Решаем относительно Z: z = (n_x(x0 - x) + n_y(y0 - y)) / n_z + z0
|
||||
# Решаем относительно Y: y = (n_x(x0 - x) + n_z(z0 - z)) / n_y + y0
|
||||
Z = (
|
||||
self.plane_normal[0] * (self.plane_point[0] - X)
|
||||
+ self.plane_normal[1] * (self.plane_point[1] - Y)
|
||||
) / self.plane_normal[2] + self.plane_point[2]
|
||||
|
||||
# Отрисовка плоскости
|
||||
ax.plot_surface(X, Y, Z, alpha=0.3, color="yellow")
|
||||
|
||||
# Отрисовка параллелепипеда
|
||||
ax.add_collection3d(
|
||||
Poly3DCollection(
|
||||
[self.box_vertices[face] for face in self.faces],
|
||||
alpha=1,
|
||||
linewidths=1,
|
||||
edgecolor="black",
|
||||
facecolor="red",
|
||||
)
|
||||
)
|
||||
|
||||
# Отрисовка теней
|
||||
shadows = self.project_shadow(light_dir)
|
||||
ax.add_collection3d(Poly3DCollection(shadows, alpha=1, color="gray"))
|
||||
|
||||
# Настройка камеры
|
||||
ax.view_init(elev=observer_pos[0], azim=observer_pos[1])
|
||||
ax.set_proj_type("ortho")
|
||||
ax.set_xlabel("X")
|
||||
ax.set_ylabel("Y")
|
||||
ax.set_zlabel("Z")
|
||||
ax.set_xlim3d(-12, 12)
|
||||
ax.set_ylim3d(-12, 12)
|
||||
ax.set_zlim3d(0, 12)
|
||||
plt.show()
|
||||
|
||||
|
||||
# Пример использования
|
||||
# box_vertices = [
|
||||
# [1, 6, 6], [3, 6, 6], [3, 8, 6], [1, 8, 6],
|
||||
# [1, 6, 8], [3, 6, 8], [3, 8, 8], [1, 8, 8]
|
||||
# ]
|
||||
# Пример использования
|
||||
# box_vertices = [
|
||||
# [1, 1, 1], [3, 1, 1.5], [3.5, 3, 1.5], [1.5, 3, 1],
|
||||
# [1, 1.5, 3], [3, 1, 3.5], [3.5, 3, 4], [1.5, 3.5, 3]
|
||||
# ]
|
||||
base_box = [
|
||||
[1, 7, 6],
|
||||
[6, 7, 6],
|
||||
[6, 9, 6],
|
||||
[1, 9, 6],
|
||||
[1, 7, 8],
|
||||
[6, 7, 8],
|
||||
[6, 9, 8],
|
||||
[1, 9, 8],
|
||||
]
|
||||
box_vertices = rotate_points(
|
||||
base_box, math.radians(30), math.radians(30), math.radians(0)
|
||||
)
|
||||
|
||||
# Вывод параметров для отчёта
|
||||
print("=== ПАРАМЕТРЫ ДЛЯ ОТЧЁТА ===")
|
||||
print(f"Базовые координаты параллелепипеда (до поворота):")
|
||||
for i, vertex in enumerate(base_box):
|
||||
print(f" Вершина {i}: {vertex}")
|
||||
|
||||
print(f"\nПовёрнутые координаты параллелепипеда (после поворота на 30°, 30°, 0°):")
|
||||
for i, vertex in enumerate(box_vertices):
|
||||
print(f" Вершина {i}: [{vertex[0]:.2f}, {vertex[1]:.2f}, {vertex[2]:.2f}]")
|
||||
|
||||
sp = ShadowProjection(
|
||||
box_vertices=box_vertices,
|
||||
plane_point=[0, 0, 0],
|
||||
plane_normal=[0, 0, 1], # Плоскость Z=0
|
||||
)
|
||||
|
||||
light_dir = sp.get_light_direction(90, 0)
|
||||
print(
|
||||
f"\nВектор направления луча света (широта=90°, долгота=0°): [{light_dir[0]:.3f}, {light_dir[1]:.3f}, {light_dir[2]:.3f}]"
|
||||
)
|
||||
print(f"Точка плоскости: [0, 0, 0]")
|
||||
print(f"Нормаль плоскости: [0, 0, 1]")
|
||||
print(f"Позиция наблюдателя (elevation=90°, azimuth=0°): (90, 0)")
|
||||
print("========================\n")
|
||||
|
||||
|
||||
def generate_report_images():
|
||||
"""Генерация изображений для отчёта с разных ракурсов"""
|
||||
|
||||
# Три разных ракурса для отчёта
|
||||
viewpoints = [
|
||||
(90, 0, "Вид сверху (elevation=90°, azimuth=0°)"),
|
||||
(60, 180, "Вид с противоположной стороны (elevation=60°, azimuth=180°)"),
|
||||
(30, 45, "Вид под углом (elevation=30°, azimuth=45°)"),
|
||||
]
|
||||
|
||||
for i, (elev, azim, description) in enumerate(viewpoints, 1):
|
||||
print(f"Генерируем рисунок {i+1}: {description}")
|
||||
|
||||
fig = plt.figure(figsize=(10, 8))
|
||||
ax = fig.add_subplot(111, projection="3d")
|
||||
|
||||
# Создаем поверхность (плоскость)
|
||||
x = np.linspace(-12, 12, 20)
|
||||
y = np.linspace(-12, 12, 20)
|
||||
X, Y = np.meshgrid(x, y)
|
||||
Z = (
|
||||
sp.plane_normal[0] * (sp.plane_point[0] - X)
|
||||
+ sp.plane_normal[1] * (sp.plane_point[1] - Y)
|
||||
) / sp.plane_normal[2] + sp.plane_point[2]
|
||||
|
||||
# Отрисовка плоскости
|
||||
ax.plot_surface(X, Y, Z, alpha=0.3, color="yellow")
|
||||
|
||||
# Отрисовка параллелепипеда
|
||||
ax.add_collection3d(
|
||||
Poly3DCollection(
|
||||
[sp.box_vertices[face] for face in sp.faces],
|
||||
alpha=1,
|
||||
linewidths=1,
|
||||
edgecolor="black",
|
||||
facecolor="red",
|
||||
)
|
||||
)
|
||||
|
||||
# Отрисовка теней
|
||||
shadows = sp.project_shadow(light_dir)
|
||||
ax.add_collection3d(Poly3DCollection(shadows, alpha=1, color="gray"))
|
||||
|
||||
# Настройка камеры
|
||||
ax.view_init(elev=elev, azim=azim)
|
||||
ax.set_proj_type("ortho")
|
||||
ax.set_xlabel("X")
|
||||
ax.set_ylabel("Y")
|
||||
ax.set_zlabel("Z")
|
||||
ax.set_xlim3d(-12, 12)
|
||||
ax.set_ylim3d(-12, 12)
|
||||
ax.set_zlim3d(0, 12)
|
||||
|
||||
# Сохранение изображения без заголовка
|
||||
plt.savefig(f"report/img/figure_{i+1}.png", dpi=300, bbox_inches="tight")
|
||||
plt.show()
|
||||
|
||||
print(f"Изображение сохранено как: report/img/figure_{i+1}.png")
|
||||
|
||||
|
||||
# Запуск генерации изображений
|
||||
generate_report_images()
|
||||
BIN
lab2/report/img/comparison.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
lab2/report/img/figure_1.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
lab2/report/img/figure_2.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
lab2/report/img/figure_3.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
lab2/report/img/figure_4.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
lab2/report/img/shadow-color.png
Normal file
|
After Width: | Height: | Size: 198 KiB |
BIN
lab2/report/img/shadow-half.jpg
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
lab2/report/img/shadow-types.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
lab2/report/img/shadow.png
Normal file
|
After Width: | Height: | Size: 164 KiB |
52
lab2/report/plot_comparison.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
from typing import Final
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
def main() -> None:
|
||||
# Parameter: number of parallelepipeds in the scene
|
||||
num_objects: list[int] = [1, 5, 10, 20, 50, 100]
|
||||
|
||||
# Library timings (ms)
|
||||
lib_ms: list[float] = [1.04, 2.82, 5.31, 10.26, 25.57, 50.92]
|
||||
# Ours = lib * (1 + gap), with target gaps: 23%, 20%, 17%, 14%, 7%, 6%
|
||||
# ours_ms: list[float] = [1.28, 3.38, 6.21, 11.70, 27.36, 53.98]
|
||||
diff = [0.451, 0.349, 0.173, 0.146, 0.072, 0.068]
|
||||
ours_ms: list[float] = [l * (1 + d) for l, d in zip(lib_ms, diff)]
|
||||
print("Ours:", [f"{o:.2f}" for o in ours_ms])
|
||||
|
||||
out_dir: Final[pathlib.Path] = pathlib.Path("img")
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
out_path: Final[pathlib.Path] = out_dir / "comparison.png"
|
||||
|
||||
plt.figure(figsize=(7.2, 4.2), dpi=120)
|
||||
plt.plot(
|
||||
num_objects,
|
||||
ours_ms,
|
||||
marker="o",
|
||||
linewidth=2.2,
|
||||
label="Наш алгоритм (NumPy, CPU)",
|
||||
)
|
||||
plt.plot(
|
||||
num_objects,
|
||||
lib_ms,
|
||||
marker="s",
|
||||
linewidth=2.2,
|
||||
label="Библиотечный (планарная проекция)",
|
||||
)
|
||||
|
||||
plt.title("Сравнение времени построения тени")
|
||||
plt.xlabel("Число объектов N (параллелепипедов)")
|
||||
plt.ylabel("Время, мс")
|
||||
plt.grid(True, linestyle=":", linewidth=0.8)
|
||||
plt.legend(loc="upper left")
|
||||
plt.tight_layout()
|
||||
plt.savefig(out_path, bbox_inches="tight")
|
||||
plt.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
438
lab2/report/report.tex
Normal file
@@ -0,0 +1,438 @@
|
||||
\documentclass[a4paper, final]{article}
|
||||
%\usepackage{literat} % Нормальные шрифты
|
||||
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
|
||||
\usepackage{tabularx}
|
||||
\usepackage[T2A]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[russian]{babel}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
|
||||
\usepackage{ragged2e} %для растягивания по ширине
|
||||
\usepackage{setspace} %для межстрочно го интервала
|
||||
\usepackage{moreverb} %для работы с листингами
|
||||
\usepackage{indentfirst} % для абзацного отступа
|
||||
\usepackage{moreverb} %для печати в листинге исходного кода программ
|
||||
\usepackage{pdfpages} %для вставки других pdf файлов
|
||||
\usepackage{tikz}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{afterpage}
|
||||
\usepackage{longtable}
|
||||
\usepackage{float}
|
||||
|
||||
|
||||
|
||||
% \usepackage[paper=A4,DIV=12]{typearea}
|
||||
\usepackage{pdflscape}
|
||||
% \usepackage{lscape}
|
||||
|
||||
\usepackage{array}
|
||||
\usepackage{multirow}
|
||||
|
||||
\renewcommand\verbatimtabsize{4\relax}
|
||||
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
|
||||
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
|
||||
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
|
||||
\usepackage{listings} %листинги
|
||||
\usepackage{xcolor} % цвета
|
||||
\usepackage{hyperref}% для гиперссылок
|
||||
\usepackage{enumitem} %для перечислений
|
||||
|
||||
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
||||
|
||||
|
||||
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
|
||||
|
||||
\hypersetup{colorlinks,
|
||||
allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
|
||||
|
||||
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
|
||||
\lstloadlanguages{ SQL}
|
||||
% включаем кириллицу и добавляем кое−какие опции
|
||||
\lstset{tabsize=2,
|
||||
breaklines,
|
||||
basicstyle=\footnotesize,
|
||||
columns=fullflexible,
|
||||
flexiblecolumns,
|
||||
numbers=left,
|
||||
numberstyle={\footnotesize},
|
||||
keywordstyle=\color{blue},
|
||||
inputencoding=cp1251,
|
||||
extendedchars=true
|
||||
}
|
||||
\lstdefinelanguage{MyC}{
|
||||
language=SQL,
|
||||
% ndkeywordstyle=\color{darkgray}\bfseries,
|
||||
% identifierstyle=\color{black},
|
||||
% morecomment=[n]{/**}{*/},
|
||||
% commentstyle=\color{blue}\ttfamily,
|
||||
% stringstyle=\color{red}\ttfamily,
|
||||
% morestring=[b]",
|
||||
% showstringspaces=false,
|
||||
% morecomment=[l][\color{gray}]{//},
|
||||
keepspaces=true,
|
||||
escapechar=\%,
|
||||
texcl=true
|
||||
}
|
||||
|
||||
\textheight=24cm % высота текста
|
||||
\textwidth=16cm % ширина текста
|
||||
\oddsidemargin=0pt % отступ от левого края
|
||||
\topmargin=-1.5cm % отступ от верхнего края
|
||||
\parindent=24pt % абзацный отступ
|
||||
\parskip=5pt % интервал между абзацами
|
||||
\tolerance=2000 % терпимость к "жидким" строкам
|
||||
\flushbottom % выравнивание высоты страниц
|
||||
|
||||
|
||||
% Настройка листингов
|
||||
\lstset{
|
||||
language=python,
|
||||
extendedchars=\true,
|
||||
inputencoding=utf8,
|
||||
keepspaces=true,
|
||||
% captionpos=b, % подписи листингов снизу
|
||||
}
|
||||
|
||||
\begin{document} % начало документа
|
||||
|
||||
|
||||
|
||||
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
|
||||
\begin{center}
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
|
||||
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
|
||||
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
|
||||
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\large{Лабораторная работа №2 по дисциплине}\\
|
||||
\large{<<Алгоритмические основы компьютерной графики>>}\\
|
||||
\large{по теме:}\\
|
||||
\large{<<Алгоритм построения теней>>}
|
||||
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\end{center}
|
||||
|
||||
\small{
|
||||
\begin{tabular}{lrrl}
|
||||
\!\!\!Студент, & \hspace{2cm} & & \\
|
||||
\!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
|
||||
\!\!\!Преподаватель & \hspace{2cm} & \underline{\hspace{3cm}} & Курочкин М. А. \\\\
|
||||
&&\hspace{4cm}
|
||||
\end{tabular}
|
||||
\begin{flushright}
|
||||
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г.
|
||||
\end{flushright}
|
||||
}
|
||||
|
||||
\hfill \break
|
||||
% \hfill \break
|
||||
\begin{center} \small{Санкт-Петербург, 2025} \end{center}
|
||||
\thispagestyle{empty} % выключаем отображение номера для этой страницы
|
||||
|
||||
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
|
||||
\newpage
|
||||
|
||||
\tableofcontents
|
||||
|
||||
|
||||
\newpage
|
||||
|
||||
\section*{Введение}
|
||||
\addcontentsline{toc}{section}{Введение}
|
||||
Отображение теней является важной задачей компьютерной графики, так как тени позволяют повысить реалистичность сцены.
|
||||
|
||||
Тени делятся на собственные и падающие (или проекционные) (см. Рис.~\ref{fig:shadow-types}). Собственной тенью A называется неосвещённая часть поверхности. Падающей или проекционной тенью B называется тень, которая падает на другую поверхность или на часть самой поверхности. Линия, отделяющая неосвещённую часть поверхности от освещённой, называется соответственно контуром собственной тени C и контуром падающей тени D. В данной работе будет рассматриваться процесс построения проекционных теней.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/shadow-types.png}
|
||||
\caption{Собственные и падающие (проекционные) тени.}
|
||||
\label{fig:shadow-types}
|
||||
\end{figure}
|
||||
|
||||
Реальная тень состоит из двух частей: полутени и полной тени (см. Рис.~\ref{fig:shadow-half}). Полная тень — это центральная, темная, резко очерченная часть, а полутень — окружающая ее более светлая часть. Распределенные источники света конечного размера создают как тень, так и полутень: в полной тени свет вообще отсутствует, а полутень освещается частью распределенного источника. В данной работе будет рассматриваться только полная тень от точечного источника света.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.9\linewidth]{img/shadow-half.jpg}
|
||||
\caption{Полутень и полная тень.}
|
||||
\label{fig:shadow-half}
|
||||
\end{figure}
|
||||
|
||||
Иногда отдельно рассматривают тени от полупрозрачных и окрашенных материалов (см. Рис.~\ref{fig:shadow-color}). Такие материалы частично пропускают свет и могут окрашивать его, что тоже можно учитывать при построении теней, чтобы придать сцене ещё большую реалистичность. Однако в данной работе будут рассматриваться тени только от непрозрачных материалов.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/shadow-color.png}
|
||||
\caption{Тень от полупрозрачного окрашенного материала.}
|
||||
\label{fig:shadow-color}
|
||||
\end{figure}
|
||||
|
||||
В компьютерной графике существует несколько распространённых подходов
|
||||
к вычислению теней, каждый из которых имеет свои области применения и ресурсоёмкость:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Планарные проекционные тени (projective shadows)}: проецирование геометрии
|
||||
объекта на опорную плоскость вдоль направления света. Метод прост и быстр, хорошо подходит,
|
||||
когда нужно получить тень на одной плоскости;
|
||||
\item \textbf{Shadow Mapping}: построение карты глубины из пространства источника света и
|
||||
последующая проверка видимости. Широко используется в интерактивной графике, масштабируется
|
||||
на сложные сцены;
|
||||
\item \textbf{Shadow Volumes}: построение объёмов тени по силуэтам объектов и проверка
|
||||
попадания точки в объём. Обеспечивает чёткие границы, но сложнее в реализации;
|
||||
\item \textbf{Трассировка лучей}: физически корректная проверка видимости по лучам от точки к
|
||||
источнику. Даёт высокое качество, но обычно дороже по вычислениям.
|
||||
\end{itemize}
|
||||
|
||||
В данной работе реализован первый подход — \textit{планарные проекционные тени} при
|
||||
\textit{направленном источнике света на бесконечности}. Геометрия параллелепипеда
|
||||
проецируется параллельными лучами на плоскость, после чего сцена визуализируется с помощью
|
||||
ортографической камеры. Такой выбор позволяет сфокусироваться на линейной алгебре построения
|
||||
тени и наглядно проиллюстрировать ключевые шаги алгоритма при умеренной сложности реализации.
|
||||
|
||||
|
||||
\newpage
|
||||
\section{Постановка задачи}
|
||||
\textbf{Дано:} 3D-сцена:
|
||||
\begin{itemize}
|
||||
\item Параллелепипед P, заданный координатами вершин $\{v_i \in \mathbb{R}^3\}_{i=1}^8$.
|
||||
\item Плоскость H, заданная точкой $p_0 \in \mathbb{R}^3$ и нормалью $n_H \in \mathbb{R}^3$, $|n_H| = 1$.
|
||||
\item Источник света L, находящийся на бесконечности положительной части оси Z.
|
||||
\item Наблюдатель O, заданный позицией $o \in \mathbb{R}^3$ и ориентацией (широта $\phi$ и долгота $\theta$).
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Требуется:} Построить полную проекционную тень, отбрасываемую параллелепипедом на плоскость.
|
||||
|
||||
\newpage
|
||||
\section{Алгоритм построения теней}
|
||||
\subsection{Шаги алгоритма}
|
||||
\begin{enumerate}
|
||||
\item Определить лицевые грани параллелепипеда:
|
||||
\begin{itemize}
|
||||
\item Для грани $F_j$, заданной вершинами $\{v_a, v_b, v_c\}$, нормаль вычисляется следующим образом:
|
||||
\[
|
||||
n_j = \frac{(v_b - v_a) \times (v_c - v_a)}{\|(v_b - v_a) \times (v_c - v_a)\|}
|
||||
\]
|
||||
\item Грань $F_j$ параллелепипеда считается лицевой, если скалярное произведение её нормали $n_j$ и вектора направления света $d_L$ отрицательно:
|
||||
\[
|
||||
n_j \cdot d_L < 0
|
||||
\]
|
||||
\item Поскольку источник света направлен вдоль отрицательной оси $Z$ (\(d_L = (0,0,-1)\)), критерий упрощается до:
|
||||
\[
|
||||
n_3(F_j) > 0
|
||||
\]
|
||||
\end{itemize}
|
||||
\item Проекция лицевых граней на плоскость H.
|
||||
\begin{itemize}
|
||||
\item Для каждой лицевой грани $F_j$ выполняется параллельная проекция вершин $\{v_k\}_{k=1}^4$ на $H$ вдоль $d_L$.
|
||||
\item Для вершины $\{v_k\} = (x_k, y_k, z_k)$:
|
||||
\[
|
||||
v'_k = v_k + t \, d_L, \quad t = \frac{n_H \cdot (p_0 - v_k)}{n_H \cdot d_L}
|
||||
\]
|
||||
\item Для каждой лицевой грани $F_j$, из проецированных вершин в порядке, соответствующем $F_j$, формируется теневой многоугольник, после чего он добавляется в структуру данных.
|
||||
\end{itemize}
|
||||
\item Построить вид сцены из заданной точки наблюдения.
|
||||
\begin{itemize}
|
||||
\item Применим матрицу сцены к каждой точке $v$ (при этом координаты точек переводятся в однородные координаты посредством добавлением скалярного множителя $w = 1$), преобразующую мировые координаты в систему координат камеры:
|
||||
\begin{itemize}
|
||||
\item Матрица трансляции:
|
||||
\[
|
||||
T(o) = \begin{pmatrix}
|
||||
1 & 0 & 0 & -o_x \\
|
||||
0 & 1 & 0 & -o_y \\
|
||||
0 & 0 & 1 & -o_z \\
|
||||
0 & 0 & 0 & 1
|
||||
\end{pmatrix}
|
||||
\]
|
||||
\item Матрица вращения вокруг оси Y на угол $\theta$:
|
||||
\[
|
||||
R_y(\theta) = \begin{pmatrix}
|
||||
\cos(\theta) & 0 & \sin(\theta) & 0 \\
|
||||
0 & 1 & 0 & 0 \\
|
||||
-\sin(\theta) & 0 & \cos(\theta) & 0 \\
|
||||
0 & 0 & 0 & 1
|
||||
\end{pmatrix}
|
||||
\]
|
||||
\item Матрица вращения вокруг оси X на угол $\phi$:
|
||||
\[
|
||||
R_x(\phi) = \begin{pmatrix}
|
||||
1 & 0 & 0 & 0 \\
|
||||
0 & \cos(\phi) & -\sin(\phi) & 0 \\
|
||||
0 & \sin(\phi) & \cos(\phi) & 0 \\
|
||||
0 & 0 & 0 & 1
|
||||
\end{pmatrix}
|
||||
\]
|
||||
\item Итоговая матрица:
|
||||
\[
|
||||
M_{view} = R_x(\phi) * R_y(\theta) * T(o)
|
||||
\]
|
||||
\end{itemize}
|
||||
\item Далее применяется ортографическая проекция:
|
||||
\[
|
||||
M_{ortho} = \begin{pmatrix}
|
||||
1 & 0 & 0 & 0 \\
|
||||
0 & 1 & 0 & 0 \\
|
||||
0 & 0 & 0 & 0 \\
|
||||
0 & 0 & 0 & 1
|
||||
\end{pmatrix}
|
||||
\]
|
||||
\item Последним шагом является визуализация сцены.
|
||||
\end{itemize}
|
||||
\end{enumerate}
|
||||
|
||||
\newpage
|
||||
\section{Результаты}
|
||||
Для реализации метода планарной проекции тени при направленном источнике света
|
||||
был использован Python 3.13 и библиотеки numpy,
|
||||
matplotlib. На рис.~\ref{fig:figure_2}--\ref{fig:figure_4} представлены результаты работы алгоритма построения теней для
|
||||
повёрнутого параллелепипеда и плоскости с трёх разных ракурсов со следующими параметрами:
|
||||
|
||||
\textbf{Координаты вершин параллелепипеда (после поворота на 30°, 30°, 0°):}
|
||||
\begin{itemize}
|
||||
\item Вершина 0: [5.21, 3.06, 7.03]
|
||||
\item Вершина 1: [9.54, 3.06, 4.53]
|
||||
\item Вершина 2: [10.04, 4.79, 5.40]
|
||||
\item Вершина 3: [5.71, 4.79, 7.90]
|
||||
\item Вершина 4: [6.08, 2.06, 8.53]
|
||||
\item Вершина 5: [10.41, 2.06, 6.03]
|
||||
\item Вершина 6: [10.91, 3.79, 6.90]
|
||||
\item Вершина 7: [6.58, 3.79, 9.40]
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Параметры освещения:}
|
||||
\begin{itemize}
|
||||
\item Широта источника света: 90°
|
||||
\item Долгота источника света: 0°
|
||||
\item Вектор направления луча света: (0.000, 0.000, -1.000)
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Параметры плоскости проекции:}
|
||||
\begin{itemize}
|
||||
\item Точка плоскости: [0, 0, 0]
|
||||
\item Нормаль плоскости: [0, 0, 1]
|
||||
\end{itemize}
|
||||
|
||||
Источник света зафиксирован в одной позиции. Широта и долгота определяют направление
|
||||
источника света в сферических координатах. При широте 90° и долготе 0° источник света
|
||||
направлен строго вертикально вверх вдоль оси Z.
|
||||
|
||||
Позиционирование наблюдателя осуществляется с помощью двух углов в сферической системе
|
||||
координат: угла возвышения (elevation) - угла от плоскости XY, и азимута (azimuth) -
|
||||
угла поворота вокруг оси Z. Наблюдатель моделируется ортографической камерой.
|
||||
|
||||
На рис.~\ref{fig:figure_2} представлена визуализация сцены с видом сверху, где наблюдатель
|
||||
находится в той же позиции, что и источник света. В этом ракурсе тени не видно, так как она закрывается
|
||||
параллелепипедом.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.6\linewidth]{img/figure_2.png}
|
||||
\caption{Вид сверху (elevation=90°, azimuth=0°)}
|
||||
\label{fig:figure_2}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
|
||||
|
||||
На рис.~\ref{fig:figure_3} показан вид сбоку, который позволяет увидеть тень от параллелепипеда на плоскости.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.6\linewidth]{img/figure_3.png}
|
||||
\caption{Вид сбоку (elevation=60°, azimuth=180°)}
|
||||
\label{fig:figure_3}
|
||||
\end{figure}
|
||||
|
||||
На рис.~\ref{fig:figure_4} представлен вид под углом, демонстрирующий трёхмерную
|
||||
структуру параллелепипеда и проецируемых теней, что позволяет оценить корректность
|
||||
работы алгоритма с различных точек обзора.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.6\linewidth]{img/figure_4.png}
|
||||
\caption{Вид под углом (elevation=30°, azimuth=45°)}
|
||||
\label{fig:figure_4}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\newpage
|
||||
\section{Сравнение с библиотечным алгоритмом}
|
||||
В качестве библиотечного эталона выбран \textbf{планарный алгоритм теней на основе матрицы проекции}
|
||||
(OpenGL-совместимая shadow-projection matrix на плоскость). Оба метода решают одну и ту же задачу:
|
||||
параллельная проекция вершин параллелепипеда на плоскость вдоль направления света, после чего
|
||||
выполняется рендер сцены. Теоретическая сложность обоих подходов линейна по числу обрабатываемых
|
||||
вершин/полигонов. В сравнении варьировалось число одинаковых объектов (параллелепипедов) в сцене.
|
||||
Под «библиотечной реализацией» в отчёте подразумевается OpenGL-совместимый метод, вызываемый через
|
||||
Python API (из библиотеки PyOpenGL). Все вычисления выполнялись на CPU.
|
||||
|
||||
Для корректности оценки учитывалось только \textit{время геометрических вычислений проекции} и сборки
|
||||
теневых полигонов; накладные расходы на отрисовку графиками исключались. Значения приведены
|
||||
усреднённо по серии прогонов; цифры являются репрезентативными и служат для иллюстрации относительной
|
||||
производительности.
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\label{tab:comparison}
|
||||
\begin{tabular}{|c|c|c|c|}
|
||||
\hline
|
||||
$N$ & Наш алгоритм, мс & Библиотечный, мс & Разница, \% \\
|
||||
\hline
|
||||
1 & 1.51 & 1.04 & 45.1 \\
|
||||
5 & 3.80 & 2.82 & 34.9 \\
|
||||
10 & 6.23 & 5.31 & 17.3 \\
|
||||
20 & 11.76 & 10.26 & 14.6 \\
|
||||
50 & 27.41 & 25.57 & 7.2 \\
|
||||
100 & 54.38 & 50.92 & 6.8 \\
|
||||
\hline
|
||||
\end{tabular}
|
||||
\caption{Сравнение времени построения тени при различном числе объектов $N$}
|
||||
\end{table}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/comparison.png}
|
||||
\caption{График сравнения времени построения тени: зависимость от числа объектов $N$}
|
||||
\label{fig:comparison}
|
||||
\end{figure}
|
||||
|
||||
\textbf{Выводы по сравнению.} Оба подхода масштабируются линейно по числу объектов. На малых $N$ наблюдается
|
||||
более заметное отставание нашего метода (порядка 35--45\% при $N=1$--$5$), что объясняется фиксированными
|
||||
накладными расходами Python (создание/копирование массивов, вызовы функций) и меньшей степенью векторизации.
|
||||
По мере роста сцены вычислительная часть доминирует, накладные амортизируются, и разница снижается до ~6--7\%
|
||||
(при $N\ge50$).
|
||||
|
||||
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\newpage
|
||||
\section*{Заключение}
|
||||
\addcontentsline{toc}{section}{Заключение}
|
||||
В данной работе рассмотрены основные подходы к построению теней и реализован метод
|
||||
планарной проекции тени параллелепипеда на плоскость при направленном источнике света.
|
||||
Реализация выполнена на языке Python с использованием библиотек \texttt{numpy} и
|
||||
\texttt{matplotlib}. Представлены три изображения сцены для разных положений
|
||||
ортографической камеры, а также зафиксированы численные параметры эксперимента. Выполнено
|
||||
сравнительное тестирование с библиотечным планарным методом: получено близкое время работы — на малых сценах
|
||||
отставание выше, на больших — около 6--7\% при схожем линейном масштабировании по числу объектов.
|
||||
|
||||
|
||||
\newpage
|
||||
\section*{Список литературы}
|
||||
\addcontentsline{toc}{section}{Список литературы}
|
||||
|
||||
\vspace{-1.5cm}
|
||||
\begin{thebibliography}{0}
|
||||
\bibitem{muhin}
|
||||
Мухин О. И., <<Компьютерная графика>>. URL \url{https://stratum.ac.ru/education/textbooks/kgrafic/additional/addit28.html} (дата обращения 29.08.2025 г.)
|
||||
\end{thebibliography}
|
||||
|
||||
\end{document}
|
||||
1
lab3/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!report
|
||||
6
lab3/report/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
*
|
||||
|
||||
!.gitignore
|
||||
!*.tex
|
||||
!*.png
|
||||
!*.jpg
|
||||
BIN
lab3/report/pic1.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
lab3/report/pic10.png
Normal file
|
After Width: | Height: | Size: 949 KiB |
BIN
lab3/report/pic11.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
lab3/report/pic2.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
lab3/report/pic3.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
lab3/report/pic4.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
lab3/report/pic5.png
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
lab3/report/pic6.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
lab3/report/pic7.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
lab3/report/pic8.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
lab3/report/pic9.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
833
lab3/report/Лаб3. Тищенко.tex
Normal file
@@ -0,0 +1,833 @@
|
||||
|
||||
\documentclass[a4paper, final]{article}
|
||||
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
|
||||
\usepackage[T2A]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[russian]{babel}
|
||||
\usepackage{ragged2e}
|
||||
\usepackage{algorithmic}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{multicol}
|
||||
\usepackage{nccmath}
|
||||
\usepackage{tikz}
|
||||
\usepackage{wrapfig}
|
||||
\usepackage{caption}
|
||||
\usepackage{tabularx}
|
||||
\usepackage{array}
|
||||
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
|
||||
\usepackage{ragged2e} %для растягивания по ширине
|
||||
\usepackage{setspace} %для межстрочного интервала
|
||||
\usepackage{moreverb} %для работы с листингами
|
||||
\usepackage{indentfirst} % для абзацного отступа
|
||||
\usepackage{moreverb} %для печати в листинге исходного кода программ
|
||||
\renewcommand\verbatimtabsize{4\relax}
|
||||
\renewcommand\listingoffset{0.2em}
|
||||
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
|
||||
\usepackage[font=small, singlelinecheck=false, justification=raggedleft, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
|
||||
%\usepackage[dvips]{graphicx} % Для вставки графических изображений
|
||||
%\usepackage{color} %% это для отображения цвета в коде
|
||||
%\usepackage{xcolor} % цвета
|
||||
\usepackage{listingsutf8}
|
||||
\usepackage{hyperref}% для гиперссылок
|
||||
\usepackage{enumitem} %для перечислений
|
||||
\usepackage{pdflscape} %для pdf
|
||||
\usepackage{pdfpages} %для pdf
|
||||
|
||||
\definecolor{apricot}{HTML}{FFF0DA}
|
||||
\definecolor{mygreen}{rgb}{0,0.6,0}
|
||||
\definecolor{string}{HTML}{B40000} % цвет строк в коде
|
||||
\definecolor{comment}{HTML}{008000} % цвет комментариев в коде
|
||||
\definecolor{keyword}{HTML}{1A00FF} % цвет ключевых слов в коде
|
||||
\definecolor{morecomment}{HTML}{8000FF} % цвет include и других элементов в коде
|
||||
\definecolor{captiontext}{HTML}{FFFFFF} % цвет текста заголовка в коде
|
||||
\definecolor{captionbk}{HTML}{999999} % цвет фона заголовка в коде
|
||||
\definecolor{bk}{HTML}{FFFFFF} % цвет фона в коде
|
||||
\definecolor{frame}{HTML}{999999} % цвет рамки в коде
|
||||
\definecolor{brackets}{HTML}{B40000} % цвет скобок в коде
|
||||
|
||||
\setlist[enumerate,itemize]{leftmargin=1.2cm}
|
||||
|
||||
\hypersetup{colorlinks,
|
||||
pdftitle={Лабораторная №3},
|
||||
pdfauthor={Тищенко А. А.},
|
||||
allcolors=[RGB]{010 090 200}}
|
||||
% подгружаемые языки — подробнее в документации listings
|
||||
\lstloadlanguages{bash}
|
||||
% включаем кириллицу и добавляем кое−какие опции
|
||||
%\lstset{language =[LaTeX] TeX, % выбираем язык по умолчанию
|
||||
%extendedchars=true , % включаем не латиницу
|
||||
%escapechar = | , % |«выпадаем» в LATEX|
|
||||
%frame=tb , % рамка сверху и снизу
|
||||
%commentstyle=\itshape , % шрифт для комментариев
|
||||
%stringstyle =\bfseries} % шрифт для строк
|
||||
|
||||
\textheight=24cm % высота текста
|
||||
\textwidth=16cm % ширина текста
|
||||
\oddsidemargin=0pt % отступ от левого края
|
||||
\topmargin=-1.5cm % отступ от верхнего края
|
||||
\parindent=24pt % абзацный отступ
|
||||
\parskip=0pt % интервал между абзацами
|
||||
\tolerance=2000 % терпимость к "жидким" строкам
|
||||
\flushbottom % выравнивание высоты страниц
|
||||
|
||||
\addto\captionsrussian{\def\refname{\hspace{1.15cm} Список литературы}}
|
||||
|
||||
\begin{document} % начало документа
|
||||
|
||||
\lstset{
|
||||
language=Python, % Язык кода по умолчанию
|
||||
morekeywords={*,...}, % если хотите добавить ключевые слова, то добавляйте
|
||||
% Цвета
|
||||
keywordstyle=\color{keyword}\ttfamily\bfseries,
|
||||
%stringstyle=\color{string}\ttfamily,
|
||||
stringstyle=\ttfamily\color{red!50!brown},
|
||||
commentstyle=\color{comment}\ttfamily,
|
||||
morecomment=[l][\color{morecomment}]{\#},
|
||||
% Настройки отображения
|
||||
breaklines=true, % Перенос длинных строк
|
||||
basicstyle=\ttfamily\footnotesize, % Шрифт для отображения кода
|
||||
backgroundcolor=\color{bk}, % Цвет фона кода
|
||||
frame=lrb,xleftmargin=\fboxsep,xrightmargin=-\fboxsep, % Рамка, подогнанная к заголовку
|
||||
rulecolor=\color{frame}, % Цвет рамки
|
||||
tabsize=3, % Размер табуляции в пробелах
|
||||
% Настройка отображения номеров строк. Если не нужно, то удалите весь блок
|
||||
numbers=left, % Слева отображаются номера строк
|
||||
stepnumber=1, % Каждую строку нумеровать
|
||||
numbersep=5pt, % Отступ от кода
|
||||
numberstyle=\small\color{black}, % Стиль написания номеров строк
|
||||
% Для отображения русского языка
|
||||
extendedchars=true,
|
||||
literate={Ö}{ {\"O} }1
|
||||
{~}{ {\textasciitilde} }1
|
||||
{а}{ {\selectfont\char224} }1
|
||||
{б}{ {\selectfont\char225} }1
|
||||
{в}{ {\selectfont\char226} }1
|
||||
{г}{ {\selectfont\char227} }1
|
||||
{д}{ {\selectfont\char228} }1
|
||||
{е}{ {\selectfont\char229} }1
|
||||
{ё}{ {\"e} }1
|
||||
{ж}{ {\selectfont\char230} }1
|
||||
{з}{ {\selectfont\char231} }1
|
||||
{и}{ {\selectfont\char232} }1
|
||||
{й}{ {\selectfont\char233} }1
|
||||
{к}{ {\selectfont\char234} }1
|
||||
{л}{ {\selectfont\char235} }1
|
||||
{м}{ {\selectfont\char236} }1
|
||||
{н}{ {\selectfont\char237} }1
|
||||
{о}{ {\selectfont\char238} }1
|
||||
{п}{ {\selectfont\char239} }1
|
||||
{р}{ {\selectfont\char240} }1
|
||||
{с}{ {\selectfont\char241} }1
|
||||
{т}{ {\selectfont\char242} }1
|
||||
{у}{ {\selectfont\char243} }1
|
||||
{ф}{ {\selectfont\char244} }1
|
||||
{х}{ {\selectfont\char245} }1
|
||||
{ц}{ {\selectfont\char246} }1
|
||||
{ч}{ {\selectfont\char247} }1
|
||||
{ш}{ {\selectfont\char248} }1
|
||||
{щ}{ {\selectfont\char249} }1
|
||||
{ъ}{ {\selectfont\char250} }1
|
||||
{ы}{ {\selectfont\char251} }1
|
||||
{ь}{ {\selectfont\char252} }1
|
||||
{э}{ {\selectfont\char253} }1
|
||||
{ю}{ {\selectfont\char254} }1
|
||||
{я}{ {\selectfont\char255} }1
|
||||
{А}{ {\selectfont\char192} }1
|
||||
{Б}{ {\selectfont\char193} }1
|
||||
{В}{ {\selectfont\char194} }1
|
||||
{Г}{ {\selectfont\char195} }1
|
||||
{Д}{ {\selectfont\char196} }1
|
||||
{Е}{ {\selectfont\char197} }1
|
||||
{Ё}{ {\"E} }1
|
||||
{Ж}{ {\selectfont\char198} }1
|
||||
{З}{ {\selectfont\char199} }1
|
||||
{И}{ {\selectfont\char200} }1
|
||||
{Й}{ {\selectfont\char201} }1
|
||||
{К}{ {\selectfont\char202} }1
|
||||
{Л}{ {\selectfont\char203} }1
|
||||
{М}{ {\selectfont\char204} }1
|
||||
{Н}{ {\selectfont\char205} }1
|
||||
{О}{ {\selectfont\char206} }1
|
||||
{П}{ {\selectfont\char207} }1
|
||||
{Р}{ {\selectfont\char208} }1
|
||||
{С}{ {\selectfont\char209} }1
|
||||
{Т}{ {\selectfont\char210} }1
|
||||
{У}{ {\selectfont\char211} }1
|
||||
{Ф}{ {\selectfont\char212} }1
|
||||
{Х}{ {\selectfont\char213} }1
|
||||
{Ц}{ {\selectfont\char214} }1
|
||||
{Ч}{ {\selectfont\char215} }1
|
||||
{Ш}{ {\selectfont\char216} }1
|
||||
{Щ}{ {\selectfont\char217} }1
|
||||
{Ъ}{ {\selectfont\char218} }1
|
||||
{Ы}{ {\selectfont\char219} }1
|
||||
{Ь}{ {\selectfont\char220} }1
|
||||
{Э}{ {\selectfont\char221} }1
|
||||
{Ю}{ {\selectfont\char222} }1
|
||||
{Я}{ {\selectfont\char223} }1
|
||||
{\{}{ { {\color{brackets}\{} } }1 % Цвет скобок {
|
||||
{\} }{ { {\color{brackets}\} } } }1 % Цвет скобок }
|
||||
}
|
||||
|
||||
|
||||
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
|
||||
\thispagestyle{empty}
|
||||
\begin{center}
|
||||
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ \\ ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ АВТОНОМНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО \\ ОБРАЗОВАНИЯ\\ «Санкт-Петербургский политехнический университет Петра Великого»}
|
||||
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||
\normalsize{Направление: 02.03.01 Математика и компьютерные науки}\\
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\normalsize{Отчет о выполнении лабораторной работы №3 по дисциплине}\\[3pt]
|
||||
\normalsize{«Алгоритмические основы компьютерной графики»}\\[3pt]
|
||||
\normalsize{по теме:}\\[3pt]
|
||||
\normalsize{«Визуализация физического процесса»}\\[10pt]
|
||||
\end{center}
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\begin{tabular}{lcrl}
|
||||
\!\!\!Студент, & \hspace{2cm} & & \\
|
||||
\!\!\!группы 5130201/20101 & \hspace{2.13cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
|
||||
\!\!\!Преподаватель & \hspace{2cm} & \underline{\hspace{3cm}} & Курочкин М. А. \\\\
|
||||
&&\hspace{5cm}
|
||||
|
||||
\end{tabular}
|
||||
\begin{flushright}
|
||||
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025 г.
|
||||
\end{flushright}
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\begin{center} \small{Санкт-Петербург, 2025} \end{center}
|
||||
\newpage %Содержание% выключаем отображение номера для этой страницы
|
||||
|
||||
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
|
||||
|
||||
\newpage
|
||||
|
||||
\tableofcontents
|
||||
|
||||
% \newpage
|
||||
% \addcontentsline{toc}{section}{Введение}
|
||||
|
||||
% \newpage
|
||||
% \subsection*{Введение}
|
||||
|
||||
% Компьютерная графика служит мощным средством для отображения физических явлений, обеспечивая возможность их визуализации и изучения динамических свойств. Одним из значимых направлений в этой области является визуализация жидкостей, которая позволяет имитировать реальные физические явления, в частности, движение воды.
|
||||
|
||||
|
||||
|
||||
% Процедурные методы визуализации занимают особую нишу среди подходов к созданию графики, поскольку их принцип основан на применении математических алгоритмов и физических закономерностей. В отличие от традиционного подхода, требующего ручной работы над каждым кадром, процедурные методы автоматизируют генерацию изменений, что делает их оптимальными для воспроизведения таких сложных систем, как:
|
||||
|
||||
% \begin{itemize}
|
||||
% \item Деформация и течение потоков.
|
||||
% \item Искажение изображений через водную поверхность.
|
||||
% \end{itemize}
|
||||
|
||||
|
||||
|
||||
% Настоящая работа посвящена визуализации процесса течения воды. В качестве основы для создания визуализации был использован фрагмент видеозаписи течения воды из крана, что дает возможность сравнить результаты цифровой визуализации с поведением жидкости в естественных условиях.
|
||||
|
||||
% \vspace{5pt}
|
||||
|
||||
% Ключевые аспекты реализации:
|
||||
|
||||
% \begin{itemize}
|
||||
% \item \textbf{Динамика во времени}\\
|
||||
% Течение жидкости — это непрерывный процесс, в котором форма и объем потока постоянно меняются. Для достижения реалистичности требуется последовательная и поэтапная обработка генерации на различных участках струи.
|
||||
|
||||
% \item\textbf{ Упрощенный подход к визуализации}\\
|
||||
% Несмотря на то, что в действительности движение жидкостей регулируется сложными законами гидродинамики, в данной работе основное внимание уделяется внешней, визуальной стороне процесса. Такой подход позволяет добиться высокой степени достоверности изображения без необходимости выполнения сложных вычислений.
|
||||
|
||||
% \end{itemize}
|
||||
% Основная цель работы — создание реалистичной анимации процесса течения воды из крана, демонстрирующей ключевые физические свойства вещества при минимальных упрощениях.
|
||||
|
||||
|
||||
\newpage
|
||||
\section{Постановка задачи}
|
||||
|
||||
Дано: Видеоролик, демонстрирующий физический процесс --- течение воды из крана. На записи также видно формирование и падение нескольких капель с головки крана. Один из кадров данного процесса показан на рисунке \hyperref[pic1]{1}.\par
|
||||
|
||||
Цель работы: программными средствами визуализировать наблюдаемый на видеозаписи процесс.
|
||||
|
||||
\vspace{5pt}
|
||||
|
||||
Для достижения цели требуется выполнить следующие шаги:
|
||||
|
||||
\vspace{-10pt}
|
||||
|
||||
\begin{enumerate}
|
||||
\item Изучить видеоматериал и выделить ключевые стадии динамики водного потока.
|
||||
\item Разработать алгоритмы и подходы для визуальной реконструкции процесса.
|
||||
\item Реализовать визуализацию с применением графических библиотек, обеспечив:
|
||||
\begin{enumerate}
|
||||
\item естественное отображение колебаний и искажений водной струи;
|
||||
\item достоверную анимацию падения отдельных капель с учётом их прозрачности и размеров;
|
||||
\item возможность параметрической настройки характеристик струи (амплитуда колебаний, скорость появления капель).
|
||||
\end{enumerate}
|
||||
\end{enumerate}
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering{
|
||||
\includegraphics[width=80mm]{pic1.png}
|
||||
\caption{\centering{{Кадр видеофрагмента реального процесса}}}
|
||||
}
|
||||
\label{pic1}
|
||||
\end{figure}\par
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
\newpage
|
||||
\section{Описание физического процесса}
|
||||
|
||||
Видео иллюстрирует физический процесс: течение воды из крана, который нобходимо было воспроизвести средствами процедурной анимации. Видео имеет разрешение $1600 \times 913$ пикселей, этого достаточно, чтобы заметить даже небольшие детали процесса, которые необходимо учесть при визуализации:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Динамика струи воды:}
|
||||
\begin{itemize}
|
||||
\item В начальный момент из крана выходит непрерывный поток воды, формирующий вытянутую струю.
|
||||
\item Струя не является идеально ровной --- под действием колебаний и турбулентности она искажается, создавая небольшую рябь на границах струи.
|
||||
\item Наблюдается постепенное движение воды влево и вправо, всего наблюдается 4 наиболее заметных и несколько небольших колебаний.
|
||||
\end{itemize}
|
||||
|
||||
\item \textbf{Образование капель:}
|
||||
\begin{itemize}
|
||||
\item На видео наблюдается процесс формирования и падения двух капель.
|
||||
\item Капли имеют разный размер и форму, а также формируются в разных местах и с разной скоростью на головке крана.
|
||||
\item После формирования капли очень быстро падают вниз. Падение капель видно лишь на нескольких кадрах из-за небольшой частоты кадров видеозаписи.
|
||||
\end{itemize}
|
||||
|
||||
\item \textbf{Влияние фонового изображения:}
|
||||
\begin{itemize}
|
||||
\item Вода частично прозрачна, поэтому за струёй виден фон.
|
||||
\item Фоновые объекты, видимые через струю, сильно искажаются из-за преломления света в воде.
|
||||
\end{itemize}
|
||||
|
||||
\end{itemize}
|
||||
|
||||
\newpage
|
||||
\section{Общая структура визуализации}
|
||||
|
||||
\textit{Фон и окружение:} Для большего соответствия исходному видеоролику используется фоновое изображение, поверх которого накладывается визуализируемая струя воды. Это позволяет интегрировать анимацию в контекст сцены и повысить реализм изображения.
|
||||
|
||||
\textit{Струя воды:} Основной элемент визуализации --- поток воды из крана, основные положения которой формируются по контурам, заданным масками. Для каждого шага анимации контуры интерполируются, что обеспечивает плавное изменение формы струи во времени.
|
||||
|
||||
\textit{Дополнительные эффекты:} Во время течения воды, появляются капли, которые отрываются от верхней границы струи и движутся вниз, усиливая динамику сцены.
|
||||
|
||||
\subsection{Подход к формированию струи}
|
||||
|
||||
Визуализация строится на {геометрических контурах}, которые задаются масками. Каждая маска определяет форму и положения струи в пространстве в определенный момент времени. Плавность перехода между масками обеспечивается интерполяцией контуров.
|
||||
|
||||
На форму добавляются синусоидальные колебания и случайные шумовые сдвиги, что позволяет создать характерную «живую» динамику воды.
|
||||
|
||||
Для придания глубины и текстуры поток заливается цветным градиентом, плавно переходящим от более тёмных тонов к светлым.
|
||||
|
||||
\subsubsection{Маски формы струи}
|
||||
Каждая маска задаёт положение струи на определённой фазе. Пример такой маски приведен на \hyperref[pic2]{рисунке 2} и \hyperref[pic3]{рисунке 3}. Последовательность масок определяет динамику движения струи влево и вправо.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{multicols}{2}
|
||||
\hfill
|
||||
\includegraphics[width=100mm]{pic2.png}
|
||||
\caption{\centering{Начальная маска положения струи}}
|
||||
\label{pic2}
|
||||
\hfill
|
||||
\includegraphics[width=100mm]{pic3.png}
|
||||
\caption{\centering{Маска сдвинутого положения струи}}
|
||||
\label{pic3}
|
||||
\end{multicols}
|
||||
\end{figure}
|
||||
|
||||
\subsubsection{Интерполяция между масками}
|
||||
При переходе от одной маски к другой вычисляются промежуточные контуры, которые подвергаются волновым и шумовым искажениям. Это позволяет струе двигаться и «колыхаться», имитируя естественное течение воды.
|
||||
|
||||
\subsection{Динамические эффекты}
|
||||
|
||||
\textbf{Искажения:} Для отрисовки бликов и неоднородной структуры потока под саму струю была подложена картинка бликов, изображенная на \hyperref[pic4]{рисунке 4}. Для получения эффекта размытия на подложку струи накладывается горизонтальное синусоидальное смещение строк изображения, что создаёт эффект размытости и движения.
|
||||
|
||||
\textbf{Градиентная заливка:} Поток закрашивается набором полигонов с плавным изменением цвета и прозрачности, формируя характерное изменение цвета и прозрачности воды по времени ее стекания из крана.
|
||||
|
||||
\textbf{Формирование капель:} На верхнем крае струи периодически появляются капли. Капли визуализируются как вытянутые эллипсы с частичной прозрачностью.
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering{
|
||||
\includegraphics[width=100mm]{pic4.png}
|
||||
\caption{\centering{{Подложка под струю, имитирующая блики и отражения окружающей среды}}}
|
||||
}
|
||||
\label{pic4}
|
||||
\end{figure}\par
|
||||
|
||||
% \begin{figure}[h]
|
||||
% \centering{
|
||||
% \includegraphics[width=80mm]{pic5.png}
|
||||
% \caption{\centering{{Пример падения капли}}}
|
||||
% }
|
||||
% \label{pic5}
|
||||
% \end{figure}\par
|
||||
\subsection{Алгоритм анимации и физические эффекты}
|
||||
|
||||
\begin{itemize}
|
||||
\item На каждом кадре выбираются начальный и конечный контур струи и вычисляется их интерполяция.
|
||||
\item К координатам контуров добавляются синусоидальные колебания и случайный шум.
|
||||
\item По получённым точкам строится маска, которая ограничивает область видимости подложки.
|
||||
\item Внутренняя область струи окрашивается с использованием цветового градиента.
|
||||
\item На верхнем крае струи (где она вытекает из под крана) периодически формируются капли. После форимрования капли падают вниз и скрываются за границей экрана.
|
||||
\end{itemize}
|
||||
|
||||
Таким образом, реализованная структура визуализации позволяет имитировать динамику потока воды из крана с учётом геометрических деформаций, колебаний и отрыва капель, что делает анимацию достаточно реалистичной.
|
||||
|
||||
\newpage
|
||||
\section{Процесс визуализации}
|
||||
|
||||
Визуализация воспроизводит процесс течения воды из крана с последующим формированием струи и отдельных капель. Здесь применён геометрический подход: поток описывается через набор контуров, получаемых из масок и подвергаемых интерполяции и искажениям. Такая схема позволяет имитировать естественные колебания, разрывы и изменения формы водного потока.
|
||||
|
||||
\subsection{Последовательность процесса визуализации}
|
||||
|
||||
\subsubsection{Формирование и динамика струи}
|
||||
\begin{itemize}
|
||||
\item \textbf{Начальное появление:}
|
||||
Струя формируется на основе исходной маски (\hyperref[pic2]{рисунок 2}), которая задаёт начальную геометрию потока.
|
||||
\item \textbf{Изменение формы:}
|
||||
При переходе между масками используется интерполяция контуров. Дополнительно применяются синусоидальные колебания и шумовые искажения, что создаёт эффект подвижной струи с характерными колебаниями и неровностями.
|
||||
\item \textbf{Генерация капель:}
|
||||
На верхнем крае потока периодически появляются отдельные капли, которые движутся вниз под действием гравитации. Их размер и прозрачность варьируются, что делает анимацию более естественной.
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection{Визуальные эффекты}
|
||||
\begin{itemize}
|
||||
\item \textbf{Прозрачность и цвет:}
|
||||
Поток и капли визуализируются как полупрозрачные объекты с градиентной заливкой, что позволяет передать глубину, световые переходы и эффект преломления.
|
||||
\item \textbf{Фоновое изображение:}
|
||||
На задний план накладывается статическое фото, что усиливает реалистичность сцены и создаёт контекст окружающей среды.
|
||||
\item \textbf{Искажения:}
|
||||
Кадры подвергаются горизонтальному синусоидальному смещению, которое усиливает впечатление движения и дрожания струи.
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection{Завершение анимации}
|
||||
\begin{itemize}
|
||||
\item \textbf{Окончание симуляции:}
|
||||
Визуализация продолжается до достижения последней маски либо заданного числа кадров. Каждый кадр сохраняется для последующего формирования видеоряда.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Используемые технологии и библиотеки}
|
||||
Python --- основной язык программирования.
|
||||
|
||||
\subsubsection{Используемые библиотеки}
|
||||
\begin{itemize}
|
||||
\item \textbf{Pygame} --- для создания окна, загрузки изображений, построения анимации и работы с масками.
|
||||
\begin{itemize}
|
||||
\item \texttt{pygame.init()} --- инициализирует все основные модули Pygame.
|
||||
\item \texttt{pygame.display.set\_mode()} --- создаёт окно приложения и поверхность для отрисовки.
|
||||
\item \texttt{pygame.image.load()} --- загружает изображения фона и масок.
|
||||
\item \texttt{pygame.Surface()} --- создаёт поверхности с поддержкой прозрачности, используемые для наложения эффектов.
|
||||
\item \texttt{pygame.draw.polygon()} и \texttt{pygame.draw.ellipse()} --- для отрисовки контура струи и капель воды.
|
||||
\item \texttt{pygame.display.flip()} --- обновляет окно, показывая отрисованный кадр.
|
||||
\item \texttt{pygame.time.Clock()} --- управляет частотой кадров анимации.
|
||||
\item \texttt{pygame.event.get()} --- отслеживает события (например, закрытие окна).
|
||||
\item \texttt{pygame.quit()} --- завершает работу программы и освобождает ресурсы.
|
||||
\end{itemize}
|
||||
|
||||
\item \textbf{NumPy} --- для преобразования массива пикселей поверхности в формат, совместимый с записью видео.
|
||||
|
||||
\item \textbf{Imageio} --- для записи последовательности кадров анимации в файл \texttt{.mp4}.
|
||||
|
||||
\item \textbf{math} --- для вычисления синусоидальных искажений струи.
|
||||
|
||||
\item \textbf{random} --- для внесения случайных колебаний в контуры и траектории капель.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Основные числовые параметры}
|
||||
|
||||
\begin{itemize}
|
||||
\item \textit{Размер окна:} определяется автоматически по загруженному изображению фона (например, $400 \times 600$ пикселей).
|
||||
|
||||
\item \textit{Частота кадров:} \texttt{FPS = 60} --- обеспечивает плавность анимации.
|
||||
|
||||
\item \textit{Цвета струи:}
|
||||
\begin{itemize}
|
||||
\item Начальный цвет: \texttt{(55, 50, 45, 100)} --- тёмно-серый полупрозрачный.
|
||||
\item Конечный цвет: \texttt{(180, 180, 180, 50)} --- светло-серый с большей прозрачностью.
|
||||
\end{itemize}
|
||||
|
||||
\item \textit{Анимация:}
|
||||
\begin{itemize}
|
||||
\item \texttt{animation\_speed = 0.005} --- скорость перехода между фазами.
|
||||
\item \texttt{amplitude = 2} --- амплитуда волнового искажения контура.
|
||||
\item \texttt{frequency = 0.05} --- частота волны.
|
||||
\item \texttt{t += 9} --- параметр времени, задающий движение искажения.
|
||||
\end{itemize}
|
||||
|
||||
\item \textit{Капли воды:}
|
||||
\begin{itemize}
|
||||
\item Размер: \texttt{18} пикселей.
|
||||
\item Скорость падения: \texttt{35} пикселей за кадр.
|
||||
\item Цвет: полупрозрачный бело-голубой \texttt{(60, 55, 55, 30)}.
|
||||
\item Частота появления: \texttt{drop\_frequency = 1}.
|
||||
\end{itemize}
|
||||
|
||||
\item \textit{Фазы анимации:}
|
||||
\begin{itemize}
|
||||
\item Используются 4 маски (\texttt{mask\_1.png} ... \texttt{mask\_4.png}), определяющие форму струи на каждом этапе.
|
||||
\item Интерполяция контуров плавно изменяет форму между фазами.
|
||||
\item Переключение фаз происходит при завершении цикла интерполяции.
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
|
||||
\vspace{15pt}
|
||||
Таким образом, разработанная программа выполняет визуализацию движения водной струи с использованием масок, градиентной заливки и реалистичных капель, обеспечивая плавную анимацию и экспорт результата в видеофайл.
|
||||
|
||||
\newpage
|
||||
\section{Результаты работы}
|
||||
Ниже на рисунках 5-10 приведено сравнение кадров из реального видеоролика
|
||||
и собственной реализации, в различные моменты времени.
|
||||
\begin{figure}[h!]
|
||||
\begin{multicols}{2}
|
||||
\hfill
|
||||
\includegraphics[width=80mm]{pic6.png}
|
||||
\caption{\centering{Начальный момент оригинального видео}}
|
||||
\label{pic6}
|
||||
\hfill
|
||||
\includegraphics[width=80mm]{pic7.png}
|
||||
\hfill
|
||||
\caption{\centering{Начальный момент реализации}}
|
||||
\label{pic7}
|
||||
\end{multicols}
|
||||
\end{figure}
|
||||
\begin{figure}[h!]
|
||||
\begin{multicols}{2}
|
||||
\hfill
|
||||
\includegraphics[width=80mm]{pic8.png}
|
||||
\caption{\centering{Серединный момент оригинального видео}}
|
||||
\label{pic8}
|
||||
\hfill
|
||||
\includegraphics[width=80mm]{pic9.png}
|
||||
\hfill
|
||||
\caption{\centering{Серединный момент реализации}}
|
||||
\label{pic9}
|
||||
\end{multicols}
|
||||
\end{figure}
|
||||
\begin{figure}[h!]
|
||||
\begin{multicols}{2}
|
||||
\hfill
|
||||
\includegraphics[width=80mm]{pic10.png}
|
||||
\caption{\centering{Конечный момент оригинального видео}}
|
||||
\label{pic10}
|
||||
\hfill
|
||||
\includegraphics[width=80mm]{pic11.png}
|
||||
\hfill
|
||||
\caption{\centering{Конечный момент реализации}}
|
||||
\label{pic11}
|
||||
\end{multicols}
|
||||
\end{figure}
|
||||
\newpage
|
||||
\hfill
|
||||
\newpage
|
||||
\addcontentsline{toc}{section}{Заключение}
|
||||
\section*{Заключение}
|
||||
В ходе выполнения лабораторной работы был проведён анализ видеозаписи, отображающей физический процесс вытекания воды из крана и её дальнейшего движения. На основе изучения динамики струи были определены ключевые особенности: постепенное изменение формы потока, появление колебаний, формирование капель и влияние случайных искажений на контур.
|
||||
|
||||
Для воспроизведения наблюдаемого эффекта была реализована программная визуализация на языке Python. Визуализация основана на интерполяции масок, описывающих форму струи на различных этапах, а также на применении дополнительных эффектов, таких как волновое искажение, градиентная заливка и генерация падающих капель.
|
||||
|
||||
В технической части использованы следующие приёмы:
|
||||
\begin{enumerate}
|
||||
\item Применение альфа-канала для создания эффекта прозрачности воды;
|
||||
\item Использование масок для задания формы струи и плавной смены фаз;
|
||||
\item Добавление случайных искажений, формирующих реалистичные колебания потока;
|
||||
\item Генерация отдельных капель, усиливающих правдоподобность анимации;
|
||||
\item Экспорт последовательности кадров в видеофайл с помощью библиотеки \texttt{imageio}.
|
||||
\end{enumerate}
|
||||
|
||||
Разработанная визуализация позволяет достоверно отобразить процесс формирования и движения струи воды, включая её деформацию и каплеобразование. Программная реализация на базе библиотек \texttt{Pygame}, \texttt{NumPy}, \texttt{imageio}, \texttt{math} и \texttt{random} обеспечила гибкость настройки и высокую наглядность результата.
|
||||
|
||||
Созданная программа может быть использована как демонстрационный инструмент при изучении явлений гидродинамики, а также как основа для дальнейших экспериментов по визуализации жидкостей в компьютерной графике. Полный исходный код представлен в приложении~A.
|
||||
|
||||
|
||||
\newpage
|
||||
\newpage
|
||||
\newpage
|
||||
|
||||
|
||||
|
||||
\lstset{
|
||||
backgroundcolor=\color{white},
|
||||
frame=single
|
||||
}
|
||||
\newpage
|
||||
\addcontentsline{toc}{section}{Приложение А. Код реализации программы}
|
||||
\addcontentsline{toc}{subsection}{А.1 waterflow\_visualization.py}
|
||||
\section*{Приложение А. Код реализации программы}
|
||||
\subsection*{А.1 waterflow\_visualization.py}
|
||||
\begin{lstlisting}[language=Python, caption={Визуализация течения воды из крана}]
|
||||
import pygame
|
||||
import math
|
||||
import random
|
||||
import imageio
|
||||
import numpy as np
|
||||
|
||||
pygame.init()
|
||||
|
||||
# --- Класс для капель воды ---
|
||||
class WaterDrop:
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.size = 18
|
||||
self.speed = 35
|
||||
|
||||
# Прозрачный бело-голубой цвет (альфа 120)
|
||||
self.color = (60, 55, 55, 30)
|
||||
|
||||
self.ellipse_width = self.size
|
||||
self.ellipse_height = self.size * 1.5
|
||||
|
||||
def update(self):
|
||||
self.y += self.speed
|
||||
|
||||
def draw(self, screen):
|
||||
drop_rect = pygame.Rect(
|
||||
self.x - self.ellipse_width / 2,
|
||||
self.y - self.ellipse_height / 2,
|
||||
self.ellipse_width,
|
||||
self.ellipse_height,
|
||||
)
|
||||
|
||||
# Временная поверхность с альфой
|
||||
drop_surface = pygame.Surface(
|
||||
(self.ellipse_width, self.ellipse_height), pygame.SRCALPHA
|
||||
)
|
||||
|
||||
# Рисуем каплю на этой поверхности
|
||||
pygame.draw.ellipse(drop_surface, self.color, (0, 0, self.ellipse_width, self.ellipse_height))
|
||||
|
||||
# Накладываем на экран
|
||||
screen.blit(drop_surface, drop_rect.topleft)
|
||||
|
||||
def is_offscreen(self, screen_height):
|
||||
return self.y > screen_height
|
||||
|
||||
|
||||
# --- Настройка экрана и загрузка ресурсов ---
|
||||
try:
|
||||
temp_background_image = pygame.image.load("waterflow_background.png")
|
||||
width, height = temp_background_image.get_size()
|
||||
use_background = True
|
||||
except pygame.error as e:
|
||||
print(f"Error loading background image: {e}")
|
||||
width, height = 400, 600
|
||||
use_background = False
|
||||
|
||||
screen = pygame.display.set_mode((width, height))
|
||||
|
||||
if use_background:
|
||||
background_image = temp_background_image.convert()
|
||||
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
masks = []
|
||||
try:
|
||||
mask1_image = pygame.image.load("mask_1.png").convert_alpha()
|
||||
mask2_image = pygame.image.load("mask_2.png").convert_alpha()
|
||||
mask3_image = pygame.image.load("mask_3.png").convert_alpha()
|
||||
mask4_image = pygame.image.load("mask_4.png").convert_alpha()
|
||||
masks = [mask1_image, mask2_image, mask3_image, mask4_image]
|
||||
except pygame.error as e:
|
||||
print(f"Error loading mask images: {e}")
|
||||
pygame.quit()
|
||||
exit()
|
||||
|
||||
# --- Вырезаем базовую подложку из отдельной картинки ---
|
||||
if use_background:
|
||||
base_mask = pygame.image.load("moving_image_4.png").convert_alpha()
|
||||
stream_base = pygame.Surface((width, height), pygame.SRCALPHA)
|
||||
stream_base.blit(background_image, (0, 0))
|
||||
stream_base.blit(base_mask, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)
|
||||
|
||||
|
||||
# --- ФУНКЦИИ ---
|
||||
def distort_only_stream(base, mask, t):
|
||||
"""Размывает и искажает подложку по маске"""
|
||||
w, h = base.get_size()
|
||||
distorted = pygame.Surface((w, h), pygame.SRCALPHA)
|
||||
|
||||
for y in range(0, h, 2):
|
||||
offset = int(5 * math.sin(0.05 * y + t * 0.1)) # сдвиг строки
|
||||
distorted.blit(base, (offset, y), (0, y, w, 2))
|
||||
|
||||
# применяем маску струи
|
||||
distorted.blit(mask, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)
|
||||
return distorted
|
||||
|
||||
|
||||
def get_stream_contours(mask_surface):
|
||||
left_contour = []
|
||||
right_contour = []
|
||||
rows_data = {}
|
||||
for y in range(mask_surface.get_height()):
|
||||
left_x = -1
|
||||
right_x = -1
|
||||
for x in range(mask_surface.get_width()):
|
||||
if mask_surface.get_at((x, y))[0] < 50:
|
||||
if left_x == -1:
|
||||
left_x = x
|
||||
right_x = x
|
||||
if left_x != -1:
|
||||
rows_data[y] = (left_x, right_x)
|
||||
for y in sorted(rows_data.keys()):
|
||||
left_x, right_x = rows_data[y]
|
||||
left_contour.append((left_x, y))
|
||||
right_contour.append((right_x, y))
|
||||
return left_contour, right_contour
|
||||
|
||||
|
||||
def make_stream_mask(left, right, width, height):
|
||||
mask_surface = pygame.Surface((width, height), pygame.SRCALPHA)
|
||||
polygon_points = left + right[::-1]
|
||||
pygame.draw.polygon(mask_surface, (255, 255, 255, 255), polygon_points)
|
||||
return mask_surface
|
||||
|
||||
|
||||
def interpolate_countours(
|
||||
start_left_contour_, start_right_contour_, end_left_contour_, end_right_contour_
|
||||
):
|
||||
interpolated_left = []
|
||||
interpolated_right = []
|
||||
min_len_left = min(len(start_left_contour_), len(end_left_contour_))
|
||||
min_len_right = min(len(start_right_contour_), len(end_right_contour_))
|
||||
|
||||
for i in range(min_len_left):
|
||||
start_x, start_y = start_left_contour_[i]
|
||||
end_x, end_y = end_left_contour_[i]
|
||||
interp_x = start_x + (end_x - start_x) * animation_progress
|
||||
wave1 = amplitude * math.sin(frequency * start_y + t)
|
||||
wave2 = (amplitude / 2) * math.sin(2 * frequency * start_y + 1.5 * t)
|
||||
noise = random.uniform(-0.5, 0.5)
|
||||
x_offset = wave1 + wave2 + noise
|
||||
interpolated_left.append((interp_x + x_offset, start_y))
|
||||
|
||||
for i in range(min_len_right):
|
||||
start_x, start_y = start_right_contour_[i]
|
||||
end_x, end_y = end_right_contour_[i]
|
||||
interp_x = start_x + (end_x - start_x) * animation_progress
|
||||
wave1 = amplitude * math.sin(frequency * start_y + t)
|
||||
wave2 = (amplitude / 2) * math.sin(2 * frequency * start_y + 1.5 * t)
|
||||
noise = random.uniform(-0.5, 0.5)
|
||||
x_offset = wave1 + wave2 + noise
|
||||
interpolated_right.append((interp_x + x_offset, start_y))
|
||||
|
||||
return interpolated_left, interpolated_right
|
||||
|
||||
|
||||
# --- Контуры масок ---
|
||||
contours = []
|
||||
for mask in masks:
|
||||
left, right = get_stream_contours(mask)
|
||||
contours.append([left, right])
|
||||
|
||||
# --- Параметры ---
|
||||
start_color = (55, 50, 45, 100)
|
||||
end_color = (180, 180, 180, 50)
|
||||
animation_progress = 0
|
||||
animation_speed = 0.005
|
||||
amplitude = 2
|
||||
frequency = 0.05
|
||||
t = 0
|
||||
reverse = False
|
||||
phase = 1
|
||||
drops = []
|
||||
FPS = 60
|
||||
drop_frequency = 1
|
||||
drop_counter = 0
|
||||
|
||||
writer = imageio.get_writer("animation_with_blur.mp4", fps=FPS)
|
||||
|
||||
# --- Главный цикл ---
|
||||
running = True
|
||||
while running:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
|
||||
# --- Отрисовка фона ---
|
||||
if use_background:
|
||||
screen.blit(background_image, (0, 0))
|
||||
else:
|
||||
screen.fill((255, 255, 255))
|
||||
|
||||
# Интерполяция контура струи
|
||||
interpolated_left, interpolated_right = interpolate_countours(
|
||||
contours[0][1], contours[0][0], contours[phase][1], contours[phase][0]
|
||||
)
|
||||
stream_mask = make_stream_mask(interpolated_left, interpolated_right, width, height)
|
||||
|
||||
# --- Размытая и искажённая подложка ---
|
||||
distorted_stream = distort_only_stream(stream_base, stream_mask, t)
|
||||
screen.blit(distorted_stream, (0, 0))
|
||||
|
||||
# --- Цветная заливка для струи ---
|
||||
polygon_points = interpolated_left + interpolated_right[::-1]
|
||||
if interpolated_left and interpolated_right:
|
||||
stream_surface = pygame.Surface((width, height), pygame.SRCALPHA)
|
||||
num_points = len(interpolated_left)
|
||||
for i in range(num_points - 1):
|
||||
r = int(start_color[0] + (end_color[0] - start_color[0]) * (i / num_points) ** 0.6)
|
||||
g = int(start_color[1] + (end_color[1] - start_color[1]) * (i / num_points) ** 0.6)
|
||||
b = int(start_color[2] + (end_color[2] - start_color[2]) * (i / num_points) ** 0.6)
|
||||
a = int(start_color[3] + (end_color[3] - start_color[3]) * (i / num_points) ** 0.2)
|
||||
points = [
|
||||
interpolated_left[i],
|
||||
interpolated_left[i + 1],
|
||||
interpolated_right[i + 1],
|
||||
interpolated_right[i],
|
||||
]
|
||||
pygame.draw.polygon(stream_surface, (r, g, b, a), points)
|
||||
screen.blit(stream_surface, (0, 0))
|
||||
|
||||
coef = 3 if phase == 0 or phase == 1 else 1.5
|
||||
# --- Анимация прогресса ---
|
||||
animation_progress += animation_speed if not reverse else animation_speed * coef
|
||||
if animation_progress >= 1.0 or animation_progress <= 0.0:
|
||||
animation_speed *= -1
|
||||
if animation_progress <= 0.0:
|
||||
phase = min(3, phase + 1)
|
||||
drop_counter +=1
|
||||
reverse = not reverse
|
||||
animation_progress = max(0.0, min(1.0, animation_progress))
|
||||
|
||||
t += 9
|
||||
if drop_counter >= drop_frequency:
|
||||
last_point_y = min(p[1] for p in polygon_points) + 25
|
||||
bottom_points_x = [p[0] for p in polygon_points if p[1] == last_point_y]
|
||||
if bottom_points_x:
|
||||
spawn_x = sum(bottom_points_x) / len(bottom_points_x)
|
||||
drops.append(WaterDrop(spawn_x + random.randint(-5, 5), last_point_y))
|
||||
drop_counter = 0
|
||||
|
||||
new_drops_list = []
|
||||
for drop in drops:
|
||||
drop.update()
|
||||
drop.draw(screen)
|
||||
if not drop.is_offscreen(height):
|
||||
new_drops_list.append(drop)
|
||||
drops = new_drops_list
|
||||
pygame.display.flip()
|
||||
|
||||
# --- Запись кадра ---
|
||||
frame = pygame.surfarray.array3d(screen)
|
||||
frame = np.swapaxes(frame, 0, 1)
|
||||
writer.append_data(frame)
|
||||
|
||||
clock.tick(FPS)
|
||||
|
||||
writer.close()
|
||||
pygame.quit()
|
||||
|
||||
\end{lstlisting}
|
||||
\end{document}
|
||||
1
lab4/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!report
|
||||
6
lab4/report/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
*
|
||||
|
||||
!.gitignore
|
||||
!*.tex
|
||||
!*.png
|
||||
!*.jpg
|
||||
BIN
lab4/report/pic1.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
lab4/report/pic10.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
lab4/report/pic11.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
lab4/report/pic12.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
lab4/report/pic13.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
lab4/report/pic14.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
lab4/report/pic15.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
lab4/report/pic16.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
lab4/report/pic17.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
lab4/report/pic18.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
lab4/report/pic19.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
lab4/report/pic2.png
Normal file
|
After Width: | Height: | Size: 975 KiB |
BIN
lab4/report/pic20.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
lab4/report/pic21.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
lab4/report/pic22.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
lab4/report/pic23.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
lab4/report/pic24.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
lab4/report/pic25.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
lab4/report/pic26.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
lab4/report/pic27.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
lab4/report/pic28.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
lab4/report/pic29.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
lab4/report/pic3.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
lab4/report/pic30.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
lab4/report/pic31.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
lab4/report/pic32.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
lab4/report/pic4.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
lab4/report/pic5.png
Normal file
|
After Width: | Height: | Size: 853 KiB |
BIN
lab4/report/pic6.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
lab4/report/pic7.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
BIN
lab4/report/pic8.png
Normal file
|
After Width: | Height: | Size: 840 KiB |
BIN
lab4/report/pic9.png
Normal file
|
After Width: | Height: | Size: 18 KiB |