用 OpenGL 绘制彩色网格?

2023-12-31

我的目标是能够创建所谓的占用网格,它类似于图块/网格游戏,看起来像附图。这是我正在从事的一个小型机器人项目。一切都是用 C/C++ 完成的。

所以我希望能够:

  1. 更改分辨率或每个网格单元的大小。例如 1x1cm 或 5x5cm 等。
  2. 根据某些标准更改每个单元格的颜色。障碍==黑色,自由==白色等。可能会添加用户单击单元格并改变颜色。例如,如果机器人从 0x0 开始,则单元格为红色,然后在下一个实例中移动到 1x1。现在 0x0 应该是白色,1x1 应该是红色。
  3. 添加某种形式的滚动或跟随(可能通过 MVP 相机完成)。

我应该采用什么 OpenGL 方法/途径?我目前正在考虑:

  1. 有一个带有颜色属性的方形着色器(两个三角形顶点和索引缓冲区)。然后有一个包含所有索引和颜色的顶点数组对象/缓冲区,但想知道如何在运行时处理颜色变化。

  2. 拥有每个网格的所有真实世界中心或角坐标(0x0、0x1、...1x1 等),并让着色器绘制各个正方形(如果可能)。

  3. 使用 NxN 的纹理/图像并更新纹理像素颜色。

我只是不确定什么是最好的可扩展或性能方法。如果我想绘制一个 10000x10000 的网格单元(例如缩小得很远),或者颜色变化很大,该怎么办?

网格最动态的方面是填充颜色。例如,我可能有 100x100 的网格。在一种情况下,所有单元格都是白色的,然后当机器人移动时,我会改变相应单元格的颜色。或者,如果它检测到单元格中存在障碍物,则更改该单元格的颜色。


我想出了一些你应该在你的项目中使用的东西。它是一个可缩放的网格,可以调整到您需要的任何尺寸。它应该相对较快,线被批处理并且四边形被实例化。它具有左键单击、右键单击以及滚轮单击和滚动的基本交互,但您应该能够根据您的需要调整此界面。 API 应该相当容易使用createCell, removeCell, etc...

一些东西:

  • 该应用程序不会渲染不在视锥体中的单元格,因此放大时性能会更好,尽管有一些技巧可以使其在缩小时也能相当快地工作
  • 我只能测试 1000x1000 方块(约 10-12fps 完全缩小),我没有足够的内存来容纳 10000x10000 方块(使用约 3GB)。

制作起来很有趣,我希望你能利用它,如果你有任何问题,请告诉我!

EDIT:是的flatten部分有点松散。该扁平化方法最初只是将 2D 模型向量扁平化为要发送到 GPU 的 1D 向量(内部_model数据)。三元运算正在确定此列表的 2D 切片(认为主网格内的盒子部分),这就是视锥体剔除的发生方式。 BottomLeft 和 topRight 用于计算相机平截头体的边界框,该边界框与整个网格相交,以将索引放入发送到 GPU 的模型和颜色的(有序)向量中,虽然有点 hacky,但它确保了交互操作与网格(添加、删除、更改颜色等)的时间复杂度为 O(1)。 有一个小错误导致某些单元格冻结,这是由于在 QuadRenderer 中将单元格重置为未初始化造成的remove()方法忽略了将其发送到 GPU(一种性能节省优化),并且导致内存偏移不正确,并导致渲染不同步。因此,可以通过两种方式解决这个问题,删除检查以仅将初始化的单元发送到 GPU(较慢),或者只是将方块设置为白色(较黑客)。因此,我选择将删除的单元格设置为白色,这意味着它们仍然会被渲染(即使它们被透明颜色伪装)。您也许可以进行更改,这样白色单元格也不会被渲染,但考虑到您保持单元格未初始化即可启动,因此不应损失太多性能(看在上帝的份上,不要将它们初始化为白色!:))

remove() 函数可以更改为:

