目标:

中间的方块会不停的旋转。

第一步,新建wpf项目

第二步,为xaml窗体布局

下面是源代码(不是我写的)

先给grid设置背景颜色: Background="Black"

然后拖一个ContentControl到窗体上,默认的contentcontrol为

删掉这些属性后后,宽高就自动变成100%了。然后将单标签改为双标签。

contentcontrol中间有个Viewport3D标签,在工具箱里找了下,纳尼,没有?这不科学!

网上关于Viewport3D的教程还是很多,但是没有人告诉我他们是怎么拖进去的,只能,手写了。

用WinForm习惯了拖控件,用WPF很不习惯,WPF更倾向于Web的编程布局方式,优势是界面控制性更强,缺点是更难掌握,WPF更适用于对前端样式有更多需求的应用。

ClipToBounds:当设置ClipToBounds为True时,超出部分一定会被裁剪掉;ClipToBounds为False时,超出部分不一定不会被裁剪掉

窗体布局到这里就完了。下面是我的代码,我的原则是感觉没有用的东西就不要写,等它报错。所以很多属性我都删掉了。

第三步 将Viewport3D 属性loaded中的Viewport3D_Loaded写出来

这段代码的作用是绘制viewport3D中的画面。也是这段程序的主体部分

下面是源代码(不是我写的)

先翻译一下

业务逻辑大概就是这样,然后补充相关的代码,让它先能跑起来。

先建一个WpfCube类,

using System.Windows.Media;
using System.Windows.Media.Media3D;
/// <summary>
/// 这个类负责创建一个 立方体的 GeometryModel3D对象
/// </summary>
public class WpfCube
{
private Point3D origin;
private double width;
private double height;
private double depth; public Point3D centerBottom()
{
Point3D c = new Point3D(
origin.X + (width / ),
origin.Y + height,
origin.Z + (depth / )
); return c;
} public Point3D center()
{
Point3D c = new Point3D(
origin.X + (width / ),
origin.Y - height / ,
origin.Z + (depth / )
); return c;
} public Point3D centerTop()
{
Point3D c = new Point3D(
origin.X + (width / ),
origin.Y,
origin.Z + (depth / )
); return c;
} public WpfCube(Point3D P0, double w, double h, double d)
{
width = w;
height = h;
depth = d; origin = P0;
} public WpfCube(WpfCube cube)
{
width = cube.width;
height = cube.height;
depth = cube.depth; origin = new Point3D(cube.origin.X, cube.origin.Y, cube.origin.Z);
} public WpfRectangle Front()
{
WpfRectangle r = new WpfRectangle(origin, width, height, ); return r;
} public WpfRectangle Back()
{
WpfRectangle r = new WpfRectangle(new Point3D(origin.X + width, origin.Y, origin.Z + depth), -width, height, ); return r;
} public WpfRectangle Left()
{
WpfRectangle r = new WpfRectangle(new Point3D(origin.X, origin.Y, origin.Z + depth),
, height, -depth); return r;
} public WpfRectangle Right()
{
WpfRectangle r = new WpfRectangle(new Point3D(origin.X + width, origin.Y, origin.Z),
, height, depth); return r;
} public WpfRectangle Top()
{
WpfRectangle r = new WpfRectangle(origin, width, , depth); return r;
} public WpfRectangle Bottom()
{
WpfRectangle r = new WpfRectangle(new Point3D(origin.X + width, origin.Y - height, origin.Z),
-width, , depth); return r;
} public static void addCubeToMesh(Point3D p0, double w, double h, double d,
MeshGeometry3D mesh)
{
WpfCube cube = new WpfCube(p0, w, h, d); double maxDimension = Math.Max(d, Math.Max(w, h)); WpfRectangle front = cube.Front();
WpfRectangle back = cube.Back();
WpfRectangle right = cube.Right();
WpfRectangle left = cube.Left();
WpfRectangle top = cube.Top();
WpfRectangle bottom = cube.Bottom(); front.addToMesh(mesh);
back.addToMesh(mesh);
right.addToMesh(mesh);
left.addToMesh(mesh);
top.addToMesh(mesh);
bottom.addToMesh(mesh);
} public GeometryModel3D CreateModel(Color color)
{
return CreateCubeModel(origin, width, height, depth, color);
} public static GeometryModel3D CreateCubeModel(Point3D p0, double w, double h, double d, Color color)
{
MeshGeometry3D mesh = new MeshGeometry3D(); addCubeToMesh(p0, w, h, d, mesh); Material material = new DiffuseMaterial(new SolidColorBrush(color)); GeometryModel3D model = new GeometryModel3D(mesh, material); return model;
}
}

