增加鼠标和键盘输入事件

OpenTK 2026-03-16 17:30:29

#include <iostream>
#include <vector>
#include <windows.h>
#include <fstream>
#include <string>
#include <cmath>  // 添加数学库

// 【关键】GLFW DLL 定义
#define GLFW_DLL 
#include <GLFW/glfw3.h>
#include <glad/glad.h>

// ImGui 头文件
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>

// 添加简单的数学结构体
struct Vec3 {
    float x, y, z;
    Vec3(float x = 0, float y = 0, float z = 0) : x(x), y(y), z(z) {}
};

// --- 修改后的着色器源码,添加变换矩阵 ---
const char* vertexShaderSource = R"(
    #version 330 core
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec3 aColor;
    
    uniform mat4 transform;  // 变换矩阵
    
    out vec3 ourColor;
    void main() {
        gl_Position = transform * vec4(aPos, 1.0);
        ourColor = aColor;
    }
)";

const char* fragmentShaderSource = R"(
    #version 330 core
    in vec3 ourColor;
    out vec4 FragColor;
    void main() {
        FragColor = vec4(ourColor, 1.0);
    }
)";

// 全局变量:三角形的颜色
float triColorR = 1.0f;
float triColorG = 0.0f;
float triColorB = 0.0f;

// ==========================================
// 变换控制变量
// ==========================================
// 平移
float transX = 0.0f, transY = 0.0f;
// 旋转(角度)
float rotation = 0.0f;
// 缩放
float scale = 1.0f;

// 鼠标控制变量
bool firstMouse = true;
float lastX = 400, lastY = 300;
float yaw = 0.0f;    // 绕Y轴旋转(左右)- 用于3D旋转
float pitch = 0.0f;  // 绕X轴旋转(上下)- 用于3D旋转

// 鼠标按键状态
bool leftMousePressed = false;   // 左键用于旋转
bool rightMousePressed = false;  // 右键用于平移

// 键盘控制速度
float moveSpeed = 0.01f;  // 降低速度使操作更精细
float rotSpeed = 1.0f;    // 降低旋转速度
float scaleSpeed = 0.03f;

// ==========================================
// 生成变换矩阵的函数(修正:先缩放,再旋转,最后平移)
// ==========================================
void generateTransformMatrix(float* matrix) {
    // 初始化为单位矩阵
    for (int i = 0; i < 16; i++) {
        matrix[i] = 0.0f;
    }
    matrix[0] = matrix[5] = matrix[10] = matrix[15] = 1.0f;

    // 1. 先应用缩放
    float scaleMatrix[16] = {
        scale, 0, 0, 0,
        0, scale, 0, 0,
        0, 0, scale, 0,
        0, 0, 0, 1
    };

    // 2. 再应用旋转(绕Z轴)
    float rad = rotation * 3.14159f / 180.0f;
    float cosR = cos(rad);
    float sinR = sin(rad);

    float rotMatrix[16] = {
        cosR, -sinR, 0, 0,
        sinR, cosR, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
    };

    // 3. 最后应用平移
    float transMatrix[16] = {
        1, 0, 0, transX,
        0, 1, 0, transY,
        0, 0, 1, 0,
        0, 0, 0, 1
    };

    // 组合矩阵:transform = trans * rot * scale
    // 先计算 rot * scale
    float temp[16];
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            temp[i * 4 + j] = 0;
            for (int k = 0; k < 4; k++) {
                temp[i * 4 + j] += rotMatrix[i * 4 + k] * scaleMatrix[k * 4 + j];
            }
        }
    }

    // 再计算 trans * (rot * scale)
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            matrix[i * 4 + j] = 0;
            for (int k = 0; k < 4; k++) {
                matrix[i * 4 + j] += transMatrix[i * 4 + k] * temp[k * 4 + j];
            }
        }
    }
}

// ==========================================
// 鼠标回调函数 - 左键旋转,右键平移
// ==========================================
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
    // 如果没有按下任何鼠标键,直接返回
    if (!leftMousePressed && !rightMousePressed) {
        firstMouse = true;
        return;
    }

    if (firstMouse) {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
        return;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos;  // 反转Y轴
    lastX = xpos;
    lastY = ypos;

    // ==========================================
    // 左键:旋转 - 使用累积旋转,更自然
    // ==========================================
    if (leftMousePressed) {
        // 灵敏度
        float sensitivity = 0.3f;
        xoffset *= sensitivity;
        yoffset *= sensitivity;

        // 累积旋转角度
        rotation += xoffset;  // 左右移动控制绕Z轴旋转

        // 可以取消下面的注释来实现3D旋转
        // yaw += xoffset;
        // pitch += yoffset;
        // if (pitch > 89.0f) pitch = 89.0f;
        // if (pitch < -89.0f) pitch = -89.0f;
        // rotation = yaw;  // 如果需要3D旋转,这里需要更复杂的矩阵
    }

    // ==========================================
    // 右键:平移 - 直接控制transX和transY
    // ==========================================
    if (rightMousePressed) {
        // 移动速度系数,根据窗口大小调整
        int width, height;
        glfwGetWindowSize(window, &width, &height);
        float panSpeed = 2.0f / (height > 0 ? height : 600);  // 归一化到[-1,1]空间

        // 直接修改平移量,累积移动
        transX += xoffset * panSpeed;
        transY -= yoffset * panSpeed;  // 注意符号
    }
}

// ==========================================
// 鼠标按钮回调 - 区分左右键
// ==========================================
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
    // 左键处理
    if (button == GLFW_MOUSE_BUTTON_LEFT) {
        if (action == GLFW_PRESS) {
            leftMousePressed = true;
            firstMouse = true;
        }
        else if (action == GLFW_RELEASE) {
            leftMousePressed = false;
        }
    }

    // 右键处理
    if (button == GLFW_MOUSE_BUTTON_RIGHT) {
        if (action == GLFW_PRESS) {
            rightMousePressed = true;
            firstMouse = true;
        }
        else if (action == GLFW_RELEASE) {
            rightMousePressed = false;
        }
    }
}

// 滚轮回调(用于缩放)
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
    scale *= (1.0f + yoffset * scaleSpeed);  // 使用乘法使缩放更自然
    if (scale < 0.1f) scale = 0.1f;
    if (scale > 5.0f) scale = 5.0f;
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
    glViewport(0, 0, width, height);
}

// ==========================================
// 键盘输入处理
// ==========================================
void processInput(GLFWwindow* window) {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    // 平移控制(WASD)
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        transY += moveSpeed;
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        transY -= moveSpeed;
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        transX -= moveSpeed;
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        transX += moveSpeed;

    // 旋转控制(方向键)
    if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS)
        rotation -= rotSpeed;
    if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS)
        rotation += rotSpeed;

    // 缩放控制(加减号)
    if (glfwGetKey(window, GLFW_KEY_EQUAL) == GLFW_PRESS ||
        glfwGetKey(window, GLFW_KEY_KP_ADD) == GLFW_PRESS)
        scale += scaleSpeed;
    if (glfwGetKey(window, GLFW_KEY_MINUS) == GLFW_PRESS ||
        glfwGetKey(window, GLFW_KEY_KP_SUBTRACT) == GLFW_PRESS)
        scale -= scaleSpeed;

    // 限制范围
    if (scale < 0.1f) scale = 0.1f;
    if (scale > 5.0f) scale = 5.0f;

    // 重置(R键)
    if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
        transX = 0.0f;
        transY = 0.0f;
        rotation = 0.0f;
        scale = 1.0f;
        yaw = 0.0f;
        pitch = 0.0f;
    }
}

// 辅助函数:检查文件是否存在
bool fileExists(const std::string& filename) {
    std::ifstream f(filename.c_str());
    return f.good();
}

int main() {
    // 设置控制台输出编码为UTF-8
    SetConsoleOutputCP(CP_UTF8);

    // 使用u8前缀输出中文到控制台
    std::cout << u8"正在初始化OpenGL程序..." << std::endl;

    // 1. 初始化 GLFW
    if (!glfwInit()) {
        std::cout << u8"初始化GLFW失败" << std::endl;
        return -1;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(800, 600, u8"OpenGL + ImGui 变换控制器", NULL, NULL);
    if (window == NULL) {
        std::cout << u8"创建GLFW窗口失败" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSwapInterval(1);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // ==========================================
    // 设置鼠标回调
    // ==========================================
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetMouseButtonCallback(window, mouse_button_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // 2. 初始化 GLAD
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << u8"初始化GLAD失败" << std::endl;
        return -1;
    }

    // ==========================================
    // 3. 初始化 ImGui
    // ==========================================
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;

    std::cout << u8"尝试加载中文字体..." << std::endl;

    // 获取当前可执行文件路径
    char exePath[MAX_PATH];
    GetModuleFileNameA(NULL, exePath, MAX_PATH);
    std::string exeDir = std::string(exePath);
    exeDir = exeDir.substr(0, exeDir.find_last_of("\\/"));

    // 要尝试的字体路径列表
    std::vector<std::string> fontPaths;
    fontPaths.push_back("C:\\Windows\\Fonts\\msyh.ttc");
    fontPaths.push_back("C:\\Windows\\Fonts\\simhei.ttf");
    fontPaths.push_back("C:\\Windows\\Fonts\\simkai.ttf");
    fontPaths.push_back(exeDir + "\\msyh.ttc");
    fontPaths.push_back(exeDir + "\\simhei.ttf");

    ImFont* font = nullptr;
    for (const auto& path : fontPaths) {
        std::cout << u8"尝试字体: " << path << std::endl;

        if (fileExists(path)) {
            std::cout << u8"文件存在,尝试加载..." << std::endl;
            io.Fonts->Clear();
            font = io.Fonts->AddFontFromFileTTF(
                path.c_str(),
                18.0f,
                NULL,
                io.Fonts->GetGlyphRangesChineseFull()
            );

            if (font) {
                std::cout << u8"✓ 成功加载字体: " << path << std::endl;
                io.Fonts->Build();
                break;
            }
            else {
                std::cout << u8"✗ 文件存在但加载失败" << std::endl;
            }
        }
        else {
            std::cout << u8"✗ 文件不存在" << std::endl;
        }
    }

    if (font == NULL) {
        std::cout << u8"⚠ 警告:所有字体加载失败,使用默认字体" << std::endl;
        io.Fonts->AddFontDefault();
        io.Fonts->Build();
    }

    ImGui::StyleColorsDark();

    // ==========================================
    // 4. 初始化 ImGui 后端
    // ==========================================
    std::cout << u8"初始化 ImGui 后端..." << std::endl;

    if (!ImGui_ImplGlfw_InitForOpenGL(window, true)) {
        std::cout << u8"初始化 ImGui GLFW 后端失败" << std::endl;
        return -1;
    }

    if (!ImGui_ImplOpenGL3_Init("#version 330")) {
        std::cout << u8"初始化 ImGui OpenGL3 后端失败" << std::endl;
        return -1;
    }

    std::cout << u8"ImGui 初始化完成!" << std::endl;

    // ==========================================
    // 5. 编译着色器
    // ==========================================
    std::cout << u8"编译着色器..." << std::endl;

    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << u8"顶点着色器编译失败:\n" << infoLog << std::endl;
        return -1;
    }

    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << u8"片段着色器编译失败:\n" << infoLog << std::endl;
        return -1;
    }

    unsigned int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << u8"着色器程序链接失败:\n" << infoLog << std::endl;
        return -1;
    }

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // 获取变换矩阵的uniform位置
    int transformLoc = glGetUniformLocation(shaderProgram, "transform");

    // ==========================================
    // 6. 准备三角形数据
    // ==========================================
    std::cout << u8"准备顶点数据..." << std::endl;

    float vertices[] = {
         0.0f,  0.5f, 0.0f,  1.0f, 0.0f, 0.0f,
         0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,
        -0.5f, -0.5f, 0.0f,  0.0f, 0.0f, 1.0f
    };

    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    glBindVertexArray(0);

    float inputR = 1.0f, inputG = 0.0f, inputB = 0.0f;

    std::cout << u8"初始化完成,开始渲染循环..." << std::endl;
    std::cout << u8"\n控制说明:" << std::endl;
    std::cout << u8"  鼠标左键拖动 - 旋转" << std::endl;
    std::cout << u8"  鼠标右键拖动 - 平移" << std::endl;
    std::cout << u8"  鼠标滚轮 - 缩放" << std::endl;
    std::cout << u8"  WASD - 平移(备选)" << std::endl;
    std::cout << u8"  左右方向键 - 旋转(备选)" << std::endl;
    std::cout << u8"  +/- - 缩放(备选)" << std::endl;
    std::cout << u8"  R - 重置" << std::endl;

    // ==========================================
    // 7. 渲染循环
    // ==========================================
    while (!glfwWindowShouldClose(window)) {
        processInput(window);

        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        // --- 创建 UI 窗口 ---
        ImGui::Begin(u8"变换控制器");

        ImGui::Text(u8"变换控制");
        ImGui::Separator();

        // 显示当前变换参数
        ImGui::Text(u8"平移: X=%.2f, Y=%.2f", transX, transY);
        ImGui::Text(u8"旋转: %.1f°", rotation);
        ImGui::Text(u8"缩放: %.2f", scale);

        ImGui::Separator();

        // 添加滑块控制
        ImGui::SliderFloat(u8"平移 X", &transX, -2.0f, 2.0f, "%.2f");
        ImGui::SliderFloat(u8"平移 Y", &transY, -2.0f, 2.0f, "%.2f");
        ImGui::SliderFloat(u8"旋转", &rotation, -180.0f, 180.0f, "%.1f°");
        ImGui::SliderFloat(u8"缩放", &scale, 0.1f, 5.0f, "%.2f");

        if (ImGui::Button(u8"重置所有变换")) {
            transX = 0.0f;
            transY = 0.0f;
            rotation = 0.0f;
            scale = 1.0f;
            yaw = 0.0f;
            pitch = 0.0f;
        }

        ImGui::Separator();
        ImGui::Text(u8"颜色控制");
        ImGui::Separator();

        ImVec4 color = ImVec4(triColorR, triColorG, triColorB, 1.0f);
        if (ImGui::ColorEdit3(u8"选择颜色", (float*)&color)) {
            triColorR = color.x;
            triColorG = color.y;
            triColorB = color.z;
            inputR = triColorR;
            inputG = triColorG;
            inputB = triColorB;
        }

        ImGui::Separator();
        ImGui::Text(u8"或者手动输入 (0.0 - 1.0):");

        ImGui::InputFloat(u8"红色", &inputR, 0.1f, 1.0f, "%.2f");
        ImGui::InputFloat(u8"绿色", &inputG, 0.1f, 1.0f, "%.2f");
        ImGui::InputFloat(u8"蓝色", &inputB, 0.1f, 1.0f, "%.2f");

        if (ImGui::Button(u8"应用输入框颜色")) {
            triColorR = (inputR < 0) ? 0 : (inputR > 1 ? 1 : inputR);
            triColorG = (inputG < 0) ? 0 : (inputG > 1 ? 1 : inputG);
            triColorB = (inputB < 0) ? 0 : (inputB > 1 ? 1 : inputB);
        }

        ImGui::Separator();
        ImGui::Text(u8"当前颜色:");
        ImGui::ColorButton(u8"当前颜色", ImVec4(triColorR, triColorG, triColorB, 1.0f),
            ImGuiColorEditFlags_NoTooltip, ImVec2(100, 20));

        ImGui::End();

        // 更新三角形颜色
        float updatedVertices[] = {
             0.0f,  0.5f, 0.0f,  triColorR, triColorG, triColorB,
             0.5f, -0.5f, 0.0f,  triColorR, triColorG, triColorB,
            -0.5f, -0.5f, 0.0f,  triColorR, triColorG, triColorB
        };

        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(updatedVertices), updatedVertices);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        // OpenGL 渲染
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(shaderProgram);

        // 生成并传递变换矩阵
        float transformMatrix[16];
        generateTransformMatrix(transformMatrix);
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, transformMatrix);

        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        // 渲染 ImGui
        ImGui::Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // 清理资源
    std::cout << u8"清理资源..." << std::endl;

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteProgram(shaderProgram);

    glfwTerminate();

    std::cout << u8"程序正常退出" << std::endl;
    return 0;
}

...全文
107 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

3

社区成员

发帖
与我相关
我的任务
社区描述
openTK、OpenGL、WebGL技术学习交流
图形渲染c#程序人生 技术论坛(原bbs) 广东省·深圳市
社区管理员
  • 亿只小灿灿
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