void remove(vec2 pos) { 

    //change color to white 
    colors[(int)pos.x][(int)pos.y] = vec3(1); 

}

这是代码:

#include <iostream>
#include <vector>
#include <algorithm>

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/string_cast.hpp>

using std::vector;
using std::cout;
using std::endl;
using glm::mat4;
using glm::vec2;
using glm::vec3;
using glm::vec4;
using glm::perspective;
using glm::radians;
using glm::normalize;

void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// grid dimensions
const unsigned int GRID_WIDTH = 1000;
const unsigned int GRID_HEIGHT = 1000;

float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;
bool realTimeUpdating = true;

// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;

// TO DO a simple camera class
vec3 cameraPos = vec3(0.0f, 0.0f, 0.0f);
vec3 cameraFront = vec3(0,0,-1);
vec3 cameraRight = vec3(1,0,0);
vec3 cameraUp = vec3(0,1,0);
mat4 model = mat4(1.0);
mat4 view;
mat4 projection;
float scrollSpeed = 2.0f;
float fov = 90.0f;
float nearDist = 0.1f;
float farDist = 1000.0f;
float ar = (float)SCR_WIDTH / (float)SCR_HEIGHT;

class LineRenderer {
    int shaderProgram;
    unsigned int VBO, VAO;
    mat4 viewProjection;

    vector<float> vertices;
public:
    LineRenderer() {

        const char *vertexShaderSource = "#version 330 core\n"
            "layout (location = 0) in vec3 aPos;\n"
            "uniform mat4 viewProjection;\n"
            "void main()\n"
            "{\n"
            "   gl_Position = viewProjection * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
            "}\0";
        const char *fragmentShaderSource = "#version 330 core\n"
            "out vec4 FragColor;\n"
            "void main()\n"
            "{\n"
            "   FragColor = vec4(0,0,0,1);\n"
            "}\n\0";

        // vertex shader
        int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        // check for shader compile errors
        int success;
        char infoLog[512];
        glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
            cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << endl;
        }
        // fragment shader
        int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);
        // check for shader compile errors
        glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
            cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << endl;
        }
        // link shaders
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
        // check for linking errors
        glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
        if (!success) {
            glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
            cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << endl;
        }
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);

        vector<float> placeHolderVertices = {
        };

        vertices.insert( vertices.end(), placeHolderVertices.begin(), placeHolderVertices.end() );
        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);
        glBindVertexArray(VAO);

        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_STATIC_DRAW);

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

        glBindBuffer(GL_ARRAY_BUFFER, 0); 
        glBindVertexArray(0); 

    }

    void setCamera(mat4 cameraMatrix) {
        viewProjection = cameraMatrix;
    }

    void addLine(vec3 start, vec3 end) {
        vector<float> lineVertices = {
             start.x, start.y, start.z,
             end.x, end.y, end.z,

        };

        vertices.insert( vertices.end(), lineVertices.begin(), lineVertices.end() );
        glBindVertexArray(VAO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
    }

    int draw() {
        glUseProgram(shaderProgram);
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "viewProjection"), 1, GL_FALSE, &viewProjection[0][0]);

        glBindVertexArray(VAO);
        glDrawArrays(GL_LINES, 0, vertices.size() / 3);
        return 0;
    }
};

// maths functions to pick which grid (not necessary, just for demonstration purposes)

vec3 rayCast(double xpos, double ypos, mat4 projection, mat4 view) {
    // converts a position from the 2d xpos, ypos to a normalized 3d direction
    float x = (2.0f * xpos) / SCR_WIDTH - 1.0f;
    float y = 1.0f - (2.0f * ypos) / SCR_HEIGHT;
    float z = 1.0f;
    vec3 ray_nds = vec3(x, y, z);
    vec4 ray_clip = vec4(ray_nds.x, ray_nds.y, -1.0f, 1.0f);
    // eye space to clip we would multiply by projection so
    // clip space to eye space is the inverse projection
    vec4 ray_eye = inverse(projection) * ray_clip;
    // convert point to forwards
    ray_eye = vec4(ray_eye.x, ray_eye.y, -1.0f, 0.0f);
    // world space to eye space is usually multiply by view so
    // eye space to world space is inverse view
    vec4 inv_ray_wor = (inverse(view) * ray_eye);
    vec3 ray_wor = vec3(inv_ray_wor.x, inv_ray_wor.y, inv_ray_wor.z);
    ray_wor = normalize(ray_wor);
    return ray_wor;
}

