Визуализация воды
Санкт-Петербургский Государственный
Электротехнический
Университет
ФКТИ
Кафедра
МОЭВМ
Дисциплина
«Компьютерная графика»
Пояснительная записка
к
курсовой работе по
теме
"
Визуализация воды "
Санкт-Петербург
2008 г.
Постановка
и анализ задачи
Моделирование водных поверхностей является сложной, но интересной задачей. Здесь можно выделить такие подзадачи, как моделирование небольших водоемов (с видимыми границами), для которых характерны небольшие изменения поверхности, то есть небольшие колебания, а также интерференция колебаний от нескольких всплесков и отраженных колебаний от границ; большие водные поверхности: здесь рассматривают небольшие поверхностные колебания (как правило, в этом случае можно обойтись изменениями лишь текстуры или нескольких текстур поверхности (Bump Mapping и др., см. далее) не деформируя непосредственно саму поверхность), небольшие колебания: в этом случае поверхность разбивается на треугольники, но достаточно большого размера и над поверхностью производят небольшие колебания, которые соответствуют, например, небольшим волнам, большие колебания: этот большие волны, брызги и др., здесь происходят значительные деформации водных поверхностей, которые достаточно сложно физически описываются, поэтому большие волны практически никогда не визуализируются. Брызги можно визуализировать, например, используя массив частиц (маленьких плоскостей, ориентированных по нормали на наблюдателя), для которых устанавливаются начальные скорости, а дальнейший их полет происходит по гравитационному закону.
Моделирование
небольших колебаний и
Моделирование небольшие водных поверхностей.
Предположим, что водная поверхность представляет собой квадратную сетку размером NxN. Водная поверхность обладает связностью (в физическом смысле), то есть внешнее воздействие на какой-то один узел влечет также изменение, как минимум, в соседних узлах. Физически описать такое взаимодействие можно описать с достаточной степенью точности, если предположить, что соседние узла связаны между собой линейно-упругой пружиной.
Для каждого узла вводятся следующие характеристики:
mi - масса узла;
- декартовы координаты узла;
- скорость узла;
- ускорение узла;
- сумма всех сил, действующих на узел;
Выводить на экран такую поверхность можно следующим образом: для каждых четырех узлов: pij pi+1j pi+1j+1 pij+1 на экран выводятся 2 треугольника: pijpi+1jpij+1 и pij+1pi+1jpi+1j+1.
Выражение для силы пружины Sk, действующей на узел pi, следует из закона Гука:
| (1) |
где , - длина покоя и текущая длина пружины соответственно; - единичный вектор в направлении пружины.
Или, если расписать:
| (1)' |
Уравнения движения всей такой системы легко выводятся из общего курса физики:
- масса узла;
- сумма сил пружин, прикрепленных к узлу;
- сумма внешних сил, приложенных к узлу;
- коэффициент демпфирования, характеризующий
сопротивление среды, в которой движется
узел.
Неплохие результаты дают следующие характеристики системы:
| Шаг сетки,
H |
Масса узла,
mij |
Коэффициент упругости,
ск |
Демпфирование, | Шаг интегрирования, |
| 1.0 | 4.0 | 7.0 | 0.05 | 0.4 |
Управлять возмущением такой системы достаточно просто: необходимо лишь изменять вектор внешних сил на некоторые узлы решетки (то есть непосредственно осуществлять внешнее воздействие). Достаточно интересным (может быть, даже более более красивым с точки зрения графики) является подход, когда мы изменяем непосредственно координаты и скорости в некоторых узлах, то есть сразу указываем результат воздействия. Также возможны гибридные подходы этих двух методов.
Задавая различные условия на узлах мы можем моделировать различные водные поверхности с различными граничными и поверхностными условиями. Например, если мы хотим моделировать бассейн или некоторую другую емкость с водой, то необходимо разрешить лишь вертикальные перемещения граничных узлов, то есть положить их горизонтальные перемещения равными нулю. Также, здесь будет хорошо заметна интерференция волн и отражение волн от границы. Если нам необходимо получить небольшой водоем, для которого характерна неизменная береговая линия, то необходимо полностью запретить перемещения граничных узлов.
Если запретить горизонтальные перемещения на все узлы, то это подчеркнет эффект прохождения волн друг сквозь друга. Также если запретить перемещения некоторых внутренних узлов, то можно смоделировать, например, островок, от которого волны будут также еще и отражаться. В данном случае узлы должны повторять контур островка.
Чтобы задать точечное возмущение можно воспользоваться следующими способами: изменить скорость в данном узле и в некоторой его окрестности, или сделать тоже самое с координатами. Характер возмущения описывается с помощью гауссовского распределения с некоторым радиусом. Также, чтобы данный процесс не замедлял основной цикл программы, данные значения просчитываются заранее, заносятся в массив, а затем во время работы программы прибавляются к текущим координатам или скоростям узлов.
Отражения.
Вода является очень хорошим отражателем, и реалистичность отображаемой водной поверхности существенно зависит от реализации отражения объектов, находящихся над водой и у поверхности воды. Но, так как вода является динамически изменяемым объектом (в данной задаче), то отражения тоже должны, по возможности, повторять форму движения водной поверхности.
Сложность моделирования отражений состоит в том, что отражения являются зависимыми от вектора наблюдения и положения наблюдателя: если наблюдатель сдвинется или повернется, то отражения изменятся. То есть при статическом положении наблюдателя можно заранее просчитать отражение, но если существует возможность изменения положения наблюдателя или вектора наблюдения, то отражения заранее просчитаны быть не могут, поскольку дискретность изменения положения наблюдателя и вектора наблюдения должна быть произвольной.
Обычно, водные поверхности моделируются на открытом пространстве. И здесь необходимо учитывать эффект отражения неба от поверхности воды. Для этого берется текстура неба и накладывается на поверхность воды, но координаты текстуры не изменяются в зависимости от изменения водной поверхности. В OpenGL это достигается следующим образом:
Прозрачность и преломление.
Вода также является в достаточно прозрачной. Но коэффициент преломления воды не совпадает с аналогичным коэффициента для воздуха, отсюда, объекты выглядят в воде искаженными. Это также необходимо учитывать при моделировании.
Для дна необходимо
подобрать соответствующую
Для водных поверхностей также характерны каустики - эффект прохождения света сквозь водную поверхность, подверженную колебаниям. В этом случае на дне возникают некоторые световые пятна, которые также можно визуализировать. Для этого над поверхностью дна делается еще один полупрозрачный слой из плоскостей, с наложенными на них текстурами каустиков. Также нельзя забывать, что так как поверхность динамична, то и текстура с каустиками должна динамически изменяться. Обычно поступают следующим образом: берется некий заранее просчитанный набор текстур с каустиками (10-15) и попеременно сменяют одну другой в соответствие с изменением соответствующей водной поверхности, в конце данный процесс зацикливают.
Bump mapping.
Bump mapping используется для передачи неровностей поверхности. Реально в природе не существует абсолютно плоских объектов, и большинство объектов, которые мы видим в реальной жизни обладают различными шероховатостями. В моделировании при передаче общей формы объекта мы разбиваем его на полигоны. Но для передачи таких "мелких" свойств, как неровности необходимо модифицировать текстуру объекта или накладывать на одну плоскость несколько текстур (делать несколько полупрозрачных плоскостей, параллельных данной, но находящихся "немного выше" нее или использовать мультитекстурирование, входящие в расширение OpenGL).
Условно говоря, нам необходимо лишь менять направление нормали в каждой точке плоскости для передачи неровностей, и это возможно, используя расширения OpenGL. Но мы будем рассматривать лишь стандарт OpenGL. Здесь подобного можно добиться, если выводить вместо каждой плоскости, для которой мы хотим произвести bump mapping, три плоскости, параллельные друг другу и находящиеся на небольшом отдалении друг от друга (чтобы визуально этого было не заметно).
Здесь используются две текстуры: исходная текстура воды (как уже отмечалось, в этой текстуре также можно передать отражение неба, то есть исходная текстура, есть комбинация текстур непосредственно воды и зеркально отраженной текстуры неба, а так как небо, будем считать, статично, то такая текстура создается до работы приложения и в течении всего времени работы не меняется) и текстура, отражающая неровности поверхности (черно белая), которая условно говоря, задает карту высот (будем называть ее картой поверхности).
Сначала мы загружаем исходную текстуру воды (1), а из карты поверхности создаем две текстуры: первая (2)- точно такая же, но в каждой точке значение делиться на два (так как текстура черно-белая, то будем считать что мы уменьшаем яркость в два раза); вторая (3)- это инвертированная (по значению яркости) карта поверхности, для которой также значение яркости делится на 2, то есть здесь: <source_pixel> = (255 - <map_pixel>) / 2.
Вывод на экран
происходит в следующей
Перед выводом
следующей плоскости
Сама плоскость выводится выводится на том же месте, что и предыдущая, но "чуть-чуть" приподнята относительно вектора наблюдения (чтобы не было перекрытий) и наложенной текстурой (3). Для нее изменяются координаты текстурами, чтобы отобразить эффект неровности поверхности. Пересчет текстурных координат происходит относительно вектора наблюдения. То есть вызывается команда glTexCoord2f(old_tex_x + tex_dx, old_tex_y + tex_dy), tex_dx и tex_dy берутся при подсчете вектора свига: tex_d = {tex_dx, tex_dy, tex_dz}. Вычисляется он следующим образом: tex_d = man_pos - plane_center, где man_pos - вектор координат месторасположения наблюдателя, plane_center - центральная точка текущей плоскости (вместо нее, для ускорения, можно взять одну из вершин рассматриваемой плоскости). Далее tex_d нормируется (представляется в единичный вектор) и далее все координаты домножаются на некоторое небольшое число (в зависимости от глобальной метрики сцены), которое и будет характеризовать расхождение, а значит и высоту шероховатости. Если указать это число достаточно большим, то будут видны просто две плоскости и никакого эффекта шероховатости создаваться не будет. В этом особенность этого метода: можно передать лишь небольшие ("невысокие") неровности, для других необходимо производить дополнительную триангуляцию.
Перед выводом третей (последней плоскости) необходимо установить следующие параметры OpenGL:
Здесь также выводится плоскость, которая параллельна предыдущим, но "чуть-чуть" приподнята относительно них по вектору наблюдения, для избежания пересечений. На плоскость накладывается текстура (1), при этом координаты никак не пересчитываются (в отличии от предыдущей).
В итоге получается изображение, на котором виден "микрорельеф".
Расскажем теперь, как bump mapping применяется по отношению к моделированию водных поверхностей. Предположим, что необходимо моделировать большую водную поверхность, без видимых границ (граница - линия горизонта), на которой присутствуют лишь небольшие волны, то есть она достаточно "спокойна". Тогда можно не разбивать водную поверхность на множество плоскостей, а сделать одну большую плоскость, для которой применить bump mapping с динамически изменяемой картой поверхности. Обычно сразу загружается достаточное количество карт глубины (10-15), затем они меняют друг друга, в соответствие со временем изменения водной поверхности, а в конце происходит их зацикливание. Таким образом нам достаточно выводить только один полигон (то есть 3 - в нашей реализации bump mapping'а), а не разбивать поверхность на множество полигонов.
Реально же используют следующую модель: водную поверхность разбивают на полигонов (но уже не так много как раньше) и с помощью этих полигонов задают общую модель поведения водной поверхности, например, в соответствии с алгоритмом рассказанным в самом начале. Затем для каждой плоскости производится bump mapping, для передачи более точных характеристик водной поверхности.
Текст программы
unit GrisSpinEdit;
interface
uses Windows, Classes, StdCtrls, ExtCtrls, Controls, Messages, SysUtils,
Forms, Graphics, Menus, Buttons,spin;
type
{ TGrisSpinEdit }
TGrisSpinEdit = class(TCustomEdit)
private
FMinValue: Extended;
FMaxValue: Extended;
FIncrement: Extended;
FButton: TSpinButton;
FEditorEnabled: Boolean;
FBackUp: Extended;
function GetMinHeight: Integer;
function GetValue: Extended;
function CheckValue (NewValue: Extended): Extended;
procedure SetValue (NewValue: Extended);
procedure SetMinValue (Value:Extended);
procedure SetMaxValue (Value:Extended);
procedure SetEditRect;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure CMEnter(var Message: TCMGotFocus); message CM_ENTER;
procedure CMExit(var Message: TCMExit); message CM_EXIT;
procedure WMPaste(var Message: TWMPaste); message WM_PASTE;
procedure WMCut(var Message: TWMCut); message WM_CUT;
protected
procedure Change; override;
procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
function IsValidChar(Key: Char): Boolean; virtual;
procedure UpClick (Sender: TObject); virtual;
procedure DownClick (Sender: TObject); virtual;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure KeyPress(var Key: Char); override;
procedure CreateParams(var Params: TCreateParams); override;
procedure CreateWnd; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Button: TSpinButton read FButton;
published
property Anchors;
property AutoSelect;
property AutoSize;
property Color;
property Constraints;
property Ctl3D;
property DragCursor;
property DragMode;
property EditorEnabled: Boolean read FEditorEnabled write FEditorEnabled default True;
property Enabled;
property Font;
property Increment: Extended read FIncrement write FIncrement;
property MaxLength;
property MaxValue: Extended read FMaxValue write SetMaxValue;
property MinValue: Extended read FMinValue write SetMinValue;
property ParentColor;
property ParentCtl3D;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ReadOnly;
property ShowHint;
property TabOrder;
property TabStop;
property Value: Extended read GetValue write SetValue;
property Visible;
property OnChange;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDrag;
end;
//----------------------------
procedure Register;
//----------------------------
implementation
procedure Register;
begin
RegisterComponents('Gris''s Edits', [TGrisSpinEdit]);
end;
{ TGrisSpinEdit }
constructor TGrisSpinEdit.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FButton := TSpinButton.Create(Self);
FButton.Width := 15;
FButton.Height := 17;
FButton.Visible := True;
FButton.Parent := Self;
FButton.FocusControl := Self;
FButton.OnUpClick := UpClick;
FButton.OnDownClick := DownClick;
Text := '0';
ControlStyle := ControlStyle - [csSetCaption];
FIncrement := 1;
FEditorEnabled := True;
ParentBackground := False;
end;
destructor TGrisSpinEdit.Destroy;
begin
FButton := nil;
inherited Destroy;
end;
procedure TGrisSpinEdit.GetChildren(
begin
end;
procedure TGrisSpinEdit.KeyDown(var Key: Word; Shift: TShiftState);
begin
if Key = VK_UP then UpClick (Self)
else if Key = VK_DOWN then DownClick (Self);
inherited KeyDown(Key, Shift);
end;
procedure TGrisSpinEdit.KeyPress(var Key: Char);
begin
if not IsValidChar(Key) then
begin
Key := #0;
end;
if Key <> #0 then inherited KeyPress(Key);
end;
function TGrisSpinEdit.IsValidChar(Key: Char): Boolean;
begin
Result := (Key in [DecimalSeparator, '-', '0'..'9']) or
((Key < #32) and (Key <> Chr(VK_RETURN)));
if (Key='-')and(pos('-',Text)>0)
then Result:=false;
if not FEditorEnabled and Result and ((Key >= #32) or
(Key = Char(VK_BACK)) or (Key = Char(VK_DELETE))) then
Result := False;
end;
procedure TGrisSpinEdit.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
{ Params.Style := Params.Style and not WS_BORDER; }
Params.Style := Params.Style or ES_MULTILINE or WS_CLIPCHILDREN;
end;
procedure TGrisSpinEdit.CreateWnd;
begin
inherited CreateWnd;
SetEditRect;
end;
procedure TGrisSpinEdit.SetEditRect;
var
Loc: TRect;
begin
SendMessage(Handle, EM_GETRECT, 0, LongInt(@Loc));
Loc.Bottom := ClientHeight + 1; {+1 is workaround for windows paint bug}
Loc.Right := ClientWidth - FButton.Width - 2;
Loc.Top := 0;
Loc.Left := 0;
SendMessage(Handle, EM_SETRECTNP, 0, LongInt(@Loc));
SendMessage(Handle, EM_GETRECT, 0, LongInt(@Loc)); {debug}
end;
procedure TGrisSpinEdit.WMSize(var Message: TWMSize);
var
MinHeight: Integer;
begin
inherited;
MinHeight := GetMinHeight;
{ text edit bug: if size to less than minheight, then edit ctrl does
not display the text }
if Height < MinHeight then
Height := MinHeight
else if FButton <> nil then
begin
if NewStyleControls and Ctl3D then
FButton.SetBounds(Width - FButton.Width - 5, 0, FButton.Width, Height - 5)
else FButton.SetBounds (Width - FButton.Width, 1, FButton.Width, Height - 3);
SetEditRect;
end;
end;
function TGrisSpinEdit.GetMinHeight: Integer;
var
DC: HDC;
SaveFont: HFont;
I: Integer;
SysMetrics, Metrics: TTextMetric;
begin
DC := GetDC(0);
GetTextMetrics(DC, SysMetrics);
SaveFont := SelectObject(DC, Font.Handle);
GetTextMetrics(DC, Metrics);
SelectObject(DC, SaveFont);
ReleaseDC(0, DC);
I := SysMetrics.tmHeight;
if I > Metrics.tmHeight then I := Metrics.tmHeight;
Result := Metrics.tmHeight + I div 4 + GetSystemMetrics(SM_CYBORDER) * 4 + 2;
end;
procedure TGrisSpinEdit.UpClick (Sender: TObject);
begin
if ReadOnly then MessageBeep(0)
else Value := Value + FIncrement;
end;
procedure TGrisSpinEdit.DownClick (Sender: TObject);
begin
if ReadOnly then MessageBeep(0)
else Value := Value - FIncrement;
end;
procedure TGrisSpinEdit.WMPaste(var Message: TWMPaste);
begin
if not FEditorEnabled or ReadOnly then Exit;
inherited;
end;
procedure TGrisSpinEdit.WMCut(var Message: TWMPaste);
begin
if not FEditorEnabled or ReadOnly then Exit;
inherited;
end;
procedure TGrisSpinEdit.CMExit(var Message: TCMExit);
begin
inherited;
if CheckValue (Value) <> Value then
SetValue (Value);
Value:=FBackUp;
end;
function TGrisSpinEdit.GetValue: Extended;
begin
try
if (Text<>'')and(Text<>'-')
then Result := StrToFloat (Text)
else Result := FBackUp;
except
Result := FBackUp;
end;
end;
procedure TGrisSpinEdit.SetValue (NewValue: Extended);
begin
Text := FloatToStr (CheckValue (NewValue));
end;
function TGrisSpinEdit.CheckValue (NewValue: Extended): Extended;
begin
Result := NewValue;
if (FMaxValue <> FMinValue) then
begin
if NewValue < FMinValue then
Result := FMinValue
else if NewValue > FMaxValue then
Result := FMaxValue;

- Визуализация данных RDFa и разработка веб-интерфейса для управлениям ими с элементами JavaScript и технологии Ajax
- Визуализация данных RDFa и разработка веб-интерфейса для управлениям ими с элементами Java Script и технологииAjax.Предметная область-Кинотеатры
- Визуализация женского образа в отечественном кинематографе 1930-х годов
- Визуализация рекламной идеи средствами графического дизайна
- Визуализация системы для исследования
- Визуализация численных методов. Решение обыкновенных дифференциальных уравнений
- Визуализация численных методов. Решение обыкновенных дифференциальных уравнений
- Визовая поддержка
- Визовые и безвизовые страны. Тонкости оформления виз и визовый запрос
- Визовые формальности
- Визовый режим
- Визуaлизaция имиджa мeнeджeрa
- Визуалды компоненттерді қолдану
- Визуалды программалау негіздері