WpfCube.cs

然后里面还有一个WpfRectangle类,这个类负责立方体的一个平面

 using System.Windows.Media;
using System.Windows.Media.Media3D;
/// <summary>
/// 这个类负责一个构建一个面
/// </summary>
public class WpfRectangle
{
private Point3D p0;
private Point3D p1;
private Point3D p2;
private Point3D p3; /// <summary>
/// 必须4个点确定一个长方形
/// </summary>
public WpfRectangle(Point3D P0, Point3D P1, Point3D P2, Point3D P3)
{
p0 = P0;
p1 = P1;
p2 = P2;
p3 = P3;
} public WpfRectangle(Point3D P0, double w, double h, double d)
{
p0 = P0; if (w != 0.0 && h != 0.0) // front / back
{
p1 = new Point3D(p0.X + w, p0.Y, p0.Z);
p2 = new Point3D(p0.X + w, p0.Y - h, p0.Z);
p3 = new Point3D(p0.X, p0.Y - h, p0.Z);
}
else if (w != 0.0 && d != 0.0) // top / bottom
{
p1 = new Point3D(p0.X, p0.Y, p0.Z + d);
p2 = new Point3D(p0.X + w, p0.Y, p0.Z + d);
p3 = new Point3D(p0.X + w, p0.Y, p0.Z);
}
else if (h != 0.0 && d != 0.0) // side / side
{
p1 = new Point3D(p0.X, p0.Y, p0.Z + d);
p2 = new Point3D(p0.X, p0.Y - h, p0.Z + d);
p3 = new Point3D(p0.X, p0.Y - h, p0.Z);
}
} public void addToMesh(MeshGeometry3D mesh)
{
WpfTriangle.addTriangleToMesh(p0, p1, p2, mesh);
WpfTriangle.addTriangleToMesh(p2, p3, p0, mesh);
} public static void addRectangleToMesh(Point3D p0, Point3D p1, Point3D p2, Point3D p3,
MeshGeometry3D mesh)
{
WpfTriangle.addTriangleToMesh(p0, p1, p2, mesh);
WpfTriangle.addTriangleToMesh(p2, p3, p0, mesh);
} public static GeometryModel3D CreateRectangleModel(Point3D p0, Point3D p1, Point3D p2, Point3D p3)
{
return CreateRectangleModel(p0, p1, p2, p3, false);
} public static GeometryModel3D CreateRectangleModel(Point3D p0, Point3D p1, Point3D p2, Point3D p3, bool texture)
{
MeshGeometry3D mesh = new MeshGeometry3D(); addRectangleToMesh(p0, p1, p2, p3, mesh); Material material = new DiffuseMaterial(
new SolidColorBrush(Colors.White)); GeometryModel3D model = new GeometryModel3D(mesh, material); return model;
}
}

WpfRectangle.cs

然后我们发现里面还有一个WpfTriangle类 好像是管构造三角形的类,构造四边形用的两个三角形拼凑起的。(为什么要这么麻烦?难道系统没有自带构建四边形的方法?)

    using System.Windows.Media;
