• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

SFML, rysowanie 3D, bufor Z

Object Storage Arubacloud
0 głosów
210 wizyt
pytanie zadane 21 lipca 2022 w C i C++ przez Daaa22 Dyskutant (8,250 p.)
edycja 21 lipca 2022 przez Daaa22

Napisałem taki kod

#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <vector>
#include <iostream>
#define _USE_MATH_DEFINES
#include <math.h>

using namespace std;
using namespace sf;

float getAngle(float x, float y)
{
    if (x == 0 && y == 0)
        return 0;
    else if (x > 0 && y >= 0)
        return atan(y / x);
    else if (x <= 0 && y > 0)
        return M_PI / 2 - atan(x / y);
    else if (x < 0 && y <= 0)
        return atan(y / x) - M_PI;
    else if (x >= 0 && y < 0)
        return -M_PI / 2 - atan(x / y);
}

inline float determinant(sf::Vector2i& A, sf::Vector2i& B, sf::Vector2i& C)
{
    return abs((A.x - C.x) * (B.y - C.y) - (B.x - C.x) * (A.y - C.y));
}

float isInsideTriangle(sf::Vector2i& A, sf::Vector2i& B, sf::Vector2i& C, sf::Vector2i point)
{
    float total = determinant(A, B, C);
    float det1 = determinant(A, B, point);
    float det2 = determinant(A, C, point);
    float det3 = determinant(B, C, point);

    if (det1 + det2 + det3 > total)
        return false;
    else return true;
}

class Scene;

class FoV
{
public:
    float x, y;

    FoV()
    {
        x = M_PI / 2;
        y = M_PI / 3;
    }

    FoV(float _x, float _y)
    {
        x = _x;
        y = _y;
    }

    FoV(float monitorWidth, float monitorHeight, float eyesLengthFromScreenCenter)
    {
        x = 2 * atan((monitorWidth / 2) / (eyesLengthFromScreenCenter));
        y = 2 * atan((monitorHeight / 2) / (eyesLengthFromScreenCenter));
    }
};

class Camera
{
public:
    friend Scene;
    float x, y, z;
    FoV fov;
    float rotationX, rotationY;
    Camera(FoV _fov = FoV(), float _x = 0, float _y = 0, float _z = 0, float _rotationX = 0, float _rotationY = 0)
    {
        fov = _fov;
        x = _x;
        y = _y;
        z = _z;
        rotationX = _rotationX;
        rotationY = _rotationY;
    }
};

class Triangle
{
    friend Scene;
    Vector3f vertex[3];
    Color color;
public:
    Triangle()
    {
        color = Color::White;
    }

    Triangle(Vector3f _vertex[3], Color _color = Color::White)
    {
        vertex[0] = _vertex[0];
        vertex[1] = _vertex[1];
        vertex[2] = _vertex[2];
        color = _color;
    }
};

class Scene
{
    vector<Triangle>triangle;
    Vertex* view;
    float* zBuffer;
    float renderDistance;
    Color backgroundColor;
 public:
    Scene( int x, int y )
    {
        view = new Vertex[x * y];
        for (int _y = 0; _y < y; _y++)
            for (int _x = 0; _x < x; _x++)
                view[_y * x + _x].position = Vector2f(_x, _y);
        zBuffer = new float[x * y];
        renderDistance = 1000;
        backgroundColor = Color::Black;
    }

    ~Scene()
    {
        delete[] view;
        delete[] zBuffer;
    }

    void setBackgroundColor(Color _backgroundColor)
    {
        backgroundColor = _backgroundColor;
    }

    void setRenderDistance(float _renderDistance)
    {
        renderDistance = _renderDistance;
    }

    void setViewSize(int x, int y)
    {
        view = new Vertex[x * y];
        for (int _y = 0; _y < y; _y++)
            for (int _x = 0; _x < x; _x++)
                view[_y * x + _x].position = Vector2f(_x, _y);
        zBuffer = new float[x * y];
    }

    void addTriangle(Triangle _triangle)
    {
        triangle.push_back(_triangle);
    }

