You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

3D相机实现异常:相机未在空间旋转,反而旋转3D物体

3D相机旋转异常问题:相机未按预期旋转,反而导致物体旋转

正在实现3D空间,开发可在自定义3D空间中移动的3D相机,但遇到异常:相机未按预期在空间中旋转,反而导致3D物体旋转,无法定位问题所在。

相关代码实现

Camera3D类代码

public class Camera3D
{
    public Vector3D Pos { get; set; }
    public Vector3D Target { get; set; }
    public Vector3D Up { get; set; }

    public double CameraSpeed { get; set; }


    public Camera3D(Vector3D pos, Vector3D target, Vector3D up)
    {
        Pos = pos;
        Target = target;
        Up = up;

        Pos.Normalize();
        Target.Normalize();
        Up.Normalize();
    }


    public Camera3D() 
    {
        Pos = new Vector3D(0, 0, 3);
        Target = new Vector3D(0, 0, 0);
        Up = new Vector3D(0, 1, 0);
    }

    private Vector3D GetCameraDirection(Vector3D pos, Vector3D target)
    {
        Vector3D dir = pos - target;
        dir.Normalize();
        return dir;
    }


    private Vector3D GetRight(Vector3D pos, Vector3D target, Vector3D up)
    {
        Vector3D right = Vector3D.CrossProduct(Up, GetCameraDirection(pos, up));
        right.Normalize();
        return right;
    }


    private Vector3D GetCameraUp(Vector3D pos, Vector3D target, Vector3D up)
    {
        Vector3D vup = Vector3D.CrossProduct(GetCameraDirection(pos, target), GetRight(pos, target, up));
        return vup;
    }


    public Matrix3D LookAt(Vector3D pos, Vector3D target, Vector3D up) 
    {
        // translation matrix for camera
        Matrix3D translation = new Matrix3D
            (
            1, 0, 0, -Pos.X,
            0, 1, 0, -Pos.Y,
            0, 0, 1, -Pos.Z,
            0, 0, 0, 1
            );


        Vector3D vRight = GetRight(pos, target, up);
        Vector3D vUp    = GetCameraUp(pos, target, up);
        Vector3D vDir   = GetCameraDirection(pos, target);
        Matrix3D m = new Matrix3D
            (
                vRight.X,   vRight.Y,   vRight.Z,       0,
                vUp.X,      vUp.Y,      vUp.Z,          0,
                vDir.X,     vDir.Y,     vDir.Z,         0,
                0,          0,          0,              1
            );

        return m * translation;
    }
}

主窗口相关字段及构造函数

// just temporary field for test rotating Camera
double a = 0;

Vector3D Translate = new Vector3D(1, 1, 1);
Vector3D Rotation = new Vector3D(0, 0, 0);
Vector3D Scaling = new Vector3D(1, 1, 1);

CTransform3D cTransform; // class for transform operations
Camera3D c = new Camera3D();
public MainWindow()
{
    InitializeComponent();
    cTransform = new CTransform3D(Rotation, Translate, Scaling);
    cTransform.Camera = c; // camera for transformations

    // load the mesh
    o.Mesh.LoadFromObjectFile("D:\\de\\test\\ship.obj");
}

绘制物体的核心Draw方法