using System.Windows.Media.Media3D;
/// <summary>
/// 负责构建三角形的类
/// </summary>
class WpfTriangle
{
private Point3D p1;
private Point3D p2;
private Point3D p3; public WpfTriangle(Point3D P1, Point3D P2, Point3D P3)
{
p1 = P1;
p2 = P2;
p3 = P3;
} public static void addTriangleToMesh(Point3D p0, Point3D p1, Point3D p2, MeshGeometry3D mesh)
{
addTriangleToMesh(p0, p1, p2, mesh, false);
} public static void addPointCombined(Point3D point, MeshGeometry3D mesh, Vector3D normal)
{
bool found = false; int i = ; foreach (Point3D p in mesh.Positions)
{
if (p.Equals(point))
{
found = true;
mesh.TriangleIndices.Add(i);
mesh.Positions.Add(point);
mesh.Normals.Add(normal);
break;
} i++;
} if (!found)
{
mesh.Positions.Add(point);
mesh.TriangleIndices.Add(mesh.TriangleIndices.Count);
mesh.Normals.Add(normal);
} } public static void addTriangleToMesh(Point3D p0, Point3D p1, Point3D p2,
MeshGeometry3D mesh, bool combine_vertices)
{
Vector3D normal = CalculateNormal(p0, p1, p2); if (combine_vertices)
{
addPointCombined(p0, mesh, normal);
addPointCombined(p1, mesh, normal);
addPointCombined(p2, mesh, normal);
}
else
{
mesh.Positions.Add(p0);
mesh.Positions.Add(p1);
mesh.Positions.Add(p2);
mesh.TriangleIndices.Add(mesh.TriangleIndices.Count);
mesh.TriangleIndices.Add(mesh.TriangleIndices.Count);
mesh.TriangleIndices.Add(mesh.TriangleIndices.Count);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
}
} public GeometryModel3D CreateTriangleModel(Color color)
{
return CreateTriangleModel(p1, p2, p3, color);
} public static GeometryModel3D CreateTriangleModel(Point3D P0, Point3D P1, Point3D P2, Color color)
{
MeshGeometry3D mesh = new MeshGeometry3D(); addTriangleToMesh(P0, P1, P2, mesh); Material material = new DiffuseMaterial(new SolidColorBrush(color)); GeometryModel3D model = new GeometryModel3D(mesh, material); return model;
} public static Vector3D CalculateNormal(Point3D P0, Point3D P1, Point3D P2)
{
Vector3D v0 = new Vector3D(P1.X - P0.X, P1.Y - P0.Y, P1.Z - P0.Z); Vector3D v1 = new Vector3D(P2.X - P1.X, P2.Y - P1.Y, P2.Z - P1.Z); return Vector3D.CrossProduct(v0, v1);
}
}

WpfTriangle.cs

然后看看源代码

我所有文件都建好了,只剩完善 MainWindow.xaml.cs中的代码了。好开心

下面重点研究下下面的方法

 WpfCube cube = new WpfCube(new System.Windows.Media.Media3D.Point3D(0, 0, 0), sceneSize / 6, sceneSize / 6, sceneSize / 6);

这行代码调用wpfcube类的构造函数,定义原点,宽高深(我还是喜欢坐标表示法中的,原点,x轴,y轴,z轴)

接着创建一个模型出来,传入模型的颜色,然后这个方法又调用绘制6个面的方法

GeometryModel3D cubeModel = cube.CreateModel(Colors.Aquamarine);

  模型有了,然后要把他放到场景中去:

//创建一个模型集合来保存我们的模型
Model3DGroup groupScene = new Model3DGroup(); //将我们的模型添加到模型集合
groupScene.Children.Add(cubeModel);

  然后需要添加光线,不然就是漆黑一片,将源码中的添加光线注释掉,然后将Grid的背景颜色设为蓝色显示如下。

创建模型的时候,我们选了颜色Colors.Aquamarine,但是显示出来是黑色就是因为没有光。

所以要正常显示还要往场景中加入光

// 添加一个方向光
groupScene.Children.Add(positionLight(new Point3D(-sceneSize, sceneSize / 2, 0.0))); // 添加环境光
groupScene.Children.Add(new AmbientLight(Colors.Gray));

  

        public DirectionalLight positionLight(Point3D position)
{
DirectionalLight directionalLight = new DirectionalLight();
directionalLight.Color = Colors.Gray;
directionalLight.Direction = new Point3D(, , ) - position;
return directionalLight;
}

positionLight方法

仔细研究了下这个方法发现这个方法是传入一个点然后返回从原点到这个点方向的灰色光线,应该是做阴影效果的。

一个环境光一个阴影光,就将立方体衬托出来了。

为viewport3D设置视觉模型

            #region 得到视觉模型 visual
// 创建一个视觉模型--我们眼睛看到的
ModelVisual3D visual = new ModelVisual3D(); // 用我们做的几何模型来填充视觉模型
visual.Content = groupScene;
#endregion // 将视觉模型visual添加到viewport3D窗体中
viewport.Children.Add(visual);

这样之就将模型场景封装成一个视觉模型,添加到viewport3D中,但是现在还看不见。

需要为viewport3D设置Camera,因为屏幕上只是个平面,我们的模型是3D的,所以要设置一个观察点,将3D模型的投影成一个平面图形。

viewport.Camera = camera();
/// <summary>
/// 得到一个透视投影摄像机
/// </summary>
/// <returns></returns>
public PerspectiveCamera camera()
{
PerspectiveCamera perspectiveCamera = new PerspectiveCamera();
perspectiveCamera.Position = new Point3D(-sceneSize, sceneSize / , sceneSize);//设置观察点
perspectiveCamera.LookDirection = new Vector3D(lookat.X - perspectiveCamera.Position.X,
lookat.Y - perspectiveCamera.Position.Y,
lookat.Z - perspectiveCamera.Position.Z);//设置观察方向 空间向量的表示方式
perspectiveCamera.FieldOfView = ;//水平视角
return perspectiveCamera;
}
     Point3D lookat = new Point3D(, , );

