OpenGL

Z Wikipedii, wolnej encyklopedii
Skocz do: nawigacja, szukaj
OpenGL
Biblioteka programistyczna / API
Logo OpenGL
Logo programu
Autor Khronos Group
Platforma sprzętowa Wieloplatformowość
System operacyjny Wieloplatformowość
Aktualna wersja stabilna 4.5 (11 sierpnia 2014; 2 lata temu)
Strona internetowa

OpenGL (ang. Open Graphics Library) – specyfikacja otwartego i uniwersalnego API do tworzenia grafiki. Zestaw funkcji składa się z 250 podstawowych wywołań, umożliwiających budowanie złożonych trójwymiarowych scen z podstawowych figur geometrycznych.

Cel[edytuj]

Główny[edytuj]

Głównym celem jest tworzenie grafiki. Dzięki temu, że polecenia są realizowane przez sprzęt (procesor graficzny = GPU), tworzenie grafiki następuje szybciej niż innymi sposobami. Ten efekt nazywamy przyspieszeniem sprzętowym. OpenGL wykorzystywany jest często przez gry komputerowe i wygaszacze ekranu, spełnia rolę analogiczną, jak konkurencyjny Direct3D (część DirectX) w systemie Windows firmy Microsoft. Również programy do przedstawiania wyników badań naukowych, CAD, oraz wirtualnej rzeczywistości używają OpenGL.

Dodatkowy[edytuj]

OpenGL jest wykorzystywane do szybkich obliczeń (GPGPU), mimo że nie był do tego zaprojektowany.

Opis działania[edytuj]

OpenGL, podobnie jak np. X Window System, działa w architekturze klient-serwer. Klientem w tym przypadku jest aplikacja wykorzystująca OpenGL, która zleca operacje graficzne do wykonania, a serwerem – aktualnie używana implementacja OpenGL (np. w sterowniku karty graficznej). Zwykle klient i serwer znajdują się na tej samej maszynie, jednak nie jest to konieczne – biblioteka jest zaprojektowana tak, aby możliwe było np. wyświetlanie grafiki OpenGL na zdalnym terminalu. Jednocześnie dzięki zastosowaniu zunifikowanego protokołu komunikacji wyświetlanie może odbywać się na zupełnie innej platformie niż ta, na której działa aplikacja.

Jedną z podstawowych cech OpenGL jest to, że jest on maszyną stanu (ang. state machine). Na stan OpenGL w danym momencie składa się szereg parametrów i trybów działania, które można ustawić lub zapamiętać na stosie i później odtworzyć. Ich konfiguracja będzie miała bezpośredni lub pośredni wpływ na otrzymany rezultat renderingu. Raz ustawiony parametr lub tryb działania pozostaje zachowany aż do następnej zmiany. Przykładami takich parametrów mogą być kolor rysowania, aktualnie używana tekstura, sposób działania bufora Z, macierz na której wykonywane są aktualnie operacje, oraz wiele innych.

Część z parametrów może być włączana lub wyłączana w sposób bardzo oczywisty, tzn. poprzez wywołanie funkcji glEnable() lub glDisable() (w tłumaczeniu brzmiałoby to: glWłącz() oraz glWyłącz()), a inne ustawiane są poprzez wykonanie powiązanych z tymi parametrami funkcji (np. glBindTexture() – ustawienie aktywnej tekstury).

Dzięki funkcji glPushAttrib() możliwe jest zapamiętanie na stosie części lub całości aktualnego stanu OpenGL w zależności od przekazanego jej argumentu. Funkcja odwrotna, czyli glPopAttrib() nie wymaga żadnych argumentów, gdyż pobiera ze szczytu stosu taki stan, jaki został wcześniej zapamiętany.

Geneza OpenGL[edytuj]

