Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Janusz Ganczarski
Biblioteki C++

Sprzężenie zwrotne

[lekcja] Rozdział 22. Zmiana trybu renderowania, bufor sprzężenia zwrotnego i jego przetwarzanie; program przykładowy.
Sprzężenie zwrotne (ang. feedback) jest jednym z trzech trybów renderingu dostępnych w bibliotece OpenGL. W trybie tym, podobnie jak w trybie selekcji, piksele nie są kopiowane do bufora ramki obrazu. W zamian za to w buforze sprzężenia zwrotnego tworzone są dane o współrzędnych wierzchołków prymitywów, składowych kolorów oraz współrzędnych tekstur.
Swoją budową bufor sprzężenia zwrotnego najbardziej przypomina bufor selekcji, z tą jednak różnicą, że zamiast tablicy liczb całkowitych mamy do czynienia z tablicą liczb zmiennoprzecinkowych typu GLfloat.
Sprzężenie zwrotne jest często wykorzystywane łącznie z opisaną wcześniej techniką selekcji obiektów do tworzenia grafiki interaktywnej.

Zmiana trybu renderowania

Aby korzystać ze sprzężenia zwrotnego w bibliotece OpenGL trzeba zmienić tryb renderingu, wywołując opisywaną już funkcję glRenderMode z parametrem GL_FEEDBACK. W trybie sprzężenia zwrotnego funkcja zwraca ilość wartości zapisanych w buforze sprzężenia zwrotnego. Jeżeli ilość przekazywanych informacji do bufora sprzężenia zwrotnego jest większa niż wielkość tego bufora, funkcja glRenderMode zwróci wartość -1.

Bufor sprzężenia zwrotnego

Utworzenie bufora sprzężenia zwrotnego wymaga wywołania funkcji:
C/C++
void glFeedbackBuffer( GLsizei size, GLenum type, GLfloat * buffer )
której parametr size określa wielkość tablicy wskazywanej w parametrze buffer, a rodzaj danych przekazywanych do bufora zwrotnego określa parametr type. Tabela 1 zawiera zestawienie rodzaju informacji zwracanych w buforze sprzężenia zwrotnego w zależności od wybranego parametru type w przypadku, gdy OpenGL pracuje w trybie RGB(A). Jeżeli biblioteka pracuje w trybie indeksowym w buforze sprzężenia zwrotnego umieszczana jest zamiast składowych (R, G, B, A) koloru jedna liczba - numer indeksu koloru. Współrzędne x, y i z położenia wierzchołków podawane są w odniesieniu do współrzędnych okna renderingu. W przypadku użycia techniki wieloteksturowania bufor sprzężenia zwrotnego zwraca wyłącznie informacje o pierwszej teksturze (GL_TEXTURE0).

Tabela 1: Rodzaje informacji zwracanych w buforze sprzężenia zwrotnego

typewspółrzedne wierzchołkaskładowe koloruwspółrzędne tekstury
GL_2D(x, y)--
GL_3D(x, y, z)--
GL_3D_COLOR(x, y, z)(R, G, B, A)-
GL_3D_COLOR_TEXTURE(x, y, z)(R, G, B, A)(s, t, r, q)
GL_4D_COLOR_TEXTURE(x, y, z, w)(R, G, B, A)(s, t, r, q)

Przetwarzanie bufora sprzężenia zwrotnego

Dane umieszczone w buforze sprzężenia zwrotnego oddzielone są specjalnymi znacznikami. Znaczniki identyfikowane są przez następujące stałe:
  • GL_POINT_TOKEN - punkt,
  • GL_LINE_TOKEN - linia,
  • GL_LINE_RESET_TOKEN - linia po wyzerowaniu wzorca linii,
  • GL_POLYGON_TOKEN - wielokąt,
  • GL_BITMAP_TOKEN - mapa bitowa,
  • GL_DRAW_PIXEL_TOKEN - mapa pikselowa,
  • GL_COPY_PIXEL_TOKEN - kopiowanie mapy pikselowej,
  • GL_PASS_THROUGH_TOKEN - znacznik zdefiniowany przez użytkownika.