camera()方法

现在的效果如下:

最后就是让模型动起来

turnModel(cube.center(), cubeModel, , , , true);
/// <summary>
/// 模型动起来
/// </summary>
/// <param name="center"></param>
/// <param name="model"></param>
/// <param name="beginAngle">开始角度</param>
/// <param name="endAngle">结束角度</param>
/// <param name="seconds"></param>
/// <param name="forever"></param>
public void turnModel(Point3D center, GeometryModel3D model, double beginAngle, double endAngle, double seconds, bool forever)
{
//向量作为两个旋转轴
Vector3D vector = new Vector3D(, , );
Vector3D vector2 = new Vector3D(, , ); // 创建AxisAngleRotation3D(三维旋转)。我们可以设置一个0度的旋转,因为我们要给他们做动画
AxisAngleRotation3D rotation = new AxisAngleRotation3D(vector, 0.0);
AxisAngleRotation3D rotation2 = new AxisAngleRotation3D(vector2, 0.0); // 创建双动画来动画我们的每一个旋转
DoubleAnimation doubleAnimation = new DoubleAnimation(beginAngle, endAngle, durationTS(seconds));
DoubleAnimation doubleAnimation2 = new DoubleAnimation(beginAngle, endAngle, durationTS(seconds)); // 为我们的动画设置重复的行为和持续时间
if (forever)
{
doubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
doubleAnimation2.RepeatBehavior = RepeatBehavior.Forever;
} doubleAnimation.BeginTime = durationTS(0.0);
doubleAnimation2.BeginTime = durationTS(0.0); // 创建2个旋转变换应用到我们的模型。每个需要一个旋转和一个中心点
RotateTransform3D rotateTransform = new RotateTransform3D(rotation, center);
RotateTransform3D rotateTransform2 = new RotateTransform3D(rotation2, center); // 创建一个转换组来保持我们的2个转换
Transform3DGroup transformGroup = new Transform3DGroup();
transformGroup.Children.Add(rotateTransform);
transformGroup.Children.Add(rotateTransform2); // 将旋转组添加到模型上
model.Transform = transformGroup; // 开始动画 -- specify a target object and property for each animation -- in this case,
//目标是我们创造的两个AxisAngleRotation3D对象 并且 我们改变每个的角度属性
rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, doubleAnimation);
rotation2.BeginAnimation(AxisAngleRotation3D.AngleProperty, doubleAnimation2);
} private int durationM(double seconds)
{
int milliseconds = (int)(seconds * );
return milliseconds;
} public TimeSpan durationTS(double seconds)
{
TimeSpan ts = new TimeSpan(, , , , durationM(seconds));
return ts;
}

turnModel方法

然后就可以动了。

但是先在做模型都是用的unity3d和cocos2d程序里面直接写模型就只能在cpu中跑,gpu就不管了

----------------------分割线 2016-8-4 15:40----------------------

今天,终于知道viewport3d控件在哪里了。

工具栏中开始是不显示的,要调出来。

步骤如下:

1、打开工具箱窗口 (视图-工具箱)

2、在工具箱面板中点击右键、选择 选择项 ,就可以看到下面的视图。然后选择自己要的添加进来就行了。

