Shading
En tilstandsmaskin
OpenGL beskrives ofte som en tilstandsmaskin. Med det forstår vi at vi fra vårt program spesifiserer en rekke egenskaper som projeksjon, transformasjoner, lys, farge, teksturer og en masse andre ting. Når dette er gjort, sender vi en rekke punkter gjennom denne tilstandsmaskinen maskinen og håper at dette manifesterer seg på skjermen i den formen vi forventer. I en viss forstand kan vi si at det eneste tegneprimitivet er et punkt. Tilstanden vi har satt spesifiserer om dette punktet skal være en del av en trekant, en firkant eller hva det måtte være.
En alt for enkel og naiv skisse kan se slik ut:
Grafikk prosessor
Når vi nå har tilgang til programmerbare grafikkprosessorer kan vi tenke slik:
Dette innebærer at vi må skrive shaderprogrammet i et eget språk, Shading Language. Oppgavene til shaderprogrammet er å prosessere punkter (vertex) og fragmenter. Vi kan la OpenGL-programmet gi beskjeder om hvordan denne prosesseringen skal foregå, og vi kan implementere egne metoder for å oppnå ønskede effekter.
Et shaderprogram består av to deler, en vertex-shader og en fragment-shader. Vertex-shaderen involveres hver gang et punkt skal prosesseres. Fragment-shaderen tar seg av alle fragmenter, vi kan for enkelhets skyld si hvert pixel.
Vi kan f.eks implementere lyseffekter i en vertex-shader. Nedenfor et eksempel, en-vertex shader, tatt fra modulen En boks
// attributes to the vertex attribute mediump vec3 aVertexNormal; attribute mediump vec3 aVertexPosition; attribute vec4 aVertexColor; // state of transformation matrices uniform mediump mat4 uNormalMatrix; uniform mediump mat4 uMVMatrix; uniform mediump mat4 uPMatrix; // values that will be passed on to the fragment shader varying lowp vec4 vColor; varying mediump vec3 vLighting; void main(void) { // predefined variable // multiply with Projection-Matrix and ModelView-Matrix to find // correct position gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); // Apply lighting effect mediump vec3 ambientLight = vec3(0.6, 0.6, 0.6); mediump vec3 directionalLightColor = vec3(0.5, 0.5, 0.75); mediump vec3 directionalVector = vec3(0.85, 0.8, 0.75); // normalize to get correct angle in dot-product below mediump vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0); mediump float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0); vLighting = ambientLight + (directionalLightColor * directional); vColor = aVertexColor; }
Fragment-shaderen er slik i samme eksempel. Den blir svært enkel fordi vi har flater med homogen farge.
// received from the vertex shader varying mediump vec3 vLighting; varying lowp vec4 vColor; void main(void) { // predefined variable gl_FragColor = vec4(vColor.rgb * vLighting, vColor.a); }