Компьютерная графика, мультимедиа и игры на Visual C#

       

Методика проектирования неподвижных и подвижных плоских фигур


Листинг 44.1. Код  для вывода формы и рисования на ней графики.

using (Form1 myForm1 = new Form1())

{

    if (!myForm1.InitializeDirectX())

    {

        MessageBox.Show("Ошибка при инициализации DirectX.");

        return;

    }

    //Показываем форму Form1:

    myForm1.Show();

    //Рисуем графику на форме Form1:

    while (myForm1.Created)

    {



        myForm1.myRendering();

        Application.DoEvents();

    }

}

Для закрытия Form1 после щелчка кнопки Cancel дважды щелкаем эту кнопку. Появляется файл Form1.cs с шаблоном, в который записываем (Close()).

Для вывода справочной Form2 (после щелчка кнопки Help на Form1) дважды щелкаем эту кнопку. Появляется файл Form1.cs с шаблоном, в который записываем:

Form2 myForm2 = new Form2(); myForm2.Show();                       (44.1)

Для ввода в проект новой формы в меню Project выбираем Add Windows Form, в панели Add New Item в окне Templates выделяем Windows Form, в окне Name оставляем имя Form2 и щелкаем кнопку Add (или Open для иной версии VS). Появляется Form2, в которую записываем (если нужно) справочную информацию. Вывод следующей формы (если в этом есть необходимость) после щелчка кнопки Next>> (или предыдущей формы после щелчка кнопки <<Back) осуществляется аналогично при помощи кода (44.1) с номером соответствующей формы.

Теперь в любом месте файла Form1.cs (например, ниже предыдущих методов для обработки щелчков по кнопкам) записываем следующие методы для связывания формы Form1 с Direct3D.

Листинг 44.2. Методы  для визуализации преобразований.

//Объявляем и инициализируем глобальную переменную

//для устройства myDevice класса Device:

Device myDevice = null;

//Устанавливаем параметры Direct3D:

public bool InitializeDirectX()

{

    try

    {

        PresentParameters myPresentParameters =

            new PresentParameters();

        myPresentParameters.Windowed = true;

        myPresentParameters.SwapEffect = SwapEffect.Discard;

        myDevice = new Device(0, DeviceType.Hardware,


Открываем файл Form1.cs (например, по схеме: File, Open, File) и выше пространства имен с именем нашего проекта (namespace Visual_DirectX_n5) записываем директивы для подключения пространств имен:
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D = Microsoft.DirectX.Direct3D;
Листинг 45.1. Методы  для визуализации преобразованных вершин фигуры.
//Глобальные переменные.
//Объявляем устройство для визуализации вершин:
Device myDevice = null;
VertexBuffer myVertexBuffer = null;
PresentParameters myPresentParameters =
    new PresentParameters();
bool myPause = false;
//Задаем параметры DirectX:
public bool InitializeDirectX()
{
    try
    {
        myPresentParameters.Windowed = true;
        myPresentParameters.SwapEffect =
            SwapEffect.Discard; 
        myPresentParameters.EnableAutoDepthStencil = true;
        myPresentParameters.AutoDepthStencilFormat =
            DepthFormat.D16;
        //Создаем устройство для визуализации:
        myDevice = new Device(0, DeviceType.Hardware, this,
            CreateFlags.SoftwareVertexProcessing,
            myPresentParameters);
        myDevice.DeviceReset +=
            new System.EventHandler(this.OnResetDevice);
        this.OnCreateDevice(myDevice, null);
        this.OnResetDevice(myDevice, null);
        myPause = false;
        return true;
    }
    catch (DirectXException)
    {
        //Перехвачена ошибка инициализации DirectX:
        return false;
    }
}
//Создаем буфер вершин фигуры:
public void OnCreateDevice(object sender, EventArgs e)
{
    Device myDev = (Device)sender;
    myVertexBuffer = new VertexBuffer(
        typeof(CustomVertex.PositionNormal), 100, myDev,
        Usage.WriteOnly, CustomVertex.PositionNormal.Format,
        Pool.Default);
    myVertexBuffer.Created += new System.EventHandler(
        this.OnCreateVertexBuffer);
    this.OnCreateVertexBuffer(myVertexBuffer, null);
}
//Задаем параметры устройству:


            this, CreateFlags.SoftwareVertexProcessing,
            myPresentParameters);
        return true;
    }
    catch (DirectXException)
    {
        return false;
    }
}
//Метод для визуализации преобразований и построения графики:
public void myRendering()
{
    if (myDevice == null)
        return;
    //Очищаем и заливаем белым цветом устр-во в виде Form1:
    myDevice.Clear(ClearFlags.Target,
        System.Drawing.Color.White, 1.0f, 0);
    //Начинаем сцену:
    myDevice.BeginScene();
    //Заканчиваем сцену:
    myDevice.EndScene();
    myDevice.Present();
}
// Чтобы закрыть Form1 после нажатия клавиши Esc:
protected override void OnKeyPress(
    System.Windows.Forms.KeyPressEventArgs e)
{
    if ((int)(byte)e.KeyChar ==
        (int)System.Windows.Forms.Keys.Escape)
        this.Close();
}
Листинг 44.3. Методы  для визуализации преобразованных вершин фигуры.
//Объявляем и инициализируем глобальные переменные:
Device myDevice = null;
VertexBuffer myVertexBuffer = null;
//Устанавливаем параметры Direct3D:
public bool InitializeDirectX()
{
    try
    {
        PresentParameters myPresentParameters =
            new PresentParameters();
        myPresentParameters.Windowed = true;
        myPresentParameters.SwapEffect = SwapEffect.Discard;
        myDevice = new Device(0, DeviceType.Hardware, this,
            CreateFlags.SoftwareVertexProcessing,
            myPresentParameters);
        this.OnCreateDevice(myDevice, null);
        return true;
    }
    catch (DirectXException)
    {
        return false;
    }
}
//Создаем массив вершин фигуры:
public void OnCreateDevice(object sender, EventArgs e)
{
    //Создаем буфер для 3-x вершин треугольника:
    myVertexBuffer = new VertexBuffer(
        typeof(CustomVertex.TransformedColored), 3,
        myDevice, 0, CustomVertex.TransformedColored.Format,
        Pool.Default);
    myVertexBuffer.Created += new System.EventHandler(
        this.OnCreateVertexBuffer);


    this.OnCreateVertexBuffer(myVertexBuffer, null);
}
//Задаем параметры вершин:
public void OnCreateVertexBuffer(object sender, EventArgs e)
{
    GraphicsStream myGraphicsStream = myVertexBuffer.Lock(0, 0, 0);
    CustomVertex.TransformedColored[] Vertex =
        new CustomVertex.TransformedColored[3];
    //Вершина 0:
    Vertex[0].X = 150; Vertex[0].Y = 50; Vertex[0].Z = 0.5f;
    Vertex[0].Rhw = 1;
    Vertex[0].Color = System.Drawing.Color.Aqua.ToArgb();
    //Вершина 1:
    Vertex[1].X = 250; Vertex[1].Y = 300; Vertex[1].Z = 0.5f;
    Vertex[1].Rhw = 1;
    Vertex[1].Color = System.Drawing.Color.Black.ToArgb();
    //Вершина 2:
    Vertex[2].X = 50; Vertex[2].Y = 300; Vertex[2].Z = 0.5f;
    Vertex[2].Rhw = 1;
    Vertex[2].Color = System.Drawing.Color.LightPink.ToArgb();
    myGraphicsStream.Write(Vertex);
    myVertexBuffer.Unlock();
}
//Метод для начала и окончания визуализации
//преобразованных вершин:
public void myRendering()
{
    if (myDevice == null)
        return;
    //Задаем белый цвет (Color.White) форме Form1:
    myDevice.Clear(ClearFlags.Target,
        System.Drawing.Color.White, 1.0f, 0);
   //Начинаем сцену:
    myDevice.BeginScene();
    myDevice.SetStreamSource(0, myVertexBuffer, 0);
    myDevice.VertexFormat = CustomVertex.TransformedColored.Format;
    myDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
    //Заканчиваем сцену:
    myDevice.EndScene();
    myDevice.Present();
}
Теперь в файле Form1.cs (или Program.cs) находим главный метод Main, комментируем весь имеющийся в этом методе автоматически сгенерированный код и записываем код со следующего листинга (для вывода формы Form1 и рисования на ней графики).
Листинг 44.4. Код  для вывода формы и рисования на ней графики.
using (Form1 myForm1 = new Form1())
{
    if (!myForm1.InitializeDirectX())
    {
        MessageBox.Show("Ошибка при инициализации DirectX.");
        return;
    }
    //Показываем форму Form1:


    myForm1.Show();
    //Рисуем графику на форме Form1:
    while (myForm1.Created)
    {
        myForm1.myRendering();
        Application.DoEvents();
    }
}
Листинг 44.5. Метод для фотографирования клиентской области формы.
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern long BitBlt(IntPtr hdcDest,
int nXDest, int nYDest, int nWidth, int nHeight,
IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
private Bitmap myMemoryImage;
private void myCaptureScreen()
{
      Graphics myGraphics = this.CreateGraphics();
      Size s = this.Size;
      myMemoryImage = new Bitmap(s.Width, s.Height,
                                                 myGraphics);
      Graphics myMemoryGraphics =
      Graphics.FromImage(myMemoryImage);
      IntPtr dc0 = myGraphics.GetHdc();
      IntPtr dc1 = myMemoryGraphics.GetHdc();
      BitBlt(dc1, 0, 0, this.ClientRectangle.Width,
            this.ClientRectangle.Height,
            dc0, 0, 0, 13369376);
            myGraphics.ReleaseHdc(dc0);
            myMemoryGraphics.ReleaseHdc(dc1);
}
Если мы забыли разместить компоненты PrintDocument и PrintDialog, то размещаем их сейчас и дважды щелкаем по значку для компонента PrintDocument. Открывается файл Form1.cs с шаблоном, который после записи одной строки нашего кода (для рисования в памяти сфотографированного выше изображения) имеет такой вид.
Листинг 44.6. Код для рисования изображения в памяти компьютера.
private void printDocument1_PrintPage(object sender,
      System.Drawing.Printing.PrintPageEventArgs e)
{
      e.Graphics.DrawImage(myMemoryImage, 0, 0);
}
Теперь дважды щелкаем по кнопке Print (рис. 44.7) в режиме проектирования. Открывается файл Form1.cs с автоматически сгенерированным шаблоном обработчика щелчка по кнопке, и этот шаблон после записи нашего кода принимает такой вид.
Листинг 44.7. Код для печати изображения на принтере.
private void button1_Click(object sender, EventArgs e)


{
    //Вызываем метод для захвата изображения:
    myCaptureScreen();
    // Передаем объекту printDialog1 информацию об объекте
    //printDocument1 при помощи свойства Document:
    printDialog1.Document = printDocument1;
    //Выводим стандартную панель Print при помощи метода
    //ShowDialog() для задания параметров печати
    //и после щелчка OK на панели Print печатаем документ
    //при помощи метода Print():
    if (printDialog1.ShowDialog() == DialogResult.OK)
                                     printDocument1.Print();
}
Последнюю строку кода можно записать также в более полном (и более понятном, но более длинном) виде:
    System.Windows.Forms.DialogResult result =
         printDialog1.ShowDialog();
    if (result == DialogResult.OK)
         printDocument1.Print();
Открываем файл Form1.cs (например, по схеме: File, Open, File) и выше пространства имен с именем нашего проекта (namespace Visual_DirectX_n3) записываем директивы для подключения этих же двух пространств имен:
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
Коды для обработки щелчков по всем кнопкам на форме Form1 (рис. 44.8), а также для клавиши Esc приведены в предыдущем параграфе. Теперь в любом месте файла Form1.cs (например, ниже предыдущих методов для обработки щелчков по кнопкам) записываем следующие методы для выполнения преобразований вершин треугольника и прямоугольника и визуализации этих преобразований.
Листинг 44.8. Методы  для визуализации преобразованных вершин фигур.
//Объявляем и инициализируем глобальные переменные.
//Общее устройство для всех фигур:
Device myDevice = null;
//Объявляем буфер вершин для треугольника:
VertexBuffer myVertexBuffer1 = null;
//Объявляем буфер вершин для прямоугольника:
VertexBuffer myVertexBuffer2 = null;
//Устанавливаем параметры Direct3D:
public bool InitializeDirectX()
{
    try
    {
        //Для треугольника:
        PresentParameters myPresentParameters1 =
            new PresentParameters();


        myPresentParameters1.Windowed = true;
        myPresentParameters1.SwapEffect = SwapEffect.Discard;
        myDevice = new Device(0, DeviceType.Hardware, this,
            CreateFlags.SoftwareVertexProcessing,
            myPresentParameters1);
        this.OnCreateDevice(myDevice, null);
        //Для прямоугольника:
        PresentParameters myPresentParameters2 =
            new PresentParameters();
        myPresentParameters2.Windowed = true;
        myPresentParameters2.SwapEffect = SwapEffect.Discard;
        myDevice = new Device(0, DeviceType.Hardware, this,
            CreateFlags.SoftwareVertexProcessing,
            myPresentParameters2);
        this.OnCreateDevice(myDevice, null);
        return true;
    }
    catch (DirectXException)
    {
        return false;
    }
}
//Метод для начала и окончания визуализации
//преобразованных вершин:
public void myRendering()
{
    if (myDevice == null)
        return;
    myDevice.Clear(ClearFlags.Target,
        System.Drawing.Color.White, 1.0f, 0);
    myDevice.VertexFormat =
        CustomVertex.TransformedColored.Format;
    //Начинаем сцену:
    myDevice.BeginScene();
    //Для треугольника:
    myDevice.SetStreamSource(0, myVertexBuffer1, 0);
    myDevice.DrawPrimitives(PrimitiveType.TriangleList,0, 1);
    //Для прямоугольника:
    myDevice.SetStreamSource(0, myVertexBuffer2, 0);
    myDevice.DrawPrimitives(PrimitiveType.TriangleStrip,0,2);
    //Заканчиваем сцену:
    myDevice.EndScene();
    myDevice.Present();              
}
//Создаем устройство и два буфера для вершин фигур:
public void OnCreateDevice(object sender, EventArgs e)
{
    Device myDev = (Device)sender;
    //Создаем буфер для вершин треугольника:
    myVertexBuffer1 = new VertexBuffer(
        typeof(CustomVertex.TransformedColored), 3,
        myDev, 0, CustomVertex.TransformedColored.Format,
        Pool.Default);
    myVertexBuffer1.Created +=
        new System.EventHandler(this.OnCreateVertexBuffer1);


    this.OnCreateVertexBuffer1(myVertexBuffer1, null);
     // Создаем буфер для вершин четырехугольника:
    myVertexBuffer2 = new VertexBuffer(
        typeof(CustomVertex.TransformedColored), 4,
        myDev, 0, CustomVertex.TransformedColored.Format,
        Pool.Default);
    myVertexBuffer2.Created += new System.EventHandler(
        this.OnCreateVertexBuffer2);
    this.OnCreateVertexBuffer2(myVertexBuffer2, null);
}
//Задаем параметры вершин треугольника:
public void OnCreateVertexBuffer1(object sender, EventArgs e)
{
    VertexBuffer myVB1 = (VertexBuffer)sender;
    GraphicsStream myGraphicsStream1 = myVB1.Lock(0, 0, 0);
    CustomVertex.TransformedColored[] Vertex1 =
        new CustomVertex.TransformedColored[3];
    //Вершина 0:
    Vertex1[0].X = 150; Vertex1[0].Y = 50; Vertex1[0].Z=0.5f;
    Vertex1[0].Rhw = 1;
    Vertex1[0].Color = System.Drawing.Color.Aqua.ToArgb();
    //Вершина 1:
    Vertex1[1].X = 250; Vertex1[1].Y =300; Vertex1[1].Z=0.5f;
    Vertex1[1].Rhw = 1;
    Vertex1[1].Color = System.Drawing.Color.Black.ToArgb();
    //Вершина 2:
    Vertex1[2].X = 50; Vertex1[2].Y = 300; Vertex1[2].Z=0.5f;
    Vertex1[2].Rhw = 1;
    Vertex1[2].Color = 
        System.Drawing.Color.LightPink.ToArgb();
    myGraphicsStream1.Write(Vertex1);
    myVB1.Unlock();
}
//Задаем параметры вершин прямоугольника:
public void OnCreateVertexBuffer2(object sender, EventArgs EvArgs)
{
    VertexBuffer myVB2 = (VertexBuffer)sender;
    GraphicsStream myGraphicsStream2 = myVB2.Lock(0, 0, 0);
    CustomVertex.TransformedColored[] Vertex2 =
        new CustomVertex.TransformedColored[4];
    //Вершина 0:
    Vertex2[0].X = 300.0f; Vertex2[0].Y = 300.0f;
    Vertex2[0].Z = 0.5f; Vertex2[0].Rhw = 1;
    Vertex2[0].Color = System.Drawing.Color.Black.ToArgb();
    //Вершина 1:
    Vertex2[1].X = 300.0f; Vertex2[1].Y = 50.0f;
    Vertex2[1].Z = 0.5f; Vertex2[1].Rhw = 1;
    Vertex2[1].Color = System.Drawing.Color.White.ToArgb();


    //Вершина 2:
    Vertex2[2].X = 500.0f; Vertex2[2].Y = 300.0f;
    Vertex2[2].Z = 0.5f; Vertex2[2].Rhw = 1;
    Vertex2[2].Color = System.Drawing.Color.Blue.ToArgb();
    //Вершина 3:
    Vertex2[3].X = 500.0f; Vertex2[3].Y = 50.0f;
    Vertex2[3].Z = 0.5f; Vertex2[3].Rhw = 1;
    Vertex2[3].Color = System.Drawing.Color.Green.ToArgb();
    myGraphicsStream2.Write(Vertex2);
    myVB2.Unlock();
}
Листинг 44.9. Методы  для визуализации преобразованных вершин фигуры.
//Глобальные переменные:
Device myDevice = null; // Our rendering device
VertexBuffer myVertexBuffer = null;
PresentParameters myPresentParameters =
    new PresentParameters();
bool myPause = false;
//Задаем параметры DirectX:
public bool InitializeDirectX()
{
    try
    {
        myPresentParameters.Windowed = true;
        myPresentParameters.SwapEffect = SwapEffect.Discard;
        myDevice = new Device(0, DeviceType.Hardware, this,
            CreateFlags.SoftwareVertexProcessing,
            myPresentParameters);
        myDevice.DeviceReset +=
            new System.EventHandler(this.OnResetDevice);
        this.OnCreateDevice(myDevice, null);
        this.OnResetDevice(myDevice, null);
        myPause = false;
        return true;
    }
    catch (DirectXException)
    {
        return false;
    }
}
//Создаем буфер вершин для прямоугольника:
public void OnCreateDevice(object sender, EventArgs e)
{
    Device myDev = (Device)sender;
    //Задаем 4 вершины прямоугольника:
    myVertexBuffer = new VertexBuffer(
        typeof(CustomVertex.PositionColored), 4, myDev, 0,
        CustomVertex.PositionColored.Format, Pool.Default);
    //Создаем геом- е данные при обработке события Created:
    myVertexBuffer.Created += new System.EventHandler(
        this.OnCreateVertexBuffer);
    this.OnCreateVertexBuffer(myVertexBuffer, null);
}
//Настраиваем параметры Direct3D:
public void OnResetDevice(object sender, EventArgs e)


{
    Device myDev = (Device)sender;
    // Выключаем режим CullMode, чтобы мы видели
    //переднюю и заднюю поверхность фигуры:
    myDev.RenderState.CullMode = Cull.None;
    //Выключаем освещение Direct3D, так как мы задали
    //наши собственные цвета в вершинах:
    myDev.RenderState.Lighting = false;
}
//Создаем буфер вершин фигуры:      
public void OnCreateVertexBuffer(object sender, EventArgs e)
{
    //Для 4-х вершин прямоугольника:
    VertexBuffer myVB = (VertexBuffer)sender;
    CustomVertex.PositionColored[] Vertex = (
        CustomVertex.PositionColored[])myVB.Lock(0, 0);
    //Вершина 0:
    Vertex[0].X = -1.0f; Vertex[0].Y = -1.0f;
    Vertex[0].Z = 0.0f;
    Vertex[0].Color = System.Drawing.Color.Black.ToArgb();
    //Вершина 1:
    Vertex[1].X = -1.0f; Vertex[1].Y = 1.0f;
    Vertex[1].Z = 0.0f;
    Vertex[1].Color =
    System.Drawing.Color.MediumOrchid.ToArgb();
    //Вершина 2:
    Vertex[2].X = 1.0f; Vertex[2].Y = -1.0f;
    Vertex[2].Z = 0.0f;
    Vertex[2].Color = System.Drawing.Color.Black.ToArgb();
    //Вершина 3:
    Vertex[3].X = 1.0f; Vertex[3].Y = 1.0f;
    Vertex[3].Z = 0.0f;
    Vertex[3].Color = System.Drawing.Color.Cornsilk.ToArgb();
    myVB.Unlock();
}
//Выполняем визуализацию преобразованных вершин:
public void myRendering()
{
    if (myDevice == null) return;
    if (myPause) return;
    //Очищаем и заливаем форму Form1 белым цветом:
    myDevice.Clear(ClearFlags.Target,
        System.Drawing.Color.White, 1.0f, 0);
    //Начинаем сцену:
    myDevice.BeginScene();
    //Используем матрицы для выполнения преобразований:
    SetupMatrices();
    myDevice.SetStreamSource(0, myVertexBuffer, 0);
    myDevice.VertexFormat =
        CustomVertex.PositionColored.Format;
    //Для прямоугольника:
    myDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
    //Заканчиваем сцену:
    myDevice.EndScene();
    myDevice.Present();
}
//Включаем таймер и выполняем матричные преобразования:


private void SetupMatrices()
{
    //Используем структуру матриц Matrix,
    // чтобы вращать фигуру вокруг оси y.
    //Один оборот на 2*PI радиан фигура совершит
    //за 1000 мс (1 секунду):
    int iTime = Environment.TickCount % 1000;
    float fAngle = iTime * (2.0f * (float)Math.PI) / 1000.0f;
    myDevice.Transform.World = Matrix.RotationY(fAngle);
    //Задаем координаты глаза наблюдателя
    //в матрице вида (view matrix):
    myDevice.Transform.View = Matrix.LookAtLH(
        new Vector3(0.0f, 3.0f, -5.0f),
        new Vector3(0.0f, 0.0f, 0.0f),
        new Vector3(0.0f, 1.0f, 0.0f));
    //При помощи матрицы проецирования (projection matrix)
    //выполняем перспективные преобразования:
    myDevice.Transform.Projection = Matrix.PerspectiveFovLH(
        (float)Math.PI / 4, 1.0f, 1.0f, 100.0f);
}
//Останавливаем вращение фигуры
//во время изменения размеров формы Form1:
protected override void OnResize(System.EventArgs e)
{
    myPause = ((this.WindowState ==
        FormWindowState.Minimized) || !this.Visible);
}
Теперь в файле Form1.cs (или Program.cs) находим главный метод Main, комментируем весь имеющийся в этом методе автоматически сгенерированный код и записываем код с листинга предыдущего параграфа (для вывода формы Form1 и рисования на ней графики).


public void OnResetDevice(object sender, EventArgs e)
{
    Device myDev = (Device)sender;
    //Выключаем режим CullMode, чтобы видеть
    //внутреннюю и наружную поверхности фигуры:
    myDevice.RenderState.CullMode = Cull.None;
    //Включаем Z - буфер:
    myDevice.RenderState.ZBufferEnable = true;
    //Делаем доступным освещение:
    myDevice.RenderState.Lighting = true;   
}
//Строим фигуру в буфере вершин:
public void OnCreateVertexBuffer(object sender, EventArgs e)
{
    VertexBuffer myVB = (VertexBuffer)sender;
    //В структуре PositionNormal
    // создаем массив из 100 пользовательских вершин:
    CustomVertex.PositionNormal[] Vertex =
        (CustomVertex.PositionNormal[])myVB.Lock(0, 0);
    for (int i = 0; i < 50; i++)
    {
        //Заполняем вершины данными:
        float theta = (float)(2 * Math.PI * i) / 49;
        //Вращающийся конус с одной неподвижной вершиной.
        //Рассчитываем координаты вершин:
        Vertex[2 * i].Position =
            new Vector3((float)Math.Sin(theta), -1,
            (float)Math.Cos(theta));
        //Рассчитываем нормали в вершинах:
        Vertex[2 * i + 1].Normal =
            new Vector3((float)Math.Sin(theta), -1,
            (float)Math.Cos(theta));
        }
        //Открываем буфер вершин:
        myVB.Unlock();
}
//Включаем таймер и выполняем матричные преобразования:
private void SetupMatrices()
{
    //Используем глобальную матрицу (world matrix),
    //чтобы вращать фигуру вокруг оси y:
    myDevice.Transform.World = Matrix.RotationAxis(
        new Vector3(
        (float)Math.Cos(Environment.TickCount / 250.0f), 1,
        (float)Math.Sin(Environment.TickCount / 250.0f)),
        Environment.TickCount / 3000.0f);
    //Задаем координаты глаза наблюдателя
    //в матрице вида (view matrix):
    myDevice.Transform.View = Matrix.LookAtLH(
        new Vector3(0.0f, 3.0f, -5.0f),
        new Vector3(0.0f, 0.0f, 0.0f),
        new Vector3(0.0f, 1.0f, 0.0f));


    //При помощи матрицы проецирования (projection matrix)
    //выполняем перспективные преобразования:
    myDevice.Transform.Projection =
        Matrix.PerspectiveFovLH(
        (float)Math.PI / 4.0f, 1.0f, 1.0f, 100.0f);
}
// Определяем освещение фигуры цветом формата ARGB:
private void SetupLights()
{
    //Устанавливаем материал и его цвет.
    //Можно одновременно использовать только один материал:
    Material myMaterial = new Material();
    Color myColor = Color.White;
    myMaterial.Diffuse = myColor;
    myMaterial.Ambient = myColor;
    myDevice.Material = myMaterial;
    //Устанавливаем белое освещение
    //с изменяющимся направлением:
    myDevice.Lights[0].Type = LightType.Directional;
    myDevice.Lights[0].Diffuse = Color.DarkTurquoise;
    myDevice.Lights[0].Direction = new Vector3(
       (float)Math.Cos(Environment.TickCount / 250.0f), 1.0f,
       (float)Math.Sin(Environment.TickCount / 250.0f));
    //Включаем освещение:
    myDevice.Lights[0].Enabled = true;
    //Включаем немного отраженного (Ambient)
    //равномерно рассеянного света:
    myDevice.RenderState.Ambient = Color.FromArgb(0x202020);
}
//Выполняем визуализацию преобразованных вершин:
public void myRendering()
{
    if (myPause) return;
    //Очищаем и заливаем форму Form1 белым цветом:
    myDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer,
        Color.White, 1.0f, 0);
    //Начинаем сцену:
    myDevice.BeginScene();
    //Устанавливаем освещение и матерал:
    SetupLights();
    //Задаем матрицы (world, view, projection):
    SetupMatrices();
    myDevice.SetStreamSource(0, myVertexBuffer, 0);
    myDevice.VertexFormat =
        CustomVertex.PositionNormal.Format;
    //Рисуем фигуру:
    myDevice.DrawPrimitives(
        PrimitiveType.TriangleStrip, 0, (4 * 25) - 2);
    //Заканчиваем сцену:
    myDevice.EndScene();
    //Обновляем экран:
    myDevice.Present();
}
//Останавливаем вращение фигуры
//во время изменения размеров формы Form1:   


