OpenGL Scale Single Pixel Line

Lesezeit: 5 Minuten

OpenGL Scale Single Pixel Line
Leuchte

Ich möchte ein Spiel erstellen, das intern 320 x 240 Pixel groß ist, aber auf dem Bildschirm mit ganzzahligen Vielfachen davon dargestellt wird (640 x 480, 960.720 usw.). Ich gehe für Retro-2D-Pixelgrafiken.

Ich habe dies erreicht, indem ich die interne Auflösung über glOrtho() eingestellt habe:

glOrtho(0, 320, 240, 0, 0, 1);

Und dann skaliere ich die Ausgabeauflösung um den Faktor 3, wie folgt:

glViewport(0,0,960,720);
window = SDL_CreateWindow("Title", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 960, 720, SDL_WINDOW_OPENGL);

Ich zeichne Rechtecke wie folgt:

glBegin(GL_LINE_LOOP);
glVertex2f(rect_x, rect_y);
glVertex2f(rect_x + rect_w, rect_y);
glVertex2f(rect_x + dst_w, dst_y + dst_h);
glVertex2f(rect_x, rect_y + rect_h);
glEnd();

Es funktioniert perfekt bei 320×240 (nicht skaliert):

Geben Sie hier die Bildbeschreibung ein

Wenn ich auf 960 x 720 hochskaliere, funktioniert das Pixel-Rendering einwandfrei! Es scheint jedoch, dass der GL_Line_Loop nicht auf einer 320×240-Leinwand gezeichnet und vergrößert wird, sondern auf der endgültigen 960×720-Leinwand gezeichnet wird. Das Ergebnis sind 1px-Linien in einer 3px-Welt 🙁

Geben Sie hier die Bildbeschreibung ein

Wie zeichne ich Linien auf die glOrtho-Leinwand mit 320 x 240 statt auf die Ausgabe-Leinwand mit 960 x 720?

  • Wenn FBOs nicht Ihre Szene sind, gibt es immer breite DIY-Linien mit Dreiecken.

    – Genpfault

    1. Januar 2017 um 3:57 Uhr

Es gibt keine “320×240 glOrtho-Leinwand”. Es gibt nur die tatsächliche Auflösung des Fensters: 960×720.

Sie skalieren lediglich die Koordinaten der Primitive, die Sie rendern. Ihr Code sagt also, dass Sie eine Zeile von beispielsweise (20, 20) bis (40, 40) rendern sollen. Und OpenGL skaliert (schließlich) diese Koordinaten in jeder Dimension um 3: (60, 60) und (120×120).

Aber das ist nur der Umgang mit der Endpunkte. Was in der Mitte passiert, basiert immer noch auf der Tatsache, dass Sie mit der tatsächlichen Auflösung des Fensters rendern.

Auch wenn Sie angestellt sind glLineWidth um die Breite Ihrer Linien zu ändern, würde das nur die Linienbreiten fixieren. Es würde nicht die Tatsache beheben, dass die Rasterung von Linien auf der tatsächlichen Auflösung basiert, mit der Sie rendern. Diagonale Linien haben also nicht das pixelige Aussehen, das Sie wahrscheinlich möchten.

Der einzige Weg, dies richtig zu machen, ist, es richtig zu machen. Rendern Sie zu einem Bild, das tatsächlich 320 x 240 groß ist, und zeichnen Sie es dann mit der tatsächlichen Auflösung des Fensters.

Sie müssen eine Textur dieser Größe erstellen und sie dann an a anhängen Framebuffer-Objekt. Binden Sie das FBO zum Rendern und rendern Sie es (wobei das Ansichtsfenster auf die Größe des Bildes eingestellt ist). Lösen Sie dann das FBO und zeichnen Sie diese Textur in das Fenster (wobei das Ansichtsfenster auf die Auflösung des Fensters eingestellt ist).

  • Ja, ich stimme zu, muss aber hinzufügen, dass Intel-Grafikkarten nicht wie erwartet oder überhaupt nicht funktionieren werden, wenn Sie in Textur rendern möchten, bis sie die Treiber reparieren, was für ältere Grafikkarten niemals auf Erfahrung basiert … in solchen Fall könnten Sie versuchen glReadPixels stattdessen …

    – Spektre

    26. April 2017 um 8:27 Uhr


  • @Spektre: Wenn Sie eine Antwort hinzufügen möchten, in der bestimmte Treiberfehler erörtert werden, können Sie dies gerne tun. Ich weiß nicht, von welchen Fehlern genau Sie sprechen oder auf welche Hardware Sie sich beziehen, daher bin ich nicht qualifiziert, darüber zu diskutieren.

    – Nicol Bolas

    26. April 2017 um 13:47 Uhr

  • Ich habe das langsame hinzugefügt OpenGL 1.0 Ansatz, der sogar auf Intel funktioniert.

    – Spektre

    27. April 2017 um 9:55 Uhr

