Reputation: 39
I'm trying to implement a scanline fill polygon algorithm using OpenGL3, and I'd like the algorithm to calculate the vertices of the line segments that need to be drawn to achieve the filling effect, and update them into the “fillVertices” vector.
Problem is that the fillVertices vector is suceessfully being filled with point data, but there is no line drawn on the screen. Besides, the last edge of the polygon isn't drawn either.
I checked the data of the fillVertices, and they seemed to be right and properly normalized coordinate data (and, there are even number points so GL_LINES should be able to function). Also, I used another fillVAO,fillVBO in case there to be any interruption between the edge-line drawing and the fill-line drawing and checked the initialization and binding, they all seemed to work well. But no filling line has been drawn.
Following is the code:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <vector>
#include <cmath>
#include <algorithm>
#include <map>
#include <iostream>
struct Edge {
float x,invM,ymax;
};
// Callback function declarations
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
void fillPolygon(const std::vector<float>& vertices);
// Initial window size
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
// Global variables for controlling click state
bool firstClick = true;
bool polygonComplete = false;
// Global variables for storing window dimensions
int windowWidth = SCR_WIDTH;
int windowHeight = SCR_HEIGHT;
// Coordinate transformation functions
inline float normalizeX(int x) { return (x / (float)windowWidth) * 2.0f - 1.0f; }
inline float normalizeY(int y) { return 1.0f - (y / (float)windowHeight) * 2.0f; }
inline int screenX(float x) { return (x + 1.0f) * 0.5f * windowWidth; }
inline int screenY(float y) { return (1.0f - y) * 0.5f * windowHeight; }
// Shader sources
const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec2 aPos;
void main()
{
gl_Position = vec4(aPos, 0.0, 1.0);
}
)";
const char* fragmentShaderSource = R"(
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 1.0, 1.0, 1.0); //FILLING WITH WHITE
}
)";
unsigned int shaderProgram, VAO, VBO, fillVAO, fillVBO;
// Global variables for storing polygon vertices
std::vector<float> vertices;
std::vector<float> fillVertices;
// Function to compile shaders and link shader program
void setupShaders()
{
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// VAO VBO for filling lines
glGenVertexArrays(1, &fillVAO);
glGenBuffers(1, &fillVBO);
glBindVertexArray(fillVAO);
glBindBuffer(GL_ARRAY_BUFFER, fillVBO);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
}
// Main function
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Polygon Scanline Fill", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
setupShaders();
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
glfwSetKeyCallback(window,key_callback);
// Main loop
while (!glfwWindowShouldClose(window)) {
// Process input
processInput(window);
// Render
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_DYNAMIC_DRAW);
glDrawArrays(GL_LINE_STRIP, 0, vertices.size() / 2);
// If polygon is complete, draw filled polygon
if (polygonComplete) {
vertices.push_back(vertices[0]);
vertices.push_back(vertices[1]);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_DYNAMIC_DRAW);
glDrawArrays(GL_LINE_STRIP, 0, vertices.size() / 2);
vertices.pop_back();
vertices.pop_back();
std::cout<<"START FILL\n";
fillPolygon(vertices);
polygonComplete = false;
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// Swap buffers and poll events
glfwSwapBuffers(window);
glfwPollEvents();
}
// Clean up
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteVertexArrays(1, &fillVAO);
glDeleteBuffers(1, &fillVBO);
glDeleteProgram(shaderProgram);
glfwTerminate();
return 0;
}
// Function to fill polygon using scanline algorithm
void fillPolygon(const std::vector<float>& vertices) {
if(vertices.size()<6) return; //unable to create polygon
//Initializing ET Table
int ymin = screenY(vertices[1]), ymax = screenY(vertices[1]);
for(int i=1;i<vertices.size();i+=2){
ymin = std::min(ymin, screenY(vertices[i]));
ymax = std::max(ymax, screenY(vertices[i]));
}
std::cout<<"ET INITIAL END"<<std::endl;
std::vector<std::vector<Edge>> ET(ymax+1);
//Filling ET Table
for(size_t i = 0;i<vertices.size();i+=2){
int x1 = screenX(vertices[i]);
int x2 = screenX(vertices[(i+2)%vertices.size()]);
int y1 = screenY(vertices[i+1]);
int y2 = screenY(vertices[(i+3)%vertices.size()]);
if(y1==y2) continue;
if(y1>y2) {std::swap(x1,x2); std::swap(y1,y2);}
float invM = static_cast<float>(x2-x1)/(y2-y1);
ET[y1].push_back({static_cast<float>(x1), invM, static_cast<float>(y2)});
}
std::cout<<"ET FILLING END"<<std::endl;
//Initializing AEL
std::vector<Edge> AEL;
std::cout<<"Initializing AEL END"<<std::endl;
//Scanline
for(int y=ymin;y<=ymax;++y)
{
for(const auto& edge:ET[y]) AEL.push_back(edge);
std::sort(AEL.begin(),AEL.end(),[](const Edge& a,const Edge& b){
if(a.x == b.x) return a.invM<b.invM; //sort with invM
return a.x<b.x; //sort with x-coordinate
});
//Update fillVertices
for (size_t i = 0; i + 1 < AEL.size(); i += 2) {
float xStart = normalizeX(AEL[i].x);
float xEnd = normalizeX(AEL[i + 1].x);
float yPos = normalizeY(y);
fillVertices.push_back(xStart);
fillVertices.push_back(yPos);
fillVertices.push_back(xEnd);
fillVertices.push_back(yPos);
}
//delete lines reaching ymax
AEL.erase(std::remove_if(AEL.begin(), AEL.end(), [y](const Edge& edge) {
return static_cast<int>(edge.ymax) == y;
}), AEL.end());
for (auto& edge : AEL)
{
edge.x += edge.invM;
}
}
std::cout << "fillVertices: \n";
for (size_t i = 0; i < fillVertices.size(); i += 2) {
std::cout << "(" << fillVertices[i] << ", " << fillVertices[i + 1] << ") ";
}
std::cout << std::endl;
if (!fillVertices.empty()) {
glBindVertexArray(fillVAO);
glBindBuffer(GL_ARRAY_BUFFER, fillVBO);
glBufferData(GL_ARRAY_BUFFER, fillVertices.size() * sizeof(float), fillVertices.data(), GL_DYNAMIC_DRAW);
glDrawArrays(GL_LINES, 0, fillVertices.size() / 2);
}
std::cout<<"Scanline End"<<std::endl;
}
// Callback function implementations
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
windowWidth = width;
windowHeight = height;
}
void processInput(GLFWwindow *window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
if(firstClick){
glClear(GL_COLOR_BUFFER_BIT);
vertices.clear();
fillVertices.clear();
vertices.push_back(normalizeX((int)xpos));
vertices.push_back(normalizeY((int)ypos));
firstClick = false;
}
else{
vertices.push_back(normalizeX((int)xpos));
vertices.push_back(normalizeY((int)ypos));
}
}
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if (key == GLFW_KEY_ENTER && action == GLFW_PRESS) {
if (vertices.size() >= 4) {
polygonComplete = true;
firstClick = true;
}
}
}
Upvotes: 1
Views: 49