Files
computer-graphics/lab3/report/Лаб3. Тищенко.tex
2025-09-17 15:12:18 +03:00

834 lines
45 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\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}