Застосування Matrix3dProjection у Silverlight

воскресенье, 23 мая 2010, Алекс

Тривимірна графіка у Silverlight

Починаючи з версії 3, Silverlight SDK надає можливість використовувати тривимірну проекцію для графічних об`єктів за допомогою матриці перетворення. Почерпнути знання про 3D проекції та матрицю переходу можна з статей вікіпедії http://en.wikipedia.org/wiki/3D_projection та http://en.wikipedia.org/wiki/Transformation_matrix.

У цій статті я б хотів поділитись з аудиторією методами побудови тривимірної сцени у Silverlight.

Silverlight SDK надає для використання класс PlaneProjection http://msdn.microsoft.com/en-us/library/system.windows.media.planeprojection(v=VS.95).aspx . Він дозволяє застосовувати базові тривимірні еффекти для графічного об’екту такі як зміщення та поворот по трьом координатам, що дуже зручно використовувати для простих додатків але недоліки його використання полягають в інкапсуляції матричних перетворень. Тобто використовуючи цей клас ми не котролюємо повністю усією проекцією.

Для побудови складних тривимірних сцен зазвичай застосовується матриця перетворення. Ця матриця переходу використовується в математиці сучасної тривимірної графіки, тому для Silverlight програміста існує можливість звернутись до багаторічного досвіду програмування та побудови тривимірних сцен за допомогою DirectX або OpenGL. Але це не означає що программісту Silverlight надається можливість використовувати усю потужність сучасного аппаратного прискорення 3D графіки.

Матриця переходу має дуже важливу властивість мультиплікативності. Тобто для побудови суммарної матриці перетворень необхідно просто перемножити усі матриці базових перетворень. Прозорий приклад побудови матриці перетворення та ї застосування в Silverlight я знайшов в проекті http://matrix3dex.codeplex.com/ звідки я брав усі приклади для програмування 3D Графіки.

Для побудови тривимірної сцени необхідно визначити базову матрицю, завдання якої буде здійснення проектування деякої ділянки світу що ми переглядаємо на екран абстрактної камери. Графічно проектування ділянки світу на екран спостерігаяа здійснюється наступним чином:

Побудова тривимірної сцени
насамперед вимагає визначення системи координат. Система координат лівої руки
передбачає що напрям вісі координат OX відносно вісі
координат OZ визначають відповідно за допомогою лівої руки. Аналогічно і з системою координат
правої руки. При побудові матриці камери потрібно враховувати яку систему
координат ми використовуемо. Нижче будемо використовувати систему координат лівої руки.

Матриця камери будуеться з трьох матриць.

1.Матриця перспективи визначає кут зору, співвідношення сторін, мінімальну та максимальну відстань до обекту:

            ///

            /// Creates a left-handed perspective projection matrix.

            ///

            /// The Field Of View in radians. See for a conversion method.

            /// The aspect ratio (width divided by height: w/h).

            /// The z-coordinate of the near view-plane.

            /// The z-coordinate of the far view-plane.

            /// A new projection matrix.

            public static Matrix3D CreatePerspectiveFieldOfViewLeftHand(double fieldOfView, double aspectRatio, double near, double far)

            {

                double h = 1.0 / Math.Tan(fieldOfView * 0.5);

                double w = h / aspectRatio;

                double d = far - near;

                double sz = far / d;

                double z = -near * far / d;

 

                return new Matrix3D(w, 0, 0, 0,

                                     0, h, 0, 0,

                                     0, 0, sz, 1,

                                     0, 0, z, 1);

            }

2.Матриця перетворення порту перегляду визначає розміри екрану на який проектується світ. Зазвичай використовують розміри області відображення сцени

            ///

            /// Creates a viewport transformation matrix.

            ///

            /// The width of the viewport in screen space.

            /// The height of the viewport in screen space.

            /// A new viewport transformation matrix.

            public static Matrix3D CreateViewportTransformation(double width, double height)

            {

                double wh = width * 0.5;

                double hh = height * 0.5;

                return new Matrix3D(wh, 0, 0, 0,

                                     0, -hh, 0, 0,

                                     0, 0, 1, 0,

                                    wh, hh, 0, 1);

            }

3.Матриця позиціонування камери визначатиме точку перебування переглядача та точку погляду переглядача.

Для побудови цієї матриці використовуеться класс MathHelper описаний в бібліотеці http://matrix3dex.codeplex.com/ .

///

            /// Creates a left-handed look-at matrix (camera).

            ///

            /// The x-coordinate of the viewer (camera) position.

            /// The y-coordinate of the viewer (camera) position.

            /// The z-coordinate of the viewer (camera) position.

            /// The x-coordinate of the target (look-at).

            /// The y-coordinate of the target (look-at).

            /// The z-coordinate of the target (look-at).

            /// The x-coordinate of the up vector.

            /// The y-coordinate of the up vector.

            /// The z-coordinate of the up vector.

            /// A new look-at matrix.

            public static Matrix3D CreateLookAtLeftHand(double eyePosX, double eyePosY, double eyePosZ, double targetX, double targetY, double targetZ, double upX, double upY, double upZ)

            {

                // Z axis

                double zx = targetX - eyePosX;

                double zy = targetY - eyePosY;

                double zz = targetZ - eyePosZ;

                MathHelper.VectorNormalize(ref zx, ref zy, ref zz);

 

                // X axis

                double xx, xy, xz;

                MathHelper.VectorCross(upX, upY, upZ, zx, zy, zz, out xx, out xy, out xz);

                MathHelper.VectorNormalize(ref xx, ref xy, ref xz);

 

                // Y axis

                double yx, yy, yz;

                MathHelper.VectorCross(zx, zy, zz, xx, xy, xz, out yx, out yy, out yz);

 

                // Eye angles

                double ex = -MathHelper.VectorDot(xx, xy, xz, eyePosX, eyePosY, eyePosZ);

                double ey = -MathHelper.VectorDot(yx, yy, yz, eyePosX, eyePosY, eyePosZ);

                double ez = -MathHelper.VectorDot(zx, zy, zz, eyePosX, eyePosY, eyePosZ);

 

                return new Matrix3D(xx, yx, zx, 0,

                                    xy, yy, zy, 0,

                                    xz, yz, zz, 0,

                                    ex, ey, ez, 1);

            }

Маючи реалізованими ці три методи можна побудувати матрицю камери почерговим перемножуванням трьох матриць:

//Переглядач буде знаходитись в точці (0,5,0) та дивитиметься на точку (0,0,-1)
var lookAt = CreateLookAtLH(0, 5, 0, 0, 0, -1);
var aspectRatio = myCanvas.Width / myCanvas.Height;
//Поле зору користувача складатиме 45 градусів. Найближча та найвіддаленіша відстань перегляду буде відповідно 1 та 4000
var projection = CreatePerspectiveFieldOfViewLH(45 * 0,017, aspectRatio, 1, 4000);
//Розміри екрану спостерігача збігатимуться з розмірами поля Canvas де будуть розташовані зпроектовані елементи
var viewport = CreateViewportTransformation(myCanvas.Width, myCanvas.Height);
 
var CameraProjection = lookAt * projection * viewport; 

Маючи матрицю камери можна переходити для побудови матриць перетворення для об`єктів, тобто для розташування наших квадратиків, прямокутничків та інших UIElement у тривимірному світі.

