使用C#加载obj格式的3d模型,并实现缩放、旋转等变换
事先说明:本文使用的是WPF程序自带的3d功能,想要用winform实现加载3d模型的可以退散了。
先上效果图吧。
我导入了两个模型,一个汽车和一个美女,都是在网上下载的obj格式的文件。导入之后调整了2个模型之间的位置关系,又添加了鼠标滚轮缩放,旋转的功能,这样我们就可以方便查看模型的各个细节了。
C#动态读取obj文件,加载的基本思路就是打开文件,解析obj格式,生成ModelVisual3D。我这里使用的是一个网上别人写的WavefrontObjLoader.cs。代码比较复杂,我们没必要全部看懂,只要会用就够了!
将WavefrontObjLoader.cs导入项目中
在初始化事件中加载模型并显示。使用的大概方式很简单,如下。
//new一个loader对象 WavefrontObjLoader wfl = new WavefrontObjLoader(); //ModelVisual3DWithName是WavefrontObjLoader定义的继承ModelVisual3D的对象,直接使用ModelVisual3D也是可以的 //导入obj,第一个模型命名为m ModelVisual3DWithName m = wfl.LoadObjFile(@"C:\Users\hasee\Desktop\WpfApplication2\WpfApplication2\Lancer_Evolution_10.obj"); m.Content = myModel3DGroup; //导入obj,第二个模型命名为n var n = wfl.LoadObjFile(@"C:\Users\hasee\Desktop\WpfApplication2\WpfApplication2\精细人体.obj"); n.Content = myModel3DGroup; //将两个模型添加到场景中 vp.Children.Add(m); vp.Children.Add(n); //vp是在xaml中定义的viewport3D
xaml中也是很简单,设置如下:
<Window x:Class="WpfApplication2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" MouseMove="Window_MouseMove" Title="MainWindow" Height="350" Width="525"> <Grid x:Name="lay" Background="Azure" MouseWheel="VP_MouseWheel"> <Viewport3D x:Name="vp" MouseLeftButtonDown="vp_MouseLeftButtonDown" Margin="0,31,0.4,-0.2"> <Viewport3D.Camera> <PerspectiveCamera FieldOfView="45" FarPlaneDistance="100" LookDirection="0,0,-3.4142135623731" NearPlaneDistance="0.1" Position="0,0,3.4142135623731" UpDirection="0,1,0"/> </Viewport3D.Camera> </Viewport3D> </Grid> </Window>
这样就实现了加载,下面是全部实现旋转和缩放,全部代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Media.Media3D; using System.Windows.Media.Animation; using System.Windows.Media.Effects; namespace WpfApplication2 { public partial class MainWindow : Window { //声明摄像头 PerspectiveCamera myPCamera; //鼠标灵敏度调节 double mouseDeltaFactor = 0.4; public MainWindow() { InitializeComponent(); //摄像头 myPCamera = new PerspectiveCamera(); myPCamera.Position = new Point3D(0, 0, 200); myPCamera.LookDirection = new Vector3D(0, 0, -1); myPCamera.FieldOfView = 1000; vp.Camera = myPCamera; Model3DGroup myModel3DGroup = new Model3DGroup(); //光源 //AmbientLight (自然光) //DirectionalLight (方向光) //PointLight (点光源) //SpotLight (聚光源) DirectionalLight myDirectionalLight = new DirectionalLight(); myDirectionalLight.Color = Colors.White; myDirectionalLight.Direction = new Vector3D(0.61, 0.5, 0.61); myModel3DGroup.Children.Add(myDirectionalLight); //DirectionalLight myDirectionalLight2 = new DirectionalLight(); //myDirectionalLight2.Color = Colors.White; //myDirectionalLight2.Direction = new Vector3D(0.61, 0.5, 0.61); //myModel3DGroup.Children.Add(myDirectionalLight2); //new一个loader对象 WavefrontObjLoader wfl = new WavefrontObjLoader(); //ModelVisual3DWithName是WavefrontObjLoader定义的继承ModelVisual3D的对象,直接使用ModelVisual3D也是可以的 //导入obj,第一个模型命名为m ModelVisual3DWithName m = wfl.LoadObjFile(@"C:\Users\hasee\Desktop\WpfApplication2\WpfApplication2\Lancer_Evolution_10.obj"); m.Content = myModel3DGroup; //导入obj,第二个模型命名为n var n = wfl.LoadObjFile(@"C:\Users\hasee\Desktop\WpfApplication2\WpfApplication2\精细人体.obj"); n.Content = myModel3DGroup; //下面是调整n的位置,初学者可以先注释掉。 var tt = new TranslateTransform3D(); tt.OffsetX = 110; tt.OffsetZ = -50; tt.OffsetY =-100; var tr = new RotateTransform3D(); tr.Rotation = new AxisAngleRotation3D(new Vector3D(1, 0, 0), 90); var tr2 = new RotateTransform3D(); tr2.Rotation= new AxisAngleRotation3D(new Vector3D(0, 0, 1), -45); var ts = new ScaleTransform3D(); ts.ScaleX = 1.5; ts.ScaleY = 1.5; ts.ScaleZ = 1.6; var tg = new Transform3DGroup(); tg.Children.Add(tr); tg.Children.Add(tr2); tg.Children.Add(tt); tg.Children.Add(ts); n.Transform = tg; //将两个模型添加到场景中 vp.Children.Add(m); vp.Children.Add(n); //添加鼠标事件,用于显示隐藏光晕特效 vp.MouseEnter += Vp_MouseEnter; vp.MouseLeave += Vp_MouseLeave; } private void Vp_MouseLeave(object sender, MouseEventArgs e) { vp.Effect = null; } private void Vp_MouseEnter(object sender, MouseEventArgs e) { DropShadowEffect BlurRadius = new DropShadowEffect(); BlurRadius.BlurRadius = 20; BlurRadius.Color = Colors.Yellow; BlurRadius.Direction = 0; BlurRadius.Opacity = 1; BlurRadius.ShadowDepth = 0; vp.Effect = BlurRadius; } public HitTestResultBehavior HTResult(HitTestResult rawresult) { //MessageBox.Show(rawresult.ToString()); // RayHitTestResult rayResult = rawresult as RayHitTestResult; RayHitTestResult rayResult = rawresult as RayHitTestResult; if (rayResult != null) { //RayMeshGeometry3DHitTestResult rayMeshResult = rayResult as RayMeshGeometry3DHitTestResult; RayHitTestResult rayMeshResultrayResult = rayResult as RayHitTestResult; if (rayMeshResultrayResult != null) { //GeometryModel3D hitgeo = rayMeshResult.ModelHit as GeometryModel3D; var visual3D = rawresult.VisualHit as ModelVisual3D; //do something } } return HitTestResultBehavior.Continue; } //鼠标位置 Point mouseLastPosition; private void vp_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { mouseLastPosition = e.GetPosition(this); //RayHitTestParameters hitParams = new RayHitTestParameters(myPCamera.Position, myPCamera.LookDirection); //VisualTreeHelper.HitTest(vp.Children[0], null, ResultCallback, hitParams); //下面是进行点击触发检测,可忽略,注释 Point3D testpoint3D = new Point3D(mouseLastPosition.X, mouseLastPosition.Y, 0); Vector3D testdirection = new Vector3D(mouseLastPosition.X, mouseLastPosition.Y, 100); PointHitTestParameters pointparams = new PointHitTestParameters(mouseLastPosition); RayHitTestParameters rayparams = new RayHitTestParameters(testpoint3D, testdirection); //test for a result in the Viewport3D VisualTreeHelper.HitTest(vp, null, HTResult, pointparams); } //鼠标旋转 private void Window_MouseMove(object sender, MouseEventArgs e) { if (Mouse.LeftButton == MouseButtonState.Pressed)//如果按下鼠标左键 { Point newMousePosition = e.GetPosition(this); if (mouseLastPosition.X != newMousePosition.X) { //进行水平旋转 HorizontalTransform(mouseLastPosition.X < newMousePosition.X, mouseDeltaFactor);//水平变换 } if (mouseLastPosition.Y != newMousePosition.Y)// change position in the horizontal direction { //进行垂直旋转 VerticalTransform(mouseLastPosition.Y > newMousePosition.Y, mouseDeltaFactor);//垂直变换 } mouseLastPosition = newMousePosition; } } //鼠标滚轮缩放 private void VP_MouseWheel(object sender, MouseWheelEventArgs e) { double scaleFactor = 13; //120 near , -120 far System.Diagnostics.Debug.WriteLine(e.Delta.ToString()); Point3D currentPosition = myPCamera.Position; Vector3D lookDirection = myPCamera.LookDirection;//new Vector3D(camera.LookDirection.X, camera.LookDirection.Y, camera.LookDirection.Z); lookDirection.Normalize(); lookDirection *= scaleFactor; if (e.Delta == 120)//getting near { //myPCamera.FieldOfView /= 1.2; if ((currentPosition.X + lookDirection.X) * currentPosition.X > 0) { currentPosition += lookDirection; } } if (e.Delta == -120)//getting far { //myPCamera.FieldOfView *= 1.2; currentPosition -= lookDirection; } Point3DAnimation positionAnimation = new Point3DAnimation(); positionAnimation.BeginTime = new TimeSpan(0, 0, 0); positionAnimation.Duration = TimeSpan.FromMilliseconds(100); positionAnimation.To = currentPosition; positionAnimation.From = myPCamera.Position; positionAnimation.Completed += new EventHandler(positionAnimation_Completed); myPCamera.BeginAnimation(PerspectiveCamera.PositionProperty, positionAnimation, HandoffBehavior.Compose); } void positionAnimation_Completed(object sender, EventArgs e) { Point3D position = myPCamera.Position; myPCamera.BeginAnimation(PerspectiveCamera.PositionProperty, null); myPCamera.Position = position; } // 垂直变换 private void VerticalTransform(bool upDown, double angleDeltaFactor) { Vector3D postion = new Vector3D(myPCamera.Position.X, myPCamera.Position.Y, myPCamera.Position.Z); Vector3D rotateAxis = Vector3D.CrossProduct(postion, myPCamera.UpDirection); RotateTransform3D rt3d = new RotateTransform3D(); AxisAngleRotation3D rotate = new AxisAngleRotation3D(rotateAxis, angleDeltaFactor * (upDown ? 1 : -1)); rt3d.Rotation = rotate; Matrix3D matrix = rt3d.Value; Point3D newPostition = matrix.Transform(myPCamera.Position); myPCamera.Position = newPostition; myPCamera.LookDirection = new Vector3D(-newPostition.X, -newPostition.Y, -newPostition.Z); //update the up direction Vector3D newUpDirection = Vector3D.CrossProduct(myPCamera.LookDirection, rotateAxis); newUpDirection.Normalize(); myPCamera.UpDirection = newUpDirection; } // 水平变换: private void HorizontalTransform(bool leftRight, double angleDeltaFactor) { Vector3D postion = new Vector3D(myPCamera.Position.X, myPCamera.Position.Y, myPCamera.Position.Z); Vector3D rotateAxis = myPCamera.UpDirection; RotateTransform3D rt3d = new RotateTransform3D(); AxisAngleRotation3D rotate = new AxisAngleRotation3D(rotateAxis, angleDeltaFactor * (leftRight ? 1 : -1)); rt3d.Rotation = rotate; Matrix3D matrix = rt3d.Value; Point3D newPostition = matrix.Transform(myPCamera.Position); myPCamera.Position = newPostition; myPCamera.LookDirection = new Vector3D(-newPostition.X, -newPostition.Y, -newPostition.Z); } } }
事实上,fontwaveloader这个类可以很方便的生成DLL文件,使用方式是一样的,这样vb语言也可以使用它加载obj了,我已经测试成功了。
c#工程文件下载:
https://pan.baidu.com/s/1o7A5o8q
下面是用vb使用dll加载的3d模型代码
https://pan.baidu.com/s/1ge5t5Bl【提取码:9crv】