Цель лекции: Создать приложения на C# и Object Pascal, моделирующие перемещение наблюдателя в лабиринте.
Пример № 13.1 Программа "Лабиринт" (вариант 1). Пример реализует программу, моделирующую перемещение наблюдателя в помещении.
Пример № 13.2 Программа "Лабиринт" (вариант 2). Пример дополняет пример 13.1: выполняется наложение текстур на стены, добавлена вращающаяся сфера с текстурой.
В данной лекции будет рассмотрен пример создания программы, моделирующей перемещение наблюдателя в помещении. На примере этой программы можно увидеть использование большей части тех возможностей библиотеки OpenGL, которые были описаны в предыдущих лекциях.
Создание программы будет выполняться в два этапа. На первом этапе будет создано базовое приложение. В этом приложении будут отображаться стены лабиринта, и выполняться перемещение наблюдателя в лабиринте. На втором этапе в приложение будет добавлена текстура для стен и вращающаяся сфера с текстурой поверхности земли.
Первоначально рассмотрим создание приложения, в котором не будет использоваться освещение и стены будут иметь различный цвет. Для моделирования нахождения наблюдателя в помещении потребуется использовать перспективную проекцию. Параметры перспективной проекции необходимо задать таким образом, чтобы боковые границы области видимости были меньше расстояния до боковых стен. Дальнюю границу области видимости расположим дальше, чем расстояние между самыми удалёнными друг от друга стенами, чтобы наблюдатель всегда мог видеть границу помещения лабиринта (листинги 13.1 и 13.2 ).
private void glControl1_Resize(object sender, EventArgs e)
{
GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Frustum(-0.5, 0.5, -0.5, 0.5, 0.5, 50);
GL.MatrixMode(MatrixMode.Modelview);
glControl1.Invalidate();
}
procedure TfrmGL.FormResize(Sender: TObject); begin glViewport(0, 0, ClientWidth, ClientHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity; glFrustum(-0.5, 0.5, -0.5, 0.5, 0.5, 50); glMatrixMode(GL_MODELVIEW); InvalidateRect(Handle, nil, False); end;
Для описания расположения координат стен в программе на C# будет использоваться массив структур типа PointFloat. Тип PointFloat объявлен в программе:
public struct PointFloat
{
public double X;
public double Y;
}
В программе на Object Pascal для описания координат стен используется тип TPointFloat, который объявлен в модуле OpenGL.pas:
Type
TPointFloat = record
x, y: Single;
end;
Для определения каждой стены в программе будет использоваться массив структур типа ) и типа ).
public struct Wall
{
public int L;
public int R;
public Color C;
}
type
TWall = record
l, r: Integer;
c: TColor;
end;
Поля структуры имеют следующие значения:
l, r – левая и правая границы стены, которые определяются индексами элементов массива coners; c – цвет стены, который используется при отображении стен в режиме без освещения.Координаты стен могут быть определены в виде массивов ( и ).
// координаты углов стен
PointFloat[] coners = new []{
new PointFloat{X = -10, Y = -10},
new PointFloat{X = -10, Y = 10},
new PointFloat{X = 10, Y = 10},
new PointFloat{X = 10, Y = -10},
new PointFloat{X = 3, Y = 10},
new PointFloat{X = 3, Y = 0}
};
// расположение стен в лабиринте
Wall[] plan = new[]
{
new Wall{L = 0, R = 1, C = Color.Red},
new Wall{L = 1, R = 2, C = Color.Green},
new Wall{L = 2, R = 3, C = Color.Yellow},
new Wall{L = 3, R = 0, C = Color.Blue},
new Wall{L = 4, R = 5, C = Color.Aqua}
};
const
coners: array [0..5] of TPointFloat = (
(x:-10; y:-10),
(x:-10; y: 10),
(x: 10; y: 10),
(x: 10; y:-10),
(x: 0; y:-10),
(x: 0; y: 0));
plan: array [0..4] of TWall = (
(l:0;r:1;c:clRed),(l:1;r:2;c:clGreen),
(l:2;r:3;c:clYellow),(l:3;r:0;c:clBlue),
(l:4;r:5;c: clAqua));
Информация обо всех стенах лабиринта будет храниться в массиве plan, каждый элемент которого описывает расположение и цвет одной из стен лабиринта.
В программе также будут использоваться несколько глобальных переменных и констант ( и ).
Bitmap bmpBrick; Bitmap bmpEarth; double AngleX = 0; double AngleY = 0; double AngleZ = 0; // шаг перемещения наблюдателя в лабиринте const double Step = 0.2; // высота стен лабиринта const double hdl = 5; const double AngleDl = 5; // координаты положения наблюдателя PointFloat Pos;
var // переменные для хранения данных о тектурах texBrick, texEarth: TGLTexture; // направление движения наблюдателя (угол его поворота), // изменяется с помощью клавиш со стрелками влево и вправо AngleY: integer; // координаты положения наблюдателя Pos: TPointFloat; // угол поворота сферы RY: integer; Const AngleDl=5; //угол разворота при нажатии на клавишу Step = 0.2; // шаг перемещения наблюдателя в лабиринте hdl=5; // высота стен лабиринта
Переменная Pos будет использоваться для хранения текущих координат положения наблюдателя в лабиринте, константа Step будет определять изменение положения наблюдателя в направлении перемещения ("длина шага" наблюдателя в лабиринте).
Изображение лабиринта будет формироваться на основе данных массивов и ).
private void glControl1_Paint(object sender, PaintEventArgs e)
{
// очистка буферов цвета и глубины
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
// поворот изображения
GL.LoadIdentity();
GL.Rotate(AngleY, 0.0, 1.0, 0.0);
GL.Translate(Pos.X, 0, Pos.Y);
// стены лабиринта
for (int i = 0; i < plan.Length; ++i)
{
// длина стены
double wdl = Math.Sqrt(Math.Pow(coners[plan[i].L].X - coners[plan[i].R].X, 2)
+ Math.Pow(coners[plan[i].L].Y - coners[plan[i].R].Y, 2)) / 4;
GL.Color4(plan[i].C);
GL.Begin(BeginMode.Quads);
GL.TexCoord2(0, 0); GL.Vertex3(coners[plan[i].L].X, -hdl,
coners[plan[i].L].Y);
GL.TexCoord2(0, hdl / 2); GL.Vertex3(coners[plan[i].L].X, hdl,
coners[plan[i].L].Y);
GL.TexCoord2(wdl, hdl / 2); GL.Vertex3(coners[plan[i].R].X, hdl,
coners[plan[i].R].Y);
GL.TexCoord2(wdl, 0); GL.Vertex3(coners[plan[i].R].X, -hdl,
coners[plan[i].R].Y);
GL.End();
}
GL.Flush();
GL.Finish();
glControl1.SwapBuffers();
}
procedure TForm1.FormPaint(Sender: TObject);
var i: integer;
begin
// очистка буфера цвета
glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); glLoadIdentity;
// перемещение наблюдателя
glRotatef(AngleY, 0.0, 1.0, 0.0);
glTranslatef(Pos.X, 0, Pos.Y);
// стены лабиринта
for i:=0 to High(plan) do begin
glBegin(GL_QUADS);
glColor(GetRValue(plan[i].c),GetGValue(plan[i].c),
GetBValue(plan[i].c));
glVertex3f(coner[plan[i].l].x, -hdl, coner[plan[i].l].y);
glVertex3f(coner[plan[i].l].x, hdl, coner[plan[i].l].y);
glVertex3f(coner[plan[i].r].x, hdl, coner[plan[i].r].y);
glVertex3f(coner[plan[i].r].x, -hdl, coner[plan[i].r].y);
glEnd;
end;
glFlush;
glFinish;
SwapBuffers(DC);
end;
Для возможности перемещения наблюдателя в лабиринте в программе будут использоваться клавиши со стрелками. Клавиши со стрелками влево и вправо будут использоваться для поворота наблюдателя в соответствующем направлении, клавиши со стрелками вперед и назад – для перемещения наблюдателя вперед и назад ( и ).
private void glControl1_KeyDown(object sender, KeyEventArgs e)
{
switch(e.KeyCode)
{
case Keys.Left: AngleY += -AngleDl; break;
case Keys.Right: AngleY -= -AngleDl; break;
case Keys.Up:
Pos.X = Pos.X - Step * Math.Sin(AngleY * Math.PI / 180);
Pos.Y = Pos.Y + Step * Math.Cos(AngleY * Math.PI / 180);
break;
case Keys.Down:
Pos.X = Pos.X + Step * Math.Sin(AngleY * Math.PI / 180);
Pos.Y = Pos.Y - Step * Math.Cos(AngleY * Math.PI / 180);
break;
}
if (AngleY>=360) AngleY -= 360;
glControl1.Invalidate();
}
procedure TfrmGL.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
case Key of
VK_LEFT: inc(AngleY, -AngleDl);
VK_RIGHT: dec(AngleY, -AngleDl);
VK_UP: begin
PosX:=PosX - Step*Sin(AngleY*pi/180);
PosZ:=PosZ + Step*Cos(AngleY*pi/180);
end;
VK_DOWN: begin
PosX:=PosX + Step*Sin(AngleY*pi/180);
PosZ:=PosZ - Step*Cos(AngleY*pi/180);
end;
end;
if AngleY>=360 then dec (AngleY, 360);
InvalidateRect(Handle, nil, False);
end;
В результате будет получено приложение, которое рисует замкнутое помещение с одной дополнительной стеной, все стены имеют различный цвет. Программа позволяет наблюдателю перемещаться в этом помещении (рис 13.1). В полученном приложении не контролируются границы стен.
(рис 13.1) Изображение лабиринта (вариант 1)
В качестве изображения текстуры для стен можно использовать фрагмент кирпичной стены (рис 13.2), а для сферы - изображение поверхности земли (рис 13.3).
(рис 13.2) Изображение фрагмента кирпичной стены |
(рис 13.3) Изображение поверхности Земли |
Для этого можно использовать фрагменты кода приведённые в листингах 13.15 и 13.16 .
private void Form1_Load(object sender, EventArgs e)
{
…
bmpBrick = new Bitmap("brick.bmp");
bmpEarth = new Bitmap("earth256x128.bmp");
GL.TexParameter(TextureTarget.Texture2D,
TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter,
(int)TextureMagFilter.Linear);
}
procedure TfrmGL.FormCreate(Sender: TObject);
begin
…
// зарузка изображений текстур
LoadTextureFromBMP('BRICK.BMP', texBrick);
LoadTextureFromBMP('earth256x128.bmp', texEarth);
// параметры масштабирования текстур
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
…
end;
Для формирования изображения сферы с текстурой можно использовать пример формирования сферы, приведенный в главе 9 (листинги 13.17 и 13.18 ).
private void glControl1_Paint(object sender, PaintEventArgs e)
{
GL.ClearColor(0.5f, 0.5f, 0.75f, 1.0f); // цвет фона
// очистка буферов цвета и глубины
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
// поворот изображения
GL.LoadIdentity();
GL.Rotate(AngleY, 0.0, 1.0, 0.0);
GL.Translate(Pos.X, 0, Pos.Y);
GLTexture.LoadTexture(bmpEarth);
GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.Lighting);
GL.PushMatrix();
GL.Rotate(RY, 0.0, 1.0, 0.0);
Sphere(1, 20, 20);
GL.PopMatrix();
GL.Disable(EnableCap.Lighting);
GL.Disable(EnableCap.Texture2D);
// стены лабиринта
GLTexture.LoadTexture(bmpBrick);
GL.Enable(EnableCap.Texture2D);
for (int i = 0; i < plan.Length; ++i)
{
// длина стены
double wdl = Math.Sqrt(Math.Pow(coners[plan[i].L].X -coners[plan[i].R].X, 2)
+ Math.Pow(coners[plan[i].L].Y - coners[plan[i].R].Y, 2)) / 4;
GL.Begin(BeginMode.Quads);
GL.TexCoord2(0, 0);
GL.Vertex3(coners[plan[i].L].X, -hdl, coners[plan[i].L].Y);
GL.TexCoord2(0, hdl / 2);
GL.Vertex3(coners[plan[i].L].X, hdl, coners[plan[i].L].Y);
GL.TexCoord2(wdl, hdl / 2);
GL.Vertex3(coners[plan[i].R].X, hdl, coners[plan[i].R].Y);
GL.TexCoord2(wdl, 0);
GL.Vertex3(coners[plan[i].R].X, -hdl, coners[plan[i].R].Y);
GL.End();
}
GL.Disable(EnableCap.Texture2D);
GL.Flush();
GL.Finish();
glControl1.SwapBuffers();
}
procedure TfrmGL.FormPaint(Sender: TObject);
var
i: integer;
wdl: GLfloat;
begin
glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // очистка буфера цвета
glLoadIdentity;
// перемещение наблюдателя
glRotatef(AngleY, 0.0, 1.0, 0.0);
glTranslatef(Pos.X, 0, Pos.Y);
// вращающаяся сфера с текстурой поверхности земли
glColor3f(1, 1, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texEarth.Width, texEarth.Height,
0, GL_RGB, GL_UNSIGNED_BYTE, @texEarth.Data[0]);
glEnable(GL_TEXTURE_2D);
glPushMatrix;
glRotatef(RY, 0.0, 1.0, 0.0);
sphere(1, 20, 20);
glPopMatrix;
glDisable(GL_TEXTURE_2D);
// стены лабиринта
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texBrick.Width, texBrick.Height,
0, GL_RGB, GL_UNSIGNED_BYTE, @texBrick.Data[0]);
glEnable(GL_TEXTURE_2D);
for i:=0 to High(plan) do begin
// длина стены
wdl:=sqrt(sqr(coners[plan[i].l].x-coners[plan[i].r].x)
+ sqr(coners[plan[i].l].y-coners[plan[i].r].y))/4;
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex3f(coners[plan[i].l].x, -hdl, coners[plan[i].l].y);
glTexCoord2f(0, hdl/2); glVertex3f(coners[plan[i].l].x, hdl, coners[plan[i].l].y);
glTexCoord2f(wdl,hdl/2); glVertex3f(coners[plan[i].r].x, hdl, coners[plan[i].r].y);
glTexCoord2f(wdl, 0); glVertex3f(coners[plan[i].r].x, -hdl, coners[plan[i].r].y);
glEnd;
end;
glDisable(GL_TEXTURE_2D);
glFlush;
glFinish;
SwapBuffers(DC);
end;
В листингах 13.17 и 13.18 при формировании стен лабиринта добавлено определение координат текстуры для каждой стены с помощью команды TexCoord. Для определения количества повторений изображения фрагмента кирпичной стены используется значение высоты стен, которое одинаково для всех стен и задано константой , и длины стены (переменная ), которая вычисляется для каждой стены лабиринта.
В листингах 13.17 и 13.18 для формирования изображения
сферы с текстурой поверхности земли используются примеры из главы 9. Угол вращения сферы RY меняется в обработчике таймера (листинги 13.19 и 13.20 ).
private void timer1_Tick(object sender, EventArgs e)
{
RY += 5;
glControl1.Invalidate();
}
procedure TfrmGL.Timer1Timer(Sender: TObject); begin inc(RY, 5); InvalidateRect(Handle, nil, False); end;
Библиотека OpenGL с одинаковой легкостью позволяет создавать различные типы приложений: как трехмерные шутеры, так и различные системы проектирования и моделирования.
Базовые знания библиотеки позволяют создавать приложения различных типов.
При создании графических приложений OpenGL важную роль играет не только умение использовать возможности библиотеки, но и знание таких разделов математики как аналитическая геометрия.
(рис 13.5) Пример модели автомобиляЦель лекции: Создать приложения на C# и Object Pascal, моделирующие перемещение наблюдателя в лабиринте.
Пример № 13.1 Программа "Лабиринт" (вариант 1). Пример реализует программу, моделирующую перемещение наблюдателя в помещении.
Пример № 13.2 Программа "Лабиринт" (вариант 2). Пример дополняет пример 13.1: выполняется наложение текстур на стены, добавлена вращающаяся сфера с текстурой.
В данной лекции будет рассмотрен пример создания программы, моделирующей перемещение наблюдателя в помещении. На примере этой программы можно увидеть использование большей части тех возможностей библиотеки OpenGL, которые были описаны в предыдущих лекциях.
Создание программы будет выполняться в два этапа. На первом этапе будет создано базовое приложение. В этом приложении будут отображаться стены лабиринта, и выполняться перемещение наблюдателя в лабиринте. На втором этапе в приложение будет добавлена текстура для стен и вращающаяся сфера с текстурой поверхности земли.
Первоначально рассмотрим создание приложения, в котором не будет использоваться освещение и стены будут иметь различный цвет. Для моделирования нахождения наблюдателя в помещении потребуется использовать перспективную проекцию. Параметры перспективной проекции необходимо задать таким образом, чтобы боковые границы области видимости были меньше расстояния до боковых стен. Дальнюю границу области видимости расположим дальше, чем расстояние между самыми удалёнными друг от друга стенами, чтобы наблюдатель всегда мог видеть границу помещения лабиринта (листинги 13.1 и 13.2 ).
private void glControl1_Resize(object sender, EventArgs e)
{
GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Frustum(-0.5, 0.5, -0.5, 0.5, 0.5, 50);
GL.MatrixMode(MatrixMode.Modelview);
glControl1.Invalidate();
}
procedure TfrmGL.FormResize(Sender: TObject); begin glViewport(0, 0, ClientWidth, ClientHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity; glFrustum(-0.5, 0.5, -0.5, 0.5, 0.5, 50); glMatrixMode(GL_MODELVIEW); InvalidateRect(Handle, nil, False); end;
Для описания расположения координат стен в программе на C# будет использоваться массив структур типа PointFloat. Тип PointFloat объявлен в программе:
public struct PointFloat
{
public double X;
public double Y;
}
В программе на Object Pascal для описания координат стен используется тип TPointFloat, который объявлен в модуле OpenGL.pas:
Type
TPointFloat = record
x, y: Single;
end;
Для определения каждой стены в программе будет использоваться массив структур типа ) и типа ).
public struct Wall
{
public int L;
public int R;
public Color C;
}
type
TWall = record
l, r: Integer;
c: TColor;
end;
Поля структуры имеют следующие значения:
l, r – левая и правая границы стены, которые определяются индексами элементов массива coners; c – цвет стены, который используется при отображении стен в режиме без освещения.Координаты стен могут быть определены в виде массивов ( и ).
// координаты углов стен
PointFloat[] coners = new []{
new PointFloat{X = -10, Y = -10},
new PointFloat{X = -10, Y = 10},
new PointFloat{X = 10, Y = 10},
new PointFloat{X = 10, Y = -10},
new PointFloat{X = 3, Y = 10},
new PointFloat{X = 3, Y = 0}
};
// расположение стен в лабиринте
Wall[] plan = new[]
{
new Wall{L = 0, R = 1, C = Color.Red},
new Wall{L = 1, R = 2, C = Color.Green},
new Wall{L = 2, R = 3, C = Color.Yellow},
new Wall{L = 3, R = 0, C = Color.Blue},
new Wall{L = 4, R = 5, C = Color.Aqua}
};
const
coners: array [0..5] of TPointFloat = (
(x:-10; y:-10),
(x:-10; y: 10),
(x: 10; y: 10),
(x: 10; y:-10),
(x: 0; y:-10),
(x: 0; y: 0));
plan: array [0..4] of TWall = (
(l:0;r:1;c:clRed),(l:1;r:2;c:clGreen),
(l:2;r:3;c:clYellow),(l:3;r:0;c:clBlue),
(l:4;r:5;c: clAqua));
Информация обо всех стенах лабиринта будет храниться в массиве plan, каждый элемент которого описывает расположение и цвет одной из стен лабиринта.
В программе также будут использоваться несколько глобальных переменных и констант ( и ).
Bitmap bmpBrick; Bitmap bmpEarth; double AngleX = 0; double AngleY = 0; double AngleZ = 0; // шаг перемещения наблюдателя в лабиринте const double Step = 0.2; // высота стен лабиринта const double hdl = 5; const double AngleDl = 5; // координаты положения наблюдателя PointFloat Pos;
var // переменные для хранения данных о тектурах texBrick, texEarth: TGLTexture; // направление движения наблюдателя (угол его поворота), // изменяется с помощью клавиш со стрелками влево и вправо AngleY: integer; // координаты положения наблюдателя Pos: TPointFloat; // угол поворота сферы RY: integer; Const AngleDl=5; //угол разворота при нажатии на клавишу Step = 0.2; // шаг перемещения наблюдателя в лабиринте hdl=5; // высота стен лабиринта
Переменная Pos будет использоваться для хранения текущих координат положения наблюдателя в лабиринте, константа Step будет определять изменение положения наблюдателя в направлении перемещения ("длина шага" наблюдателя в лабиринте).
Изображение лабиринта будет формироваться на основе данных массивов и ).
private void glControl1_Paint(object sender, PaintEventArgs e)
{
// очистка буферов цвета и глубины
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
// поворот изображения
GL.LoadIdentity();
GL.Rotate(AngleY, 0.0, 1.0, 0.0);
GL.Translate(Pos.X, 0, Pos.Y);
// стены лабиринта
for (int i = 0; i < plan.Length; ++i)
{
// длина стены
double wdl = Math.Sqrt(Math.Pow(coners[plan[i].L].X - coners[plan[i].R].X, 2)
+ Math.Pow(coners[plan[i].L].Y - coners[plan[i].R].Y, 2)) / 4;
GL.Color4(plan[i].C);
GL.Begin(BeginMode.Quads);
GL.TexCoord2(0, 0); GL.Vertex3(coners[plan[i].L].X, -hdl,
coners[plan[i].L].Y);
GL.TexCoord2(0, hdl / 2); GL.Vertex3(coners[plan[i].L].X, hdl,
coners[plan[i].L].Y);
GL.TexCoord2(wdl, hdl / 2); GL.Vertex3(coners[plan[i].R].X, hdl,
coners[plan[i].R].Y);
GL.TexCoord2(wdl, 0); GL.Vertex3(coners[plan[i].R].X, -hdl,
coners[plan[i].R].Y);
GL.End();
}
GL.Flush();
GL.Finish();
glControl1.SwapBuffers();
}
procedure TForm1.FormPaint(Sender: TObject);
var i: integer;
begin
// очистка буфера цвета
glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); glLoadIdentity;
// перемещение наблюдателя
glRotatef(AngleY, 0.0, 1.0, 0.0);
glTranslatef(Pos.X, 0, Pos.Y);
// стены лабиринта
for i:=0 to High(plan) do begin
glBegin(GL_QUADS);
glColor(GetRValue(plan[i].c),GetGValue(plan[i].c),
GetBValue(plan[i].c));
glVertex3f(coner[plan[i].l].x, -hdl, coner[plan[i].l].y);
glVertex3f(coner[plan[i].l].x, hdl, coner[plan[i].l].y);
glVertex3f(coner[plan[i].r].x, hdl, coner[plan[i].r].y);
glVertex3f(coner[plan[i].r].x, -hdl, coner[plan[i].r].y);
glEnd;
end;
glFlush;
glFinish;
SwapBuffers(DC);
end;
Для возможности перемещения наблюдателя в лабиринте в программе будут использоваться клавиши со стрелками. Клавиши со стрелками влево и вправо будут использоваться для поворота наблюдателя в соответствующем направлении, клавиши со стрелками вперед и назад – для перемещения наблюдателя вперед и назад ( и ).
private void glControl1_KeyDown(object sender, KeyEventArgs e)
{
switch(e.KeyCode)
{
case Keys.Left: AngleY += -AngleDl; break;
case Keys.Right: AngleY -= -AngleDl; break;
case Keys.Up:
Pos.X = Pos.X - Step * Math.Sin(AngleY * Math.PI / 180);
Pos.Y = Pos.Y + Step * Math.Cos(AngleY * Math.PI / 180);
break;
case Keys.Down:
Pos.X = Pos.X + Step * Math.Sin(AngleY * Math.PI / 180);
Pos.Y = Pos.Y - Step * Math.Cos(AngleY * Math.PI / 180);
break;
}
if (AngleY>=360) AngleY -= 360;
glControl1.Invalidate();
}
procedure TfrmGL.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
case Key of
VK_LEFT: inc(AngleY, -AngleDl);
VK_RIGHT: dec(AngleY, -AngleDl);
VK_UP: begin
PosX:=PosX - Step*Sin(AngleY*pi/180);
PosZ:=PosZ + Step*Cos(AngleY*pi/180);
end;
VK_DOWN: begin
PosX:=PosX + Step*Sin(AngleY*pi/180);
PosZ:=PosZ - Step*Cos(AngleY*pi/180);
end;
end;
if AngleY>=360 then dec (AngleY, 360);
InvalidateRect(Handle, nil, False);
end;
В результате будет получено приложение, которое рисует замкнутое помещение с одной дополнительной стеной, все стены имеют различный цвет. Программа позволяет наблюдателю перемещаться в этом помещении (рис 13.1). В полученном приложении не контролируются границы стен.
(рис 13.1) Изображение лабиринта (вариант 1)
В качестве изображения текстуры для стен можно использовать фрагмент кирпичной стены (рис 13.2), а для сферы - изображение поверхности земли (рис 13.3).
(рис 13.2) Изображение фрагмента кирпичной стены |
(рис 13.3) Изображение поверхности Земли |
Для этого можно использовать фрагменты кода приведённые в листингах 13.15 и 13.16 .
private void Form1_Load(object sender, EventArgs e)
{
…
bmpBrick = new Bitmap("brick.bmp");
bmpEarth = new Bitmap("earth256x128.bmp");
GL.TexParameter(TextureTarget.Texture2D,
TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter,
(int)TextureMagFilter.Linear);
}
procedure TfrmGL.FormCreate(Sender: TObject);
begin
…
// зарузка изображений текстур
LoadTextureFromBMP('BRICK.BMP', texBrick);
LoadTextureFromBMP('earth256x128.bmp', texEarth);
// параметры масштабирования текстур
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
…
end;
Для формирования изображения сферы с текстурой можно использовать пример формирования сферы, приведенный в главе 9 (листинги 13.17 и 13.18 ).
private void glControl1_Paint(object sender, PaintEventArgs e)
{
GL.ClearColor(0.5f, 0.5f, 0.75f, 1.0f); // цвет фона
// очистка буферов цвета и глубины
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
// поворот изображения
GL.LoadIdentity();
GL.Rotate(AngleY, 0.0, 1.0, 0.0);
GL.Translate(Pos.X, 0, Pos.Y);
GLTexture.LoadTexture(bmpEarth);
GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.Lighting);
GL.PushMatrix();
GL.Rotate(RY, 0.0, 1.0, 0.0);
Sphere(1, 20, 20);
GL.PopMatrix();
GL.Disable(EnableCap.Lighting);
GL.Disable(EnableCap.Texture2D);
// стены лабиринта
GLTexture.LoadTexture(bmpBrick);
GL.Enable(EnableCap.Texture2D);
for (int i = 0; i < plan.Length; ++i)
{
// длина стены
double wdl = Math.Sqrt(Math.Pow(coners[plan[i].L].X -coners[plan[i].R].X, 2)
+ Math.Pow(coners[plan[i].L].Y - coners[plan[i].R].Y, 2)) / 4;
GL.Begin(BeginMode.Quads);
GL.TexCoord2(0, 0);
GL.Vertex3(coners[plan[i].L].X, -hdl, coners[plan[i].L].Y);
GL.TexCoord2(0, hdl / 2);
GL.Vertex3(coners[plan[i].L].X, hdl, coners[plan[i].L].Y);
GL.TexCoord2(wdl, hdl / 2);
GL.Vertex3(coners[plan[i].R].X, hdl, coners[plan[i].R].Y);
GL.TexCoord2(wdl, 0);
GL.Vertex3(coners[plan[i].R].X, -hdl, coners[plan[i].R].Y);
GL.End();
}
GL.Disable(EnableCap.Texture2D);
GL.Flush();
GL.Finish();
glControl1.SwapBuffers();
}
procedure TfrmGL.FormPaint(Sender: TObject);
var
i: integer;
wdl: GLfloat;
begin
glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // очистка буфера цвета
glLoadIdentity;
// перемещение наблюдателя
glRotatef(AngleY, 0.0, 1.0, 0.0);
glTranslatef(Pos.X, 0, Pos.Y);
// вращающаяся сфера с текстурой поверхности земли
glColor3f(1, 1, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texEarth.Width, texEarth.Height,
0, GL_RGB, GL_UNSIGNED_BYTE, @texEarth.Data[0]);
glEnable(GL_TEXTURE_2D);
glPushMatrix;
glRotatef(RY, 0.0, 1.0, 0.0);
sphere(1, 20, 20);
glPopMatrix;
glDisable(GL_TEXTURE_2D);
// стены лабиринта
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texBrick.Width, texBrick.Height,
0, GL_RGB, GL_UNSIGNED_BYTE, @texBrick.Data[0]);
glEnable(GL_TEXTURE_2D);
for i:=0 to High(plan) do begin
// длина стены
wdl:=sqrt(sqr(coners[plan[i].l].x-coners[plan[i].r].x)
+ sqr(coners[plan[i].l].y-coners[plan[i].r].y))/4;
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex3f(coners[plan[i].l].x, -hdl, coners[plan[i].l].y);
glTexCoord2f(0, hdl/2); glVertex3f(coners[plan[i].l].x, hdl, coners[plan[i].l].y);
glTexCoord2f(wdl,hdl/2); glVertex3f(coners[plan[i].r].x, hdl, coners[plan[i].r].y);
glTexCoord2f(wdl, 0); glVertex3f(coners[plan[i].r].x, -hdl, coners[plan[i].r].y);
glEnd;
end;
glDisable(GL_TEXTURE_2D);
glFlush;
glFinish;
SwapBuffers(DC);
end;
В листингах 13.17 и 13.18 при формировании стен лабиринта добавлено определение координат текстуры для каждой стены с помощью команды TexCoord. Для определения количества повторений изображения фрагмента кирпичной стены используется значение высоты стен, которое одинаково для всех стен и задано константой , и длины стены (переменная ), которая вычисляется для каждой стены лабиринта.
В листингах 13.17 и 13.18 для формирования изображения
сферы с текстурой поверхности земли используются примеры из главы 9. Угол вращения сферы RY меняется в обработчике таймера (листинги 13.19 и 13.20 ).
private void timer1_Tick(object sender, EventArgs e)
{
RY += 5;
glControl1.Invalidate();
}
procedure TfrmGL.Timer1Timer(Sender: TObject); begin inc(RY, 5); InvalidateRect(Handle, nil, False); end;
Библиотека OpenGL с одинаковой легкостью позволяет создавать различные типы приложений: как трехмерные шутеры, так и различные системы проектирования и моделирования.
Базовые знания библиотеки позволяют создавать приложения различных типов.
При создании графических приложений OpenGL важную роль играет не только умение использовать возможности библиотеки, но и знание таких разделов математики как аналитическая геометрия.
(рис 13.5) Пример модели автомобиляДля получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.