Усі перетворення для об’єктів також мають мультиплікативну властивість. Розглянемо просте перетворення трансляції, що надає можливість змістити об’єкт на певний вектор:

     ///

            /// Creates a translation matrix.

            ///

            /// The offset along the x-axis.

            /// The offset along the y-axis..

            /// The offset along the z-axis.

            /// A new translation matrix.

            public static Matrix3D CreateTranslation(double x, double y, double z)

            {

                return new Matrix3D(1, 0, 0, 0,

                                     0, 1, 0, 0,

                                     0, 0, 1, 0,

                                     x, y, z, 1);

            }

Маючи матрицю камери та матрицю перетворень, застосуємо суммарну матрицю проекції для певного UIElement. Наприклад картинки:

var ViewProjection = CameraProjection * CreateTranslation(0,0,-1); 
 
myImageControl.Projection = new Matrix3DProjection { ProjectionMatrix = viewProjection };

Тепер картинка буде трансформована згідно матриці перетворення.

Що дає такий складний підхід до програмування проекційних перетворень?

Такий підхід надає можливість створити бібліотеку для створення складних тривимірних сцен у Silverlight додатку, які чудово виглядають та легко програмуються.

Наприклад тривимірна сцена реалізована за допомогою вищезгаданого підходу виглядає наступним чином: http://dl.dropbox.com/u/2681028/CodeplexData/Matrix3DEx/Sample/Matrix3DExSampleTestPage.html

Або ж реалізована мною спрощена модель галактики:

Сподіваюсь описаний мною підхід полегшить роботу по створенню тривимірних Silverlight додатків.

Компании из статьи


Microsoft Украина


Сайт:
http://www.microsoft.com/ukr/ua/

Microsoft Украина Украинское подразделение компании Microsoft.

Ищите нас в интернетах!

Комментарии

Свежие вакансии