Используйте OpenGL для динамического рисования фигур (используйте эпицикл и ряды Фурье) - PullRequest
0 голосов
/ 19 сентября 2019

Я хочу визуализировать некоторую форму, используя ряд Фурье, и я закончил класс инструментов рендеринга (стрелка и ручка).Но конечный результат выглядит неудобно.Моя идея рендеринга состоит в том, чтобы сначала установить позиции стрелок, а затем использовать их для рендеринга кругов в виде рукописных фигур.После поворота на фиксированный угол устанавливается стрелка.Эта идея дает следующий вывод:

эффект вывода:

На самом деле я хочу отрисовать строку, и я не знаю, какчтобы решить это.Я использую FBO для хранения почерка и рендеринга стрелки в FBO по умолчанию, коды следующие:

  1. main.cpp
// include for create a window
#include <glad/glad.h>
#include <GLFW/glfw3.h>

// include for matrix calculation
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>

// include for creating arrow
#include "Arrow.h"

// include for creating pen 
#include "Pen.h"

// include for drawing circle
#include <cmath>
#include <iostream>
#include <stdio.h>
using namespace std;

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
void renderBoard();

const int SCR_WIDTH = 800;
const int SCR_HEIGHT = 800;

int main()
{
    // initialize window glfw and configure
    // ------------------------------------
    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); // uncomment this statement to fix compilation on OS X
#endif
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

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

    // Create drawing board FBO
    unsigned int boardFBO;
    glGenFramebuffers(1, &boardFBO);
    glBindFramebuffer(GL_FRAMEBUFFER, boardFBO);
    unsigned int boardColorBuffer;
    glGenTextures(1, &boardColorBuffer);
    glBindTexture(GL_TEXTURE_2D, boardColorBuffer);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, boardColorBuffer, 0);

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        cout << "Board Color Buffer isn't complete\n" << endl;
    }
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    // Create arrow
    Arrow arrow1(1.1), arrow2;

    // Create Pen
    Pen pen = Pen(0.0, 0.0, 0.0, 0.004);
    pen.setOriginPosition(glm::vec3(0.0, 0.0, 0.0));

    // Create Sahder
    Shader arrowShader("arrow.vs", "arrow.fs");
    Shader boardShader("board.vs", "board.fs");

    boardShader.use();
    boardShader.setInt("boardColorBuffer", 0);
    bool isFirst = true;

    // render loop
    while (!glfwWindowShouldClose(window))
    {
        // input
        processInput(window);
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);

        arrow1.rotateArrow(0.5f);
        arrow1.setPosition(glm::vec3(0.0, 0.0, 0.0));
        arrow2.rotateArrow(5.0f);
        arrow2.setPosition(arrow1.getHeadPosition());


        // render pen draw
        // glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
        glBindFramebuffer(GL_FRAMEBUFFER, boardFBO);
        glClearColor(0.0, 0.0, 0.0, 1.0);
        if (isFirst) {
            glClear(GL_COLOR_BUFFER_BIT);
            isFirst = false;
        }
        arrowShader.use();
        pen.setOriginPosition(arrow2.getHeadPosition());
        pen.drawPen(arrowShader);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        // glViewport(0, 0, SCR_WIDTH * 2, SCR_HEIGHT * 2);
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);

        boardShader.use();
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, boardColorBuffer);
        renderBoard();

        arrowShader.use();
        arrow1.drawArrow(arrowShader);
        arrow2.drawArrow(arrowShader);

        // render
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

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);
    }
}

unsigned int boardVAO = 0;
unsigned int boardVBO;
void renderBoard() {
    if (boardVAO == 0) {
        float boardVertices[] = {
            -1.0,  1.0, 0.0,   0.0, 1.0,    // up-left
            -1.0, -1.0, 0.0,   0.0, 0.0,    // down-left
            1.0,  1.0, 0.0,   1.0, 1.0,    // up-right
            1.0,  -1.0, 0.0,  1.0, 0.0     // down right
        };
        glGenVertexArrays(1, &boardVAO);
        glBindVertexArray(boardVAO);
        glGenBuffers(1, &boardVBO);
        glBindBuffer(GL_ARRAY_BUFFER, boardVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(boardVertices), boardVertices, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
        glEnableVertexAttribArray(1);
        glBindVertexArray(0);
    }
    glBindVertexArray(boardVAO);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glBindVertexArray(0);
}

Arrow.h
#ifndef Arrow_h
#define Arrow_h

#include <GLFW/glfw3.h>
#include <glad/glad.h>

// include for matrix operation
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "Shader.h"

class Arrow {
public:
    Arrow(float scale = 1.0, glm::vec3 position = glm::vec3(0.0, 0.0, 0.0)) {
        scale = scale;
        float arrowVertices[] = {
            // rectangle
            -0.001, 0.0,  0.0,      // bottom-left
            -0.001, 0.11, 0.0,      // up-left
            0.001, 0.0,  0.0,      // bottom-right
            0.001, 0.11, 0.0,      // up-right
                                   // triangle
                                   0.0,  0.15,  0.0,      // up
                                   -0.01, 0.11,  0.0,      // bottom-left
                                   0.01, 0.11,  0.0       // bottom-right
        };
        int arrowIndices[] = {
            0, 1, 2,
            2, 1, 3,
            4, 5, 6
        };
        arrowVertices[4] *= scale;
        arrowVertices[10] *= scale;
        arrowVertices[13] *= scale;
        arrowVertices[16] *= scale;
        arrowVertices[19] *= scale;

        headPos = initHeadPos = glm::vec3(0.0, arrowVertices[13], 0.0);
        tailPos = initTailPos = glm::vec3(0.0, 0.0, 0.0);

        rotateOffsetModel = glm::mat4(1.0);
        positionOffset = glm::mat4(1.0);
        positionOffset = glm::translate(positionOffset, position);

        glGenVertexArrays(1, &VAO);
        glBindVertexArray(VAO);

        glGenBuffers(1, &VBO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(arrowVertices), arrowVertices, GL_STATIC_DRAW);

        glGenBuffers(1, &EBO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(arrowIndices), arrowIndices, GL_STATIC_DRAW);

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


    }

    // draw arrow
    void drawArrow(Shader shader) {
        shader.use();
        // alert arrow position
        shader.setMat4("model", positionOffset * rotateOffsetModel);

        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 9, GL_UNSIGNED_INT, (void*)0);
        glBindVertexArray(0);
    }

    // rotate arrow in local coordinate
    void rotateArrow(float degree, glm::vec3 axis = glm::vec3(0.0, 0.0, 1.0)) {
        rotateOffsetModel = glm::rotate(rotateOffsetModel, glm::radians(degree), axis);     // get rotate matrix
        headPos = glm::vec3(rotateOffsetModel * glm::vec4(initHeadPos, 1.0));
        tailPos = glm::vec3(rotateOffsetModel * glm::vec4(initTailPos, 1.0));
    }

    // set arrow's position
    void setPosition(glm::vec3 Position) {
        positionOffset = glm::mat4(1.0);
        positionOffset = glm::translate(positionOffset, Position);
        headPos = glm::vec3(positionOffset * rotateOffsetModel * glm::vec4(initHeadPos, 1.0));
        tailPos = glm::vec3(positionOffset * rotateOffsetModel * glm::vec4(initTailPos, 1.0));
    }

    // alert head and tail info
    void alertHeadTailPosition(glm::mat4 alertModel) {
        headPos = glm::vec3(alertModel * glm::vec4(initHeadPos, 1.0));
        tailPos = glm::vec3(alertModel * glm::vec4(initTailPos, 1.0));
    }

    // get head ( the up coordinate of triangle ) coordinate
    glm::vec3 getHeadPosition() {
        return headPos;
    }

    // get tail ( the middle coordinate of triangle's bottom ) coordinate
    glm::vec3 getTailPosition() {
        return tailPos;
    }
private:
    unsigned int VAO, VBO, EBO;
    glm::mat4 rotateOffsetModel, positionOffset;
    glm::vec3 headPos, tailPos;
    glm::vec3 initHeadPos, initTailPos;  // I find that the offset set will add if I always use one variable
    GLfloat Scale;
};

#endif /* Arrow_h */

Ручка
// This class is used to create a pen which is used to draw the graph

#include <GLFW/glfw3.h>
#include <glad/glad.h>
#include <iostream>
#include "Shader.h"
#include <vector>
#include <math.h>
using namespace std;

#ifndef Pen_h
#define Pen_h
class Pen {
public:
    Pen(GLfloat xPos, GLfloat yPos, GLfloat zPos, GLfloat Radius)
        : originPosition(glm::vec3(xPos, yPos, zPos)), radius(Radius), edges(360) {
        configurePen();
    }

    Pen(glm::vec3 penPosition, GLfloat Radius)
        : originPosition(penPosition), radius(Radius), edges(360) {
        configurePen();
    }