protected override void OnResize(System.EventArgs e)
{
    myPause = ((this.WindowState ==
        FormWindowState.Minimized) || !this.Visible);
}
// Закрываем форму Form1 после нажатия клавиши Esc:
protected override void OnKeyPress(KeyPressEventArgs e)
{
    if ((int)(byte)e.KeyChar == (int)Keys.Escape)
        this.Close();
}
Листинг 45.2. Код  для вывода формы и рисования на ней графики.
using (Form1 myForm1 = new Form1())
{
    if (!myForm1.InitializeDirectX())
    {
        MessageBox.Show("Ошибка при инициализации DirectX.");
        return;
    }
    //Показываем форму Form1:
    myForm1.Show();
    //Рисуем графику на форме Form1:
    while (myForm1.Created)
    {
        myForm1.myRendering();
        Application.DoEvents();
    }
}
Открываем файл Form1.cs (например, по схеме: File, Open, File) и выше пространства имен с именем нашего проекта (namespace Visual_DirectX_n6) записываем директивы для подключения пространств имен:
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D = Microsoft.DirectX.Direct3D;
Коды для обработки щелчков по всем кнопкам на форме Form1 (рис. 45.4) приведены выше в этой части книги.
Теперь в любом месте файла Form1.cs (например, ниже предыдущих методов для обработки щелчков по кнопкам) записываем следующие методы для выполнения матричных преобразований вершин фигуры (с текстуры) и визуализации этих преобразований в динамике.
Листинг 45.3. Методы  для визуализации преобразованных вершин фигуры.
//Глобальные переменные.
//Объявляем устройство для визуализации вершин:
Device myDevice = null;
VertexBuffer myVertexBuffer = null;
//Объявляем и инициализируем объект myTexture
//класса Texture:
Texture myTexture = null;
PresentParameters myPresentParameters =
    new PresentParameters();
