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

17.01.2013, 13:33

2D Wasser

Hallo.
Ich versuche gerade diesen Effekt für 2D Wasser zu implementieren. Ich möchte dafür SFML 2 verwenden. Es erscheint mir am effizientesten das ganze direkt per Shader mit RenderTexturen auf der GPU zu machen, anstatt mit Pixel Arrays wie im Artikel beschrieben. Momentan versuche ich die Heightmap zu erstellen. Allerdings treffe ich auf ein paar Probleme, die ich nicht lösen kann.
Aber nochmal zurück zum Code. Da man auf der GPU ja nicht aus einer Textur lesen und gleichzeitig in sie schreiben kann, verwende ich drei anstatt zwei Texturen. firstBuffer enthält die Heightmap von vor 2 Frames, secondBuffer die aus dem letzten und in finalBuffer wird geschrieben.
Hier mein code:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <SFML/Graphics.hpp>
#include <iostream>

int main()
{
    sf::RenderWindow window(sf::VideoMode(600, 600), "SFML works!");
    window.setFramerateLimit(60);

    sf::RenderTexture buffers[3];
    buffers[0].create(500, 500);
    buffers[1].create(500, 500);
    buffers[2].create(500, 500);
    sf::RenderTexture* firstBuffer = buffers;
    sf::RenderTexture* secondBuffer = &buffers[1];
    sf::RenderTexture* finalBuffer = &buffers[2];

    sf::Shader waterHeightmapShader;
    waterHeightmapShader.loadFromFile("waterHeightmapShader.glsl", sf::Shader::Fragment);

    sf::Sprite sprite;
    sprite.setPosition(50, 50);
    sprite.setTexture(finalBuffer->getTexture());

    while (window.isOpen())
    {
        waterHeightmapShader.setParameter("mousePosition", sf::Vector2f(-1, -1));

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

            if(event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::Escape)
                window.close();

            // when the spacebar is pressed, add a new ripple at the mouse position
            if(event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::Space)
            {
                sf::Vector2i mousePosition = sf::Mouse::getPosition(window);
                if(mousePosition.x > 50 && mousePosition.y > 50 && mousePosition.x < 600 - 50 && mousePosition.y < 600 - 50)
                {
                    mousePosition.x -= 50;
                    mousePosition.y -= 50;

                    sf::Vector2f tester(mousePosition);

                    tester.x /= 500.f;
                    tester.y /= 500.f;

                    tester.y = 1 - tester.y;


                    std::cout << tester.x << " " << tester.y << std::endl;

                    waterHeightmapShader.setParameter("mousePosition", tester);
                }
            }

        }

        waterHeightmapShader.setParameter("textureTwoFramesAgo", firstBuffer->getTexture());
        waterHeightmapShader.setParameter("textureOneFrameAgo", secondBuffer->getTexture());

        // create the heightmap
        finalBuffer->clear(sf::Color::Transparent);
        finalBuffer->draw(sf::Sprite(secondBuffer->getTexture()), &waterHeightmapShader);
        finalBuffer->display();


        sprite.setTexture(finalBuffer->getTexture());

        window.clear();
        window.draw(sprite);
        window.display();

        // swap the buffers around, first becomes second, second becomes third and third becomes first
        sf::RenderTexture* swapper = firstBuffer;
        firstBuffer = secondBuffer;
        secondBuffer = finalBuffer;
        finalBuffer = swapper;
    }

    return 0;
}

und der Shader (GLSL):

HLSL-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
// Scene buffer
uniform sampler2D textureTwoFramesAgo;
uniform sampler2D textureOneFrameAgo;
uniform vec2 mousePosition;

const float textureSize = 500.0;
const float pixelSize = 1.0 / textureSize;

void main()
{
    // pixels position
    vec2 position = gl_TexCoord[0].st;

    vec4 finalColor = (texture2D(textureTwoFramesAgo, vec2(position.x - pixelSize, position.y)) +
                       texture2D(textureTwoFramesAgo, vec2(position.x + pixelSize, position.y)) +
                       texture2D(textureTwoFramesAgo, vec2(position.x, position.y + pixelSize)) +
                       texture2D(textureTwoFramesAgo, vec2(position.x, position.y - pixelSize))   ) / 2.0 -
                       texture2D(textureOneFrameAgo, position);

    // damping
//    finalColor *= 0.9;

    // add new ripples
    if(mousePosition.x > 0.0)
        if(distance(position, mousePosition) < pixelSize * 5.0)
        {
            finalColor = vec4(1.0, 1.0, 1.0, 1.0);
        }

    gl_FragColor = finalColor;
}


