事先說明:本文使用的是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】