使用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】

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM