Ajattelin lähettää tänne shader koodivinkin, kun en sellaista huomannut täällä olevan.
OpenGL sisältää tuen shadereille GLSL (GL shader language) kielen muodossa. Shadereilla saa kaikenlaisia efektejä aikaiseksi, esimerkiksi varjostuksia tai itsemääriteltyjä valaistuksia.
Shadereita on erilaisia. OpenGL tuntee seuraavat: vertex shader, fragment shader, sekä uudemmat versiot myös geometry shaderin.
Shadereilla korvataa OpenGL:n omat piirtomekanismit. Vertex shaderilla korvataan verteksien perspektiivilaskenta. Fragment shaderilla korvataan fragmentin (osapikseli) väriarvo. Geometry shaderilla voidaan luoda olemassa olevista vertekseistä uusia verteksejä.
Kun shaderi määritellään, poistuu sen korvaama piirtomekanismi kokonaan käytöstä. Esimerkiksi fragment shaderin korvattua on valaistukset ja värjäykset ohjelmoitava itse shaderiin.
Tässä koodivinkissä luodaan GLSL:llä ohjelmoidut vertex ja fragment -shaderit. Esimerkin vertex shader muokkaa verteksin z-koordinaattia luoden aaltoefektin, ja välittää tiettyjä tietoja fragment shaderille. Fragment shader taas luo yksinkertaisen plasmaefektin.
Vinkin shaderit eivät oikeastaan tee mitään hyödyllistä. Suurempi ilo on varmaankin vinkin koodi, jolla OpenGL konfiguroidaan käyttämään shadereita. Demossa käytetty plasmaefekti löytyy jo koodivinkeistä, joten sitä ei liene tarvitse sen enempää selitellä.
Kirjastona ikkunan luomiselle ja syötteen lukemiseen käytetään SFML-kirjastoa. (http://sfml-dev.org)
Seuraavat kirjastot täytyy linkata: sfml-window, sfml-system, GLU ja GL
GNU:n C++ kääntäjällä kääntäminen onnistuu seuraavasti:
g++ -O2 -lsfml-window -lsfml-system -lGLU -o shader-demo main.cpp
Ai niin, vaatii toki toimiakseen tuen OpenGL shadereille näytönohjaimelta ja ajureilta. Ilman sitä tulee ruudulle vain valkoinen suorakaide...
Lisätietoa shader ohjelmoinnista esimerkiksi: (toimiva tutoriaali)
http://www.lighthouse3d.com/opengl/glsl/index.php?intro
main.cpp
//Koodia saa käyttää, mutta en vastaa seuraamuksista #define GL_GLEXT_PROTOTYPES #include <SFML/Window.hpp> #include <iostream> #include <fstream> //Vakiot const char *VertexShaderFile = "vertex.shader"; const char *FragmentShaderFile = "fragment.shader"; const int NumberOfTrianglesX = 100; const int NumberOfTrianglesY = 1; const float TriangleSizeX = 1.f; const float TriangleSizeY = 100.f; //Funktioiden prototyypit void drawTriangles(); void initGL(); void initShaders(); void printShaderInfoLog(GLuint obj); void printProgramInfoLog(GLuint obj); char* readSource(const char *filename); //Globaalit GLuint shaderProgram; //Main int main(int argc, char **argv) { //GLint loc; int mouseX, mouseY; int windowWidth, windowHeight; mouseX = 0; mouseY = 0; windowWidth = 800; windowHeight = 800; std::cout << argv[0] << std::endl; //Luodaan ikkuna, 4x antialiasointi sf::Window App(sf::VideoMode(windowWidth, windowHeight, 32), "Shader demo", sf::Style::Resize | sf::Style::Close, sf::WindowSettings(24, 8, 4)); //Ruudunpäivitys maksimissaan 60 kertaa sekunnissa App.SetFramerateLimit(60); //Luodaan kello-olio sf::Clock clock; //OpenGL alustukset initGL(); //Shader alustukset initShaders(); //Pyöritään silmukassa niin kauan kuin ohjelma on päällä while (App.IsOpened()) { //käydään eventit läpi sf::Event Event; while (App.GetEvent(Event)) { switch (Event.Type) { case sf::Event::Closed: //Ohjelman sammuttaminen case sf::Event::KeyPressed: //Mikä tahansa näppäimen painallus App.Close(); break; case sf::Event::Resized: //Ikkunan koon muuttaminen windowWidth = Event.Size.Width; windowHeight = Event.Size.Height; glViewport(0, 0, Event.Size.Width, Event.Size.Height); break; case sf::Event::MouseMoved: //Hiiren liikutus mouseX = Event.MouseMove.X - windowWidth / 2; mouseY = Event.MouseMove.Y - windowHeight / 2; break; default: break; } } //Tyhjennetään ruutu tyhjennysvärillä glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Tehdään muunnokset OpenGL modelview matriisiin glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //Koordinaatisto syvemmälle glTranslatef(0.f, 0.f, -200.f); //Pyöritetään hiiren sijainnin mukaan glRotatef(mouseY * .3f, 1.f, 0.f, 0.f); glRotatef(mouseX * .3f, 0.f, 1.f, 0.f); //Siirretään vielä koordinaatistoa glTranslatef(-50.f, -50.f, 0.f); //renderöinti drawTriangles(); //Piirrä ruutu App.Display(); } return EXIT_SUCCESS; } //Piirtää ruudulle kolmioista koostuvan suorakaiteen muotoisen alueen //ja huolehtii shadereiden time muuttujasta void drawTriangles() { static float slide = 0.f; GLuint loc; slide += 0.03f; //Asetetaan kaikille shadereille näkyvä vakio time loc = glGetUniformLocation(shaderProgram,"time"); glUniform1f(loc, slide); //Luodaan kolmiosysteemi for (int y=0; y<NumberOfTrianglesY; y++) { glBegin(GL_TRIANGLE_STRIP); for (int x=0; x<NumberOfTrianglesX; x++) { glVertex2f(x*TriangleSizeX, y*TriangleSizeY); glVertex2f(x*TriangleSizeX, y*TriangleSizeY+TriangleSizeY); } glEnd(); } } void initGL() { //Tyhjennysväri glClearColor(0.f, 0.f, 0.f, 0.f); //Tyhjennyssyvyys glClearDepth(1.f); //Syvyystarkistus päälle glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); //Asetetaan OpenGL:n perspektiivimatriisi glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(40.f, 1.f, 1.f, 500.f); } void initShaders() { GLuint vertexShader; GLuint fragmentShader; const GLchar *fragmentCode; const GLchar *vertexCode; //Luodaan shader objektit fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); vertexShader = glCreateShader(GL_VERTEX_SHADER); //Luetaan shader koodit tiedostoista ja asetetaan koodit objekteihin fragmentCode = readSource(FragmentShaderFile); vertexCode = readSource(VertexShaderFile); if (fragmentCode == NULL || vertexCode == NULL) { std::cout << "Shader tiedosto puuttuu!" << std::endl; exit(0); } glShaderSource(fragmentShader, 1, &fragmentCode, NULL); glShaderSource(vertexShader, 1, &vertexCode, NULL); delete [] fragmentCode; delete [] vertexCode; //Käännetään shaderit glCompileShader(fragmentShader); glCompileShader(vertexShader); //Tulostetaan käännöstiedot printShaderInfoLog(fragmentShader); printShaderInfoLog(vertexShader); //Luodaan ja linkataan shader ohjelma shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, fragmentShader); glAttachShader(shaderProgram, vertexShader); glLinkProgram(shaderProgram); //Tuolostetaan ohjelman linkkaustiedot printProgramInfoLog(shaderProgram); //Asetetaan ohjelma käyttöön glUseProgram(shaderProgram); } char* readSource(const char *filename) { //Luodaan ifstream olio osoittamaan tiedoston loppuun std::ifstream f(filename, std::ios::in | std::ios::ate); int size; char *str; if (!f.is_open()) return NULL; //Kutsutaan tellg tiedoston lopussa, saadaan tiedoston koko size = static_cast<int>(f.tellg()); //varataan muisti ja luetaan tiedosto str = new char[size+1]; str[size] = 0; f.seekg (0, std::ios::beg); f.read(str, size); f.close(); return str; } //Tulostaa tiedot shaderin kääntämisestä void printShaderInfoLog(GLuint obj) { int size = 0; char *log; glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &size); if (size > 0) { log = new char[size]; glGetShaderInfoLog(obj, size, NULL, log); std::cout << log << std::endl; delete [] log; } } //Tulostaa tiedot shader ohjelmasta void printProgramInfoLog(GLuint obj) { int size = 0; char *log; glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &size); if (size > 0) { log = new char[size]; glGetProgramInfoLog(obj, size, NULL, log); std::cout << log << std::endl; delete [] log; } }
fragment.shader
//Universaali aika, kaikille fragmenteille sama uniform float time; //location lasketaan automaattisesti interpoloimalla verteksien antamat arvot varying vec2 location; void main() { float white; vec4 color; vec2 plasmaloc; plasmaloc.x = location.x + cos(time * 0.1) * 100; plasmaloc.y = location.y + sin(time * 0.1) * 70; plasmaloc /= 50.0; //Jonkinlainen plasmaefektilasku white = sin(plasmaloc.x) * sin(plasmaloc.y) * 2.5 + sin(plasmaloc.x*(20 + 5*cos(plasmaloc.y*5))) * 1.5 + sin(plasmaloc.y*(30 + 7*cos(plasmaloc.x*3))) * 1.5; white = abs(sin(white)); //Määritellään fragmentin lopullinen väri color.x = (1 - white) * abs(sin(plasmaloc.y)); color.y = white; color.z = 0.5 - 0.5 * white + 0.5; color.w = 1.0; //Kerrotaan vielä OpenGL:lle väri... gl_FragColor = color; }
vertex.shader
//time on määriteltu main.cpp tiedostossa //uniform tyyppi tarkoittaa, että se näkyy kaikille shadereille samana uniform float time; //varying muuttuja location välitetään verteksiltä fragmenteille //fragmentin saama arvo on interpoloitu sen muodostaneiden verteksien //arvoista varying vec2 location; void main() { //Talletetaan vektoriin v verteksin sijainti vec4 v = vec4(gl_Vertex); //Muokataan verteksin z-koordinaattia v.z = v.z + sin(0.3*v.x + time) * 5; //Asetetaan verteksin x ja y -koordinaatit location muuttujaan //location vektori välittyy interpoloituna fragmenteille location.x = v.x; location.y = v.y; //Verteksin lopullinen sijainti saadaan kertomalla se //projektiomatriisin kanssa gl_Position = gl_ModelViewProjectionMatrix * v; }
Makefile (kääntämiseen laiskureita varten)
shader-test: main.cpp g++ -O2 -lsfml-window -lsfml-system -lGLU -o shader-demo main.cpp
Pohjatietoa shadereiden käytännön toteutuksesta suomeksi. Kiitän tästä.
Näin assyjen innoittamana taas into opetella shadereiden käyttöä kasvaa luokattomaksi. Ja kun vielä Suomeksi tietoa löytyi niin pitää kiittää tonneittain! :)
Aihe on jo aika vanha, joten et voi enää vastata siihen.