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

自制P5.js 3D立方体旋转时显示异常问题求助

问题分析与解决方案

嘿,我仔细看了你的代码,手动实现3D立方体旋转的思路真的很棒,但旋转时出现线条错误的问题主要出在鼠标拖动的角度计算和变量作用域上,下面给你详细拆解和修复方案:

核心问题点

1. 鼠标拖动的角度计算逻辑错误

你在mouseDragged里计算旋转角度的公式完全不合理:

let currentX = (mouseX / (framesPerSecond * 1000)) * movedX;
let currentY = -(mouseY / (framesPerSecond * 1000)) * movedY;

这个公式没有任何物理意义,导致旋转角度要么微乎其微,要么异常巨大,直接造成立方体线条扭曲、绘制错误。我们需要把鼠标的像素移动量转换成合理的弧度值,通常给一个小系数(比如0.01)来控制旋转速度,让操作更直观。

2. 旋转函数中的变量未声明(全局污染)

你的rotateX3D、rotateY3D、rotateZ3D函数里的rotatedX、rotatedY、rotatedZ没有用let/var声明,会变成全局变量,多个旋转操作会互相干扰,导致顶点计算错误。

3. 原始顶点被直接修改(累积误差)

每次拖动时你直接修改vertices数组的原始数据,多次旋转后会累积计算误差,时间久了立方体形状会越来越失真。更好的做法是保留原始顶点,每次绘制时实时计算旋转后的顶点。

修正后的完整代码

const xIndex = 0;
const yIndex = 1;
const zIndex = 2;
const triSides = 3;
const ratio = 110;
const framesPerSecond = 30;
const screenWidth = 600;
const screenHeight = 600;
const fntSize = 20;

// 保留原始顶点数据,避免直接修改导致累积误差
const originalVertices = [
    // 南面
    [ [-1.0, -1.0, -1.0], [-1.0, 1.0, -1.0], [1.0, 1.0, -1.0] ],
    [ [-1.0, -1.0, -1.0], [1.0, 1.0, -1.0], [1.0, -1.0, -1.0] ],
    // 顶面
    [ [-1.0, 1.0, -1.0], [-1.0, 1.0, 1.0], [1.0, 1.0, 1.0] ],
    [ [-1.0, 1.0, -1.0], [1.0, 1.0, 1.0], [1.0, 1.0, -1.0] ],
    // 西面
    [ [-1.0, -1.0, 1.0], [-1.0, 1.0, 1.0], [-1.0, 1.0, -1.0], ],
    [ [-1.0, -1.0, 1.0], [-1.0, 1.0, -1.0], [-1.0, -1.0, -1.0] ],
    // 东面
    [ [1.0, -1.0, -1.0], [1.0, 1.0, -1.0], [1.0, 1.0, 1.0] ],
    [ [1.0, -1.0, -1.0], [1.0, 1.0, 1.0], [1.0, -1.0, 1.0] ],
    // 底面
    [ [1.0, -1.0, 1.0], [-1.0, -1.0, 1.0], [-1.0, -1.0, -1.0] ],
    [ [1.0, -1.0, 1.0], [-1.0, -1.0, -1.0], [1.0, -1.0, -1.0] ],
    // 北面
    [ [1.0, -1.0, 1.0], [1.0, 1.0, 1.0], [-1.0, 1.0, 1.0] ],
    [ [1.0, -1.0, 1.0], [-1.0, 1.0, 1.0], [-1.0, -1.0, 1.0] ],
];

// 用于存储旋转角度,替代直接修改原始顶点
let rotationX = 0;
let rotationY = 0;

//let myFont;
//function preload() {
//  myFont = loadFont('assets/Roboto-Black.ttf');
//}

function setup() {
    createCanvas(screenWidth, screenHeight, WEBGL);
    strokeWeight(1);
    frameRate(framesPerSecond);
    //textFont(myFont, fntSize);
    stroke(0, 0, 0);
}

function draw() {
    background(220);
    for (let i = 0; i < originalVertices.length; i++) {
        const tri = originalVertices[i];
        // 实时计算旋转后的顶点
        let originPoint = applyRotation(tri[0]);
        let point2 = applyRotation(tri[1]);
        let point3 = applyRotation(tri[2]);
        
        // 缩放顶点到屏幕尺寸
        originPoint = scaleVertex(originPoint);
        point2 = scaleVertex(point2);
        point3 = scaleVertex(point3);
        
        switch (i) {
            case 0:
            case 1: // 红色
                fill(255, 100, 100, 100);
                break;
            case 2:
            case 3: // 黄色
                fill(255, 255, 100, 100);
                break;
            case 4:
            case 5: // 蓝色
                fill(100, 255, 255, 100);
                break;
            case 6:
            case 7: // 紫色
                fill(100, 100, 255, 100);
                break;
            case 8:
            case 9: // 白色
                fill(255, 255, 255, 100);
                break;
            case 10:
            case 11: // 黑色
                fill(0, 0, 0, 100);
        }
        beginShape();
        vertex(originPoint.x, originPoint.y, originPoint.z);
        vertex(point2.x, point2.y, point2.z);
        vertex(point3.x, point3.y, point3.z);
        endShape();
    }
}

// 应用所有旋转操作的辅助函数
function applyRotation(vertex) {
    let rotated = [...vertex];
    rotated = rotateX3D(rotated, rotationX);
    rotated = rotateY3D(rotated, rotationY);
    return {
        x: rotated[xIndex],
        y: rotated[yIndex],
        z: rotated[zIndex]
    };
}

// 缩放顶点到屏幕尺寸的辅助函数
function scaleVertex(vertex) {
    return {
        x: vertex.x * ratio,
        y: vertex.y * ratio,
        z: vertex.z * ratio
    };
}

// Z轴旋转 - 修复变量作用域
function rotateZ3D(obj, angleRotation) {
    let angleCos = cos(angleRotation);
    let angleSin = sin(angleRotation);
    let rotatedX = (obj[xIndex] * angleCos) - (obj[yIndex] * angleSin);
    let rotatedY = (obj[xIndex] * angleSin) + (obj[yIndex] * angleCos);
    let rotatedZ = obj[zIndex];
    return [rotatedX, rotatedY, rotatedZ];
}

// Y轴旋转 - 修复变量作用域
function rotateY3D(obj, angleRotation) {
    let angleCos = cos(angleRotation);
    let angleSin = sin(angleRotation);
    let rotatedX = (obj[xIndex] * angleCos) + (obj[zIndex] * angleSin);
    let rotatedY = obj[yIndex];
    let rotatedZ = (obj[xIndex] * -angleSin) + (obj[zIndex] * angleCos);
    return [rotatedX, rotatedY, rotatedZ];
}

// X轴旋转 - 修复变量作用域
function rotateX3D(obj, angleRotation) {
    let angleCos = cos(angleRotation);
    let angleSin = sin(angleRotation);
    let rotatedX = obj[xIndex];
    let rotatedY = (obj[yIndex] * angleCos) + (obj[zIndex] * -angleSin);
    let rotatedZ = (obj[yIndex] * angleSin) + (obj[zIndex] * angleCos);
    return [rotatedX, rotatedY, rotatedZ];
}

function mouseDragged() {
    if (mouseX < screenWidth && mouseY < screenHeight) {
        // 将鼠标移动量转换成合理的弧度值,0.01是旋转速度系数,可调整
        rotationY += movedX * 0.01;
        rotationX += movedY * 0.01;
    }
}

关键修改说明

  1. 分离原始顶点与旋转状态:新增originalVertices存储原始立方体顶点,用rotationX和rotationY记录旋转角度,每次绘制时实时计算旋转后的顶点,彻底避免累积误差。
  2. 修复鼠标旋转逻辑:直接用movedX和movedY乘以系数0.01得到旋转弧度,拖动鼠标时旋转方向和速度都更直观。
  3. 修复变量作用域:在所有旋转函数中用let声明rotatedX/Y/Z,避免全局变量污染导致的计算错误。
  4. 新增辅助函数:applyRotation和scaleVertex让代码结构更清晰,逻辑分离更容易维护。

现在你拖动鼠标旋转立方体,应该就能看到正常的3D效果啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 18:22:35