Po znaczniku w buforze umieszczone są dane o wierzchołkach, których ilość określa parametr type funkcji glFeedbackBuffer. W przypadku znaczników: GL_POINT_TOKEN, GL_BITMAP_TOKEN, GL_DRAW_PIXEL_TOKEN i GL_COPY_PIXEL_TOKEN będą to dane pojedynczego wierzchołka (lub współrzędnych położenia rastra). Analogicznie po znacznikach GL_LINE_TOKEN i GL_LINE_RESET_TOKEN w buforze znajdują się informacje o dwóch wierzchołkach. Jedynie po znaczniku GL_POLYGON_TOKEN znajduje się liczba określająca ilość wierzchołków wielokąta, a po niej dane kolejnych wierzchołków.
Wystąpienie w buforze sprzężenia zwrotnego znacznika zdefiniowanego przez użytkownika wymaga wywołania funkcji:
C/C++
void glPassThrough( GLfloat token )
Wartość parametru token jest umieszczana w buforze zaraz po samym znaczniku GL_PASS_THROUGH_TOKEN.

Program przykładowy

Program przykładowy (plik sprzezenie_zwrotne.cpp) prezentuje tworzenie bufora sprzężenia zwrotnego i pobierania z niego informacji. Trudno nazwać go specjalnie odkrywczym, ale Czytelnik powinien zwrócić szczególną uwagę na to jakie dane są przekazywane do bufora sprzężenia zwrotnego i porównać je z tym co jest rysowane na ekranie. Przykładowe efekty działania programu przedstawiają rysunki 1, 2 i 3.
Warto jeszcze zwrócić uwagę, że program działając w trybie sprzężenia zwrotnego wyświetla wyłącznie testowe prymitywy graficzne. Sprawdzenie bieżącego trybu renderowania umożliwia zmienna stanu GL_RENDER_MODE.
Rysunek 1. Program Sprzężenie zwrotne - linie
Rysunek 1. Program Sprzężenie zwrotne - linie
Rysunek 2. Program Sprzężenie zwrotne - wielokąt
Rysunek 2. Program Sprzężenie zwrotne - wielokąt

Plik sprzezenie_zwrotne.cpp

Rysunek 3. Program Sprzężenie zwrotne - punkty
Rysunek 3. Program Sprzężenie zwrotne - punkty
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <string>
#include "colors.h"

// stałe do obsługi menu podręcznego

enum
{
    POINTS, // rodzaj testu - punkty
    LINES, // rodzaj testu - linie
    POLYGON, // rodzaj testu - wielokąt
    BITMAPS, // rodzaj testu - mapy bitowe
    PIXMAPS, // rodzaj testu - mapy pikselowe
    PASS_THROUGH, // rodzaj testu - znacznik użytkownika
    EXIT // wyjście
};

// tablica ciągów znaków z opisem zawartości bufora sprzężenia zwrotnego

std::vector < std::string > strings;

// rodzaj wykonywanego testu

int test = POINTS;

// rozmiar bufora sprzężenia zwrotnego

const GLint MAX_FEEDBACK_BUFFER = 128;

// tablica na bufor sprzężenia zwrotnego

GLfloat feedback_buf[ MAX_FEEDBACK_BUFFER ];

// wypisanie informacji zawartej w tablicy strings

void DrawStrings()
{
    // kolor napisu
    glColor3fv( Black );
   
    // wypisanie kolejnych ciągów znaków
    for( int str = 0; str < strings.size(); str++ )
    {
        // ustalenie pozycji rastra
        glRasterPos2i( 4, 4 + str * 16 );
       
        // wyświetlenie napisu
        int len = strlen( strings[ str ].c_str() );
        for( int i = 0; i < len; i++ )
             glutBitmapCharacter( GLUT_BITMAP_9_BY_15, strings[ str ][ i ] );
       
    }
}

