Member 14074655 Ответов: 1

Случайная линия рисуется в проекте C++ opengl


Привет, я довольно новичок в C++ и OpenGL и кодировал версию C++ OpenGL 2d Raycasting Challenge с канала Coding Train YouTube(ep.145). Все это, кажется, работает хорошо, за исключением одной вещи. В проекте всякий раз, когда луч 0 ° (Луч, который указывает прямо направо) не отбрасывает себя, то есть не касается границы(стены), из ниоткуда появляется случайный луч, который продолжается до бесконечности.
Вот код, чтобы вы могли это увидеть.

main.cpp
#include "Raycasting.hpp"
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cmath>

void framebuffer_size_callback(GLFWwindow* window, int w, int h);

int width, height;
std::vector<Boundary> walls;
Particle particle = Particle(width, height);
double mx, my;
int main()
{
  srand(time(NULL));
  GLFWwindow* window;
  if (!glfwInit())
    return 1;
  width  = 600;
  height = 600;
  window = glfwCreateWindow(width, height, "Window", NULL, NULL);
  if (!window) {
    glfwTerminate();
    return 1;
  }
  for(int i = 0; i < 5; i++){
    walls.push_back(Boundary(rand()%width, rand()%width, rand()%height, rand()%height));
  }
  glfwMakeContextCurrent(window);
  if(glewInit()!=GLEW_OK)
    std::cout<<"Error"<<std::endl;
  glEnable(GL_DEPTH_TEST);
  glMatrixMode(GL_PROJECTION);
  glfwGetFramebufferSize(window, &width, &height);
  glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
  Particle particle = Particle(width, height);
  glOrtho(0, width*(width/height), height, 0, -2, 2);
  while(!glfwWindowShouldClose(window)) {
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glClearColor(0.11, 0.14, 0.17, 1);
    glfwGetCursorPos(window, &mx, &my);
    for(Boundary &wall : walls)
      wall.show();
    particle.update((float)mx, (float)my);
    particle.show();
    particle.look(walls);
    glfwSwapBuffers(window);
    glfwPollEvents();
  }
  glfwTerminate();
}