vec3 rayPlaneIntersection(vec3 ray_position, vec3 ray_direction, vec3 plane_normal, vec3 plane_position) {
    float d = dot(plane_normal, plane_position - ray_position) / (0.001+dot(ray_direction, plane_normal));
    return ray_position + ray_direction * d;
}

template<typename T>
vector<T> flatten(const vector<vector<T>> &orig, vec2 bottomLeft=vec2(0,0), vec2 topRight=vec2(GRID_WIDTH, GRID_HEIGHT))
{   
    vector<T> ret;
    // for(const auto &v: orig)
    //     ret.insert(ret.end(), v.begin(), v.end());
    float rx = (topRight.x >= GRID_WIDTH ? GRID_WIDTH : topRight.x+1);
    float ry = (topRight.y >= GRID_HEIGHT ? GRID_HEIGHT : topRight.y+1);

    float lx = (bottomLeft.x <= 0 ? 0 : bottomLeft.x);
    float ly = (bottomLeft.y <= 0 ? 0 : bottomLeft.y);

    for(int i = lx; i < rx; i++) {
        vector<T> v = orig[i];
        ret.insert(ret.end(), v.begin()+ly, v.begin() + ry);                                                                                          
    }
    return ret;
}   

class QuadRenderer {

public:

    unsigned int shaderProgram;
    unsigned int VBO, VAO, EBO;

    unsigned int matrixBuffer;
    unsigned int colorBuffer;

    vector<vector<vec3>> colors;
    vector<vector<vec3>> frustumCulledColors;   
    vector<vec3> _colors;

    vector<vector<mat4>> models;
    vector<vector<mat4>> frustumCulledModels;
    vector<mat4> _models;

    mat4 viewProjection;

    vec2 bottomLeft=vec2(0,0);
    vec2 topRight=vec2(GRID_WIDTH, GRID_HEIGHT);