Pierwotna wersja biblioteki wyewoluowała ze stworzonej przez firmę Silicon Graphics Inc. dla jej systemów graficznych biblioteki IRIS GL. W chwili obecnej implementacje OpenGL można znaleźć w praktycznie wszystkich nowoczesnych platformach. W większości przypadków implementacje te rozwijane są przez producentów sprzętu graficznego. Istnieje również otwarta implementacja – Mesa, zgodna z OpenGL na poziomie kodu, która jednak ze względu na brak licencji określana jest jako "bardzo podobna".

Do połowy 2006 rozwój OpenGL był kontrolowany przez organizację ARB (Architectural Review Board), powołaną w 1992 i zrzeszającą 10 firm (3DLabs, Apple, ATI, Dell, Evans & Sutherland, Hewlett-Packard, IBM, Intel, Matrox, NVIDIA, SGI, Sun Microsystems), które spotykały się raz na 3 miesiące, aby głosować i podejmować decyzje. 31 lipca 2006 komitet standaryzacyjny ARB opowiedział się w głosowaniu za wcieleniem tej organizacji do grupy roboczej Khronos, która prowadzi prace nad wieloma pokrewnymi projektami w tym OpenGL ES, co w założeniach miało uprościć formalności, przyspieszyć prace i ujednolicić dalszy rozwój tych standardów. Prawa do logo i nazwy OpenGL nadal jednak należą do SGI.

Prace nowej grupy roboczej pozytywnie wpłynęły na podejście do społeczności programistów OpenGL. Postawiono na intensywną współpracę, szeroki kontakt oraz częste informowanie o postępach w pracy nad nowymi rozwiązaniami. Na życzenie użytkowników zaczęto pracować nad jednolitym SDK, które ma ułatwić poznanie tej biblioteki nowym deweloperom oraz dostarczać narzędzia przydatne do analizy, budowania i optymalizacji aplikacji, które z niej korzystają.

We wrześniu 2004 ARB opublikowała specyfikację OpenGL w wersji 2.0. Kolejna wersja 2.1 ujrzała światło dzienne w sierpniu 2006 i jest już oficjalnie firmowana przez Khronos. Na rok 2007 zapowiedziano premierę dwóch kolejnych wersji OpenGL o kodowych nazwach "Longs Peak" i "Mt. Evans". Pierwsza z nich miała być zgodna z dotychczasowymi akceleratorami wykorzystującymi standard Shader Model 3.0, kolejna miała obsługiwać już tylko karty następnej generacji.

Wersje[edytuj]

Zależność między Opengl i GLSL:[1]

Wersja OpenGL Wersja GLSL #version tag
1.2
2.0 1.10.59 110
2.1 1.20.8 120
3.0 1.30.10 130
3.1 1.40.08 140
3.2 1.50.11 150
3.3 3.30.6 330
4.0 4.00.9 400
4.1 4.10.6 410
4.2 4.20.6 420
4.3 4.30.6 430

Biblioteki pomocnicze[edytuj]

Mimo dużych możliwości, samo OpenGL jest interfejsem niskopoziomowym. Oznacza to, że zawiera ono np. funkcje rysujące pojedyncze wielokąty lub serie wielokątów, albo funkcje operujące na macierzy widoku, ale już na przykład funkcje pozwalające na ustawienie obserwatora w pewnym punkcie patrzącego w inny punkt (gluLookAt), narysowanie całej sfery (gluSphere), bądź automatyczne wygenerowanie mipmap dla danej tekstury (gluBuild2DMipmaps), realizowane są za pomocą biblioteki pomocniczej GLU (ang. GL Utility Library).

Poza GL i GLU do wykorzystania OpenGL w aplikacji potrzebne są zwykle również inne biblioteki. Wynika to z faktu, że OpenGL zajmuje się tylko renderingiem grafiki i nie zawiera funkcji związanych z tworzeniem kontekstu graficznego, obsługą środowiska okienkowego, czy obsługą zdarzeń takich jak naciśnięcie klawisza na klawiaturze lub ruch kursora myszy. Funkcjonalność tę można dodać poprzez biblioteki, jak GLUT (ang. GL Utility Toolkit), GLUI, czy przeznaczone dla poszczególnych platform GLX (w przypadku środowiska X Window System) lub WGL (w przypadku Windows).

Istnieje również biblioteka SDL (ang. Simple DirectMedia Layer), realizująca funkcje podobne jak DirectX i jednocześnie umożliwiająca łatwe użycie OpenGL z jej poziomu.

Zarówno SDL jak i GLUT dostępne są dla wielu platform, a zatem pozwalają na efektywne tworzenie przenośnych aplikacji. W przeciwieństwie do SDL klasyczny GLUT (dzieło Marka Kilgarda) nie jest już jednak rozwijany i choć pojawiają się nieoficjalne wersje typu freeglut, to rozwiązują one bardziej kwestie licencyjne niż podtrzymują rozwój tego oprogramowania.

Rozszerzenia[edytuj]

OpenGL został zaprojektowany w taki sposób, aby producenci sprzętu graficznego mogli rozszerzać jego funkcjonalność poprzez własne rozszerzenia. Rozszerzenia takie dystrybuowane są poprzez publikację zarówno sterowników obsługujących dane funkcje, jak również plików nagłówkowych z definicjami tychże, aby z funkcji tych mogli skorzystać programiści piszący oprogramowanie używające OpenGL.

Funkcje lub stałe występujące w rozszerzeniach oznaczane są skrótami przyporządkowanymi poszczególnym producentom (np. funkcje firmy NVIDIA oznaczane są skrótem NV). Jeśli funkcja jest używana przez więcej niż jednego producenta, oznaczana jest skrótem EXT, a w przypadku, gdy zostanie oficjalnie zaakceptowana przez Architectural Review Board, staje się ona rozszerzeniem standardowym i otrzymuje oznaczenie ARB. Później, rozszerzenie może się stać oficjalnie częścią standardu OpenGL, w kolejnej jego wersji.

Przykładowy kod[edytuj]

Potok programowalny OpenGL 3.3

Najmniejszy, najprostszy program wykorzystując możliwości OpenGL składa się z pięciu plików. Głównego pliku programu - main.cpp, klasy wykorzystującej możliwości OpenGL(mainwindow.cpp) oraz nagłówka do tej klasy (mainwindow.h), a także dwóch plików z kodem shaderów.

Kolejność przekształceń punktów/wierzchołków

Przykład został napisany w języku C++ z wykorzystaniem bibliotek Qt. Wykorzystuje on OpenGL w wersji 3.3.

Niezbędne funkcje i klasy do napisania programu:

main:

  • QApplication,
  • Nasza nowa klasa wykorzystująca OpenGL,
  • QSurfaceFormat
  • QSufraceFormat::defaultFormat();
  • QSurfaceFormat::setProfile();
  • QSurfaceFormat::setVersion();
  • QSurfaceFormat::setDefaultFormat();

mainwindow:

1) Przygotowanie:

  • inicializeOpenGLFunctions();
  • glCreateProgram();
  • glCreateShader();
  • glShaderSource();
  • glCompileShader();
  • glGetShaderiv();
  • glAttachShader();
  • glDeleteShader();
  • glGetShaderInfoLog();
  • glLinkProgram();
  • glGetProgramiv();

3) Obiekty - przygotowanie:

  • glGenVertexArray();
  • glBindVertexArray();
  • glEnableVertexAtribArray();
  • glGenBuffers();
  • glBindBuffer();
  • glBufferData();
  • glVertexAttribPointer();
  • glGenTextures();
  • glActiveTexture();
  • glBindTexture();
  • glTexImage2D()
  • glTexParameteri();

4) Opcjonalne:

  • glGenFramebuffers();
  • glBindFramebuffer();
  • glFramebufferTexture2D();
  • glCheckFramebufferStatus();