void framebuffer_size_callback(GLFWwindow* window, int w, int h)
{
  //float ratio = width/height;
  width = w;
  height = h;
  float ratio = width/height;
  if(width<height)
    ratio = height/width;
  else
    ratio = width/height;
  //std::cout<<"Width: "<<width<<" "<<"Height: "<<height<<std::endl;
  glViewport(0,0,width,height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if(width>height)
    glOrtho(0, width*ratio, height, 0, -2, 2);
  else
    glOrtho(0, width, height*ratio, 0, -2, 2);
}

Raycasting.ГЭС
#ifndef RAYCASTING
#define RAYCASTING

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cmath>
#include <vector>
/**DRAWING FUNCTIONS, no need to check these**/
void DrawEllipse(float cx, float cy, float rx, float ry, int num_segments) 
{ 
    float theta = 2 * 3.1415926 / float(num_segments); 
    float c = cosf(theta);//precalculate the sine and cosine
    float s = sinf(theta);
    float t;

    float x = 1;//we start at angle = 0 
    float y = 0; 

    glBegin(GL_LINE_LOOP); 
    for(int ii = 0; ii < num_segments; ii++) 
    { 
        //apply radius and offset
        glVertex2f(x * rx + cx, y * ry + cy);//output vertex 

        //apply the rotation matrix
        t = x;
        x = c * x - s * y;
        y = s * t + c * y;
    } 
    glEnd(); 
}


void stroke(int value){
    float v = (float)value/255;
    glColor3f(v,v,v);
}

void line(float x1, float y1, float x2, float y2){
    glBegin(GL_LINES);
    glVertex2f(x1,y1);
    glVertex2f(x2,y2);
    glEnd();
}

void ellipse(float x, float y, float width, float height){
    DrawEllipse(x, y, width/2, height/2, 10800);
}
/**END OF DRAWING FUNCTIONS**/


class V2D{
public:
    V2D(){}
    V2D(float _x, float _y){
        x = _x;
        y = _y;
    }
    V2D(long int n){
        isNull = true;
    }
    bool isNull = false;
    float x;
    float y;
    float magnitude(){
        return sqrt(x*x + y*y);
    }
    void normalize(){
        float mag = magnitude();
        x /= mag;
        y /= mag;
    }
};

float dist(V2D start, V2D end){
    return sqrt((start.x-end.x)*(start.x-end.x) + (start.y-end.y)*(start.y-end.y));
}

class Boundary{
public:
    Boundary(float x1, float y1, float x2, float y2){
        a = V2D(x1, y1);
        b = V2D(x2, y2);
    }
    V2D a,b;
    void show(){
        stroke(255);
        line(a.x, a.y, b.x, b.y);
    }
};

class Ray{
public:
    Ray(V2D _pos, float angle){
        pos = _pos;
        dir = V2D(cos(angle), sin(angle));
    }
    V2D pos,dir;
    void lookAt(float x, float y){
        dir.x = x - pos.x;
        dir.y = y - pos.y;
        dir.normalize();

    }
    void show(){
        stroke(255);
        line(pos.x, pos.y, pos.x + dir.x * 10, pos.y + dir.y * 10);
    }
    V2D cast(Boundary wall){
        const float x1 = wall.a.x;
        const float y1 = wall.a.y;
        const float x2 = wall.b.x;
        const float y2 = wall.b.y;

        const float x3 = pos.x;
        const float y3 = pos.y;
        const float x4 = pos.x + dir.x;
        const float y4 = pos.y + dir.y;

        const float den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
        if(den==0){
            return V2D(NULL);
        }
        const float t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / den;
        const float u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den;
        if(t > 0 && t < 1 && u > 0){
            float X = x1 + t * (x2 - x1);
            float Y = y1 + t * (y2 - y1);
            return V2D(X, Y);
        }
        else
            return V2D(NULL);
    }
};

class Particle{
public:
    Particle(int width, int height){
        pos = V2D(width/2, height/2);
        rays = {};
        for(int i = 0; i < 360; i+=1){
            rays.push_back(Ray(pos, i*M_PI/180));
        }
    }
    V2D pos;
    std::vector<Ray> rays = {};
    void update(float x, float y){
        for(Ray &ray : rays){
            ray.pos = V2D(x,y);
        }
    }
    void show(){
        for(int i = 0; i <rays.size(); i++){
            rays[i].show();
        }
    }
    void look(std::vector<Boundary> walls){
        for(int i = 0; i < rays.size(); i++){
            V2D closest;
            float record = 100000;
            for(Boundary &wall : walls){
                V2D pt = rays[i].cast(wall);
                if(!pt.isNull){
                    const float d = dist(rays[i].pos, pt);
                    if(d < record){
                        record = d;
                        closest = pt;
                    }
                }  
            }
            if(!closest.isNull){
                line(rays[i].pos.x, rays[i].pos.y, closest.x, closest.y);
            }
        }
    }
};

#endif

файл Makefile
main.o: main.cpp
	g++ main.cpp -lglfw -I/usr/include/libdrm -L/usr/lib64 -lGLEW -lGLU -lGL -o app


Что я уже пробовал:

Я не знаю, что конкретно делать, но любая помощь/объяснение будут полностью оценены

1 Ответов

Рейтинг:
12

Stefan_Lang

Это всего лишь предположение, так как код слишком длинный для полного анализа, но, судя по вашему описанию проблемы, это всего лишь угловой случай, и в нем есть фрагмент кода. Ray::cast() это может вызвать проблемы в угловых случаях:

const float den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if(den==0){
Я подозреваю, что вычисление den страдает от эффектов ошибки округления и может быть не совсем 0, даже если это должно быть так математически. Вместо этого вы получите значение 0.123 e-8 или аналогичное, а затем, когда вы попытаетесь разделить это число, вы внезапно получите огромные значения, что приведет к "бесконечной" линии, которую вы наблюдали.

Попробуйте заменить это if состояние с чем-то вроде
const float epsilon=1e-6;
if(std::abs(den)<epsilon)


П. С. Еще один выстрел в темноте:
В функции Ray::look(), вы не инициализируете переменную closest. Если ваш луч не попадает ни в одну из стен, вы звоните closest.IsNull(), но результат может быть неопределенным, если он не инициализирован. В конечном итоге вы можете нарисовать линию с неопределенной конечной точкой.


Member 14074655

@Stefan_Lang Извини, что я не подумал об этой штуке с поплавком. Однако это, к сожалению, не удаляет ту дополнительную линию, которая приходит из ниоткуда. Главный признак заключается в том, что если луч 0 градусов не отбрасывается на стену, то возникает эта ошибка.

Stefan_Lang

Смотрите мое заявление P.S.: Вы забыли инициализировать 'closest' в 'Ray::look()'

Member 14074655

- Да!! Это работает, большое вам спасибо!