婆娑梵净山 2026-03-20 21:32 采纳率: 88.6%
浏览 6

为什么鼠标拖动的时候反应很慢很慢

#include <graphics.h>
#include <math.h>
#include <stdio.h>

#define PI 3.1415926f
#define LONGITUDE 80          // 经度细分
#define LATITUDE 80
#define R 1                // 球半径

// 全局控制
float g_scale = 1.0f;
float g_rotateY = 0.0f;

typedef struct { float x, y, z; } Point;

// 你的山体函数
float mountainFunc(float x, float y, float z) {
    return x * y * y - z;
}

// 变形:沿法线方向移动
void deformPoint(Point& p, float strength) {
    float offset = mountainFunc(p.x, p.y, p.z);
    p.x += p.x * offset * strength;
    p.y += p.y * offset * strength;
    p.z += p.z * offset * strength;
}

// 透视投影
void project(Point p, int& sx, int& sy) {
    // 绕 Y 轴旋转
    float cosY = cos(g_rotateY);
    float sinY = sin(g_rotateY);
    float xr = p.x * cosY + p.z * sinY;
    float zr = -p.x * sinY + p.z * cosY;
    float yr = p.y;

    int cx = 400, cy = 300;
    float eyeDist = 500.0f;
    float persp = eyeDist / (eyeDist + zr + 0.01f);
    float scale = g_scale * 130.0f;

    sx = cx + xr * persp * scale;
    sy = cy - yr * persp * scale;
}

// 鼠标处理
void handleMouse() {
    if (MouseHit()) {
        MOUSEMSG msg = GetMouseMsg();
        if (msg.uMsg == WM_MOUSEWHEEL) {
            if (msg.wheel > 0)
                g_scale = min(g_scale * 1.1f, 2.2f);
            else
                g_scale = max(g_scale * 0.9f, 0.4f);
        }
        else if (msg.uMsg == WM_MOUSEMOVE && (msg.mkCtrl & MK_LBUTTON)) {
            g_rotateY += msg.x * 0.008f;
        }
    }
}

