834 lines
45 KiB
TeX
834 lines
45 KiB
TeX
|
||
\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}
|