Die Probleme die dich hab sind folgende:
1. Wenn ich einen neuen "Kreis" erzeuge, dann erscheint dieser korrekt. Im nächsten Frame ist der Kreis aber um die Mittelachse gespiegelt. Und der Frame darauf ist schwarz. Ich hab keine Ahnung wie es zu der Spieglung kommt und warum ein Frame nicht bemalt wird...
2. Der kreis breitet sich zwar kreisförmig aus, aber ensteht kein Wellenmuster. Woran liegt das?
3. damping scheint überhaupt keinen Effekt zu haben...

Ich habe versucht ein Video davon zu machen, aber weder mich einem Screen Recorder noch mit der Webcam hat es funktioniert. Ich hoffe trotzdem, dass mir jemand weiter helfen kann.
Vielen Dank,
Foaly

edit: Ich weiß, dass die Beschreibung wirklich nicht sehr gut ist, aber ich weiß nicht wie ichs anders beschreiben soll...

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Foaly« (17.01.2013, 16:53)


2

22.01.2013, 14:45

Also nachdem ich noch einige Zeit mit dem Code und dem Algorithmus beschäftigt hab, ist mir aufgefallen, das ne ganze Menge nicht an diesem Code stimmt... Tut mir Leid wegen der vorschnellen Frage. (Hab ja auch verständlicherweise keine Antwort gekriegt :D)
Deshalb melde ich mich jetzt mal mit einer spezifischeren Frage zurück. Ich hab festgestellt, das eines meiner Problem ist, das der area-sampling Algorithmus, welcher für die wellen Bewegung ("hoch und runter") und das kreisförmige ausbreiten zuständig ist, nur dann funktioniert, wenn die Daten in den beiden Buffern im Bereich −32.768 und 32.767 sind. Hier der original Code:

C-/C++-Quelltext

1
2
3
4
5
CurrentFrame[x, y] = (( LastFrame[x+1, y] +
                        LastFrame[x-1, y] +
                        LastFrame[x, y+1] +
                        LastFrame[x, y-1] ) / 2 ) -
                        PreviousToLastFrame[x, y]
Da ich aber auf der GPU arbeiten möchte und statt int arrays sf::RenderTextures (im Grunde FBO glaub ich) verwende, sind meine Variablen im Bereich 0 bis 1. Der Algorithmus funktioniert also nicht korrekt. Meine Überlegung war nun von dem Wert den man aus den Texturen ausliest 0.5 zu subtrahieren und nach der Berechnung wieder 0.5 zu addieren. So würde man wieder auf die negativen Zahlen kommen die für den Algorithmus entscheidend seid. Das ganze sähe dann so aus:

HLSL-Quelltext

1
2
3
4
5
6
7
8
9
vec4 finalColor = (texture2D(textureOneFrameAgo, vec2(position.x - pixelSize, position.y)) - 0.5 +
                   texture2D(textureOneFrameAgo, vec2(position.x + pixelSize, position.y)) - 0.5 +
                   texture2D(textureOneFrameAgo, vec2(position.x, position.y + pixelSize)) - 0.5 +
                   texture2D(textureOneFrameAgo, vec2(position.x, position.y - pixelSize)) - 0.5  ) / 2.0 -
                   texture2D(textureTwoFramesAgo, position) - 0.5;

finalColor.rgb += 0.5;

gl_FragColor = finalColor;
Leider funktioniert das nicht... Meine Frage wäre nun also hat jemand eine Idee, wie ich den Algorithmus dazu kriegen anstatt int (−32.768 bis 32.767) mit float (0 bis 1) zu funktionieren? Hier gibts noch genaueres zum Algorithmus: http://www.gamedev.net/page/resources/_/…-explained-r915 und hier http://freespace.virgin.net/hugo.elias/graphics/x_water.htm
Ich wäre für jede Hilfe sehr dankbar!
Danke schon mal im voraus.
Foaly

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

3

22.01.2013, 16:35

x - y - z != x - (y - z)
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]

4

22.01.2013, 16:57

