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

3D三角形随相机旋转异常问题求助

问题

我正在开发一款程序,可输入右手坐标系下的三个坐标生成三角形,并实现相机绕三角形旋转。我理解的实现步骤如下:

  • 创建透视投影矩阵:
std::array<std::array<float, 4>, 4> CreateProjectionMatrix(float fov, float aspectRatio, float nearPlane, float farPlane) {
    float f = 1.0f / std::tan((fov * 3.141 / 180) / 2.0f);
    std::array<std::array<float, 4>, 4> projectionMatrix = {
        f / aspectRatio, 0.0f, 0.0f, 0.0f,
        0.0f, f, 0.0f, 0.0f,
        0.0f, 0.0f, (farPlane + nearPlane) / (nearPlane - farPlane), (2.0f * farPlane * nearPlane) / (nearPlane - farPlane),
        0.0f, 0.0f, -1.0f, 0.0f
    };
    return projectionMatrix;
}
  • 创建视图/相机矩阵:
std::array<std::array<float, 4>, 4> createViewMatrix(
    Vec4<float> cameraPosition,
    float pitch, float yaw, float roll) {

    pitch = toRadians(pitch);
    yaw = toRadians(yaw);
    roll = toRadians(roll);

    float cosPitch = cos(pitch);
    float sinPitch = sin(pitch);
    float cosYaw = cos(yaw);
    float sinYaw = sin(yaw);


    Vec4<float> xaxis( cosYaw, 0, -sinYaw );
    Vec4<float> yaxis(sinYaw * sinPitch, cosPitch, cosYaw * sinPitch);
    Vec4<float> zaxis(sinYaw * cosPitch, -sinPitch, cosPitch * cosYaw);

    std::array<std::array<float, 4>, 4> viewMatrix = {
        xaxis.x(),            yaxis.x(),            zaxis.x(),      0,
        xaxis.y(),            yaxis.y(),            zaxis.y(),      0,
        xaxis.z(),            yaxis.z(),            zaxis.z(),      0,
        -dot(xaxis, cameraPosition), -dot(yaxis, cameraPosition), -dot(zaxis, cameraPosition), 1
    };
   

    return viewMatrix;
}
  • 创建模型矩阵:我认为无需使用,因所有点接近坐标中心,但可能有误。
  • 按正确顺序相乘矩阵:PointsB = Point * (projectionMatrix * viewMatrix)

当前代码如下:

// 输入参数
float CAMERA_X = 0;
float CAMERA_Y = 0;
float CAMERA_Z = 0.5;
float FOV = 90;

const float ASPECT_RATIO = 16.0f / 9.0f;
const float NEAR_PLANE = 0.1f;
const float FAR_PLANE = 1000.0f;

float CAMERA_Y_ANGLE = 45.f;

// 三角形顶点
Vec4 p1(-100, 0, 0); // 投影后输出:360 259
Vec4 p2(100, 0, -100); // 投影后输出:398 305
Vec4 p3(100, 0, 100); // 投影后输出:394 301


Vec2D<float> Project(Vec4<float> point) {

 auto projectionMatrix = CreateProjectionMatrix(FOV, ASPECT_RATIO, NEAR_PLANE, FAR_PLANE);
 auto viewMatrix = createViewMatrix(Vec4<float>(0, 0, 0), 0, CAMERA_Y_ANGLE, 0);

 auto ViewProj = MultiplyMatrices(projectionMatrix, viewMatrix);

 Vec4<float> projectedPoint(
     point.x() * ViewProj[0][0] + point.y() * ViewProj[1][0] + point.z() * ViewProj[2][0] + ViewProj[3][0],
     point.x() * ViewProj[0][1] + point.y() * ViewProj[1][1] + point.z() * ViewProj[2][1] + ViewProj[3][1],
     point.x() * ViewProj[0][2] + point.y() * ViewProj[1][2] + point.z() * ViewProj[2][2] + ViewProj[3][2],
     point.x() * ViewProj[0][3] + point.y() * ViewProj[1][3] + point.z() * ViewProj[2][3] + ViewProj[3][3]
 );

 if (projectedPoint.w() != 0.0f) {
     projectedPoint.set_x(projectedPoint.x() / projectedPoint.w());
     projectedPoint.set_y(projectedPoint.y() / projectedPoint.w());
     projectedPoint.set_z(projectedPoint.z() / projectedPoint.w());
 }

 // 窗口尺寸为800*600,因为投影点为负数,这里是临时解决方案
 return VectorHelpers<float>(projectedPoint.x() + 400, projectedPoint.z() + 300);
}

