Предлагаю поделиться туториалами Managed DirectX на C#.
Условия такие:
1. Код должен компилироваться на VS2005, при использовании самой свежей версии Managed DirectX.
2. Программа должна быть минимальной!!! в размере кода — 1 лист (и лист с шейдерами, если есть)
3. Если код содран откуда-то, обязательно ссылку.
Сложность не имеет значения. Программа может демонстрировать любой простой эффект или метод, но,
желательно, не повторяясь.
P.S.Если встречу поддержку, со временем могу выложить не менее полусотни таких туториалов.
И повторяюсь — МИНИМУМ кода.
Вот два способа, которыми можно выполнить инициализацию DirectX,
с малым количеством кода. Мои примеры будут использовать их в
зависимости от конкретного случая.
Обычный — используя перерисовку окна событием OnPaint.
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
class WindowsFormDemo : Form
{
// Создаем устройствоprivate Device device = null;
/// <summary>
/// конструктор
/// </summary>public WindowsFormDemo()
{
// заголовок окнаthis.Text = "Инициализация устройства DirectX";
// минимальные размеры окнаthis.MinimumSize = new Size(50, 100);
// размер клиентской областиthis.ClientSize = new Size(640, 480);
}
public void InitializeGraphics()
{
// параметры представления
PresentParameters presentParams = new PresentParameters();
// работает ли приложение в полноэкранном режиме - да.
presentParams.IsWindowed = true;
// режим работы буферной подкачки
presentParams.SwapEffect = SwapEffect.Discard;
// Инициализация устройства.
device = new Device(0, // идентификатор адаптера
DeviceType.Hardware, // тип устройства this.Handle, // дескриптор окна визуализации
CreateFlags.SoftwareVertexProcessing, // флаг режима работы устройства
presentParams); // параметры представления
}
public static void Main()
{
using (WindowsFormDemo frm = new WindowsFormDemo())
{
frm.InitializeGraphics(); // Инициализация графического устройства
Application.Run(frm);
}
}
////////////////////////////////////////////////////////
//---------------------------------------------------------------------------------------
// Метод: void OnPaint(System.Windows.Forms.PaintEventArgs e)
//
// Цель: Обработка события Paint
//---------------------------------------------------------------------------------------protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
device.Clear(ClearFlags.Target, // что именно хотим очистить (текущее окно)
System.Drawing.Color.Black, // цвет очистки
1.0f, // буфер глубины.( Число 0.0 представляет
// самое близкое расстояние к зрителю; число 1.0 представляет
// самое дальнее расстояние.
0);
device.Present();
}
}
Этот способ позаимствован из SDK. С его помощью можно находить гораздо более
эффективные решения:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
class MyDirect : Form
{
// Создаем устройствоprivate Device device = null;
/// <summary>
/// Точка входа в приложение.
/// </summary>
[STAThread] // аттрибут указывает на то, что поточная модель
// COM для приложения является однопоточной средой (STA).public static void Main()
{
using (MyDirect frm = new MyDirect())
{
Application.Idle += new EventHandler(frm.OnApplicationIdle);
// Application.Idle - Событие, которое происходит, когда
// приложение заканчивает обработку и собирается перейти
// в состояние незанятости.
frm.InitializeGraphics();
Application.Run(frm);
}
}
/// <summary>
/// Инициализация устройства DirectX
/// </summary>public void InitializeGraphics()
{
// параметры представления
PresentParameters presentParams = new PresentParameters();
// работает ли приложение в полноэкранном режиме - да.
presentParams.IsWindowed = true;
// режим работы буферной подкачки
presentParams.SwapEffect = SwapEffect.Discard;
// Инициализация устройства.
device = new Device(0, // идентификатор адаптера
DeviceType.Hardware, // тип устройства this.Handle, // дескриптор окна визуализации
CreateFlags.SoftwareVertexProcessing, // флаг режима работы устройства
presentParams); // параметры представления
}
/// <summary>
/// Содержимое функции будет выполнено, если нет сообщений,
/// т.е. в момент простоя
/// </summary>
/// <param name="sender">Sending object</param>
/// <param name="e">Event arguments</param>private void OnApplicationIdle(object sender, EventArgs e)
{
while (AppStillIdle)
{
device.Clear(ClearFlags.Target, // что именно хотим очистить (текущее окно)
System.Drawing.Color.Black, // цвет очистки
1.0f, // буфер глубины.( Число 0.0 представляет
// самое близкое расстояние к зрителю; число 1.0 представляет
// самое дальнее расстояние.
0);
//...................................
// Здесь будет происходить прорисовка
//...................................
device.Present();
}
}
/// <summary>
/// Выясняется, поступало ли сообщение.
/// true - если не поступало, false - если поступало.
/// </summary>private bool AppStillIdle
{
get
{
NativeMethods.Message msg;
return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
}
}
/// <summary>
/// Общее описание NativeMethods.
/// </summary>public class NativeMethods
{
/// <summary>Сообщения Windows</summary>
/* Класс StructLayoutAttribute позволяет пользователю
* управлять физическим размещением полей данных класса или
* структуры.
* LayoutKind - управляет макетом объекта при его экспорте
* в неуправляемый код.
* Sequential - члены объекта располагаются последовательно,
* в порядке, в котором они появляются при экспортировании
* в неуправляемую память. Члены располагаются в соответствии
* с заданной в StructLayoutAttribute.Pack компоновкой и
* могут быть независимыми.*/
[StructLayout(LayoutKind.Sequential)] // аттрибутpublic struct Message
{
public IntPtr hWnd;
public uint msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point p;
}
/*System.Security.SuppressUnmanagedCodeSecurity -
* позволяет управляемому коду передавать управление
* неуправляемому коду без проверки стека. */
[System.Security.SuppressUnmanagedCodeSecurity] // аттрибут
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
}
}
Своеобразным "Hello World!" в программировании графики является вывод
трехмерного треугольника на экран. Этот пример полезен для новичков,
поскольку демонстрирует сразу несколько элементов:
прорисовка примитива с использованием GraphicsBuffer,
установка проекции и камеры, трансформирование мировых координат.
Источник: это известный пример, но адаптированный мной под
b-версию Managed DirectX. Для гладкой работы приложения
использовано событие OnResize(), в момент которого очищается устройство.
(посмотрите, что будет, если этого не сделать.)
В других примерах я покажу, как проделать то же, но используя
вершинные шейдеры, и как применить системный таймер для того, чтобы
не зависеть от мощности компьютера.
//-----------------------------------------------------------------
// Трехмерный треугольник. Вращение треугольника.
//-----------------------------------------------------------------using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.Generic;
using Microsoft.DirectX.Direct3D.CustomVertex;
class WindowsFormDemo : Form
{
// Создаем устройствоprivate Device device = null;
// параметры представления
PresentParameters presentParams = new PresentParameters();
// Переменная, используемая при вращении объекта.private float angle = 0.0f;
/// <summary>
/// конструктор
/// </summary>public WindowsFormDemo()
{
// заголовок окнаthis.Text = "Инициализация устройства DirectX";
// минимальные размеры окнаthis.MinimumSize = new Size(50, 100);
// размер клиентской областиthis.ClientSize = new Size(640, 480);
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
}
/// <summary> Инициализация графического устройства </summary>public void InitializeGraphics()
{
if (BuildPresentParameters())
{
// Инициализация устройства.
device = new Device(0, DeviceType.Hardware, this.Handle,
CreateFlags.SoftwareVertexProcessing, presentParams);
}
else
{
Console.WriteLine("Устройство не построено!");
return;
}
}
/// <summary> Задаем параметры представления. </summary>public bool BuildPresentParameters()
{
presentParams = new PresentParameters();
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.IsWindowed = true;
return true;
}
/// <summary> Строим камеру. </summary>private void SetupCamera()
{
// Параметры камеры
device.Transform.Projection =
// представление поля y (угол при вершине пирамиды).
Matrix.PerspectiveFieldOfViewLeftHanded((float)Math.PI / 4,
(float)this.Width / this.Height, 1.0f, 100.0f);
// Установка камеры
device.Transform.View =
// положение камеры во внешней системе координат
Matrix.LookAtLeftHanded(new Vector3(0, 0, 5.0f),
// местоположение объекта, на который нацелена камераnew Vector3(),
// направление камеры (у нас - вверх). new Vector3(0, 1, 0));
//-----!!!----- Вращение объекта относительно оси Z
//device.Transform.World = Matrix.RotationZ((float)Math.PI/6.0f);
//device.Transform.World = Matrix.RotationY((System.Environment.TickCount / 25.0f) / (float)Math.PI / 6);
//device.Transform.World = Matrix.RotationZ(angle/(float)Math.PI);
// В следующей функции мы сначала определяем ось, вокруг которой намерены
// вращать треугольник, а затем вводим угол поворота.
device.Transform.World = Matrix.RotationAxis(
new Vector3(angle / ((float)Math.PI * 2.0f),
angle / ((float)Math.PI * 4.0f),
angle / ((float)Math.PI * 6.0f)),
angle / (float)Math.PI);
angle += 0.06f; // такой подход вообще говоря не оправдан, поскольку скорость
// вращения зависит от мощности компьютера
// Отключим освещение в сцене. Это позволяет видеть объект
// в обычном режиме без установки нормалей.
device.RenderState.Lighting = false;
//----!!!Режим отбора и устранения(исключени невидимой поверхности)
// В таком режиме, треугольник не исчезает при вращении, обернувшись
// к нам задней поверхностью.
device.RenderState.CullMode = Cull.None;
}
/// <summary> Точка входа в приложение </summary>public static void Main()
{
using (WindowsFormDemo frm = new WindowsFormDemo())
{
try
{
frm.InitializeGraphics(); // Инициализация графического устройства
}
catch
{
MessageBox.Show("Инициализация DirectX не выполнена!");
}
Application.Run(frm);
}
}
/// <summary> Обработка события Paint </summary>protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
device.Clear(ClearFlags.Target, // что именно хотим очистить (текущее окно)
System.Drawing.Color.Black, // цвет очистки
1.0f, // буфер глубины.
0);
SetupCamera(); // Установка камеры
//Рисуем треугольник
GraphicsBuffer<PositionColored> verts = new GraphicsBuffer<PositionColored>(3);
verts[0] =
new PositionColored(0.0f, 1.0f, 0.0f, System.Drawing.Color.Green);
verts[1] =
new PositionColored(-1.0f, -1.0f, 1.0f, System.Drawing.Color.Blue);
verts[2] =
new PositionColored(1.0f, -1.0f, 1.0f, System.Drawing.Color.Purple);
device.BeginScene();
device.VertexFormat = PositionColored.Format;
device.DrawUserPrimitives(PrimitiveType.TriangleList, 1, verts);
device.EndScene();
device.Present();
this.Invalidate();
}
/// <summary> Обработка события OnResize() </summary>protected override void OnResize(EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
return;
}
if (device != null)
{
if (!BuildPresentParameters())
{
throw new Exception("Устройство сбросить невозможно!");
}
device.Reset(presentParams);
}
base.OnResize(e);
}
}
Здравствуйте, mukhomor, Вы писали:
M>Предлагаю поделиться туториалами Managed DirectX на C#.
Думаю, что всем известный сайт(да и не русский к тому же, зато обновляется часто) но всё-таки:
Для примера, выкладываю демонстрацию альфа-смешивания.
Запуск — copy/paste в Empty Project на C#. Используется DirectX 2-й версии.
!!!В директиву с кодом добавьте произвольные 3 файла формата JPEG : tex1.jpg, tex2.jpg, tex3.jpg,
которые будут использоваться как текстуры.
//-----------------------------------------------------------------
// Альфа-смешивание. Задается порог смешивания.
//-----------------------------------------------------------------
/* Пример Альфа- смешивания текстурированных объектов*/using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.Generic;
using Microsoft.DirectX.Direct3D.CustomVertex;
class WindowsFormDemo : Form
{
// Создаем устройствоprivate Device device = null;
// параметры представления
PresentParameters presentParams = new PresentParameters();
private VertexBuffer vb_cube;
Texture tex1;
Texture tex2;
Texture tex3;
float angle = 0.0f;
private Microsoft.DirectX.Direct3D.Font font = null;
int blendfactor = 128;///////////////////////////////
/// <summary>
/// конструктор
/// </summary>public WindowsFormDemo()
{
this.Text = "Инициализация устройства DirectX";
this.MinimumSize = new Size(50, 100);
this.ClientSize = new Size(640, 480);
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
}
/// <summary> Инициализация графического устройства </summary>public void InitializeGraphics()
{
if (BuildPresentParameters())
{
// Инициализация устройства.
device = new Device(0, DeviceType.Hardware, this.Handle,
CreateFlags.SoftwareVertexProcessing, presentParams);
}
else
{
Console.WriteLine("Устройство не построено!");
return;
}
InitCube();
font = new Microsoft.DirectX.Direct3D.Font(device, new System.Drawing.Font
("Arial", 12.0f, FontStyle.Bold | FontStyle.Italic));
// Load the texture
tex1 = new Texture(device, "..\\..\\tex1.jpg");
tex2 = new Texture(device, "..\\..\\tex2.jpg");
tex3 = new Texture(device, "..\\..\\tex3.jpg");
}
/// <summary> Задаем параметры представления. </summary>public bool BuildPresentParameters()
{
presentParams = new PresentParameters();
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.IsWindowed = true;
presentParams.EnableAutoDepthStencil = true;
presentParams.AutoDepthStencilFormat = DepthFormat.D16;
return true;
}
/// <summary> Строим камеру. </summary>private void SetupCamera()
{
// Параметры камеры
device.Transform.Projection =
Matrix.PerspectiveFieldOfViewLeftHanded((float)Math.PI / 4,
(float)this.Width / (float)this.Height, 1.0f, 100.0f);
// Установка камеры
device.Transform.View =
Matrix.LookAtLeftHanded(new Vector3(0, 0, -9.0f),
new Vector3(),
new Vector3(0, 1, 0));
device.Transform.World = Matrix.RotationY(angle / (float)Math.PI * 4.0f) *
Matrix.RotationZ(angle / (float)Math.PI * 4.0f) *
Matrix.RotationX(angle / (float)Math.PI * 4.0f);
// Отключаем освещение
device.RenderState.Lighting = false;
//Режим отбора и устранения(исключени невидимой поверхности)
device.RenderState.CullMode = Cull.None;
// устанавливаем параметры смешивания
SetAlphablending();
}
public void SetAlphablending()//////////////////////////////////////////////////////////////////////
{
device.RenderState.AlphaBlendEnable = true;
device.RenderState.BlendOperation = BlendOperation.Add;
device.RenderState.SourceBlend = Blend.BlendFactor;/////////////////////////////////////////////
device.RenderState.DestinationBlend = Blend.InvBlendFactor;/////////////////////////////////////
device.RenderState.BlendFactor = Color.FromArgb(blendfactor, blendfactor, blendfactor, blendfactor);/////////
}
/// <summary> Точка входа в приложение </summary>public static void Main()
{
using (WindowsFormDemo frm = new WindowsFormDemo())
{
try
{
frm.InitializeGraphics(); // Инициализация графического устройства
}
catch
{
MessageBox.Show("Инициализация DirectX не выполнена!");
}
Application.Run(frm);
}
}
/// <summary> Обработка события Paint </summary>protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, // что именно хотим очистить (текущее окно)
System.Drawing.Color.Black, // цвет очистки
1.0f, // буфер глубины.
0);
angle += 0.01f;
SetupCamera(); // Установка камеры
device.BeginScene();
// Отобразим текст#region поясняющий текст
font.DrawString(null, string.Format("Используйте клавиши Up, Down."), new Rectangle(10, 0, 0, 0),
DrawStringFormat.NoClip, Color.BlanchedAlmond);
font.DrawString(null, string.Format("Blendfactor: {0}",
blendfactor.ToString()), new Rectangle(10, 20, 0, 0),
DrawStringFormat.NoClip, Color.BlanchedAlmond);
#endregion
#region рисуем кубики///////////////////
// !!! Альфа смешивание зависит от порядка построения кубов.
// сначала рисуется внутрениий и т.д.
// Обрати внимание на этот прием!
// Исключаем смешивание с фоном самого маленького кубика
device.RenderState.AlphaBlendEnable = false;//////////////////////////////////////
DrawСube(angle / (float)Math.PI,
angle / (float)Math.PI * 1.0f,
angle / (float)Math.PI / 8.0f,
0.0f, 0.0f, 0.0f,
new Vector3(0.5f, 0.5f, 0.5f),
tex3);
device.RenderState.AlphaBlendEnable = true;////////////////////////////////////////
DrawСube(angle / (float)Math.PI,
angle / (float)Math.PI * 2.0f,
angle / (float)Math.PI / 4.0f,
0.0f, 0.0f, 0.0f,
new Vector3(1.0f, 1.0f, 1.0f),
tex2);
DrawСube(angle / (float)Math.PI,
angle / (float)Math.PI * 5.0f,
angle / (float)Math.PI / 8.0f,
0.0f, 0.0f, 0.0f,
new Vector3(2.0f, 2.0f, 2.0f),
tex1);
#endregion
device.EndScene();
device.Present();
this.Invalidate();
}
/// <summary> Обработка события OnResize() </summary>protected override void OnResize(EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
return;
}
if (device != null)
{
if (!BuildPresentParameters())
{
throw new Exception("Устройство сбросить невозможно!");
}
font.OnLostDevice();
device.Reset(presentParams);
}
base.OnResize(e);
}
private void InitCube()
{
PositionColoredTextured[] verts = {
new PositionColoredTextured( -1.0f, 1.0f, -1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( 1.0f, 1.0f, -1.0f, Color.White, 1.0f, 0.0f ),
new PositionColoredTextured( 1.0f, -1.0f, -1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( -1.0f, 1.0f, -1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( 1.0f, -1.0f, -1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( -1.0f, -1.0f, -1.0f, Color.White, 0.0f, 1.0f ),
new PositionColoredTextured( 1.0f, 1.0f, -1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( 1.0f, 1.0f, 1.0f, Color.White, 1.0f, 0.0f ),
new PositionColoredTextured( 1.0f, -1.0f, 1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( 1.0f, 1.0f, -1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( 1.0f, -1.0f, 1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( 1.0f, -1.0f, -1.0f, Color.White, 0.0f, 1.0f ),
new PositionColoredTextured( 1.0f, 1.0f, 1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( -1.0f, 1.0f, 1.0f, Color.White, 1.0f, 0.0f ),
new PositionColoredTextured( -1.0f, -1.0f, 1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( 1.0f, 1.0f, 1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( -1.0f, -1.0f, 1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( 1.0f, -1.0f, 1.0f, Color.White, 0.0f, 1.0f ),
new PositionColoredTextured( -1.0f, 1.0f, 1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( -1.0f, 1.0f, -1.0f, Color.White, 1.0f, 0.0f ),
new PositionColoredTextured( -1.0f, -1.0f, -1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( -1.0f, 1.0f, 1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( -1.0f, -1.0f, -1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( -1.0f, -1.0f, 1.0f, Color.White, 0.0f, 1.0f ),
new PositionColoredTextured( -1.0f, 1.0f, 1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( 1.0f, 1.0f, 1.0f, Color.White, 1.0f, 0.0f ),
new PositionColoredTextured( 1.0f, 1.0f, -1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( -1.0f, 1.0f, 1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( 1.0f, 1.0f, -1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( -1.0f, 1.0f, -1.0f, Color.White,0.0f, 1.0f ),
new PositionColoredTextured( -1.0f, -1.0f, -1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( 1.0f, -1.0f, -1.0f, Color.White, 1.0f, 0.0f ),
new PositionColoredTextured( 1.0f, -1.0f, 1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( -1.0f, -1.0f, -1.0f, Color.White, 0.0f, 0.0f ),
new PositionColoredTextured( 1.0f, -1.0f, 1.0f, Color.White, 1.0f, 1.0f ),
new PositionColoredTextured( -1.0f, -1.0f, 1.0f, Color.White, 0.0f, 1.0f )
};
// Вершинный буфер
vb_cube = new VertexBuffer(device,
verts.Length * PositionColoredTextured.StrideSize,
Usage.WriteOnly,
PositionColoredTextured.Format,
Pool.Managed,
null);
GraphicsBuffer<PositionColoredTextured> buffer = vb_cube.Lock<PositionColoredTextured>(0, 0, LockFlags.None);
buffer.Write(verts);
vb_cube.Unlock();
buffer.Dispose();
}
private void DrawСube(float yaw, float pitch, float roll, float x, float y, float z, Vector3 scale, Texture t)
{
device.Transform.World = Matrix.Scaling(scale) * Matrix.RotationYawPitchRoll(yaw, pitch, roll) * Matrix.Translation(x, y, z);
device.VertexFormat = PositionColoredTextured.Format;
device.SetTexture(0, t);
device.SetStreamSource(0, vb_cube, 0, PositionColoredTextured.StrideSize);
device.DrawPrimitives(PrimitiveType.TriangleList, 0, 12);
}
protected override void OnKeyDown(KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up://////////////////////////////////////////////////////////
blendfactor += 2;
if (blendfactor > 255) blendfactor = 255;
break;
case Keys.Down:////////////////////////////////////////////////////////
blendfactor -= 2;
if (blendfactor < 0) blendfactor = 0;
break;
default:
break;
}
SetAlphablending();
base.OnKeyDown(e);
}
}
Для сравнения приведу программу с использованием NativeMethods.
Вы можете увидеть, насколько более плавным стало вращение треугольника.
Остаточные рывки связаны с приращением параметра angle.
Когда речь дойдет до FPS, будет отчетливо видно, что количество кадров
в сек заметно возросло.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.Generic;
using Microsoft.DirectX.Direct3D.CustomVertex;
namespace DxFrameworkSample
{
class MyDirect : Form
{
private Device device = null;
// параметры представления
PresentParameters presentParams = new PresentParameters();
private float angle = 0.0f;
public MyDirect()
{
// заголовок окнаthis.Text = "Инициализация устройства DirectX";
// минимальные размеры окнаthis.MinimumSize = new Size(50, 100);
// размер клиентской областиthis.ClientSize = new Size(640, 480);
}
/// <summary>
/// Точка входа в приложение.
/// </summary>
[STAThread]
public static void Main()
{
using (MyDirect frm = new MyDirect())
{
Application.Idle += new EventHandler(frm.OnApplicationIdle);
frm.InitializeGraphics();
Application.Run(frm);
}
}
/// <summary> Параметры представления </summary>public bool BuildPresentParameters()
{
presentParams = new PresentParameters();
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.IsWindowed = true;
return true;
}
/// <summary> Инициализация графического устройства </summary>public void InitializeGraphics()
{
if (BuildPresentParameters())
{
// Инициализация устройства.
device = new Device(0, DeviceType.Hardware, this.Handle,
CreateFlags.SoftwareVertexProcessing, presentParams);
}
else
{
Console.WriteLine("Устройство не построено!");
return;
}
}
/// <summary> Строим камеру. </summary>private void SetupCamera()
{
// Параметры камеры
device.Transform.Projection =
// представление поля y (угол при вершине пирамиды).
Matrix.PerspectiveFieldOfViewLeftHanded((float)Math.PI / 4,
(float)this.Width / this.Height, 1.0f, 100.0f);
// Установка камеры
device.Transform.View =
// положение камеры во внешней системе координат
Matrix.LookAtLeftHanded(new Vector3(0, 0, 5.0f),
// местоположение объекта, на который нацелена камераnew Vector3(),
// направление камеры (у нас - вверх). new Vector3(0, 1, 0));
device.Transform.World = Matrix.RotationAxis(
new Vector3(angle / ((float)Math.PI * 2.0f),
angle / ((float)Math.PI * 4.0f),
angle / ((float)Math.PI * 6.0f)),
angle / (float)Math.PI);
device.RenderState.Lighting = false;
//Режим отбора
device.RenderState.CullMode = Cull.None;
}
/// <summary> Прорисовка </summary>private void RenderFrame()
{
device.Clear(ClearFlags.Target,
System.Drawing.Color.Black,
1.0f,
0);
SetupCamera();
//----------------------- Рисуем треугольник ----------------------------------------
GraphicsBuffer<PositionColored> verts = new GraphicsBuffer<PositionColored>(3);
verts[0] =
new PositionColored(0.0f, 1.0f, 0.0f, System.Drawing.Color.HotPink);
verts[1] =
new PositionColored(-1.0f, -1.0f, 1.0f, System.Drawing.Color.Peru);
verts[2] =
new PositionColored(1.0f, -1.0f, 1.0f, System.Drawing.Color.Purple);
/* Замечание: обратите внимание на то, в какой последовательности рисуются вершины. */
device.BeginScene();
device.VertexFormat = PositionColored.Format;
device.DrawUserPrimitives(PrimitiveType.TriangleList, 1, verts);
device.EndScene();
device.Present();
}
/// <summary>
/// Обновление параметров анимации
/// </summary>private void UpdateFrame()
{
angle += 0.06f;
}
protected override void OnResize(EventArgs e)
{
//!!! Если не исключить случай минимизации окна, в приложении
// произойдет ошибка!if (this.WindowState == FormWindowState.Minimized)
{
return;
}
if (device != null)
{
if (!BuildPresentParameters())
{
throw new Exception("Устройство сбросить невозможно!");
}
device.Reset(presentParams);
}
base.OnResize(e);
}
/// <summary>
/// Содержимое функции будет выполнено, если нет сообщений,
/// т.е. в момент простоя
/// </summary>private void OnApplicationIdle(object sender, EventArgs e)
{
while (AppStillIdle)
{
//...................................
UpdateFrame();
RenderFrame();
//...................................
}
}
/// <summary>
/// Выясняется, поступало ли сообщение.
/// true - если не поступало, false - если поступало.
/// </summary>private bool AppStillIdle
{
get
{
NativeMethods.Message msg;
return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
}
}
}
#region Native
public class NativeMethods
{
/// <summary>Сообщения Windows</summary>
[StructLayout(LayoutKind.Sequential)] // аттрибутpublic struct Message
{
public IntPtr hWnd;
public uint msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point p;
}
[System.Security.SuppressUnmanagedCodeSecurity] // аттрибут
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
}
#endregion
}
В начале обучения Managed DirectX наибольший интерес представляют два
объекта — mesh и sprite. Поэтому, забегая вперед, сначала покажу, как подгрузить
mesh.
Как запустить: 1. В папке Solution (или просто на одном уровне с проектом) создайте
дополнительную папку Media. В нее скопируйте любой .x файл из одноименной папки SDK,
включая текстуры к нему. Например, LandShark.x
2. Остальное, как обычно — copy/paste в empty project.
Сделать свой сбственный .x файл можно, если использовать, в частности, конвертор
из того же SDK. На мой взгляд, самый эффективный способ это использовать
предустановленное SDK для 3ds max или Maya и скомпилировать проект из
...Utilites\Source\Max(Maya) в папке SDK к VS. Так в папке Plugins для 3ds max
появится конвертор. А дальше просто : File > Save As...
/* Для работы с Mesh нам необходима различная информация о поверхности.
Например, нас интересует количество вершин, граней, текстур и, конечно,
размеры объекта.
* Выведем на экран информацию о количестве вершин и граней. Кроме этого,
* трансформируем объект под нашу камеру.
*/using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.Generic;
using Microsoft.DirectX.Direct3D.CustomVertex;
class WindowsFormDemo : Form
{
// Создаем устройствоprivate Device device = null;
// параметры представления
PresentParameters presentParams = new PresentParameters();
private float angle = 0.0f;
private Microsoft.DirectX.Direct3D.Font font = null; /////////////////////////////////////
// Объект типа Mesh
Mesh mesh = null;
Material[] meshMaterials;
BaseTexture[] meshTextures;
string FilePath = "..\\..\\..\\Media\\";// Путь к файлу с Mesh
//string FileName = "tiny.x";// имя .x файла
//string FileName = "LandShark.x";// имя .x файлаstring FileName = "Dwarf.x";// имя .x файлаprivate Vector3 objectCenter; // центр ограничивающей сферы//////////////////////////////////private float objectRadius; // радиус ограничивающей сферы///////////////////////////////////private Matrix worldCenter; // мировая матрица для размещения mesh///////////////////////////
/// <summary>
/// конструктор
/// </summary>public WindowsFormDemo()
{
this.Text = "Mesh - примитивы.";
this.MinimumSize = new Size(50, 100);
this.ClientSize = new Size(640, 480);
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
}
/// <summary> Инициализация графического устройства </summary>public void InitializeGraphics()
{
if (BuildPresentParameters())
{
// Инициализация устройства.
device = new Device(0, DeviceType.Hardware, this.Handle,
CreateFlags.SoftwareVertexProcessing, presentParams);
}
else
{
Console.WriteLine("Устройство не построено!");
return;
}
font = new Microsoft.DirectX.Direct3D.Font(device, new System.Drawing.Font//////////////////////////////
("Arial", 14.0f, FontStyle.Bold | FontStyle.Italic));
LoadMesh(FileName);
}
/// <summary> Задаем параметры представления. </summary>public bool BuildPresentParameters()
{
presentParams = new PresentParameters();
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.IsWindowed = true;
presentParams.EnableAutoDepthStencil = true;
presentParams.AutoDepthStencilFormat = DepthFormat.D16;
return true;
}
/// <summary> Строим камеру. </summary>private void SetupCamera()
{
device.Transform.Projection =
Matrix.PerspectiveFieldOfViewLeftHanded(
(float)Math.PI / 4,
(float)this.ClientSize.Width / this.ClientSize.Height,
1.0f,
100.0f);
device.Transform.View = Matrix.LookAtLeftHanded(
new Vector3(0, 0, -5.0f),
new Vector3(),
new Vector3(0, 1, 0));
device.RenderState.Ambient = Color.White;
device.Lights[0].LightType = LightType.Directional;
device.Lights[0].Diffuse = Color.White;
device.Lights[0].Direction = new Vector3(0, -1, 5);
device.Lights[0].Enabled = true;
//Режим отбора и устранения(исключени невидимой поверхности)
//device.RenderState.CullMode = Cull.CounterClockwise;
}
/// <summary> Точка входа в приложение </summary>public static void Main()
{
using (WindowsFormDemo frm = new WindowsFormDemo())
{
try
{
frm.InitializeGraphics(); // Инициализация графического устройства
}
catch
{
MessageBox.Show("Инициализация DirectX не выполнена!");
}
Application.Run(frm);
}
}
/// <summary> Обработка события Paint </summary>protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
angle += 0.01f;
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, // что именно хотим очистить (текущее окно)
System.Drawing.Color.Blue, // цвет очистки
1.0f, // буфер глубины.
0);
device.Transform.World = worldCenter*Matrix.RotationYawPitchRoll(angle / (float)Math.PI,//////////////////
angle / (float)Math.PI * 2.0f,
angle / (float)Math.PI / 4.0f);
SetupCamera(); // Установка камеры
device.BeginScene();
for (int i = 0; i < meshMaterials.Length; i++)
{
device.Material = meshMaterials[i];
device.SetTexture(0, meshTextures[i]);
mesh.DrawSubset(i);
}
// Отобразим текст
font.DrawString(null, string.Format("Количество вершин: {0}",/////////////////////////////
mesh.VertexCount), new Rectangle(10, 10, 0, 0),
DrawStringFormat.NoClip, Color.BlanchedAlmond);
font.DrawString(null, string.Format("Количество граней: {0}",/////////////////////////////
mesh.FaceCount), new Rectangle(10, 30, 0, 0),
DrawStringFormat.NoClip, Color.BlanchedAlmond);
device.EndScene();
device.Present();
this.Invalidate();
}
/// <summary>
/// Загрузка Mesh объекта из файла
/// </summary>
/// <param name="file">Имя файла.</param>private void LoadMesh(string file)
{
GraphicsBuffer outputAdjacency = new GraphicsBuffer();
MaterialList materials = new MaterialList();
EffectInstanceList effects = new EffectInstanceList();
// Загрузка поверхности
mesh = new Mesh(device, FilePath + FileName, MeshFlags.Managed, outputAdjacency, materials, effects);
// эффекты не используются
effects.Dispose();
using (outputAdjacency)
{
// Оптимизируем mesh для работы
mesh.OptimizeInPlace(MeshFlags.OptimizeVertexCache | MeshFlags.OptimizeCompact |
MeshFlags.OptimizeAttributeSort, outputAdjacency);
}
// Поиск параметров ограничительной сферы и матрицы центрированияusing (VertexBuffer vb = mesh.VertexBuffer)//////////////////////////////////////////////////////
{
// блокируем буфер для извлечения параметровusing (GraphicsBuffer stm = vb.Lock(0, 0, LockFlags.NoSystemLock))
{
try
{
// ограничительная сфера
BoundingSphere bs = Geometry.ComputeBoundingSphere(stm,
mesh.VertexCount, mesh.VertexFormat);
// ее радиус
objectRadius = bs.Radius;
//Console.WriteLine(objectRadius);
//Console.WriteLine(objectCenter.ToString());
// ее центр
objectCenter = bs.Center;
// паралельный перенос
worldCenter = Matrix.Translation(-objectCenter);
// коэффициент сжатияfloat scaleFactor = 2.0f / objectRadius;
// масштабирование
worldCenter *= Matrix.Scaling(scaleFactor, scaleFactor, scaleFactor);
}
finally
{
// разблокируем буффер
vb.Unlock();
}
}
}
// Имеются ли материалы?if ((materials != null) && (materials.Count > 0))
{
// Массивы для хранения материалов и текстур
meshMaterials = new Material[materials.Count];
meshTextures = new BaseTexture[materials.Count];
// Скопируем каждый материал и восстановим текстурыfor (int i = 0; i < materials.Count; i++)
{
// сначала копируем материал
meshMaterials[i] = materials[i].Material;
// Имеется ли у данного материала текстура??if (string.IsNullOrEmpty(materials[i].TextureFileName))
continue; // Если да - продолжим...
ImageInformation info = new ImageInformation();
// Здесь мы полагаем, что в .x файле содержится имя файла текстуры
// (путь к ней тот же, что и к .x файлу)string textureFile = FilePath + materials[i].TextureFileName;
try
{
// Извлекаем информацию о текстуре
info = BaseTexture.GetImageInformationFromFile(textureFile);
//Console.WriteLine("Файл с текстурой:" + textureFile);
//Console.WriteLine(info);
}
catch
{
Console.WriteLine("Текстура не найдена!");
}
switch (info.ResourceType)
{
case ResourceType.Texture:
meshTextures[i] = new Texture(device, textureFile);
break;
case ResourceType.CubeTexture:
meshTextures[i] = new CubeTexture(device, textureFile);
break;
case ResourceType.VolumeTexture:
meshTextures[i] = new VolumeTexture(device, textureFile);
break;
}
}
}
}
/// <summary> Обработка события OnResize() </summary>protected override void OnResize(EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
return;
}
if (device != null)
{
if (!BuildPresentParameters())
{
throw new Exception("Устройство сбросить невозможно!");
}
font.OnLostDevice();//////////////////////////////////////////////////////////
device.Reset(presentParams);
}
base.OnResize(e);
}
// Выбор режима отображения - сеточный (WireFrame) или сплошной (Solid)protected override void OnKeyPress(KeyPressEventArgs e)
{
if ((int)e.KeyChar == (int)Keys.Escape)
{
this.Close();
}
if ((e.KeyChar == 'w') || (e.KeyChar == 'ц')) { device.RenderState.FillMode = FillMode.WireFrame; }
if ((e.KeyChar == 's') || (e.KeyChar == 'ы')) { device.RenderState.FillMode = FillMode.Solid; }
base.OnKeyPress(e);
}
}
Re: Вершинный и индексный буфферы.
От:
Аноним
Дата:
12.04.06 16:19
Оценка:
Предлагаю вашему вниманию еще несколько стандартных уроков
на демонстрацию буфферов.
//-----------------------------------------------------------------
// Вершинный буфер. Плоский треугольник.
//-----------------------------------------------------------------using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.Generic;
using Microsoft.DirectX.Direct3D.CustomVertex;
class WindowsFormDemo : Form
{
// Создаем устройствоprivate Device device = null;
// параметры представления
PresentParameters presentParams = new PresentParameters();
private GraphicsBuffer<PositionColored> vb;///////////////////////////////////
/// <summary>
/// конструктор
/// </summary>public WindowsFormDemo()
{
this.Text = "Инициализация устройства DirectX";
this.MinimumSize = new Size(50, 100);
this.ClientSize = new Size(640, 480);
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
}
/// <summary> Инициализация графического устройства </summary>public void InitializeGraphics()
{
if (BuildPresentParameters())
{
// Инициализация устройства.
device = new Device(0, DeviceType.Hardware, this.Handle,
CreateFlags.SoftwareVertexProcessing, presentParams);
}
else
{
Console.WriteLine("Устройство не построено!");
return;
}
vb = new GraphicsBuffer<PositionColored>(3);/////////////////////////////////
vb.Write(new PositionColored(-2.0f, -2.0f, 5.0f, Color.Red));////////////////
vb.Write(new PositionColored(0.0f, 2.0f, 5.0f, Color.Green));////////////////
vb.Write(new PositionColored(2.0f, -2.0f, 5.0f, Color.Blue));////////////////
}
/// <summary> Задаем параметры представления. </summary>public bool BuildPresentParameters()
{
presentParams = new PresentParameters();
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.IsWindowed = true;
presentParams.EnableAutoDepthStencil = true;
presentParams.AutoDepthStencilFormat = DepthFormat.D16;
return true;
}
/// <summary> Строим камеру. </summary>private void SetupCamera()
{
// Параметры камеры
device.Transform.Projection =
Matrix.PerspectiveFieldOfViewLeftHanded((float)Math.PI / 4,
(float)this.Width / this.Height, 1.0f, 100.0f);
// Выключаем освещение
device.RenderState.Lighting = false;////////////////////////////////
//Режим отбора и устранения(исключени невидимой поверхности)
device.RenderState.CullMode = Cull.None;
}
/// <summary> Точка входа в приложение </summary>public static void Main()
{
using (WindowsFormDemo frm = new WindowsFormDemo())
{
try
{
frm.InitializeGraphics(); // Инициализация графического устройства
}
catch
{
MessageBox.Show("Инициализация DirectX не выполнена!");
}
Application.Run(frm);
}
}
/// <summary> Обработка события Paint </summary>protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, // что именно хотим очистить (текущее окно)
System.Drawing.Color.Black, // цвет очистки
1.0f, // буфер глубины.
0);
SetupCamera(); // Установка камеры
device.BeginScene();
device.VertexFormat = PositionColored.Format;//////////////////////////////////////////
device.DrawUserPrimitives(PrimitiveType.TriangleList, 1, vb);//////////////////////////
device.EndScene();
device.Present();
this.Invalidate();
}
/// <summary> Обработка события OnResize() </summary>protected override void OnResize(EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
return;
}
if (device != null)
{
if (!BuildPresentParameters())
{
throw new Exception("Устройство сбросить невозможно!");
}
device.Reset(presentParams);
}
base.OnResize(e);
}
}