// funkcja generująca scenę 3D

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // wykonanie wybranego testu
    switch( test )
    {
        // znacznik użytkownika
    case PASS_THROUGH:
        glPassThrough( 10.0 );
       
        // punkty
    case POINTS:
        glPointSize( 40 );
        glBegin( GL_POINTS );
        glColor3fv( Red );
        glVertex2i( 100, 400 );
        glColor3fv( Green );
        glVertex2i( 250, 400 );
        glColor3fv( Blue );
        glVertex2i( 400, 400 );
        glEnd();
        break;
       
        // linie
    case LINES:
        glLineWidth( 40 );
        glBegin( GL_LINES );
        glColor3fv( Red );
        glVertex2i( 100, 350 );
        glColor3fv( Green );
        glVertex2i( 400, 350 );
        glColor3fv( Blue );
        glVertex2i( 100, 420 );
        glColor3fv( Red );
        glVertex2i( 400, 420 );
        glEnd();
        break;
       
        // wielokąt
    case POLYGON:
        glLineWidth( 40 );
        glBegin( GL_TRIANGLES );
        glColor3fv( Red );
        glVertex2i( 100, 400 );
        glColor3fv( Green );
        glVertex2i( 400, 400 );
        glColor3fv( Blue );
        glVertex2i( 250, 250 );
        glEnd();
        break;
       
        // mapy bitowe
    case BITMAPS:
        glColor3fv( Blue );
        glRasterPos2i( 230, 400 );
        glutBitmapCharacter( GLUT_BITMAP_HELVETICA_18, 'A' );
        glutBitmapCharacter( GLUT_BITMAP_HELVETICA_18, 'B' );
        glutBitmapCharacter( GLUT_BITMAP_HELVETICA_18, 'C' );
        break;
       
        // mapy pikselowe
    case PIXMAPS:
        unsigned char pixmap[ 4 * 4 * 3 ] =
        {
            0x00, 0x00, 0x00,
            0xFF, 0xFF, 0xFF,
            0x00, 0x00, 0x00,
            0xFF, 0xFF, 0xFF,
            0xFF, 0xFF, 0xFF,
            0x00, 0x00, 0x00,
            0xFF, 0xFF, 0xFF,
            0x00, 0x00, 0x00,
            0x00, 0x00, 0x00,
            0xFF, 0xFF, 0xFF,
            0x00, 0x00, 0x00,
            0xFF, 0xFF, 0xFF,
            0xFF, 0xFF, 0xFF,
            0x00, 0x00, 0x00,
            0xFF, 0xFF, 0xFF,
            0x00, 0x00, 0x00
        };
       
        // narysowanie szachownicy
        glRasterPos2i( 220, 400 );
        glPixelZoom( 6.0, 6.0 );
        glDrawPixels( 4, 4, GL_RGB, GL_UNSIGNED_BYTE, pixmap );
       
        // skopiowanie szachownicy
        glPixelZoom( 1.0, 1.0 );
        glRasterPos2i( 260, 400 );
        glCopyPixels( 220, 400, 24, 24, GL_COLOR );
        break;
    }
   
    // sprawdzenie trybu renderowania
    GLint render_mode;
    glGetIntegerv( GL_RENDER_MODE, & render_mode );
   
    // jeżeli jesteśmy w trybie renderingu wyświetlamy
    // opis informacji zgromadzonych w buforze zwrotnym
    if( render_mode == GL_RENDER )
         DrawStrings();
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// pobranie informacji o pojedynczym wierzchołku

void VertexInfo( GLint buf_pos )
{
    // tablica pomocnicza
    char str[ 128 ];
   
    // informacja o współrzędnych wierzchołka
    sprintf( str, "(x,y,z)=(%f,%f,%f)", feedback_buf[ buf_pos + 0 ],
    feedback_buf[ buf_pos + 1 ], feedback_buf[ buf_pos + 2 ] );
    strings.insert( strings.end(), str );
   
    // informacja o składowych koloru
    sprintf( str, "(R,G,B,A)=(%f,%f,%f,%f)", feedback_buf[ buf_pos + 3 ],
    feedback_buf[ buf_pos + 4 ], feedback_buf[ buf_pos + 5 ],
    feedback_buf[ buf_pos + 6 ] );
   
    // dodanie informacji do tablicy strings
    strings.insert( strings.end(), str );
}

// zmiana wielkości okna oraz przetworzenie informacji o sprzężeniu zwrotnym