目前效果仅为三角形在窗口中心自转,而非相机绕物体移动。我猜测是点的相对关系错误或坐标系混淆,寻求帮助及代码示例。

补充:当前三角形沿Y轴自转,我需要实现相机绕三角形移动,比如相机Y轴旋转180度时,因朝向背离三角形而不可见。

解决方案

主要问题出在投影和视图矩阵,以下是修正后的代码(改为左手坐标系),虽无裁剪逻辑导致近距离物体显示异常,但功能正常:

std::array<std::array<float, 4>, 4> createViewMatrix(
    Vec4<float> cameraPosition,
    Vec4<float> cameraTarget,
    Vec4<float> cameraUpVector) {
    Vec4<float> A = Normalize(cameraPosition - cameraTarget);
    Vec4<float> B = Normalize(Cross(cameraUpVector, A));
    Vec4<float> C = Cross(A, B);
    std::array<std::array<float, 4>, 4> viewMatrix = {
        B.x(),                      C.x(),                      A.x(),                      0,
        B.y(),                      C.y(),                      A.y(),                      0,
        B.z(),                      C.z(),                      A.z(),                      0,
        -dot(B, cameraPosition),    -dot(C, cameraPosition),    -dot(A, cameraPosition),    1
    };
    return viewMatrix;
}
std::array<std::array<float, 4>, 4> CreateProjectionMatrix(float fov, float aspectRatio, float nearPlane, float farPlane) {
    float f = 1.0f / std::tan((fov * 3.141 / 180) / 2.0f);
    std::array<std::array<float, 4>, 4> projectionMatrix = {
        f / aspectRatio, 0.0f, 0.0f, 0.0f,
        0.0f, f, 0.0f, 0.0f,
        0.0f, 0.0f, farPlane / (nearPlane - farPlane), -1,
        0.0f, 0.0f, (nearPlane * farPlane) / (nearPlane - farPlane), 0.0f
    };
    return projectionMatrix;
}
Vec2D<float> Project(Vec4<float> point) {
    std::array<std::array<float, 4>, 4> ObjectMatrix = {
        1,0,0,0,
        0,1,0,0,
        0,0,1,0,
        0,0,0,1
    };
    auto projectionMatrix = CreateProjectionMatrix(FOV, ASPECT_RATIO, NEAR_PLANE, FAR_PLANE);
    auto viewMatrix = createViewMatrix(CameraPosition, 
                                       CameraTarget,
                                       CameraUpVector);
    auto ViewProj = ObjectMatrix * viewMatrix * projectionMatrix;
    Vec4<float> projectedPoint(
        point.x() * ViewProj[0][0] + point.y() * ViewProj[1][0] + point.z() * ViewProj[2][0] + ViewProj[3][0],
        point.x() * ViewProj[0][1] + point.y() * ViewProj[1][1] + point.z() * ViewProj[2][1] + ViewProj[3][1],
        point.x() * ViewProj[0][2] + point.y() * ViewProj[1][2] + point.z() * ViewProj[2][2] + ViewProj[3][2],
        point.x() * ViewProj[0][3] + point.y() * ViewProj[1][3] + point.z() * ViewProj[2][3] + ViewProj[3][3]
    );

    projectedPoint.set_x(projectedPoint.x() / projectedPoint.z());
    projectedPoint.set_y(projectedPoint.y() / projectedPoint.z());

    if (projectedPoint.z() < 0.01) {
        // 不绘制该点
        return Vec2D<float>(0, 0);
    }

    auto x = projectedPoint.x() * 600.f + 600.f / 2.0f;
    auto y = -projectedPoint.y() * 600.f + 600.f / 2.0f; // 窗口尺寸为600*600
    return Vec2D<float>(x, y);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 10:14:55