    QuadRenderer() {

        // create an empty grid
        colors.resize(GRID_WIDTH);
        for (int j = 0; j < GRID_WIDTH; j++) {
            colors[j].resize(GRID_HEIGHT);
            std::fill(colors[j].begin(),colors[j].end(),vec3(0));
        }

        models.resize(GRID_WIDTH);
        for (int j = 0; j < GRID_WIDTH; j++) {
            models[j].resize(GRID_HEIGHT);
            std::fill(models[j].begin(),models[j].end(),mat4(0));
        }

        const char *vertexShaderSource = "#version 330 core\n"
        "layout (location = 0) in vec3 aPos;\n"
        "layout (location = 1) in mat4 aInstanceMatrix;\n"
        "layout (location = 5) in vec3 aCol\n;"
        "uniform mat4 viewProjection;\n"
        "out vec3 color;\n"
        "void main()\n"
        "{\n"
        "   gl_Position = viewProjection * aInstanceMatrix * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
        "   color = aCol;\n"
        "}\0";
    const char *fragmentShaderSource = "#version 330 core\n"
        "out vec4 FragColor;\n"
        "in vec3 color;\n"
        "void main()\n"
        "{\n"
        "   FragColor = vec4(color,1);\n"
        "}\n\0";

        // build and compile our shader program
        // ------------------------------------
        // vertex shader
        unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        // check for shader compile errors
        int success;
        char infoLog[512];
        glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
            cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << endl;
        }
        // fragment shader
        unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);
        // check for shader compile errors
        glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
            cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << endl;
        }
        // link shaders
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
        // check for linking errors
        glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
        if (!success) {
            glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
            cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << endl;
        }
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);

        float vertices[] = {
             1.0f,  1.0f, 0.0f,  // top right
             1.0f,  0.0f, 0.0f,  // bottom right
             0.0f,  0.0f, 0.0f,  // bottom left
             0.0f,  1.0f, 0.0f   // top left 
        };

        unsigned int indices[] = {  // note that we start from 0!
            0, 1, 3,  // first Triangle
            1, 2, 3   // second Triangle
        };

        _models = flatten(models);
        _colors = flatten(colors);

        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);
        glGenBuffers(1, &EBO);
        glGenBuffers(1, &matrixBuffer);
        glGenBuffers(1, &colorBuffer);

        // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
        glBindVertexArray(VAO);

        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

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

        glBindBuffer(GL_ARRAY_BUFFER, 0); 


        glBindBuffer(GL_ARRAY_BUFFER, matrixBuffer);
        glBufferData(GL_ARRAY_BUFFER, _models.size() * sizeof(mat4), &_models.front(), GL_STATIC_DRAW);

        // set attribute pointers for matrix (4 times vec4)

        glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(mat4), (void*)0);
        glEnableVertexAttribArray(1);

        glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(mat4), (void*)(sizeof(vec4)));
        glEnableVertexAttribArray(2);


        glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(mat4), (void*)(2 * sizeof(vec4)));
        glEnableVertexAttribArray(3);

        glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(mat4), (void*)(3 * sizeof(vec4)));
        glEnableVertexAttribArray(4);

        glVertexAttribDivisor(1, 1);
        glVertexAttribDivisor(2, 1);
        glVertexAttribDivisor(3, 1);
        glVertexAttribDivisor(4, 1);

        glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);

        glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
        glBufferData(GL_ARRAY_BUFFER, _colors.size() * sizeof(vec3), &_colors.front(), GL_STATIC_DRAW);

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

        glBindBuffer(GL_ARRAY_BUFFER, 0); 
        glBindVertexArray(0); 
    }

    void calculateFrustum() {
        vec3 rayWorld = rayCast(0, SCR_HEIGHT, projection, view);
        vec3 worldPos = rayPlaneIntersection(cameraPos, rayWorld, vec3(0,0,1), vec3(0,0,0));
        bottomLeft = vec2((int)worldPos.x, (int)worldPos.y);

        rayWorld = rayCast(SCR_WIDTH, 0, projection, view);
        worldPos = rayPlaneIntersection(cameraPos, rayWorld, vec3(0,0,1), vec3(0,0,0));
        topRight = vec2((int)worldPos.x, (int)worldPos.y);
    }

    // send updated data to GPU
    void update() {
        _models = flatten(models, bottomLeft, topRight);
        _colors = flatten(colors, bottomLeft, topRight);

        vector<mat4> shadedCellModels = {};
        vector<vec3> shadedCellColors = {};

        for (int i = 0; i < _models.size(); i++) {
            // only send initialized cells to the GPU
            if (_models[i] != mat4(0) && _colors[i] != vec3(0)) {
                shadedCellModels.push_back(_models[i]);
                shadedCellColors.push_back(_colors[i]);
            }
        }


        glBindBuffer(GL_ARRAY_BUFFER, matrixBuffer);
        glBufferSubData(GL_ARRAY_BUFFER, 0, shadedCellModels.size() * sizeof(mat4), &shadedCellModels.front());

        glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
        glBufferSubData(GL_ARRAY_BUFFER, 0, shadedCellColors.size() * sizeof(vec3), &shadedCellColors.front());
    }

    void addQuad(vec2 pos, vec3 col) {

        mat4 model = translate(mat4(1.0), vec3(pos, 0.0));
        models[(int)pos.x][(int)pos.y] = model;
        colors[(int)pos.x][(int)pos.y] = col;
    }

    void remove(vec2 pos) {
        //change color to white
        colors[(int)pos.x][(int)pos.y] = vec3(1);
    }

    void setCamera(mat4 cameraMatrix) {
        viewProjection = cameraMatrix;
    }

    void draw() {

        glUseProgram(shaderProgram);
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "viewProjection"), 1, GL_FALSE, &viewProjection[0][0]);

        // render quad
        glBindVertexArray(VAO);
        glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, _models.size()); 
        glBindVertexArray(0);

    }

};