    void configurePen() {

        allPenVertices.push_back(originPosition);
        GLfloat doublePi = 2.0 * M_PI;
        for (int i = 1; i < edges + 2; i++) {
            float x, y;
            x = originPosition.x + cos((float)i / edges * doublePi) * radius;
            y = originPosition.y + sin((float)i / edges * doublePi) * radius;
            allPenVertices.push_back(glm::vec3(x, y, 0.0));
        }
        glGenVertexArrays(1, &VAO);
        glBindVertexArray(VAO);
        glGenBuffers(1, &VBO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, allPenVertices.size() * 3 * sizeof(float), &allPenVertices[0], GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
        glBindVertexArray(0);
    }

    void drawPen(Shader shader) {
        shader.use();
        glBindVertexArray(VAO);
        shader.setMat4("model", offsetModel);
        glDrawArrays(GL_TRIANGLE_FAN, 0, allPenVertices.size());
    }

    void setOriginPosition(glm::vec3 Position) {
        offsetModel = glm::mat4(1.0);
        offsetModel = glm::translate(offsetModel, glm::vec3(Position));
    }

private:
    GLfloat radius;
    glm::vec3 originPosition;
    GLint edges, numberOfVertices;
    vector<glm::vec3> allPenVertices;
    unsigned int VAO, VBO;
    glm::mat4 offsetModel;
};

#endif /* Pen_h */

Должен ли я изменить свою идею рендеринга?Или я должен использовать алгоритм для решения?

1 Ответ

0 голосов
/ 19 сентября 2019

[...] он не может позволить им соединиться друг с другом [...]

Конечно.Вы рисуете 1 точку на кадр, и процесс запускается быстро, чтобы создать «соединенные» точки.

Вы должны нарисовать более 1 точки на кадр.Например:

glBindFramebuffer(GL_FRAMEBUFFER, boardFBO);
if (isFirst) {
    glClear(GL_COLOR_BUFFER_BIT);
    isFirst = false;
}

arrowShader.use();
const int perFrame = 5;
for (int i = 0; i < perFrame; ++i) {
    arrow1.rotateArrow( 0.5f / perFrame );
    arrow1.setPosition( glm::vec3( 0.0, 0.0, 0.0 ) );
    arrow2.rotateArrow( 5.0f / perFrame );
    arrow2.setPosition( arrow1.getHeadPosition() );

    pen.setOriginPosition( arrow2.getHeadPosition() );
    pen.drawPen( arrowShader );
}

glBindFramebuffer(GL_FRAMEBUFFER, 0);


Я рекомендую включить Мультисэмплирование сглаживания .

Определите количество выборок на пиксель до создания окна GLFW (GLFW_SAMPLES). Например:

glfwWindowHint(GLFW_SAMPLES, 8);

Включить GL_MULTISAMPLE (это состояние попо умолчанию включено):

glEnable(GL_MULTISAMPLE);

Поскольку вы используете кадровый буфер, вы можете Multisample Render Target кадровый буфер для улучшения результата.

Настройте кадровый буфер иприсоедините сэмплер GL_TEXTURE_2D_MULTISAMPLE к цветному буферу:

unsigned int boardColorBuffer;
glGenTextures(1, &boardColorBuffer);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, boardColorBuffer);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 8, GL_RGB, SCR_WIDTH, SCR_HEIGHT, false);
glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

unsigned int boardFBO;
glGenFramebuffers(1, &boardFBO);
glBindFramebuffer(GL_FRAMEBUFFER, boardFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                       GL_TEXTURE_2D_MULTISAMPLE, boardColorBuffer, 0);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
    cout << "Board Color Buffer isn't complete\n" << endl;
}

Обратите внимание, текстура должна быть привязана к цели GL_TEXTURE_2D_MULTISAMPLE:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, boardColorBuffer);

Интерполировать сэмплы вфрагментный шейдер.Например:

in vec2 v_uv;
uniform sampler2DMS boardColorBuffer;

void main()
{
    const int samples = 8;

    vec2 size = textureSize(boardColorBuffer);
    vec4 color = vec4(0.0);
    for (int i=0; i<samples; ++i)
        color += texelFetch(boardColorBuffer, ivec2(v_uv*size), i);
    color /= float(samples);  

    // ...
}

В этом случае вы должны включить смешивание с уравнением смешивания dest * 1 + source * 1.Обратите внимание: поскольку фрагменты с несколькими выборками интерполируются, цвет фрагментов на границе круга может быть не полностью «белым».При смешивании вы можете избежать того, чтобы такой фрагмент перекрывал полностью «белый» фрагмент.например:

glBindFramebuffer(GL_FRAMEBUFFER, boardFBO);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glBlendEquation(GL_FUNC_ADD);  

// draw pen respectively the pen-loop
// [...]

glDisable(GL_BLEND);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...