Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

09.10.2013, 19:55

Raytracing mit SFML/C++

Hallo Leute,
da wir in der Schule in Mathe gerade Vektorgeometrie machen und unser Lehrer da kurz Raytracing angesprochen hat, bin ich auf die Idee gekommen das mal selber auszuprobieren.
Also hab ich mich an den PC gesetzt, Visual Studio geöffnet und angefangen zu coden :D
Nach gut ein bis zwei Stunden komm ich nun so weit:

(Link)

(--> Hier ist die Kamera auf [0,0,0])

(Link)

(--> Hier ist die Kamera so verschoben, dass man alles gut erkennt)
So wie ihr seht hab ich zwei Probleme:
1. Warum auch immer funktionieren die Strahlen nur im Rechteck rechtsoben, also wenn sowohl der x- als auch der y-Wert des Richtungsvektors positiv ist.

C-/C++-Quelltext

1
sf::Vector3f curDirection = normalize(sf::Vector3f(static_cast<float>(x-window.getSize().x/2), static_cast<float>(window.getSize().y/2-y), 400.0f));

So berechne ich die Richtung für den aktuellen Strahl (Fenster ist 800x600 groß).
Und das hier sind meine "Vektorfunktionen":

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
inline float dot(const sf::Vector3f& a, const sf::Vector3f& b)
{
    return a.x*b.x + a.y*b.y + a.z*b.z;
}

inline float length(const sf::Vector3f& vec)
{
    return sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);
}

inline const sf::Vector3f& normalize(const sf::Vector3f& vec)
{
    return vec/length(vec);
}

2. Problem:
Das Rauschen im "Selbstschatten", also an den lichtabgewandten Seiten (mein Licht ist zurzeit einfach nur ein 3D-Vektor, also nur eine Position im Raum). Komischerweise wird der Schatten der von anderen Objekten (auf der grünen Kugel zu sehen) geworfen wird, völlig korrekt gezeichnet/berechnet.
So und um ganz sicher zu gehen hier nochmal meine 2 wichtigen Hauptfunktionen.
Die Renderfunktion des Renderers, um das Bild zu zeichnen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
void Renderer::render()
{
    sf::Image image;
    image.create(800, 600);
    for(int y = 0; y < window.getSize().y; ++y)
    {
        for(int x = 0; x < window.getSize().x; ++x)
        {
            sf::Vector3f curDirection = normalize(sf::Vector3f(static_cast<float>(x-window.getSize().x/2), static_cast<float>(window.getSize().y/2-y), 400.0f));
            float nearestDistance = 100.0f;
            RenderObject* nearestObject = NULL;
            sf::Vector3f nearestPoint = position;
            for(std::list<RenderObject*>::iterator itRenderObjects(renderObjects.begin()), itEnd(renderObjects.end());
                itRenderObjects!=itEnd;
                ++itRenderObjects)
            {
                sf::Vector3f curPoint = (*itRenderObjects)->testRay(position, curDirection);
                float curDistance = length(curPoint - position);
                if(curDistance > 0.0f)
                {
                    if(curDistance < nearestDistance)
                    {
                        nearestDistance = curDistance;
                        nearestObject = *itRenderObjects;
                        nearestPoint = curPoint;
                    }
                }
            }
            sf::Color pixelColor = backgroundColor;
            if(nearestObject != NULL)
            {
                pixelColor = nearestObject->getColor(nearestPoint);
                //
                sf::Vector3f directionToLight = normalize(lightPos-nearestPoint);
                for(std::list<RenderObject*>::iterator itRenderObjects(renderObjects.begin()), itEnd(renderObjects.end());
                    itRenderObjects!=itEnd;
                    ++itRenderObjects)
                {
                    sf::Vector3f curPoint = (*itRenderObjects)->testRay(nearestPoint, directionToLight);
                    float curDistance = length(curPoint - nearestPoint);
                    if(curDistance > 0.0f)
                    {
                        float factor =0.5f;
                        pixelColor = sf::Color(pixelColor.r * factor, pixelColor.g * factor, pixelColor.b * factor);
                        break;
                    }
                }

            }
            image.setPixel(x, y, pixelColor);
        }
    }
    renderResult.loadFromImage(image);
    window.draw(sf::Sprite(renderResult));
}

Und die Strahlentestfunktion der Kugel:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const sf::Vector3f& Sphere::testRay(const sf::Vector3f& origin, const sf::Vector3f& direction)
{
    float factor = -dot((origin-transform.position), direction);
    sf::Vector3f x = origin + factor * direction;
    float xLength = length(x-transform.position);
    float distance = sqrt((transform.scale.x/2 * transform.scale.x/2) - (xLength * xLength));
    if(factor > distance)
    {
        return origin + (factor - distance) * direction;
    }
    else if(factor + distance > 0)
    {
        return origin + (factor + distance) * direction;
    }
    else
    {
        return origin;
    }
}