vec3 selectedColor = vec3(0,1,0);
bool leftMouseButtonPressed = false;
bool rightMouseButtonPressed = false;

class Grid {
public:

    LineRenderer lines;
    QuadRenderer cells;

    Grid() {

        //glLineWidth(2);

        // draw horizontal lines of grid
        for (int j = 0; j <= GRID_HEIGHT; j++) {
            lines.addLine(vec3(0, j, 0), vec3(GRID_WIDTH, j, 0));
        }
        // draw vertical lines of grid
        for (int i = 0; i <= GRID_WIDTH; i++) {
            lines.addLine(vec3(i, 0, 0), vec3(i,GRID_HEIGHT, 0));
        };

    }

    void addCell(vec2 gridPos, vec3 color, bool updateImmediately=true) {

        // ignore mouse clicks outside the grid
        if (gridPos.x < 0 || gridPos.x > (GRID_WIDTH -1) || gridPos.y < 0 || gridPos.y > (GRID_HEIGHT -1)) {
            return;
        }
        cells.addQuad(gridPos, color);  
        if (updateImmediately) {
            cells.update();
        }
    }

    void removeCell(vec2 gridPos, bool updateImmediately=true) {

        // ignore mouse clicks outside the grid
        if (gridPos.x < 0 || gridPos.x > (GRID_WIDTH -1) || gridPos.y < 0 || gridPos.y > (GRID_HEIGHT -1)) {
            return;
        }
        cells.remove(gridPos);
        if (updateImmediately) {
            cells.update();
        }
    }

    void draw() {

        cells.draw();
        lines.draw(); 
    }
};

Grid *grid;

int main()
{

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

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "grid", NULL, NULL);
    if (window == NULL)
    {
        cout << "Failed to create GLFW window" << endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetMouseButtonCallback(window, mouse_button_callback);
    glfwSetScrollCallback(window, scroll_callback);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        cout << "Failed to initialize GLAD" << endl;
        return -1;
    }

    grid = new Grid();
    for (int i = 0; i < GRID_WIDTH; i++) {
        for (int j = 0; j < GRID_HEIGHT; j++) {
            // when setting up grid have updateImmediately=false
            // and batch update once at the end
            grid->addCell(vec2(i,j), vec3(1,0,0), false);
        }
    }
    // batch update
    grid->cells.update();


    // point camera at center of the grid, 15 units back from the grid
    cameraPos = vec3(GRID_WIDTH/2, GRID_HEIGHT/2, 15.0f);

    projection = perspective(radians(fov), ar, nearDist, farDist);
    glClearColor(1.0, 1.0, 1.0, 1.0);

    // set camera
    view = lookAt(cameraPos,  cameraPos + cameraFront, vec3(0,1,0));
    grid->cells.setCamera(projection * view);
    grid->lines.setCamera(projection * view);

    while (!glfwWindowShouldClose(window))
    {

        // float currentFrame = glfwGetTime();
        // deltaTime = currentFrame - lastFrame;
        // lastFrame = currentFrame;

        // std::cout << "FPS: " << 1.0/(0.00000001+deltaTime) << std::endl;
        processInput(window);

        glClear(GL_COLOR_BUFFER_BIT);

        grid->draw();

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();

    delete grid;

    return 0;
}