WPF学习系列 绘制旋转的立方体的更多相关文章

  1. WPF学习系列之五(WPF控件)

    控件:    1.内容控件------这些控件能够包含嵌套的元素,为它们提供几乎无限的显示能力.内容控件包括Lable,Button 以及ToolTip类. 内容控件是更特殊的控件类型,它们可以包含( ...

  2. WPF学习系列 游戏-选张图片做成9宫格拼图

    今天要学习一个拼图项目. 目标是传入一张图片,然后将它分成9份,去掉一份,鼠标点击进行拼图. 源文件结构很简单 第一步.新建项目 这一步没什么好说的,新建一个项目就跟源文件结构一样了 第二步.页面布局 ...

  3. WPF学习系列之八(形状,画刷和变换)

    形状,画刷和变换   概述: 在许多用户界面技术中,普通控件和自定义绘图之间具有清晰的区别.通常来说,绘图特性只用于特定的应用程序--如游戏,数据可视化和物理仿真等.而WPF具有一个非常不同的原则.它 ...

  4. WPF学习系列 简单的窗体设置

    今天要学习的源码是一个窗体设置.效果如下,可拖拽.这让我想起了vs的启动界面 下面是源码的情况 项目结构: 窗体代码: cs代码 1.新建项目 略 2.设置窗体 AllowsTransparency= ...

  5. WPF学习系列之七 (样式与行为)

    样式(Styles)是组织和重用格式化选项的重要工具.不是使用重复的标记填充XAML,以设置诸如边距.颜色及字体等细节,而可以创建一系列封装所有这些细节的样式.然后可以在需要之处通过一个属性应用样式. ...

  6. WPF学习系列之六 (元素绑定)

    元素绑定 简单地说,数据绑定是一种关系,该关系告诉WPF从一个源对象提取一些信息,并使用这些信息设置目标对象的属性.目标属性总是依赖属性,并且通常位于WPF元素中. 一.将元素绑定到一起 <Wi ...

  7. WPF学习系列之四(WPF事件5大类)

    WPF最重要的5类事件: 生命周期事件:这些事件将在元素被初始化,加载或卸载时发生. 鼠标事件 这些事件是鼠标动作的结果. 键盘事件 这些事件是键盘动作的结果. 手写笔事件 这些事件是作用类似铅笔的手 ...

  8. WPF学习系列之二 (依赖项属性)

    依赖属性;(dependency property)  它是专门针对WPF创建的,但是WPF库中的依赖项属性都使用普通的.NET属性过程进行了包装.从而可能通过常规的方式使用它们,即使使用他们的代码不 ...

  9. [WPF系列]从基础起步学习系列计划

    引言 WPF技术已经算不什么新技术,一搜一大把关于WPF基础甚至高级的内容.之前工作中一直使用winform所以一直没有深入学习WPF,这次因项目中使用了WPF技术来实现比较酷的展示界面.我在这里只是 ...

随机推荐

  1. H5 meta小结

    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1, ...

  2. iOS10 适配问题-Xcode8

    前段时间升级了Xcode8,整体来说对OC的影响不大,但是还是跳一个坑,消耗了不少时间.这里总结下遇到的适配问题. 1.权限问题 Xcode8 访问相机.相册等需要权限的地方崩溃 解决办法: 在使用私 ...

  3. 「视频直播技术详解」系列之七:直播云 SDK 性能测试模型

    ​关于直播的技术文章不少,成体系的不多.我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面.深入地了解视频直播技术,更好地技术选型. 本系列文章大纲如下: ...

  4. iOS sqlite 的各种操作

    iOS --SQL的增加.删除.查找.修改 iOS对于数据库的操作:增加.删除.查找.修改 首先需要创建一个数据库:本程序的数据库是在火狐浏览器里的插件里写的微量型数据库 火狐找查找SQLite Ma ...

  5. Spring:Aop before after afterReturn afterThrowing around 的原理

    在写这篇文章前,在网上看了一下大多数的文章,在说这一块时,都是用语言来表达的.before.after.afterReturn.afterThrowing 这四个用语言是可以说清楚的,但 around ...

  6. 修复 Windows7 资源管理器左侧收藏夹无法展开问题

    相信大家在网上搜多到的解决办法大多数都是修改注册表,但是这个办法多数是无效的 1.运行regedit 2.展开到HKEY_CLASSES_ROOT\lnkfile 3.添加一个字符串值:IsShort ...

  7. 字符串匹配算法--Brute-Force算法

    Brute-Force(暴力)算法是字符串匹配中最简单也是最容易理解的算法. 主要思想是 按顺序遍历母串,将每个字符作为匹配的起始字符,判断是否匹配字串.若第一个字符与字串匹配,则比较下一个字符,否则 ...

  8. 程序设计模式浅析(plain framework商业版设计模式)

    程序设计其实对程序开发者来说十分重要,但是在工作中往往我们却忽略了这一块,因为我们所用的都是现有的模式.一个设计模式的好坏,往往能够体现出程序的专业性,还有整个项目的可持续性.这就是为什么有些公司,在 ...

  9. 浅谈Vue.js

    作为一名Vue.js的忠实用户,我想有必要写点文章来歌颂这一门美好的语言了,我给它的总体评价是“简单却不失优雅,小巧而不乏大匠”,下面将围绕这句话给大家介绍Vue.js,希望能够激发你对Vue.js的 ...

  10. 数组,集合分割函数,join()

    join函数定义如下: // 串联类型为 System.String 的 System.Collections.Generic.IEnumerable<T> 构造集合的成员,其中在每个成员 ...