5) Rysowanie/Działanie:

  • glGetIntegerv();
  • glBindFramebuffer();
  • glViewport();
  • glClear();
  • glUseProgram();
  • glActiveTexture();
  • glBindTexture()
  • glGetUniformLocation();
  • glUniform1i();
  • glBindVertexArray();
  • glDrawArrays();

Niezbędne makra:

  • GL_VERTEX_SHADER,
  • GL_COMPILE_STATUS,
  • GL_FRAGMENT_SHADER,
  • GL_LINK_STATUS,
  • GL_ARRAY_BUFFER,
  • GL_STATIC_DRAW,
  • GL_FALSE,
  • GL_FLOAT,
  • GL_TEXTURE0,
  • GL_TEXTURE_2D,
  • GL_RGBA,
  • GL_BGRA,
  • GL_UNSIGNED_BYTE,
  • GL_TEXTURE_MIN_FILTER,
  • GL_LINEAR,
  • GL_COLOR_BUFFER_BIT,
  • GL_TRIANGLES,

Dodatkowe makra:

  • GL_FRAMEBUFFER,,
  • GL_COLOR_ATTACHMENT0,
  • GL_DRAW_FRAMEBUFFER_BINDING,

Szczegóły na temat poszczególnych funkcji oraz makr podane są na stronie dokumentacji OpenGL.

Plik main.cpp

 1 #include "mainwindow.h"
 2 #include <QApplication>
 3 #include <QSurfaceFormat>
 4 
 5 int main(int argc, char *argv[])
 6 {
 7     QApplication a(argc, argv);
 8 
 9     QSurfaceFormat format = QSurfaceFormat::defaultFormat();
10     format.setProfile(QSurfaceFormat::CoreProfile);
11     format.setVersion(3,3);
12     QSurfaceFormat::setDefaultFormat(format);
13 
14     MainWindow w;
15     w.show();
16 
17     return a.exec();
18 }

Plik mainwindow.h

 1 #ifndef MAINWINDOW_H
 2 #define MAINWINDOW_H
 3 
 4 //#include <QMainWindow>
 5 #include <QOpenGLFunctions_3_3_Core>
 6 #include <QOpenGLWidget>
 7 
 8 class MainWindow : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core //public QMainWindow,
 9 {
10     //Q_OBJECT
11 
12     GLuint uchwytProg1; //unsigned int
13 
14     GLuint VertexArrayO;  // Vertex Array Object
15     int ilWiesz;   //ilosc wierzcholkow
16 
17     GLuint tex0;
18 
19 public:
20     MainWindow();
21     ~MainWindow();
22 
23     void initializeGL();
24     void paintGL();
25     void resizeGL(int w, int h);
26 };
27 
28 #endif // MAINWINDOW_H