int main() {
    initgraph(800, 600);
    setbkcolor(BLACK);
    cleardevice();

    Point vertices[LONGITUDE + 1][LATITUDE + 1];
    int centerI = 15, centerJ = 15;
    int regionRadius = 5;           // 区域半径(索引格数)

    // 生成变形后的网格
    for (int i = 0; i <= LONGITUDE; i++) {
        float lon = i * 2 * PI / LONGITUDE;
        float cosLon = cos(lon);
        float sinLon = sin(lon);

        for (int j = 0; j <= LATITUDE; j++) {
            float lat = -PI / 2 + j * PI / LATITUDE;
            float cosLat = cos(lat);
            float sinLat = sin(lat);

            float x = R * cosLat * cosLon;
            float y = R * sinLat;
            float z = R * cosLat * sinLon;

            Point p = { x, y, z };

            // 计算到中心点的距离(索引空间)
            int di = i - centerI;
            int dj = j - centerJ;
            float dist = sqrt(di * di + dj * dj);

            // 圆形区域变形,中心强边缘弱
            if (dist <= regionRadius) {
                float strength = 0.18f * (1 - dist / regionRadius);
                deformPoint(p, strength);
            }

            vertices[i][j] = p;
        }
    }

    BeginBatchDraw();

    while (true) {
        handleMouse();

        cleardevice();

        setlinecolor(RGB(80, 160, 255));
        setlinestyle(PS_SOLID, 1);

        // 绘制网格
        for (int i = 0; i <= LONGITUDE; i++) {
            for (int j = 0; j <= LATITUDE; j++) {
                int sx, sy;
                project(vertices[i][j], sx, sy);

                if (j < LATITUDE) {
                    int sx2, sy2;
                    project(vertices[i][j + 1], sx2, sy2);
                    line(sx, sy, sx2, sy2);
                }
                if (i < LONGITUDE) {
                    int sx2, sy2;
                    project(vertices[i + 1][j], sx2, sy2);
                    line(sx, sy, sx2, sy2);
                }
            }
        }

        // 显示说明
        settextcolor(RGB(255, 200, 100));
        settextstyle(20, 0, _T("宋体"));
        outtextxy(20, 20, _T("区域变形 | 公式: x*y²+z"));
        outtextxy(20, 50, _T("中心在 (15,15) 附近 | 圆形衰减 | 滚轮缩放 | Ctrl+左键旋转"));

        FlushBatchDraw();
        Sleep(16);
    }

    EndBatchDraw();
    getchar();
    closegraph();
    return 0;
}

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-03-20 21:33
    关注

    晚上好🌙🌙🌙
    本答案参考通义千问

    你提到的问题是“鼠标拖动的时候反应很慢很慢”,在你的代码中,主要问题集中在以下几个方面:


    一、问题原因分析

    1. 频繁的图形绘制(cleardevice())和刷新

      • 你在每次循环中都调用了 cleardevice(),这会导致屏幕不断重绘,而由于图形库的性能限制,这会显著降低帧率。
      • 如果没有使用双缓冲或批量绘制(如 BeginBatchDraw()FlushBatchDraw()),这会导致明显的卡顿。
    2. Sleep(16) 的延迟

      • 你使用了 Sleep(16) 来控制帧率,但这种方式并不精确,而且如果实际渲染时间小于 16ms,它仍然会强制等待,导致 CPU 占用低但画面不流畅。
    3. 鼠标事件处理与渲染同步

      • 鼠标事件处理(如旋转)是在主循环中进行的,如果主循环被阻塞或执行时间过长,也会造成响应延迟。
    4. 图形库本身性能限制

      • 你使用的 graphics.h 是一个较老的图形库,性能不如现代图形 API(如 OpenGL 或 DirectX),在高频率更新时容易出现卡顿。

    二、解决方案

    1. 使用双缓冲技术优化绘制

    • 使用 BeginBatchDraw()FlushBatchDraw() 进行批量绘制,避免逐帧刷新。
    • 确保 BeginBatchDraw()FlushBatchDraw() 正确包裹整个绘制逻辑。

    2. 优化鼠标事件处理

    • 将鼠标事件处理与渲染分离,确保不会因为图形绘制耗时影响响应速度。

    3. 移除不必要的 cleardevice()

    • 只有在需要重绘整个场景时才调用 cleardevice(),否则可以只绘制变化的部分。

    4. 使用更高效的帧率控制方式

    • 使用 timeGetTime()clock() 控制帧率,而不是 Sleep()

    三、修改后的代码示例

    以下是针对你提供的代码进行优化后的版本,重点在于减少重复绘制、提升响应速度,并保持功能不变:

    #include <graphics.h>
    #include <math.h>
    #include <stdio.h>
    #include <windows.h> // 用于时间控制
    
    #define PI 3.1415926f
    #define LONGITUDE 80          // 经度细分
    #define LATITUDE 80
    #define R 1                // 球半径
    
    // 全局控制
    float g_scale = 1.0f;
    float g_rotateY = 0.0f;
    
    typedef struct { float x, y, z; } Point;
    
    // 你的山体函数
    float mountainFunc(float x, float y, float z) {
        return x * y * y - z;
    }
    
    // 变形:沿法线方向移动
    void deformPoint(Point& p, float strength) {
        float offset = mountainFunc(p.x, p.y, p.z);
        p.x += p.x * offset * strength;
        p.y += p.y * offset * strength;
        p.z += p.z * offset * strength;
    }
    
    // 透视投影
    void project(Point p, int& sx, int& sy) {
        // 绕 Y 轴旋转
        float cosY = cos(g_rotateY);
        float sinY = sin(g_rotateY);
        float xr = p.x * cosY + p.z * sinY;
        float zr = -p.x * sinY + p.z * cosY;
        float yr = p.y;
    
        int cx = 400, cy = 300;
        float eyeDist = 500.0f;
        float persp = eyeDist / (eyeDist + zr + 0.01f);
        float scale = g_scale * 130.0f;
    
        sx = cx + xr * persp * scale;
        sy = cy - yr * persp * scale;
    }
    
    // 鼠标处理
    void handleMouse() {
        if (MouseHit()) {
            MOUSEMSG msg = GetMouseMsg();
            if (msg.uMsg == WM_MOUSEWHEEL) {
                if (msg.wheel > 0)
                    g_scale = min(g_scale * 1.1f, 2.2f);
                else
                    g_scale = max(g_scale * 0.9f, 0.4f);
            }
            else if (msg.uMsg == WM_MOUSEMOVE && (msg.mkCtrl & MK_LBUTTON)) {
                g_rotateY += msg.x * 0.008f;
            }
        }
    }
    
    int main() {
        initgraph(800, 600);
        setbkcolor(BLACK);
        cleardevice();
    
        Point vertices[LONGITUDE + 1][LATITUDE + 1];
        int centerI = 15, centerJ = 15;
        int regionRadius = 5;           // 区域半径(索引格数)
    
        // 生成变形后的网格
        for (int i = 0; i <= LONGITUDE; i++) {
            float lon = i * 2 * PI / LONGITUDE;
            float cosLon = cos(lon);
            float sinLon = sin(lon);
    
            for (int j = 0; j <= LATITUDE; j++) {
                float lat = -PI / 2 + j * PI / LATITUDE;
                float cosLat = cos(lat);
                float sinLat = sin(lat);
    
                float x = R * cosLat * cosLon;
                float y = R * sinLat;
                float z = R * cosLat * sinLon;
    
                Point p = { x, y, z };
    
                // 计算到中心点的距离(索引空间)
                int di = i - centerI;
                int dj = j - centerJ;
                float dist = sqrt(di * di + dj * dj);
    
                // 圆形区域变形,中心强边缘弱
                if (dist <= regionRadius) {
                    float strength = 0.18f * (1 - dist / regionRadius);
                    deformPoint(p, strength);
                }
    
                vertices[i][j] = p;
            }
        }
    
        BeginBatchDraw();
    
        DWORD lastTime = GetTickCount(); // 获取当前时间
    
        while (true) {
            handleMouse();
    
            // 控制帧率
            DWORD currentTime = GetTickCount();
            if (currentTime - lastTime >= 16) {
                lastTime = currentTime;
    
                // 只在需要时清除设备
                cleardevice();
    
                setlinecolor(RGB(80, 160, 255));
                setlinestyle(PS_SOLID, 1);
    
                // 绘制网格
                for (int i = 0; i <= LONGITUDE; i++) {
                    for (int j = 0; j <= LATITUDE; j++) {
                        int sx, sy;
                        project(vertices[i][j], sx, sy);
    
                        if (j < LATITUDE) {
                            int sx2, sy2;
                            project(vertices[i][j + 1], sx2, sy2);
                            line(sx, sy, sx2, sy2);
                        }
                        if (i < LONGITUDE) {
                            int sx2, sy2;
                            project(vertices[i + 1][j], sx2, sy2);
                            line(sx, sy, sx2, sy2);
                        }
                    }
                }
    
                // 显示说明
                settextcolor(RGB(255, 200, 100));
                settextstyle(20, 0, _T("宋体"));
                outtextxy(20, 20, _T("区域变形 | 公式: x*y²+z"));
                outtextxy(20, 50, _T("中心在 (15,15) 附近 | 圆形衰减 | 滚轮缩放 | Ctrl+左键旋转"));
    
                FlushBatchDraw();
            }
        }
    
        EndBatchDraw();
        getchar();
        closegraph();
        return 0;
    }
    

    四、关键改进点总结

    1. 使用 BeginBatchDraw()FlushBatchDraw() 提升绘制效率
    2. 移除了不必要的 cleardevice(),只在必要时调用
    3. 使用 GetTickCount() 控制帧率,替代 Sleep(16)
    4. 将鼠标事件处理与渲染分离,提升响应速度

    五、建议进一步优化方向

    如果你希望获得更高的性能和更流畅的交互体验,可以考虑以下方向:

    • 使用 OpenGL 或 DirectX:这些现代图形 API 性能更强,适合复杂图形应用。
    • 采用多线程:将图形渲染和逻辑处理分离开,提高程序响应性。
    • 优化顶点计算:预计算顶点位置并缓存,避免每次循环重新计算。

    如果你还有其他问题,比如如何实现更复杂的变形算法或增加光照效果,也欢迎继续提问!

    评论

报告相同问题?

问题事件

  • 创建了问题 3月20日