bool myPause = false;
//Задаем параметры DirectX:
public bool InitializeDirectX()
{
    try
    {
        myPresentParameters.Windowed = true;
        myPresentParameters.SwapEffect = SwapEffect.Discard;


        myPresentParameters.EnableAutoDepthStencil = true;
        myPresentParameters.AutoDepthStencilFormat =
            DepthFormat.D16;
        //Создаем устройство для визуализации:
        myDevice = new Device(0, DeviceType.Hardware, this,
            CreateFlags.SoftwareVertexProcessing,
            myPresentParameters);
        myDevice.DeviceReset +=
            new System.EventHandler(this.OnResetDevice);
        this.OnCreateDevice(myDevice, null);
        this.OnResetDevice(myDevice, null);
        myPause = false;
        return true;
    }
    catch (DirectXException)
    {
        //Перехвачена ошибка инициализации DirectX:
        return false;
    }
}
//Создаем буфер вершин фигуры:
public void OnCreateDevice(object sender, EventArgs e)
{
    Device myDev = (Device)sender;
    myVertexBuffer = new VertexBuffer(
        typeof(CustomVertex.PositionNormalTextured), 100,
        myDev, Usage.WriteOnly,
        CustomVertex.PositionNormalTextured.Format,
        Pool.Default);
    myVertexBuffer.Created +=
        new System.EventHandler(this.OnCreateVertexBuffer);
    this.OnCreateVertexBuffer(myVertexBuffer, null);
}
//Задаем параметры устройству:
public void OnResetDevice(object sender, EventArgs e)
{
    Device myDev = (Device)sender;
    //Выключаем режим CullMode, чтобы видеть
    //внутреннюю и наружную поверхности фигуры:
    myDev.RenderState.CullMode = Cull.None;
    //Выключаем трехмерное освещение:
    myDev.RenderState.Lighting = false;
    //Включаем Z - буфер (ZBuffer):
    myDev.RenderState.ZBufferEnable = true;
    //Создаем нашу текстуру.
    // Загружаем на поверхность фигуры наш рисунок Texture_1
    //в виде текстуры:
    myTexture = TextureLoader.FromFile(myDev,
        Application.StartupPath + @"\..\..\Texture_1.bmp");
}
//Строим фигуру в буфере вершин:
public void OnCreateVertexBuffer(object sender, EventArgs e)
{
    VertexBuffer myVB = (VertexBuffer)sender;
    //В структуре PositionNormalTextured


    // создаем массив из 100 пользовательских вершин:
    CustomVertex.PositionNormalTextured[] Vertex =
        (CustomVertex.PositionNormalTextured[])myVB.Lock(0, 0); // Lock the buffer (which will return our structs)
    for (int i = 0; i < 50; i++)
    {
        //Заполняем вершины данными:
        float theta = (float)(2 * Math.PI * i) / 49;
        //Рассчитываем нормали в вершинах:
        Vertex[2 * i].Normal = new Vector3((float)Math.Sin(theta), 0, (float)Math.Cos(theta));
        //Добавляем в вершину v-компоненту текстуры:
        Vertex[2 * i].Tv = 1.0f;
        //Рассчитываем координаты вершин:
        Vertex[2 * i + 1].Position = new Vector3((float)Math.Sin(theta), 1, (float)Math.Cos(theta));
        //Добавляем в вершину u-компоненту текстуры:
        Vertex[2 * i + 1].Tu = ((float)i) / (50 - 1);
    }
    //Открываем буфер вершин:
    myVB.Unlock();
}
//Включаем таймер и выполняем матричные преобразования:
private void SetupMatrices()
{
    //Используем глобальную матрицу (world matrix),
    //чтобы вращать фигуру вокруг оси y.
    myDevice.Transform.World = Matrix.RotationAxis(
        new Vector3(
        (float)Math.Cos(Environment.TickCount / 250.0f), 1,
        (float)Math.Sin(Environment.TickCount / 250.0f)),
        Environment.TickCount / 1000.0f);
    //Задаем координаты глаза наблюдателя
    //в матрице вида (view matrix):
    myDevice.Transform.View = Matrix.LookAtLH(
        new Vector3(0.0f, 3.0f, -5.0f),
        new Vector3(0.0f, 0.0f, 0.0f),
        new Vector3(0.0f, 1.0f, 0.0f));
    //При помощи матрицы проецирования (projection matrix)
    //выполняем перспективные преобразования:
    myDevice.Transform.Projection =
        Matrix.PerspectiveFovLH(
        (float)Math.PI / 4.0f, 1.0f, 1.0f, 100.0f);
}
//Выполняем визуализацию преобразованных вершин:
public void myRendering()
{
    if (myPause) return;
    //Очищаем и заливаем форму Form1 белым цветом:
    myDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer,


        Color.White, 1.0f, 0);
    //Начинаем сцену:
    myDevice.BeginScene();
    //Задаем матрицы (world, view, projection):
    SetupMatrices();
    //Устанавливаем нашу текстуру:   
    myDevice.SetTexture(0, myTexture);
    myDevice.TextureState[0].ColorOperation =
        TextureOperation.Modulate;
    myDevice.TextureState[0].ColorArgument1 =
        TextureArgument.TextureColor;
    myDevice.TextureState[0].ColorArgument2 =
        TextureArgument.Diffuse;
    myDevice.TextureState[0].AlphaOperation =
        TextureOperation.Disable;
    //Рисуем фигуру:
    myDevice.SetStreamSource(0, myVertexBuffer, 0);
    myDevice.VertexFormat =
        CustomVertex.PositionNormalTextured.Format;
    myDevice.DrawPrimitives(
        PrimitiveType.TriangleStrip, 0, (4 * 25) - 2);
    //Заканчиваем сцену:
    myDevice.EndScene();
    //Обновляем экран:
    myDevice.Present();
}
//Останавливаем вращение фигуры
// во время изменения размеров формы Form1:
protected override void OnResize(System.EventArgs e)
{
    myPause = ((this.WindowState ==
        FormWindowState.Minimized) || !this.Visible);
}
//Закрываем форму Form1 после нажатия клавиши Esc:
protected override void OnKeyPress(KeyPressEventArgs e)
{
    if ((int)(byte)e.KeyChar == (int)Keys.Escape)
        this.Close();
}
Теперь в файле Form1.cs (или Program.cs) находим главный метод Main, комментируем весь имеющийся в этом методе автоматически сгенерированный код и записываем код со следующего листинга.
Листинг 45.4. Код  для вывода формы и рисования на ней графики.
using (Form1 myForm1 = new Form1())
{
    if (!myForm1.InitializeDirectX())
    {
        MessageBox.Show("Ошибка при инициализации DirectX.");
        return;
    }
    //Показываем форму Form1:
    myForm1.Show();
    //Рисуем графику на форме Form1:
    while (myForm1.Created)
    {
        myForm1.myRendering();
        Application.DoEvents();
    }
}
По этой методологии можно в проектах Visual C# при помощи DirectX проектировать разнообразные анимированные объемные изображения.

Содержание раздела