Plik mainwindow.cpp

  1 #include "mainwindow.h"
  2 
  3 #include <QOpenGLFunctions_3_3_Core>
  4 #include <QFile>
  5 #include <Qdebug>
  6 
  7 struct vec3 { float x,y,z; };
  8 
  9 MainWindow::MainWindow() //QMainWindow(parent)
 10 { }
 11 
 12 MainWindow::~MainWindow()
 13 { }
 14 
 15 void MainWindow::initializeGL()
 16 {
 17     initializeOpenGLFunctions();
 18 
 19     qDebug() << "initializeGL";
 20 
 21     GLint status;
 22 
 23     // program kopiujący teksture na ekran
 24     uchwytProg1 = glCreateProgram();
 25 
 26     QFile file("vshaderS.sh");
 27     if(file.open(QFile::ReadOnly))
 28     {
 29         QTextStream stream(&file);
 30         std::string vshader_zrodlo = stream.readAll().toStdString();
 31 
 32         GLuint shader = glCreateShader(GL_VERTEX_SHADER);
 33         GLchar* srcs[] = {(GLchar*)vshader_zrodlo.c_str()};
 34         glShaderSource(shader, 1, srcs, NULL);
 35         glCompileShader(shader);
 36 
 37         glGetShaderiv(shader, GL_COMPILE_STATUS, &status); //skompilowany
 38         if( status == true )
 39         {
 40             glAttachShader(uchwytProg1, shader);
 41             glDeleteShader(shader);
 42         } else { qDebug() << "Shadera nie skompilowano. V"; }
 43          qDebug() << "BB" << vshader_zrodlo.c_str();
 44     } else qDebug() << "Brak pliku";
 45     file.close();
 46 
 47     QFile file2("fshaderS.sh");
 48     if(file2.open(QFile::ReadOnly))
 49     {
 50         QTextStream stream(&file2);
 51         std::string vshader_zrodlo = stream.readAll().toStdString();
 52 
 53         GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
 54         GLchar* srcs[] = {(GLchar*)vshader_zrodlo.c_str()};
 55         glShaderSource(shader, 1, srcs, NULL);
 56         glCompileShader(shader);
 57         glGetShaderiv(shader, GL_COMPILE_STATUS, &status); //skompilowany
 58         if( status == true )
 59         {
 60             glAttachShader(uchwytProg1, shader);
 61             glDeleteShader(shader);
 62         } else {
 63             qDebug() << "Błąd komp. szhadera. F";
 64             GLchar infoLog[10240];
 65             glGetShaderInfoLog(shader, 10240, NULL, infoLog);
 66             qDebug() << infoLog << endl;
 67         }
 68 
 69         qDebug() << "AA" << srcs;
 70     } else qDebug() << "Brak pliku";
 71     file2.close();
 72 
 73     glLinkProgram(uchwytProg1);
 74     glGetProgramiv(uchwytProg1, GL_LINK_STATUS, &status);
 75     if( status == true) { qDebug() << "Program przygotowany"; }
 76     else qDebug() << "Problemy z programem.";
 77 
 78     glUseProgram(uchwytProg1);
 79     GLint polozenie = glGetUniformLocation(uchwytProg1, "textura");
 80     qDebug() << "Polozenie, textura: " << polozenie;
 81 
 82     // prostokat na cały ekran
 83     vec3 wieszcholki[] = { {-1,-1,0}, {1,-1,0}, //Dwa trójkąty...
 84                            {1,1,0},    {1,1,0},
 85                            {-1,1,0},   {-1,-1,0} };
 86    
 87     ilWiesz = 6;
 88 
 89     glGenVertexArrays(1, &VertexArrayO);
 90         glBindVertexArray(VertexArrayO);
 91         glEnableVertexAttribArray(0); //włączenie obsługi konkretnej tablicy atrybutów,
 92 
 93         //wygenerowanie nazwy bufora dla atrybutu i dodanie do mapy pod konkretnym indexem
 94             GLuint buforVertex;
 95             glGenBuffers(1, &buforVertex);
 96 
 97         // przypisanie bufora do tablicy vao
 98             glBindBuffer(GL_ARRAY_BUFFER, buforVertex);
 99 
100         // stworzenie bufora i skopiowanie do niego danych
101             glBufferData(GL_ARRAY_BUFFER, ilWiesz*sizeof(vec3), wieszcholki, GL_STATIC_DRAW);
102 
103         // ustawienie odpowiednich typow i wielkosci bufora wierzcholkow vbo
104             glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
105 
106        glBindBuffer(GL_ARRAY_BUFFER, 0);
107    glBindVertexArray(0);
108 
109     glGenTextures(1, & tex0);
110 
111     glActiveTexture(GL_TEXTURE0);
112 
113     // tekstura wczytana z pliku
114         QImage img("lena.png");
115         if(img.width() == 0) qt_assert("QImage not loaded!", __FILE__, __LINE__);
116 
117         glBindTexture(GL_TEXTURE_2D, tex0);
118             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, img.bits());
119             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
120 
121         glBindTexture(GL_TEXTURE_2D, 0);
122 }
123 
124 void MainWindow::resizeGL(int w, int h)
125 {
126     qDebug() << "resizeGL";
127 }
128 
129 void MainWindow::paintGL()
130 {
131     qDebug() << "PaintGL - rysuje";
132 
133     GLuint polozenie;
134 
135     /* niektore implementacje nie przypisuja domyslnego FB ekranu == 0
136         wtedy trzeba zapamietac samemu jego numer: */
137         GLint screenFB;
138         glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &screenFB);
139         qDebug() << "Numer dla ScreenFramebuffer:  " << screenFB;
140 
141         int tryb = 1;
142 
143     if( tryb == 1 )
144     {
145         // rysowanie tekstury na domyslnym FrameBufferze
146 
147         // glBindFramebuffer(GL_FRAMEBUFFER, 0);
148         // lub
149         glBindFramebuffer(GL_FRAMEBUFFER, screenFB);
150         glViewport(0,0,width(), height());
151 
152         glClear(GL_COLOR_BUFFER_BIT);
153 
154         glUseProgram(uchwytProg1);
155 
156             // Ustawienie tekstury
157             polozenie = glGetUniformLocation(uchwytProg1, "textura");
158             if(polozenie != -1) glUniform1i(polozenie, 0); else qDebug() << "Brak zmiennej Uniform textura";
159             
160             glActiveTexture(GL_TEXTURE0);
161             glBindTexture(GL_TEXTURE_2D, tex0);
162 
163             glBindVertexArray(VertexArrayO);
164                 glDrawArrays(GL_TRIANGLES, 0, ilWiesz);
165             glBindVertexArray(0);
166 
167             glBindTexture(GL_TEXTURE_2D, tex0);
168             glViewport(width()/2.0, 0, width()/2.0, height());
169 
170             glBindVertexArray(VertexArrayO);
171                 glDrawArrays(GL_TRIANGLES, 0, ilWiesz);
172             glBindVertexArray(0);
173 
174         glUseProgram(0);
175     }
176 }