Also erstmal danke das du dir die Zeit genommen hast dich mit meinem Problem zu beschäftigen. Aber ich finde es ist immer sehr schwierig, wenn man eine Formel zusammenhanglos und ohne weitere Erklärung hin geknallt bekommt. Mir ist klar das das Assoziativgesetz bei der Subtraktion nicht gilt. Aber ich sehe nicht ganz den Zusammenhang mit meinem Algorithmus. Könntest du das noch etwas genauer erklären?
Vielen Dank Foaly

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

5

22.01.2013, 17:15

Zeile 4 und 5 in Deinem letzten Code.
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]

6

22.01.2013, 17:34

Ich steh ein bisschen auf dem Schlauch. Wenn man sich lange mit einer Sache mit einer Sache beschäftigt, dann übersieht man ja ab und zu mal das offensichtlichste. Vielleicht ja liegst daran.
Könntest du etwas ausführlicher erklären was du meinst? Sind die Divisionen in der falschen Reihenfolge?

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

7

22.01.2013, 17:53

HLSL-Quelltext

1
2
3
4
5
vec4 finalColor = (texture2D(textureOneFrameAgo, vec2(position.x - pixelSize, position.y)) - 0.5 +
                   texture2D(textureOneFrameAgo, vec2(position.x + pixelSize, position.y)) - 0.5 +
                   texture2D(textureOneFrameAgo, vec2(position.x, position.y + pixelSize)) - 0.5 +
                   texture2D(textureOneFrameAgo, vec2(position.x, position.y - pixelSize)) - 0.5  ) / 2.0
                   - texture2D(textureTwoFramesAgo, position) - 0.5;


vs

HLSL-Quelltext

1
2
3
4
5
vec4 finalColor = (texture2D(textureOneFrameAgo, vec2(position.x - pixelSize, position.y)) - 0.5 +
                   texture2D(textureOneFrameAgo, vec2(position.x + pixelSize, position.y)) - 0.5 +
                   texture2D(textureOneFrameAgo, vec2(position.x, position.y + pixelSize)) - 0.5 +
                   texture2D(textureOneFrameAgo, vec2(position.x, position.y - pixelSize)) - 0.5  ) / 2.0
                   - (texture2D(textureTwoFramesAgo, position) - 0.5);
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

22.01.2013, 18:37

Natürlich! :dash: ich bin in der Zwischenzeit schon selbst drauf gekommen, was du meintest… war ein klassischer fall von man sieht den Wald vor lauter Bäumen nicht… ich bin leider gerade nicht Zuhause und kann's deshalb nicht sofort ausprobieren. Aber ich bin sicher daran wird's gelegen haben. Vielen dank für die Hilfe!

9

26.01.2013, 18:32

Hallo!
Ich hab weiter an meinem Wasserprogramm gearbeitet und bin auch ganz gut weiter gekommen, aber ich bin auf ein merkwürdiges Problem gestoßen, dass ich nicht versteh und zu dem mir keine Lösung einfällt.
Also mittlerweile funktioniert alles ganz gut. Ich kann neue Wellen hinzufügen und diese breiten sich auch kreisförmig aus. Allerdings sind die Wellen nur sehr klein und verschwinden sehr schnell wieder und das obwohl ich kein damping multipliziere. Es ist eher das Gegenteil der Fall, wenn ich das ganze "verstärke" wird erst sichtbar, das die Wellen vorhanden sind. Dies sollte aber nicht so sein. Wenn ich den Artikel richtig verstehe, verschwinden die Wellen nicht, wenn man kein damping hinzufügt. Kann mir jemand zeigen, was ich falsch mache?
Hier mein Code:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <SFML/Graphics.hpp>
#include <iostream>