    void draw(RenderWindow& window, Camera camera)
    {
        for (int i = 0; i < window.getSize().x * window.getSize().y; i++)
        {
            view[i].color = Color::Black;
            zBuffer[i] = renderDistance;
        }

        float a, b, c, d;
        float lineX, lineY, lineZ;
        Vector3f intersection;
        float distance;

        Vector2i screenTriangleVertex[3];
        Vector3f relativePosition[3];
        float r;
        float angle;
        bool flag = false;
        for (auto it = triangle.begin(); it != triangle.end(); it++)
        {
            //ta petla zmienia polozenie trojkata w zaleznosci od polozenia kamery i tego jak ona jest obrocona
            for (int i = 0; i < 3; i++)
            {
                //to oczywiste chyba
                relativePosition[i].x = it->vertex[i].x - camera.x;
                relativePosition[i].y = it->vertex[i].y - camera.y;
                relativePosition[i].z = it->vertex[i].z - camera.z;

                //obrot prawo lewo
                r = sqrt(relativePosition[i].x * relativePosition[i].x + relativePosition[i].z * relativePosition[i].z);
                angle = getAngle(relativePosition[i].x, relativePosition[i].z) - camera.rotationX;
                relativePosition[i].x = r * cos(angle);
                relativePosition[i].z = r * sin(angle);
                
                //obrot gora dol
                r = sqrt(relativePosition[i].y * relativePosition[i].y + relativePosition[i].z * relativePosition[i].z);
                angle = getAngle(relativePosition[i].z, relativePosition[i].y) - camera.rotationY;
                relativePosition[i].y = r * sin(angle);
                relativePosition[i].z = r * cos(angle);

                //jezeli ktorys z wierzcholkow trojkata jest za mna to nie rysuj go
                if (relativePosition[i].z <= 0)
                {
                    flag = true;
                    break;
                }

                //tlumaczenie trojkata 3D na plaski ekran
                screenTriangleVertex[i].x = window.getSize().x / 2 + relativePosition[i].x * window.getSize().x / 2 / relativePosition[i].z / tan(camera.fov.x / 2);
                screenTriangleVertex[i].y = window.getSize().y / 2 - relativePosition[i].y * window.getSize().y / 2 / relativePosition[i].z / tan(camera.fov.y / 2);
            }

            if (flag)
            {
                flag = false;
                continue;
            }

            //rownanie plaszczyzny 3D na ktorej lezy aktualnie rysowany trojkat (ax+by+cz+d=0)
            a = (relativePosition[1].y - relativePosition[0].y) * (relativePosition[2].z - relativePosition[0].z) - (relativePosition[2].y - relativePosition[0].y) * (relativePosition[1].z - relativePosition[0].z);
            b = (relativePosition[1].z - relativePosition[0].z) * (relativePosition[2].x - relativePosition[0].x) - (relativePosition[2].z - relativePosition[0].z) * (relativePosition[1].x - relativePosition[0].x);
            c = (relativePosition[1].x - relativePosition[0].x) * (relativePosition[2].y - relativePosition[0].y) - (relativePosition[2].x - relativePosition[0].x) * (relativePosition[1].y - relativePosition[0].y);
            d = -(a * relativePosition[0].x + b * relativePosition[0].y + c * relativePosition[0].z);

            //po kolei kolorujemy piksele
            for (int y = 0; y < window.getSize().y; y++)
                for (int x = 0; x < window.getSize().x; x++)
                    if (isInsideTriangle(screenTriangleVertex[0], screenTriangleVertex[1], screenTriangleVertex[2], Vector2i(x, y)))
                    {
                        //polprosta jest dana rownaniem y = (lineX * t, lineY * t, lineZ * t), t > 0
                        lineX = x - window.getSize().x / 2;
                        lineY = -y + window.getSize().y / 2;
                        lineZ = window.getSize().x / 2 / tan(camera.fov.x / 2);

                        //punkt wspolny polprostej i plaszczyzny na ktorej jest rysowany trojkat
                        intersection.x = d / (a * lineX + b * lineY + c * lineZ) * lineX;
                        intersection.y = d / (a * lineX + b * lineY + c * lineZ) * lineY;
                        intersection.z = d / (a * lineX + b * lineY + c * lineZ) * lineZ;

                        //odleglosc tego trojkata
                        distance = sqrt(intersection.x * intersection.x + intersection.y * intersection.y + intersection.z * intersection.z);
                        if (zBuffer[y * window.getSize().x + x] > distance)
                        {
                            view[y * window.getSize().x + x].color = it->color;
                            zBuffer[y * window.getSize().x + x] = distance;
                        }
                    }
        }

        window.draw(view, window.getSize().x* window.getSize().y, PrimitiveType::Points);
    }
};

