diff --git a/lab3/.gitignore b/lab3/.gitignore new file mode 100644 index 0000000..9512215 --- /dev/null +++ b/lab3/.gitignore @@ -0,0 +1 @@ +!report \ No newline at end of file diff --git a/lab3/report/.gitignore b/lab3/report/.gitignore new file mode 100644 index 0000000..81d1b44 --- /dev/null +++ b/lab3/report/.gitignore @@ -0,0 +1,6 @@ +* + +!.gitignore +!*.tex +!*.png +!*.jpg \ No newline at end of file diff --git a/lab3/report/pic1.png b/lab3/report/pic1.png new file mode 100644 index 0000000..ad6043e Binary files /dev/null and b/lab3/report/pic1.png differ diff --git a/lab3/report/pic10.png b/lab3/report/pic10.png new file mode 100644 index 0000000..0a1601c Binary files /dev/null and b/lab3/report/pic10.png differ diff --git a/lab3/report/pic11.png b/lab3/report/pic11.png new file mode 100644 index 0000000..8664422 Binary files /dev/null and b/lab3/report/pic11.png differ diff --git a/lab3/report/pic2.png b/lab3/report/pic2.png new file mode 100644 index 0000000..c349001 Binary files /dev/null and b/lab3/report/pic2.png differ diff --git a/lab3/report/pic3.png b/lab3/report/pic3.png new file mode 100644 index 0000000..a2d8c58 Binary files /dev/null and b/lab3/report/pic3.png differ diff --git a/lab3/report/pic4.png b/lab3/report/pic4.png new file mode 100644 index 0000000..fd891a8 Binary files /dev/null and b/lab3/report/pic4.png differ diff --git a/lab3/report/pic5.png b/lab3/report/pic5.png new file mode 100644 index 0000000..aca93dc Binary files /dev/null and b/lab3/report/pic5.png differ diff --git a/lab3/report/pic6.png b/lab3/report/pic6.png new file mode 100644 index 0000000..ec2f86a Binary files /dev/null and b/lab3/report/pic6.png differ diff --git a/lab3/report/pic7.png b/lab3/report/pic7.png new file mode 100644 index 0000000..381f7c3 Binary files /dev/null and b/lab3/report/pic7.png differ diff --git a/lab3/report/pic8.png b/lab3/report/pic8.png new file mode 100644 index 0000000..a1f1b68 Binary files /dev/null and b/lab3/report/pic8.png differ diff --git a/lab3/report/pic9.png b/lab3/report/pic9.png new file mode 100644 index 0000000..bc4748f Binary files /dev/null and b/lab3/report/pic9.png differ diff --git a/lab3/report/Лаб3. Тищенко.tex b/lab3/report/Лаб3. Тищенко.tex new file mode 100644 index 0000000..80b3eff --- /dev/null +++ b/lab3/report/Лаб3. Тищенко.tex @@ -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}