飘遥的Blog

C/C++/.NET
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

乱弹琴 Silverlight 2.0 (20) 手工打造 3D 动画

Posted on 2008-05-07 04:09  Zzx飘遥  阅读(683)  评论(0编辑  收藏  举报
前言:Silverlight 2.0 Beta1 已经发布,加入了许多激动人心的新特性:WPF UI 框架、丰富的控件、丰富的网络支持、丰富的基础类库支持等。这是本人的学习笔记,写的比较乱,因此定名为乱弹琴Silverlight 2.0 系列文章。
 
本篇介绍手工打造3D动画-旋转立方体。

3D动画是很有潜力的一个方向,3D程序作图已经有很多成熟的软件包,如DirectX、OpenGL等等,但并不是所有场合都能用上述软件包三维作图,如目前的Silverlight,有兴趣可以学习一下底层的三维图形生成算法。
主要知识点是三维变换、投影、消隐、纹理、光照等,具体算法可以参考计算机图形学教程等资料。
本例主要用到的知识是简单的三维点、三维向量的矩阵变换,消隐是用Canvas的ZIndex属性实现的。
具体算法不多介绍,有机会后面的文章会详细介绍。
示例代码:
XAML:
<UserControl x:Class="Test3D.Page"
    xmlns
="http://schemas.microsoft.com/client/2007"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    Width
="700" Height="500">
    
<Grid x:Name="LayoutRoot" Background="DarkGreen">

    
</Grid>
</UserControl>

C#:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;

using Xianfen.Net;

namespace Test3D
{
    
/// <summary>
    
///
    
/// ** 绘制三维图形 **
    
///
    
/// File: Page.xaml.cs
    
///
    
/// Author: 周振兴 (Zxjay 飘遥)
    
///
    
/// E-Mail: tda7264@163.com
    
///
    
/// Date: 08-05-06
    
///
    
/// http://xianfen.net
    
///
    
/// </summary>
    public partial class Page : UserControl
    {
        
double a = 0.0;

        Point3D A;  
//定义立方体顶点
        Point3D B;
        Point3D C;
        Point3D D;
        Point3D E;
        Point3D F;
        Point3D G;
        Point3D H;

        Polygon p1
= new Polygon();
        Polygon p2
= new Polygon();
        Polygon p3
= new Polygon();
        Polygon p4
= new Polygon();
        Polygon p5
= new Polygon();
        Polygon p6
= new Polygon();

        PointCollection pc
= null;


        
public Page()
        {
            InitializeComponent();

            DispatcherTimer t
= new DispatcherTimer();  //定义定时器
            t.Interval = new TimeSpan(0, 0, 0, 0, 10);
            t.Tick
+= new EventHandler(t_Tick);
            t.Start();

            
//设置多边形(四边形)基本属性
            p1.Stroke = p2.Stroke = p3.Stroke = p4.Stroke = p5.Stroke = p6.Stroke = new SolidColorBrush(Colors.Red);
            p1.Fill
= p2.Fill = p3.Fill = p4.Fill = p5.Fill = p6.Fill = new SolidColorBrush(Colors.Yellow);
            p1.Opacity
= p2.Opacity = p3.Opacity = p4.Opacity = p5.Opacity = p6.Opacity = 0.8;

            LayoutRoot.Children.Add(p1);
            LayoutRoot.Children.Add(p2);
            LayoutRoot.Children.Add(p3);
            LayoutRoot.Children.Add(p4);
            LayoutRoot.Children.Add(p5);
            LayoutRoot.Children.Add(p6);
        }

        
private void ChangeView(double ax, double ay, double az)
        {
            
//立方体顶点坐标
            A.X = 0;    A.Y = 0;    A.Z = 0;
            B.X
= 0;    B.Y = 100;  B.Z = 0;
            C.X
= 100;  C.Y = 100;  C.Z = 0;
            D.X
= 100;  D.Y = 0;    D.Z = 0;
            E.X
= 100;  E.Y = 0;    E.Z = 100;
            F.X
= 0;    F.Y = 0;    F.Z = 100;
            G.X
= 0;    G.Y = 100;  G.Z = 100;
            H.X
= 100;  H.Y = 100;  H.Z = 100;

            
//定义变换矩阵及进行的矩阵变换:
            
//(1)按向量(300, 200, 200)平移
            
//(2)X轴旋转ax弧度
            
//(3)Y轴旋转ay弧度
            
//(4)X轴旋转az弧度
            
//(5)按向量(20, 20, 20)平移
            Matrix3D m = Matrix3D.Translation(new Point3D(300, 200, 200)) *
                         Matrix3D.RotationX(ax)
*
                         Matrix3D.RotationY(ay)
*
                         Matrix3D.RotationZ(az)
*
                         Matrix3D.Translation(
new Point3D(20, 20, 20));

            
//三维点执行矩阵变换
            A.Transform(m);
            B.Transform(m);
            C.Transform(m);
            D.Transform(m);
            E.Transform(m);
            F.Transform(m);
            G.Transform(m);
            H.Transform(m);

            
//做X,Y方向的投影,绘制多边形
            pc = new PointCollection();
            pc.Add(
new Point(A.X, A.Y));
            pc.Add(
new Point(B.X, B.Y));
            pc.Add(
new Point(C.X, C.Y));
            pc.Add(
new Point(D.X, D.Y));
            p1.Points
= pc;
            
//设置XIndex,实现消隐
            Canvas.SetZIndex(p1, (int)(A.Z + B.Z + C.Z + D.Z + 0.5));

            pc
= new PointCollection();
            pc.Add(
new Point(A.X, A.Y));
            pc.Add(
new Point(F.X, F.Y));
            pc.Add(
new Point(G.X, G.Y));
            pc.Add(
new Point(B.X, B.Y));
            p2.Points
= pc;
            Canvas.SetZIndex(p2, (
int)(A.Z + F.Z + G.Z + B.Z + 0.5));

            pc
= new PointCollection();
            pc.Add(
new Point(A.X, A.Y));
            pc.Add(
new Point(D.X, D.Y));
            pc.Add(
new Point(E.X, E.Y));
            pc.Add(
new Point(F.X, F.Y));
            p3.Points
= pc;
            Canvas.SetZIndex(p3, (
int)(A.Z + D.Z + E.Z + F.Z + 0.5));

            pc
= new PointCollection();
            pc.Add(
new Point(C.X, C.Y));
            pc.Add(
new Point(H.X, H.Y));
            pc.Add(
new Point(E.X, E.Y));
            pc.Add(
new Point(D.X, D.Y));
            p4.Points
= pc;
            Canvas.SetZIndex(p4, (
int)(C.Z + H.Z + E.Z + D.Z + 0.5));

            pc
= new PointCollection();
            pc.Add(
new Point(E.X, E.Y));
            pc.Add(
new Point(H.X, H.Y));
            pc.Add(
new Point(G.X, G.Y));
            pc.Add(
new Point(F.X, F.Y));
            p6.Points
= pc;
            Canvas.SetZIndex(p6, (
int)(E.Z + H.Z + G.Z + F.Z + 0.5));

            pc
= new PointCollection();
            pc.Add(
new Point(B.X, B.Y));
            pc.Add(
new Point(G.X, G.Y));
            pc.Add(
new Point(H.X, H.Y));
            pc.Add(
new Point(C.X, C.Y));
            p5.Points
= pc;
            Canvas.SetZIndex(p5, (
int)(B.Z + G.Z + H.Z + C.Z + 0.5));
        }

        
void t_Tick(object sender, EventArgs e)
        {
            
//改变弧度值,实现旋转动画
            a += 0.005;

            ChangeView(a, a,
0);
        }
    }
}


using System;

namespace Xianfen.Net
{
    
/// <summary>
    
///
    
/// ** 定义矩阵 **
    
///
    
/// File: Matrix3D.cs
    
///
    
/// Author: 周振兴 (Zxjay 飘遥)
    
///
    
/// E-Mail: tda7264@163.com
    
///
    
/// Date: 08-05-06
    
///
    
/// http://xianfen.net
    
///
    
/// </summary>
    public struct Matrix3D
    {
        
public double
            M11, M12, M13, M14,
            M21, M22, M23, M24,
            M31, M32, M33, M34,
            M41, M42, M43, M44;

        
public Matrix3D(
            
double m11, double m12, double m13, double m14,
            
double m21, double m22, double m23, double m24,
            
double m31, double m32, double m33, double m34,
            
double m41, double m42, double m43, double m44)
        {
            M11
= m11; M12 = m12; M13 = m13; M14 = m14;
            M21
= m21; M22 = m22; M23 = m23; M24 = m24;
            M31
= m31; M32 = m32; M33 = m33; M34 = m34;
            M41
= m41; M42 = m42; M43 = m43; M44 = m44;
        }

        
/// <summary>
        
/// 绕X轴旋转
        
/// </summary>
        
/// <param name="angle"></param>
        
/// <returns></returns>
        public static Matrix3D RotationX(double angle)
        {
            
double sin = Math.Sin(angle);
            
double cos = Math.Cos(angle);

            
return new Matrix3D(
                
1, 0,   0,    0,
                
0, cos, -sin, 0,
                
0, sin, cos,  0,
                
0, 0,   0,    1
                );
        }


        
/// <summary>
        
/// 绕Y轴旋转
        
/// </summary>
        
/// <param name="angle"></param>
        
/// <returns></returns>
        public static Matrix3D RotationY(double angle)
        {
            
double sin = Math.Sin(angle);
            
double cos = Math.Cos(angle);

            
return new Matrix3D(
                cos,  
0, sin, 0,
                
0,    1, 0,   0,
                
-sin, 0, cos, 0,
                
0,    0, 0,   1
                );
        }

        
/// <summary>
        
/// 绕Z轴旋转
        
/// </summary>
        
/// <param name="angle"></param>
        
/// <returns></returns>
        public static Matrix3D RotationZ(double angle)
        {
            
double sin = Math.Sin(angle);
            
double cos = Math.Cos(angle);

            
return new Matrix3D(
                cos,
-sin, 0, 0,
                sin, cos,  
0, 0,
                
0,   0,    1, 0,
                
0,   0,    0, 1
                );
        }

        
/// <summary>
        
/// 平移操作
        
/// </summary>
        
/// <param name="point"></param>
        
/// <returns></returns>
        public static Matrix3D Translation(Point3D point)
        {
            
return new Matrix3D(
                
1,       0,       0,      0,
                
0,       1,       0,      0,
                
0,       0,       1,      0,
                point.X, point.Y, point.Z,
1
                );
        }

        
/// <summary>
        
/// 矩阵相乘
        
/// </summary>
        
/// <param name="m1"></param>
        
/// <param name="m2"></param>
        
/// <returns></returns>
        public static Matrix3D operator *(Matrix3D m1, Matrix3D m2)
        {
            
return new Matrix3D(
              m2.M11
* m1.M11 + m2.M12 * m1.M21 + m2.M13 * m1.M31 + m2.M14 * m1.M41,
              m2.M11
* m1.M12 + m2.M12 * m1.M22 + m2.M13 * m1.M32 + m2.M14 * m1.M42,
              m2.M11
* m1.M13 + m2.M12 * m1.M23 + m2.M13 * m1.M33 + m2.M14 * m1.M43,
              m2.M11
* m1.M14 + m2.M12 * m1.M24 + m2.M13 * m1.M34 + m2.M14 * m1.M44,

              m2.M21
* m1.M11 + m2.M22 * m1.M21 + m2.M23 * m1.M31 + m2.M24 * m1.M41,
              m2.M21
* m1.M12 + m2.M22 * m1.M22 + m2.M23 * m1.M32 + m2.M24 * m1.M42,
              m2.M21
* m1.M13 + m2.M22 * m1.M23 + m2.M23 * m1.M33 + m2.M24 * m1.M43,
              m2.M21
* m1.M14 + m2.M22 * m1.M24 + m2.M23 * m1.M34 + m2.M24 * m1.M44,

              m2.M31
* m1.M11 + m2.M32 * m1.M21 + m2.M33 * m1.M31 + m2.M34 * m1.M41,
              m2.M31
* m1.M12 + m2.M32 * m1.M22 + m2.M33 * m1.M32 + m2.M34 * m1.M42,
              m2.M31
* m1.M13 + m2.M32 * m1.M23 + m2.M33 * m1.M33 + m2.M34 * m1.M43,
              m2.M31
* m1.M14 + m2.M32 * m1.M24 + m2.M33 * m1.M34 + m2.M34 * m1.M44,

              m2.M41
* m1.M11 + m2.M42 * m1.M21 + m2.M43 * m1.M31 + m2.M44 * m1.M41,
              m2.M41
* m1.M12 + m2.M42 * m1.M22 + m2.M43 * m1.M32 + m2.M44 * m1.M42,
              m2.M41
* m1.M13 + m2.M42 * m1.M23 + m2.M43 * m1.M33 + m2.M44 * m1.M43,
              m2.M41
* m1.M14 + m2.M42 * m1.M24 + m2.M43 * m1.M34 + m2.M44 * m1.M44);
        }
    }
}


namespace Xianfen.Net
{
    
/// <summary>
    
///
    
/// ** 三维点 **
    
///
    
/// File: Point3D.cs
    
///
    
/// Author: 周振兴 (Zxjay 飘遥)
    
///
    
/// E-Mail: tda7264@163.com
    
///
    
/// Date: 08-05-06
    
///
    
/// http://xianfen.net
    
///
    
/// </summary>
    public struct Point3D
    {
        
public double X, Y, Z;

        
public Point3D(double x, double y, double z)
        {
            X
= x;
            Y
= y;
            Z
= z;
        }

        
/// <summary>
        
/// 实现三维点的矩阵变换
        
/// </summary>
        
/// <param name="m"></param>
        public void Transform(Matrix3D m)
        {
            
double x = X;
            
double y = Y;
            
double z = Z;

            X
= x * m.M11 + y * m.M21 + z * m.M31 + m.M41;
            Y
= x * m.M12 + y * m.M22 + z * m.M32 + m.M42;
            Z
= x * m.M13 + y * m.M23 + z * m.M33 + m.M43;
        }
    }
}



结束语

三维图形的绘制底层算法有些比较复杂,需要一定的数学功底,感觉自己该充电了^_^