int main()
{
    Keyboard keyboard;
    Mouse mouse;

    ContextSettings settings;
    settings.antialiasingLevel = 8;
    RenderWindow window(VideoMode::getDesktopMode(), "3D", Style::Fullscreen, settings);
    mouse.setPosition(Vector2i(window.getSize().x / 2, window.getSize().y / 2), window);

    window.setMouseCursorVisible(false);

    Camera player( FoV(1920, 1080, 3000)); //wszystko jest w jednostce pikseli, mozna tez uzyc centymetrow, wazne tylko zeby stosunek tych odleglosci sie zgadzal
    float speed = 10;

    Clock moveClock;

    Scene test(window.getSize().x, window.getSize().y);

    Vector3f t[3] = { {-90, -30, 800}, {60, -160, 1000}, {190, 30, 400} };
    Vector3f v[3] = { {-70, -70, 600}, {60, -60, 700}, {60, 80, 580} };

    test.addTriangle(Triangle(v, Color::Red));
    test.addTriangle(Triangle(t, Color::Blue));

    while (window.isOpen())
    {
        window.clear();

        Event event;
        while (window.pollEvent(event))
        {
            if (event.type == Event::Closed)
                window.close();

            if (mouse.getPosition(window) != Vector2i(window.getSize().x / 2, window.getSize().y / 2))
            {
                player.rotationX += ((float)window.getSize().x / 2 - mouse.getPosition(window).x) / 700;
                player.rotationY += ((float)window.getSize().y / 2 - mouse.getPosition(window).y) / 700;
                if (player.rotationY > M_PI / 2)
                    player.rotationY = M_PI / 2;
                else if (player.rotationY < -M_PI / 2)
                    player.rotationY = -M_PI / 2;
                mouse.setPosition(Vector2i(window.getSize().x / 2, window.getSize().y / 2), window);
            }
        }

        if (moveClock.getElapsedTime().asMilliseconds() >= 2)
        {
            moveClock.restart();
            if (keyboard.isKeyPressed(Keyboard::W))
            {
                player.x -= sin(player.rotationX) * speed;
                player.z += cos(player.rotationX) * speed;
            }
            if (keyboard.isKeyPressed(Keyboard::S))
            {
                player.x += sin(player.rotationX) * speed;
                player.z -= cos(player.rotationX) * speed;
            }
            if (keyboard.isKeyPressed(Keyboard::A))
            {
                player.x -= cos(player.rotationX) * speed;
                player.z -= sin(player.rotationX) * speed;
            }
            if (keyboard.isKeyPressed(Keyboard::D))
            {
                player.x += cos(player.rotationX) * speed;
                player.z += sin(player.rotationX) * speed;
            }
            if (keyboard.isKeyPressed(Keyboard::Space))
                player.y += speed;
            if (keyboard.isKeyPressed(Keyboard::LShift))
                player.y -= speed;
        }

        test.draw(window, player);
        window.display();
    }

    return 0;
}

Chodzi mi o metode Scene::draw, dałem kilka komentarzy co tam się w środku dzieje. Narysowałem sobie to w zeszycie i powinno działać, ale nie działa jak powinno, zauważyłem też że dla prawej górnej części ekranu (od środka) wszystko jest w porządku, ale w całej reszcie dzieją się dziwne rzeczy. Byłby ktoś taki dobry i zobaczył w którym miejscu się pomyliłem (między 173 a 240 linijką)? Może gdzieś nie przewidziałem dzielenia przez 0 albo przepełnienie zachodzi, ale nie mogę tego znaleźć a długo szukałem. Jeszcze załączę rysunek jak te koordynaty działają u mnie

Użyłem metody takiej że dla każdego piksela oddzielnie program liczy jak daleko jest jaki kolor i rysuje tylko najbliższy (to się chyba bufor Z nazywało).

komentarz 21 lipca 2022 przez adrian17 Ekspert (344,860 p.)
Uhh, jedno pytanie... na pewno chcesz z ręki implementować robotę karty graficznej, a nie wskoczyć w kontekst OpenGLa?
komentarz 21 lipca 2022 przez Daaa22 Dyskutant (8,250 p.)
Tak, bo bardziej dla zajawki to robię niż dla jakiegoś większego projektu. Jak naprawię ten błąd to się za optymalizacje wezmę i może niewiele mniej wydajne od najlepszego rozwiązania będzie
komentarz 21 lipca 2022 przez Daaa22 Dyskutant (8,250 p.)
Zresztą SFML też z openGLa korzysta i metody buforu Z

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
0 odpowiedzi 535 wizyt
pytanie zadane 5 lutego 2018 w C i C++ przez damian1111 Nowicjusz (120 p.)
0 głosów
1 odpowiedź 1,648 wizyt
pytanie zadane 5 lutego 2018 w C i C++ przez Ditrix Mądrala (5,650 p.)
+1 głos
1 odpowiedź 677 wizyt
pytanie zadane 10 marca 2018 w C i C++ przez Hiskiel Pasjonat (22,830 p.)

92,555 zapytań

141,403 odpowiedzi

319,559 komentarzy

61,940 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...