void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    // ray cast to find quad underneath mouse cursor

    if (leftMouseButtonPressed) {
        // cast from camera through mouse position
        vec3 rayWorld = rayCast(lastX, lastY, projection, view);
        // check for intersection with grid
        vec3 worldPos = rayPlaneIntersection(cameraPos, rayWorld, vec3(0,0,1), vec3(0,0,0));

        vec2 gridPos = vec2((int)worldPos.x, (int)worldPos.y);

        grid->addCell(gridPos, selectedColor);
    } 
    if (rightMouseButtonPressed) {

        // cast from camera through mouse position
        vec3 rayWorld = rayCast(lastX, lastY, projection, view);
        // check for intersection with grid
        vec3 worldPos = rayPlaneIntersection(cameraPos, rayWorld, vec3(0,0,1), vec3(0,0,0));

        vec2 gridPos = vec2((int)worldPos.x, (int)worldPos.y);

        grid->removeCell(gridPos);
    }

    if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS)
        selectedColor = vec3(1,0,0);
    if (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS)
        selectedColor = vec3(0,1,0);    
    if (glfwGetKey(window, GLFW_KEY_3) == GLFW_PRESS)
        selectedColor = vec3(0,0,1);    
    if (glfwGetKey(window, GLFW_KEY_4) == GLFW_PRESS)
        selectedColor = vec3(1,1,0);  
    if (glfwGetKey(window, GLFW_KEY_5) == GLFW_PRESS)
        selectedColor = vec3(1,0,1);
    if (glfwGetKey(window, GLFW_KEY_6) == GLFW_PRESS)
        selectedColor = vec3(1,1,0);       
}


void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{

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

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; 

    lastX = xpos;
    lastY = ypos;

    // switch to update less frequently when many squares on the screen
    if (cameraPos.z > 15.0f) {
        realTimeUpdating=false;
    } else {
        realTimeUpdating=true;
    }

    // scrolling moves camera closer and further from the grid
    int state = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE);
    if (state == GLFW_PRESS)
    {
        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
        cameraPos -= scrollSpeed * vec3(xoffset/(float)SCR_WIDTH, yoffset/(float)SCR_WIDTH, 0);
        // update camera
        view = lookAt(cameraPos,  cameraPos + cameraFront, vec3(0,1,0));
        grid->cells.setCamera(projection * view);
        grid->lines.setCamera(projection * view);
        grid->cells.calculateFrustum();

        if (realTimeUpdating) {
            grid->cells.update();
        }
    } else {
        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
        firstMouse = true;
    }
}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    scrollSpeed = cameraPos.z * 0.1;
    cameraPos += (float)yoffset * scrollSpeed * rayCast(lastX, lastY, projection, view);
    // update camera
    view = lookAt(cameraPos,  cameraPos + cameraFront, vec3(0,1,0));
    grid->cells.setCamera(projection * view);
    grid->lines.setCamera(projection * view);
    grid->cells.calculateFrustum();
    grid->cells.update();
}