Freue mich auf Lösungen, Kritik und Verbesserungsvorschläge :)
Wenn was fehlt einfach Bescheid sagen ;)

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

11.10.2013, 00:36

Das mit dem Selbstschatten löst man, indem man den Schatten-Test-Strahl ein kleines bisschen von der Oberfläche wegschiebt (entlang des Normalenvektors an der getroffenen Stelle). Sonst kann es nämlich passieren, dass er sofort wieder das eigene Objekt schneidet. Das ist mehr oder weniger zufällig wegen der begrenzten Genauigkeit, darum das Rauschen.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

3

11.10.2013, 06:37

Also mit wegschieben hatte ich das nie gelöst. Jede Stelle schaut halt, welche Lichtquellen sie sieht. Schon anhand der Normalen lässt sich aber sagen, welche Lichtquellen keinesfalls gesehen werden können, dafür muss man nix verschieben.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

4

11.10.2013, 08:32

Ich mach auch immer wegschieben, ist bei Raymarching aber ohnehin naheliegender. Eine andere Moeglichkeit ist sicher ein numerisch stabiles Interpolationsverfahren zu benutzen um dann einen Strahl zu bestimmen, der auch nach Rundungsfehlern ausserhalb des Objektes liegt.

BlueCobolds Ansatz ist auch, so wir ich ihn verstehe, keine allgemeine Loesung. Es sei denn man zerlegt alles in konvexe Teilobjekte. Koennte also spaeter Probleme machen.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

5

11.10.2013, 08:36

Stimmt natürlich, das gilt auf den ersten Blick nur für konvexe Objekte. Für konkave (oder eben für Punkte, deren Normale nicht von der Lichtquelle "weg" zeigt) erledige ich das so, dass eine Verdeckung nur dann stattfinden kann, wenn ein ermittelter Schnittpunkt zwischen einem Objekt und der Lichtquelle eine Normale besitzt, die von der Lichtquelle "weg" zeigt. Damit das funktioniert, müssen alle Objekte solide sind und somit zu jeder Seite, die von der Lichtquelle "weg" zeigt auch eine Seite existieren, die zur Lichtquelle "hin" zeigt. Der Step fällt dann komplett weg und es funktioniert trotzdem mit self-shadowing. Das Stepping ist vermutlich aber wohl einfacher, wenn auch je nach Größe des Steps nicht ganz korrekt.

PS: Vielleicht sollte ich dazu noch ein Bild malen? ;)
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »BlueCobold« (11.10.2013, 08:44)


David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

6

11.10.2013, 09:18

BlueCobold: Ich denke, dein Ansatz funktioniert unabhängig davon, ob das Objekt konvex oder konkav ist. Wenn ich das richtig verstanden habe, ignoriert man einfach Schnitte, wo der Normalenvektor in Richtung des Strahls zeigt. Das führt dann halt dazu, dass man ggf. den ersten gefundenen Schnitt ignoriert. Das ist eventuell minimal langsamer als den Strahl ein bisschen zu verschieben, um diesen Schnitt gar nicht erst zu finden. Oder meintest du etwas anderes? So geht's jedenfalls auch ;)

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

7

11.10.2013, 09:22

Jo, das meinte ich. Und jo, das funktioniert auch mit konkaven. Der Vektor-Check allein aus Antwort #3 allein geht aber nicht bei konkaven Objekten.
Bei Raytracing im Gegensatz zu Raycasting findet man den Ausgangs-Schnittpunkt so oder so, auch wenn man's verschiebt. Nur eliminiert man den hinterher durch die Verschiebung, weil er nicht auf "der richtigen Seite" liegt. Ich eliminiere den eben einfach nur anders anhand der Normalen.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

8

14.10.2013, 18:17

Vielen Dank erstmal! :)
Leider hatte ich in der letzten Woche viel zu tun, aber ich hab mich heute mal wieder ein bisschen hingesetzt, und tada:

(Link)


Sieht schonmal super aus ;) (Ja ich weiß Eigenlob stinkt und so xD)

Aber so sieht das leider nur aus, wenn die Kamera nach links unten verschoben wurde :(
Wenn ich sie am Nullvektor lasse, dann kommt immer noch das hier raus:

(Link)


Bisschen doof, weil ich ja so nur ein Viertel der Fläche nutzen kann. Aber ich weiß einfach nicht woran das liegen könnte.

Der dafür wichtige Code ist immer noch der gleiche wie zu Threadstart.

Ich hoffe ihr könnt mir dabei helfen :)

EDIT:
Habs geschafft xD
Einfach zu

C-/C++-Quelltext

1
sf::Vector3f curDirection = normalize(sf::Vector3f(x-window.getSize().x/2.0f, window.getSize().y/2.0f-y, 400.0f));

umgeschrieben :D
Trotzdem danke euch allen ;)

(Link)

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »poorsider« (14.10.2013, 20:00)


Werbeanzeige