int main()
{
    sf::RenderWindow window(sf::VideoMode(1000, 1000), "SFML works!");
    window.setFramerateLimit(12);

    sf::RenderTexture buffers[3];
    buffers[0].create(500, 500);
    buffers[1].create(500, 500);
    buffers[2].create(500, 500);
    sf::RenderTexture* firstBuffer = buffers;
    sf::RenderTexture* secondBuffer = &buffers[1];
    sf::RenderTexture* finalBuffer = &buffers[2];

    firstBuffer->clear(sf::Color(128, 128, 128));
    secondBuffer->clear(sf::Color(128, 128, 128));
    finalBuffer->clear(sf::Color(128, 128, 128));

    sf::Shader waterHeightmapShader;
    waterHeightmapShader.loadFromFile("waterHeightmapShader.glsl", sf::Shader::Fragment);

    sf::Sprite spritefirst;
    spritefirst.setPosition(0, 0);
    spritefirst.setTexture(firstBuffer->getTexture());

    sf::Sprite spritesecond;
    spritesecond.setPosition(500, 0);
    spritesecond.setTexture(secondBuffer->getTexture());

    sf::Sprite spritefinal;
    spritefinal.setPosition(0, 500);
    spritefinal.setTexture(finalBuffer->getTexture());

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

            if(event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::Escape)
                window.close();
        }


        waterHeightmapShader.setParameter("mousePosition", sf::Vector2f(-1.f, -1.f));
        // if mouse button is pressed add new ripples
        if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
        {
            sf::Vector2i mousePosition = sf::Mouse::getPosition(window);
            if(mousePosition.x < 500 && mousePosition.y < 500)
            {
                sf::Vector2f mouse(mousePosition);

                mouse.x /= 500.f;
                mouse.y /= 500.f;

                mouse.y = 1 - mouse.y;


                std::cout << mouse.x << " " << mouse.y << std::endl;

                waterHeightmapShader.setParameter("mousePosition", mouse);
            }
        }


        waterHeightmapShader.setParameter("textureTwoFramesAgo", firstBuffer->getTexture());
        waterHeightmapShader.setParameter("textureOneFrameAgo", secondBuffer->getTexture());

        // create the heightmap
        secondBuffer->display();
        finalBuffer->clear(sf::Color(128, 128, 128));
        finalBuffer->draw(sf::Sprite(secondBuffer->getTexture()), &waterHeightmapShader);
        finalBuffer->display();


        spritefirst.setTexture(firstBuffer->getTexture());
        spritesecond.setTexture(secondBuffer->getTexture());
        spritefinal.setTexture(finalBuffer->getTexture());

        window.clear();
        window.draw(spritefirst);
        window.draw(spritesecond);
        window.draw(spritefinal);
        window.display();

        // swap the buffers around, first becomes second, second becomes third and third becomes first
        sf::RenderTexture* swapper = firstBuffer;
        firstBuffer = secondBuffer;
        secondBuffer = finalBuffer;
        finalBuffer = swapper;
    }

    return 0;
}

Shader:

HLSL-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
uniform sampler2D textureTwoFramesAgo;
uniform sampler2D textureOneFrameAgo;
uniform vec2 mousePosition;

const float textureSize = 500.0;
const float pixelSize = 1.0 / textureSize;

void main()
{
    // pixels position
    vec2 position = gl_TexCoord[0].st;

    vec4 finalColor = ((texture2D(textureOneFrameAgo, vec2(position.x - pixelSize, position.y)) +
                        texture2D(textureOneFrameAgo, vec2(position.x + pixelSize, position.y)) +
                        texture2D(textureOneFrameAgo, vec2(position.x, position.y + pixelSize)) +
                        texture2D(textureOneFrameAgo, vec2(position.x, position.y - pixelSize)) - 2.0) / 2) -
                       (texture2D(textureTwoFramesAgo, position) - 0.5);

    // damping
//    finalColor.rgb *=0.9 // damping 

//    finalColor.rgb *= 1.9;  // <--- Verstärkung (wenn man das hier unkommentiert sieht man die Wellen besser)
    finalColor.rgb += 0.5;

    // add new ripples
    if(mousePosition.x > 0.0)
    {
        if(distance(position, mousePosition) < pixelSize * 5.0)
        {
            finalColor = vec4(0.9, 0.9, 0.9, 1.0);
        }
    }

    gl_FragColor = finalColor;

}


Ich hoffe jemand kann mir helfen! Ich wäre sehr dankbar :)
Vielen Dank,
Foaly

10

06.02.2013, 20:59

Ich hatte die Gelegenheit ein wenig mit einer CPU implementierung herum zuspielen. Dabei hat sich heraus gestellt, das ich recht hatte und das sich die Wellen endlos ausbreiten, wenn man kein damping hinzufügt. Das bedeutet, dass wie ich vermutet hab mit meinem code etwas nicht stimmt...
Leider hab ich in der zwischenzeit immer noch keine lösung gefunden. Ich bin mittlerweile etwas verzweifelt, da ich nicht weiß, was ich noch probieren soll. Falls irgendwer irgendeine idee oder irgendeinen hinweis hat, der mir weiterhelfen könnte wäre ich sehr dankbar!
Foaly

Werbeanzeige

Ähnliche Themen