Plik vshader.glsl - Shader Vertexów - Shader wieschołków:

#version 330

layout (location=0) in vec3 VertexPosition; //atrybut zerowy,
                   out vec2 textureCoords;

void main()
{
    //Macierz skalowania,
    mat4 macierz = mat4(1, 0, 0, 0,
                        0, 1, 0, 0,
                        0, 0, 1, 0,
                        0, 0, 0, 1);

    vec4 a = vec4(VertexPosition.x, VertexPosition.y, VertexPosition.z, 1);
    vec4 b = a * macierz;

    // Macierz translacji - przesuwania
    mat4 macierzTran = mat4(  1, 0, 0, 0,
                              0, 1, 0, 0,
                              0, 0, 1, 0,
                              0, 0, 0, 1);

    b = b * macierzTran;

    gl_Position = b; 
    // lub:
    //gl_Position = vec4(VertexPosition, 1); //-- wówczas będzie bez rotacji i bez translacji
    textureCoords = VertexPosition.xy * 0.5f + 0.5f;
}

Plik fshader.glsl - Shader Fragmentów:

 1 #version 330 //Dyrektywa informująca o wersji OpenGL'a
 2 
 3 // Zmienne wbudowane shadera:
 4 // in vec4 gl_FragCoord 
 5 // in bool gl_FrontFacing
 6 // in vec2 gl_PointCoord
 7 
 8 uniform sampler2D tekstura;
 9           in vec2 teksturaCoords;
10          out vec4 FragColor; //Obowiązkowa zmienna wyjściowa
11 
12 //fragment shader kopiujacy texele z tekstury na wyjscie
13 
14 void main()
15 {
16     //vec2 jest wbudowanym typem [wektor dwuelementowy]
17     vec2 tc = vec2(teksturaCoords.x, 1-teksturaCoords.y );
18 
19     FragColor = texture(tekstura, tc);
20     // discard; - spowoduje nie nadanie wartości danemu tekslowi, na danych współrzędnych
21 }

Zobacz też[edytuj]

Przypisy

Linki zewnętrzne[edytuj]