public void Draw()
{
    try
    {
        // util classes
        MatrixUtil mu = new MatrixUtil();

        cv.Children.Clear(); // clear canvas

        foreach (var triangle in o.Mesh.Triangles)
        {
           Triangle tProjected = new Triangle(), tTransformed = new Triangle(), tViewed = new Triangle();

           // just init for right compile
           tProjected.Points = new Vector3D[3] { new Vector3D(0, 0, 0), new Vector3D(0, 0, 0), new Vector3D(0, 0, 0) };
           tTransformed.Points = new Vector3D[3] { new Vector3D(0, 0, 0), new Vector3D(0, 0, 0), new Vector3D(0, 0, 0) };
           tViewed.Points = new Vector3D[3] { new Vector3D(0, 0, 0), new Vector3D(0, 0, 0), new Vector3D(0, 0, 0) };

           // project and transform matrices
           Matrix3D ProjectionMatrix = cTransform.ProjectionMatrix(5, Width, Height, 0.1, 1000); // just projection matrix
           Matrix3D TransformMatrix = RotationMatrices.RotatationYMatrix(Rotation.Y); // rotate y
           Matrix3D CameraMatrix = cTransform.GetCameraTransform(); // camera matrix that contains only LookAt
           c.Pos = new Vector3D(Math.Sin(a), 0, Math.Cos(a)); // change position of camera


           // transform points
           tTransformed.Points[0] = mu.Matrix_MulVector(TransformMatrix, triangle.Points[0]);
           tTransformed.Points[1] = mu.Matrix_MulVector(TransformMatrix, triangle.Points[1]); 
           tTransformed.Points[2] = mu.Matrix_MulVector(TransformMatrix, triangle.Points[2]);

           // multiple camera matrix to transformed points
           tViewed.Points[0] = mu.Matrix_MulVector(CameraMatrix, tTransformed.Points[0]);
           tViewed.Points[1] = mu.Matrix_MulVector(CameraMatrix, tTransformed.Points[1]); 
           tViewed.Points[2] = mu.Matrix_MulVector(CameraMatrix, tTransformed.Points[2]);


           // project the object
           tProjected.Points[0] = mu.Matrix_MulVector(ProjectionMatrix, tViewed.Points[0]);
           tProjected.Points[1] = mu.Matrix_MulVector(ProjectionMatrix, tViewed.Points[1]);
           tProjected.Points[2] = mu.Matrix_MulVector(ProjectionMatrix, tViewed.Points[2]);
           
           // just draw the result
           new CTriangle(
                triangle.Points[0].X + o.Position.X, triangle.Points[0].Y + o.Position.Y ,
                triangle.Points[1].X + o.Position.X, triangle.Points[1].Y + o.Position.Y ,
                triangle.Points[2].X + o.Position.X, triangle.Points[2].Y + o.Position.Y ,
                cv, Brushes.Gray, Brushes.Black);
        }

        a+= .1;

        Rotation.X += 0.05f;
        Rotation.Y += 0.05f;
        Rotation.Z += 0.05f;

    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

CTransform类的ProjectionMatrix方法

public Matrix3D ProjectionMatrix(double fov, double width, double height, double near, double far)
{
    int fAspectRatio = (int)(width / height);
    double fFovRad = 1.0f / Math.Tan(fov * 0.5f / 180.0f * 3.14159f);

    return new Matrix3D
        (
        fAspectRatio * fFovRad, 0, 0, 0,
        0, fFovRad, 0, 0,
        0, 0, far / (far - near), 1,
        0, 0, (-far * near) / (far - near), 1
        );
}

CTransform类的GetCameraTransform方法

public Matrix3D GetCameraTransform()
{
    Matrix3D cameraLookAt = Camera.LookAt(camera.Pos, camera.Target, camera.Up);
    return cameraLookAt;
}

CTransform类的相机字段

private Camera3D camera;
public Camera3D Camera { get { return camera; } set { camera = value; } }

MatrixUtil类的矩阵向量乘法方法

public Vector3D Matrix_MulVector(Matrix3D m, Vector3D v)
{
    Vector3D res = new Vector3D();

    // additional W component of vector usually will 1. So just add offsets
    res.X = v.X * m.M11 + v.Y * m.M21 + v.Z * m.M31 + m.OffsetX;
    res.Y = v.X * m.M12 + v.Y * m.M22 + v.Z * m.M32 + m.OffsetY;
    res.Z = v.X * m.M13 + v.Y * m.M23 + v.Z * m.M33 + m.OffsetZ;

    return res;
}

已尝试的解决措施

  • 更改流水线中矩阵与三角形的乘法顺序,问题更严重;
  • 修改相机平移矩阵,无变化;
  • 修改LookAt方法,无变化;
  • 检查并修改其他物体变换矩阵,无变化。

需求

希望理解3D相机在3D图形中的工作原理,并解决当前实现问题。


内容的提问来源于stack exchange,提问作者White

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 09:02:54