void Reshape( int width, int height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, width, height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // rzutowanie prostokątne
    gluOrtho2D( 0, width, 0, height );
   
    // utworzenie bufora sprzężenia zwrotnego
    glFeedbackBuffer( MAX_FEEDBACK_BUFFER, GL_3D_COLOR, feedback_buf );
   
    // włączenie trybu sprzężenia zwrotnego
    glRenderMode( GL_FEEDBACK );
   
    // generowanie sceny 3D
    Display();
   
    // włączenie trybu renderingu, pobranie ilości informacji
    // zawartych w buforze sprzężenia zwrotnego
    GLint buf_size = glRenderMode( GL_RENDER );
   
    // wyczyszenie tablicy ciągów znaków, w której znajdą się informacje
    // o danych zgromadzonych w buforze sprzężenia zwrotnego
    strings.erase( strings.begin(), strings.end() );
   
    // tablica pomocnicza
    char str[ 128 ];
   
    // pętla przeglądająca kolejne informacje znajdujące
    // się w buforze sprzężenia zwrotnego
    GLint buf_pos = 0;
    while( buf_pos < buf_size )
    switch(( int ) feedback_buf[ buf_pos ] )
    {
        // pobranie informacji o punkcie
    case GL_POINT_TOKEN:
        strings.insert( strings.end(), "GL_POINT_TOKEN" );
        VertexInfo( buf_pos + 1 );
        buf_pos += 8;
        break;
       
        // pobranie informacji o linii
    case GL_LINE_TOKEN:
        strings.insert( strings.end(), "GL_LINE_TOKEN" );
       
        // informacja o współrzędnych początku linii
        VertexInfo( buf_pos + 1 );
        buf_pos += 8;
       
        // informacja o współrzędnych końca linii
        VertexInfo( buf_pos );
        buf_pos += 7;
        break;
       
        // pobranie informacji o linii po wyzerowaniu wzorca linii
    case GL_LINE_RESET_TOKEN:
        strings.insert( strings.end(), "GL_LINE_RESET_TOKEN" );
       
        // informacja o współrzędnych początku linii
        VertexInfo( buf_pos + 1 );
        buf_pos += 8;
       
        // informacja o współrzędnych końca linii
        VertexInfo( buf_pos );
        buf_pos += 7;
        break;
       
        // informacja o wierzchołkach wielokąta
    case GL_POLYGON_TOKEN:
        strings.insert( strings.end(), "GL_POLYGON_TOKEN" );
       
        // kolejne wierzchołki wielokąta
        for( int i = 0; i < feedback_buf[ buf_pos + 1 ]; i++ )
             VertexInfo( buf_pos + 1 + i * 7 );
       
        buf_pos += 7 * feedback_buf[ buf_pos + 1 ] + 2;
        break;
       
        // mapa bitowa
    case GL_BITMAP_TOKEN:
        strings.insert( strings.end(), "GL_BITMAP_TOKEN" );
       
        // informacja o współrzędnych położenia rastra
        VertexInfo( buf_pos + 1 );
        buf_pos += 8;
        break;
       
        // mapa pikselowa
    case GL_DRAW_PIXEL_TOKEN:
        strings.insert( strings.end(), "GL_DRAW_PIXEL_TOKEN" );
       
        // informacja o współrzędnych położenia rastra
        VertexInfo( buf_pos + 1 );
        buf_pos += 8;
        break;
       
        // kopiowanie pikseli
    case GL_COPY_PIXEL_TOKEN:
        strings.insert( strings.end(), "GL_COPY_PIXEL_TOKEN" );
       
        // informacja o współrzędnych położenia rastra
        VertexInfo( buf_pos + 1 );
        buf_pos += 8;
        break;
       
        // znacznik użytkownika
    case GL_PASS_THROUGH_TOKEN:
        strings.insert( strings.end(), "GL_PASS_THROUGH_TOKEN" );
       
        // pobranie wartości znacznika użytkownika
        sprintf( str, "TOKEN=%f", feedback_buf[ buf_pos + 1 ] );
        strings.insert( strings.end(), str );
        buf_pos += 2;
        break;
    }
   
    // generowanie sceny 3D
    Display();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // wybór rodzaj testu
    case POINTS:
    case LINES:
    case POLYGON:
    case BITMAPS:
    case PIXMAPS:
    case PASS_THROUGH:
        test = value;
       
        // odrysowanie okna
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_WIDTH ) );
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Sprzężenie zwrotne" );
    #else
   
    glutCreateWindow( "Sprzezenie zwrotne" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // utworzenie podmenu - Sprzężenie zwrotne
    int MenuFeedback = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Punkty", POINTS );
    glutAddMenuEntry( "Linie", LINES );
    glutAddMenuEntry( "Wielokąt", POLYGON );
    glutAddMenuEntry( "Mapy bitowe", BITMAPS );
    glutAddMenuEntry( "Mapy pikselowe", PIXMAPS );
    glutAddMenuEntry( "Znacznik użytkownika", PASS_THROUGH );
    #else
   
    glutAddMenuEntry( "Punkty", POINTS );
    glutAddMenuEntry( "Linie", LINES );
    glutAddMenuEntry( "Wielokat", POLYGON );
    glutAddMenuEntry( "Mapy bitowe", BITMAPS );
    glutAddMenuEntry( "Mapy pikselowe", PIXMAPS );
    glutAddMenuEntry( "Znacznik uzytkownika", PASS_THROUGH );
    #endif
   
    // menu główne
    glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddSubMenu( "Sprzężenie zwrotne", MenuFeedback );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddSubMenu( "Sprzezenie zwrotne", MenuFeedback );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującego menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Selekcja obiektów Tablice wierzchołków