void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{

    if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
        leftMouseButtonPressed = true;
    } else {
        leftMouseButtonPressed = false;
    }

    if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) {
        rightMouseButtonPressed = true;
    } else {
        rightMouseButtonPressed = false;
    }

    if (button == GLFW_MOUSE_BUTTON_MIDDLE && action == GLFW_RELEASE) {
        if (!realTimeUpdating) {
            grid->cells.update();
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用 OpenGL 绘制彩色网格? 的相关文章

随机推荐

  • 转换并合并两个相同接口但不同类型的列表

    我有两个使用相同接口的实体 我想将从实体框架获得的两个结果合并到一个列表中IKurs public async Task
  • 获取出现次数最多的外键

    我有以下表格 user option question answer 我想获取以下记录 按用户分组的答案数 按用户分组的回答最多的选项 我尝试使用以下查询 SELECT u id u email COUNT a question id as
  • 向数据框列添加百分号

    我一直在尝试向数据框中的列添加百分号 但无济于事 有人知道吗 import pandas as pd names jimmy red julie brad oranges score 82 38 55 19 33 df pd DataFra
  • 使用 Linq-to-XML ASP.Net C# 查询嵌套 XML 元素

    在我的 ASP Net C 应用程序中 我正在尝试将嵌套的 XML 元素读取到匿名类型集合 这是 XML 示例
  • 为什么布尔原语不调用原型 toString()?

    假设我有这段代码 Boolean prototype toString function toString return this valueOf 1 0 var object true true false false 1 1 0 0 t
  • 是否可以优化我的自定义反序列化器?

    我创建了一个新的自定义反序列化器 当 json 中存在空字符串时 它会返回 null public class CustomDeserializer extends JsonDeserializer
  • 无法安装动物园包(R)

    我正在尝试使用以下方法下载动物园包来处理时间序列 install packages zoo 但我收到以下消息 Please select a CRAN mirror for use in this session Warning unabl
  • JS insertBefore() 并传递一个包含 html 的字符串

    我正在寻找一种方法 如何在纯 JavaScript 中的元素之前插入内容 我有一个页脚元素 html
  • WordPress 和被动事件侦听器的最佳实践

    所以我在 Chrome 中对一个网站进行了审核 谷歌说我应该使用被动事件侦听器 我查看了代码并发现它来自 wordpress 表情符号集成
  • 如何在启动时运行 shell 脚本

    On an 亚马逊S3 https en wikipedia org wiki Amazon S3Linux 实例 我有两个名为start my app and stop my app启动和停止forever https www npmjs
  • 如何以编程方式打开 SearchView?

    ActionBar 有一个名为 SearchView 的小部件 不使用时 它看起来像这样 当它在使用时 它看起来像这样 我想 当然以编程方式 打开搜索视图 使其 使用中 我尝试了几个功能 例如 SearchView searchView S
  • 如何在 R 中复制月度周期图

    我想使用 R 和任何使其看起来不错的包输出一个与本页 右侧 所示的图表类似的图表 http processtrends com pg charts monthly cycle chart htm http processtrends com
  • 检索SQL语句的输出参数

    我正在使用一个有两列的表格 customer id and customer name customer name是一个简单的varchar customer id是一个自动递增的主键 我想使用我的 C 应用程序插入customer nam
  • 更改 BATCH 中的区域和语言选项

    如何使用批处理代码更改 Windows XP 和 7 中的区域和语言选项 我想使用 bat 文件将 标准和格式 更改为墨西哥西班牙语 这些设置位于注册表中HKCU Control Panel International 您可以使用 reg
  • ExtJS:在局部变量中获取代理帖子的响应

    我是 ExtJS 的新手 所以如果这是非常基本的 请原谅 我用谷歌搜索但找不到任何有用的答案 我有一个带有 AJAX 代理类型的商店 tableStore Ext create Ext data Store model TableData
  • 如何通过 IAM 控制用户对 Amazon DynamoDB 数据的访问?

    Does AWS 身份和访问管理 IAM http aws amazon com iam 提供一种方法 使用户只能编辑或删除某个项目中的项目亚马逊动态数据库 http aws amazon com dynamodb 他之前添加的表 AWS
  • Qt 的图表库[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • SSIS - OLE DB 目标 - 表或视图加载与快速加载

    据我读到 表或视图数据访问模式将每一行作为一个事务一次提交 因此 制作一个传输 500 万行的包需要很长时间 30 分钟以上 快速加载数据访问模式允许在插入到目标时指定批处理行和提交大小 例如 插入 500 万条记录只需 2 分钟多一点 现
  • 如何在 ASP.NET MVC 中使用 Windows 语音合成器

    我尝试使用System Speech在 ASP NET mvc 应用程序中生成语音的类 HttpPost public ActionResult TTS string text SpeechSynthesizer speechSynthes
  • 用 OpenGL 绘制彩色网格?

    我的目标是能够创建所谓的占用网格 它类似于图块 网格游戏 看起来像附图 这是我正在从事的一个小型机器人项目 一切都是用 C C 完成的 所以我希望能够 更改分辨率或每个网格单元的大小 例如 1x1cm 或 5x5cm 等 根据某些标准更改每