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.