1646239811 656 OpenGL Scale Single Pixel Line
Gespenst

Wie ich in meinem Kommentar erwähnt habe Intel-OpenGL Treiber hat Probleme mit dem direkten Rendern in Textur und ich kenne keine Problemumgehung, die funktioniert. In einem solchen Fall hilft nur die Nutzung glReadPixels Bildschirminhalte hineinkopieren Zentralprozessor Speicher und kopieren Sie es dann zurück Grafikkarte als Textur. Das ist natürlich viel langsamer als das direkte Rendern in Textur. Also hier ist der Deal:

  1. Ansicht mit niedriger Auflösung einstellen

    Ändern Sie nicht die Auflösung Ihres Fensters, nur die glViewport Werte. Rendern Sie dann Ihre Szene in niedriger Auflösung (auf nur einem Bruchteil der Bildschirmfläche).

  2. Kopieren Sie den gerenderten Bildschirm in die Textur

  3. Zielauflösungsansicht einstellen
  4. Rendern Sie die Textur

    verwenden nicht vergessen GL_NEAREST Filter. Das Wichtigste ist, dass Sie die Puffer erst danach tauschen, nicht vorher !!! sonst hättest du flackern.

Hier C++ Quelle dafür:

void gl_draw()
    {
    // render resolution and multiplier
    const int xs=320,ys=200,m=2;

    // [low res render pass]
    glViewport(0,0,xs,ys);
    glClearColor(0.0,0.0,0.0,1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);
    // 50 random lines
    RandSeed=0x12345678;
    glColor3f(1.0,1.0,1.0);
    glBegin(GL_LINES);
    for (int i=0;i<100;i++)
     glVertex2f(2.0*Random()-1.0,2.0*Random()-1.0);
    glEnd();

    // [multiply resiolution render pass]
    static bool _init=true;
    GLuint  txrid=0;        // texture id
    BYTE map[xs*ys*3];      // RGB
    // init texture
    if (_init)              // you should also delte the texture on exit of app ...
        {
        // create texture
        _init=false;
        glGenTextures(1,&txrid);
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D,txrid);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);   // must be nearest !!!
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_COPY);
        glDisable(GL_TEXTURE_2D);
        }
    // copy low res screen to CPU memory
    glReadPixels(0,0,xs,ys,GL_RGB,GL_UNSIGNED_BYTE,map);
    // and then to GPU texture
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D,txrid);         
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, xs, ys, 0, GL_RGB, GL_UNSIGNED_BYTE, map);
    // set multiplied resolution view
    glViewport(0,0,m*xs,m*ys);
    glClear(GL_COLOR_BUFFER_BIT);
    // render low res screen as texture
    glBegin(GL_QUADS);
    glTexCoord2f(0.0,0.0); glVertex2f(-1.0,-1.0);
    glTexCoord2f(0.0,1.0); glVertex2f(-1.0,+1.0);
    glTexCoord2f(1.0,1.0); glVertex2f(+1.0,+1.0);
    glTexCoord2f(1.0,0.0); glVertex2f(+1.0,-1.0);
    glEnd();
    glDisable(GL_TEXTURE_2D);

    glFlush();
    SwapBuffers(hdc);   // swap buffers only here !!!
    }

Und Vorschau:

Vorschau

Ich habe das bei einigen getestet Intel-HD Grafiken (Gott weiß welche Version) habe ich zur Verfügung gestellt und es funktioniert (während Standard-Render-to-Texture-Ansätze es nicht sind).

914150cookie-checkOpenGL Scale Single Pixel Line

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy