Systemprogrammering i C på en mobil plattform. Kurskompendium



Relevanta dokument
Programmeringsteknik med C och Matlab

Programmeringsteknik med C och Matlab

Enkla datatyper minne

Övning från förra gången: readword

BINÄRA TRÄD. (X = pekarvärdet NULL): struct int_bt_node *pivot, *ny; X X X 12 X X 12 X X -3 X X

Uppgifter till praktiska tentan, del A. (7 / 27)

Boken?!?! Vad är ett program? Kompilerande-Interpreterande Programmeringsmiljö Hello World! Att programmera och ett enkelt program Variabler printf

Kompilering och exekvering. Föreläsning 1 Objektorienterad programmering DD1332. En kompilerbar och körbar java-kod. Kompilering och exekvering

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

Programmering i C++ En manual för kursen Datavetenskaplig introduktionskurs 5p

Agenda. Arrayer deklaration, åtkomst Makron Flerdimensionella arrayer Initiering Strängar Funktioner och arrayer. Övningar nu och då

2 Pekare och dynamiska variabler.

En kort text om programmering i C.

Programmering i C++ Kompilering från kommandoraden

Föreläsning 6 pekare och pekare tillsammans med arrayer

Programmering, grundkurs, 8.0 hp, Elektro, KTH, hösten Programmering: att instruera en maskin att utföra en uppgift, kräver olika språk:

Planering Programmering grundkurs HI1024 HT TIDAA

Programmering med Java. Grunderna. Programspråket Java. Programmering med Java. Källkodsexempel. Java API-exempel In- och utmatning.

*Pekarvärden *Pekarvariabler & *

F5: Högnivåprogrammering

F5: Högnivåprogrammering

Tentamen i Programmering grundkurs och Programmering C

Poster ( structar ) Postdeklarationer

Föreläsning 11. Strängar

Planering Programmering grundkurs HI1024 HT data

Objektorienterad Programmering (TDDC77)

I Skapa Hej.java och skriv programmet. I Kompilera med javac Hej.java. I Rätta fel och repetera tills du lyckas kompilera ditt program

Föreläsning 10. Pekare (Pointers)

Föreläsning 13. In- och utmatning

Dynamiskt minne. Vad är dynamiskt minne Motivering Hur gör man i C Övningar

Johan Karlsson Datavetenskap för teknisk kemi, 10p, moment 1 Datavetenskap Umeå Universitet. Tentamen

Introduktion till programmering, hösten 2011

KPP053, HT2016 MATLAB, Föreläsning 1. Introduktion till MATLAB Skript Inläsning och utskrift av variabler Ekvationssystem Anonyma funktioner

Laboration: Grunderna i MATLAB

Java: Utvecklingsverktyg, datatyper, kontrollstrukturer

Grafiska pipelinens funktion

Värmedistribution i plåt

TDIU01 - Programmering i C++, grundkurs

Det finns många flaggor till g++,

EDAA20 Programmering och databaser. Mål komprimerat se kursplanen för detaljer. Checklista. Föreläsning 1-2 Innehåll. Programmering.

Tentamen i. för D1 m fl, även distanskursen. lördag 19 januari 2013

#include <stdio.h> #include <string.h>

HI1024, Programmering, grundkurs, 8hp KTH STH TENTAMEN. HI1024:TEN2 - Praktisk tentamen Tid: Fredagen den 21 oktober 2011,

Lab5 för prgmedcl04 Grafik

Programmering B med Visual C

Innehåll. Introduktion till objektorientering. OOP (objektorienterad programmering) Objekt, instanser, klasser

Att komma igång. Föreläsning 1

Grafiska pipelinen. Edvin Fischer

Tentamen *:58/ID100V Programmering i C Exempel 3

Funktionspekare, inledning: funktionsanropsmekanismen. Anrop via pekare

Dagens föreläsning. Specialtecken. Mer om printf. Formateringssträngar. Mer om scanf. Programmeringsteknik för Ingenjörer VT05

Tentamen i Programmering grundkurs och Programmering C

Strängar. Strängar (forts.)

Alla filer som bearbetar PHP script ska avslutas med ändelsen.php, exempelvis ska en indexsida till en hemsida heta index.php

Lösningar till uppgifterna sätts ut på kurssidan på nätet i dag kl Tentamen i Programmering C, Fri, Kväll,

Att komma igång. Föreläsning 1

Deklarera en struct som kan användas för att representera en rät linje

Vem är vem på kursen. Objektorienterad programvaruutveckling GU (DIT011) Kursbok Cay Horstmann: Big Java 3rd edition.

Omkoppling av in- och utmatning. In- och utmatning i Unix. Kommando exempel, ls, pipe forts. Kommando exempel, ls, pipe

Tentamen i TDP004 Objektorienterad Programmering Praktisk del

Programmering, grundkurs, 8.0 hp HI1024, HI1900 etc., Tentamen TEN1. Måndagen den 10 januari 2011,

Planering Programmering grundkurs HI1024 HT 2014

Datorteknik 2 (AVR 2)

Föreläsning 3.1: Datastrukturer, en översikt

Introduktion till Datalogi DD1339. Föreläsning 2 22 sept 2014

Att använda pekare i. C-kod

1 Funktioner och procedurell abstraktion

4 Sammansatta datatyper

Lösningar till uppgifterna sätts ut på kurssidan på nätet i dag kl Omtentamen i Programmering C, Fri, Kväll,

Föreläsning 2. Operativsystem och programmering

Tentamen i Programmering grundkurs och Programmering C

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Felsökning. Datatyper. Referenstyper. Metoder / funktioner

Code-Lite tutorial ( /RoJ)

Shaders. Renderingssystem. Renderingssystem. Renderingssystem. Hårdvara för 3D-rendering. Hårdvara för 3D-rendering

Tentamen i. Programmering i språket C

Procedurell grottgenerator och eld i GLSL. Marcus Widegren

Miniprojekt: MEX och molekyldynamik

F4. programmeringsteknik och Matlab

Vad har vi lärt oss så här långt Vad är en sträng? Strängkonstanter. Att skriva ut och läsa in strängar. Att arbeta med strängar.

ID1004 Laboration 3, 5-6 November 2012

Föreläsning 3. Programmering, C och programmeringsmiljö

C++ Lektion Tecken och teckenfält

OBS! All teori i detta och följande dokument kompletteras med genomgångar på lektionerna. Så det är viktigt att närvara och göra egna anteckningar.

FrontPage Express. Ämne: Datorkunskap (Internet) Handledare: Thomas Granhäll

Lär dig programmera! Prova på programmering med enkla exempel! Björn Regnell

Instruktioner för att kunna programmera på skolans datorer

C-programmering, föreläsning 1 Jesper Wilhelmsson

Föreläsning 2. Variabler, tilldelning och kodblock{} if-satsen Logiska operatorer Andra operatorer Att programmera

Classes och Interfaces, Objects och References, Initialization

Filer och structer Del 2

Fortsä'ning Pekare. Ulf Assarsson. Originalslides av Viktor Kämpe

Övningsuppgifter till föreläsning 2 Variabler och uttryck

Introduktion till programmering och Python Grundkurs i programmering med Python

Enklast att skriva variabelnamn utan ; innehåll och variabelnamn skrivs ut

NetBeans 7. Avsikt. Projektfönster

Skriv i mån av plats dina lösningar direkt i tentamen. Skriv ditt kodnummer längst upp på varje blad.

Programmering, grundkurs, 8.0 hp HI1024, omtentamen, TEN1. Tisdagen den 7 juni 2011,

EDAA20 Programmering och databaser. Mål komprimerat se kursplanen för detaljer. Om att lära sig programmera. Föreläsning 1-2 Innehåll.

Tentamen i TDP004 Objektorienterad Programmering Praktisk del

Objektorientering i liten skala

Transkript:

Systemprogrammering i C på en mobil plattform Kurskompendium Jonny Karlsson 8.3.2011 1

INNEHÅLL 1. INTRODUKTION TILL C... 4 2. KOMPILERING AV C-PROGRAM I LINUX-MILJÖ... 5 2.1. Kompilering av program med flere källkodsfiler... 5 3. DE VÄSENTLIGASTE SKILLNADERNA MELLAN C OCH C++... 7 3.1. Pekare... 7 3.1.1. Användning av pekare för hantering av teckensträngar... 7 3.1.2. Pekare som parametrar till funktioner... 8 3.2. Poster (struct)... 9 3.3. Dynamisk minneshantering... 11 4. FILHANTERING I C... 14 4.1. Strömpekare... 14 4.2. Att skriva till / läsa från strömpekare... 14 5. INTRODUKTION TILL OPENGL ES 2.0... 16 5.1. Versionshistoria och användningsområde... 16 6. OPENGL ES 2.0 SDK FÖR LINUX... 17 6.1. Att komma igång med SDKn... 17 6.2. Att skriva kod och kompilera + köra program i Emulator... 17 6.3. Att kompilera OGLES2 program för Nokia N900... 17 6.3.1. Scratchbox... 17 7. STRUKTUREN AV ETT OGLES2 PROGRAM... 19 7.1. OpenGL ES Shading Lanugage och Shaders... 19 7.2. Grafikrörledningen graphics pipeline i OGLES2... 19 7.3. Att bygga ett OGLES2 program... 21 7.3.1. Initialisering... 21 7.3.2. Skapa Shaders... 21 7.3.3. Kompilera shaders och länka ihop dem till ett shader program... 21 7.3.4. Skapa och rita ut triangeln... 22 2

8. ATT RITA UT PRIMITIV I OGLES2... 25 8.1. Funktionen gldrawarrays()... 25 8.2. Funktionen gldrawelements()... 26 8.3. Utritningsoperationer... 26 8.3.1. Gallring ( culling )... 26 9. ATT PROGRAMMERA SHADERS... 28 9.1. Vertex shaders... 28 9.1.1. Datatyper... 28 9.1.2. Variabeltyper... 28 9.1.3. Precision av variabler... 29 9.2. Fragment shaders... 30 9.2.1. Variabeltyper... 30 10. ATT PROGRAMMERA TRANSFORMATIONER I OGLES2... 31 10.1. Rotation... 31 10.2. Perspektiv... 31 11. TEXTURER... 34 11.1. 2D-Texturer... 34 11.2. Skapa texturobjekt och ladda texturer... 35 11.2.1. Att läsa in texturdata från bitmap-filer... 36 11.3. Filtrering av texturer... 37 11.4. Texturkordinater... 37 11.5. Hantering av texturer i fragment shadern... 38 11.6. Att rita ut flera texturer... 40 12. HANTERING AV TANGENTBORDS-, MUS- OCH PEKSKÄRMSHÄNDELSER... 42 3

1. INTRODUKTION TILL C C är idag fortfarande ett av de vanligaste programmeringsspråken. Programmeringsspråket är utvecklat av AT&T Bell Labs 1973. C hör till de programmeringsspråk som haft störst inflytande på programvaruindustrin. Det finns knappast någon del av programvaruvärlden där C inte används eller använts. C är en föregångare till Java, C++ och C#. C anses idag vara mera ett lågnivå- eller systemprogrammeringsspråk än ett högnivåspråk. Även om C fortfarande kan användas för att skapa de flesta typer av applikationer är det högnivåspråk (såsom Java och C#) som tagit över på användarapplikationssidan, eftersom man med högnivåspråk kan utföra samma operationer med betydligt färre instruktioner än i C. Fördelen med C är dock att en erfaren programmerare kan skriva mer kompakta och resurssnåla program än vad som är möjligt med ett högnivåspråk. Av denna orsak lämpar sig C väldigt bra för systemprogrammering och i inbyggda system. Eftersom C har utvecklats hand i hand med UNIX har språket fått en väldigt stark ställning inom UNIX-industrin. C är fortfarande mycket använt t.ex. i GNU/Linux (vars kärna är skriven huvudsakligen i C) 4

2. KOMPILERING AV C-PROGRAM I LINUX-MILJÖ För att kompilera C-program i Linux-miljö kan man använda kommandot cc eller gcc på kommandoraden: gcc kallkodsfil.c o programnamn T.ex. om kallkodsfilen heter systemprogram.c och man vill att den körbara filen skall heta mittforstasystemprogram kan man med gcc kompilera så här: gcc systemprogram.c o mittforstasystemprogram 2.1. Kompilering av program med flere källkodsfiler Antag att ett C-program består av 3 källkodsfiler del1.c, del2.c, del3.c. För GNU C i Linux skapas en körbar binärfil med namnet progfil enklast med gcc -o progfil del1.c del2.c del3.c Alternativt kan behövliga kompilerings- och länkningsdirektiv inskrivas i en fil med namnet makefile eller Makefile. En makefile för ovanstående källkodsfiler kunde se ut så här: progfil: del1.o del2.o del3.o gcc del1.o del2.o del3.o -o progfil del1.o: del1.c gcc -c del1.c del2.o: del2.c gcc -c del2.c del3.o: del3.c gcc -c del3.c makefile - raderna skrivs alltid parvis, den undre raden indragen med tabulator. På övre raden skrivs först namnet på en fil före ett ':'-tecken. Efter ':'-tecknet skrivs namnen på de filer, från vilka filen före ':'-tecknet skapas. På den indragna undre raden skrivs kommandot, som skapar filen bakom ':'-tecknet. T.ex progfil skapas från del1.o, del2.o, del3.o med gcc del1.o del2.o del3.o -o progfil. makefile - direktiven skrivs "bakifrån": - först det direktiv som behövs för att skapa den körbara binärfilen (progfil) - sedan de direktiv som skapar de filer, som det första direktivet behöver (del1.o, del2.o, del3.o) - osv. skrivs direktiven enligt en trädformad struktur så att de sista direktiven skapar filer från givna källkodsfiler En körbar binärfil kompileras i två skeden. Först skapas en eller flere s.k. objektfiler (filtypen ".o"). Av källkodsfilerna i ett C-program kan objektfiler skapas oberoende av varandra. Detta underlättar felsökning i källkoden. Flere objektfiler kan sedan länkas ihop till en körbar binärfil. 5

Direktiven i en "makefile" verkställs med att utföra kommandot "make" från samma katalog. Härvid kontrolleras med hjälp av filernas tidsinformation vilka källkodsfiler har ändrats efter föregående "make". Endast behövliga direktiv utförs, vilket effektiverar skapandet av den körbara binärfilen. Detaljinformation om UNIX-kommandot "make" fås t.ex. med man make 6

3. DE VÄSENTLIGASTE SKILLNADERNA MELLAN C OCH C++ Detta kapitel är till stora delar kopierat ur Kary Främlings och Göran Pulkkis material i kursen C- språket2 från våren 2000 och 2003. 3.1. Pekare Pekarhantering i C avviker egentligen inte från C++ förutom att man i C inte kan använda sej av referenser på samma sätt som i C++ utan måsta använda rena pekare. Med tanke på pekarhanteringens väsentlighet i C är det ändå värt att repetera. Med en pekare avses en variabel som innehåller en minnesadress, d.v.s. som pekar till en given plats i datorns minne. Användning av pekare kan få ett program att bli mycket snabbare om det görs på ett klokt sätt. I C deklareras en pekarvariabel med hjälp av tecknet * (stjärna) enligt följande: void main() char *p1; /* Pekare på en char. */ int *p2; /* Pekare på ett heltal. */ float *p2; /* Pekare på ett flyttal. */ 3.1.1. Användning av pekare för hantering av teckensträngar I C -språket består en teckensträng av en tabell av tecken, vars sista tecken alltid bör vara noll -tecknet \0. Då vi deklarerar en tabellvariabel, är tabellvariabeln egentligen en pekare på tabellens första element, d.v.s. den innehåller minnesadressen till det första elementet. Exempel. Pekare, adress och värde (kompilerat och kört) #include <stdio.h> #include <string.h> /* För att kunna anvanda "strcpy()" */ void main() /* Teckenstrang for 19 tecken + \0 */ char teckenstrang[20]; char *p; /* Pekare på en teckensträng */ 7

/* Initialisering av teckenstrangen */ strcpy(teckenstrang, "Hejsan svejsan"); printf("%s\n", teckenstrang); /* 'p' pekar nu till början av teckenstrangen */ p = teckenstrang; printf("%s\n", p); /* 'p' pekar nu till början av teckenstrangen */ p = &teckenstrang[0]; printf("%s\n", p); Operationen p = teckenstrang; innebär att p nu pekar på början av teckensträngen. Faktum är att en pekarvariabel och en tabellvariabel i de flesta fall kan användas på samma sätt. Operationen p = &teckenstrang[0]; har samma effekt. Operatorn & ger nämligen adressen till den variabel som den tillämpas på. Om a är en variabel för ett heltal, ett tecken, ett flyttal, en post eller vilken annan typ som helst utom en tabell, så ger &a dess adress i minnet. Denna adress kan ges som värde åt en pekare av motsvarande typ, d.v.s. om a är en heltalsvariabel och vi vill skriva p = &a;, så måsta p vara deklarerad som en pekare till ett heltal. Annars ger kompilatorn ett felmeddelande. Operatorn * returnerar värdet som pekaren pekar på. Alltså, om p är en pekare till ett heltal, kan vi skriva a = *p; ifall variabeln a är en heltalsvariabel. Om pekaren p pekar på det första elementet i en tabell, så kan tabellens element i hänvisas till på flera olika sätt, t.ex *(p+i) eller p[i]. 3.1.2. Pekare som parametrar till funktioner Ifall en C -funktion vill ändra på värdet av någon av sina parametrar, måste parametrarna ges som pekare. Detta kallas för referensparametrar, i motsats till värdeparametrar som vi sett tidigare. Exempel. Referensparametrar (kompilerat och kört) #include <stdio.h> void swap(int *pa, int *pb) int tmp; tmp = *pa; *pa = *pb; *pb = tmp; 8

void main() int a = 1, b = 2; printf("a = %d, b = %d\n", a, b); swap(&a, &b); printf("a = %d, b = %d\n", a, b); Ifall parametrarna inte deklarerats som pekare gör swap -funktionen ingenting, d.v.s. den arbetar endast med sina lokala kopior av a och b. Exempel. implicita pekare för tabeller (kompilerat och kört) #include <stdio.h> #include <string.h> #define MAXCHAR 50 void swap(char *pa, char *pb) char tmp[maxchar]; strcpy(tmp, pa); strcpy(pa, pb); strcpy(pb, tmp); void main() char a[] = "Kalle", b[] = "Ville"; printf("a = %s, b = %s\n", a, b); swap(a, b); printf("a = %s, b = %s\n", a, b); Eftersom tabellvariabler redan är pekare, behöver det inte preciseras i funktionsdeklarationen! 3.2. Poster (struct) C är inte ett objektorienterat programmeringsspråk som C++ och man kan därför i C inte skapa klasser. Man kan dock i C skapa en egen datatyp dit man binder ett flertal olika andra datatyper genom att skapa en post (Eng. Struct). Hantering av poster påminner väldigt mycket om hantering av klasser. 9

Exempel. Deklaration och användning av en enkel post struct person int alder; char fornamn[10]; char efternamn[10]; ; int main(void) //deklarerar två variabler av posten struct person struct person p1, p2; //läser in värden till posten från tangentbordet scanf( %d, &p1.alder); scanf( %s, p1.fornamn); scanf( %s, p1.efternamn); scanf( %d, &p1.alder); För att slippa deklarera post-varibler med orden struct postnamn variabelnamn kan man använda sej av nyckelordet typedef vid deklaration av en post: typedef struct person int alder; char fornamn[10]; char efternamn[10]; pinfo; int main(void) //deklarerar två variabler av posten struct person pinfo p1, p2; //läser in värden till posten från tangentbordet scanf( %d, &p1.alder); scanf( %s, p1.fornamn); scanf( %s, p1.efternamn); scanf( %d, &p2.alder); 10

3.3. Dynamisk minneshantering Ett program kan inte alltid på förhand känna till hur mycket minne det kommer att behöva under sin exekvering. Dynamisk minneshantering innebär att ett program reserverar mera minne efter behov och frigör det då det inte längre behövs. De två mest använda funktionerna för att reservera minne är funktionerna malloc och calloc. Funktionen malloc() tar som parameter antalet bytes som skall reserveras. Returvärdet är en pekare till en void, så vanligtvis måste den egentliga typen anges med en konverteringsoperator. Funktionen calloc() tar som parametrar antalet datavärden som vi vill reservera plats för samt dessa datavärdens storlek. Returvärdet är en pekare till en void, så vanligtvis måste den egentliga typen anges med en konverteringsoperator. Standardfunktionen sizeof() returnerar storleken i bytes för den variabel eller datatyp som ges som värde. Exempel. Inläsning av ett variabelt antal teckensträngar (kompilerat och kört) #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_TEXT_LEN 128 main() char text[max_text_len]; char **s; int i, text_cnt; /* Fraga hur manga texter som skall sparas. */ printf("hur många texter? "); scanf("%d", &text_cnt); gets(text); /* Las in radbytet fore foljande "gets()". /* Skapa en tabell av pekare for att peka till dem. */ s = (char**) calloc(text_cnt, sizeof(char*)); /* Lat anvandaren mata in texterna. Spara dem dynamiskt. */ for ( i = 0 ; i < text_cnt ; i++ ) printf("mata in text %d: ", i); 11

gets(text); s[i] = (char *) malloc(strlen(text) + 1); strcpy(s[i], text); /* Skriv ut texterna igen. */ for ( i = 0 ; i < text_cnt ; i++ ) printf("%s\n", s[i]); /* Frigor det reserverade minnet. */ for ( i = 0 ; i < text_cnt ; i++ ) free(s[i]); free(s); Exempel 1 visar ett typexempel på en situation då vi behöver dynamisk minneshantering. Eftersom både malloc och calloc returnerar pekare, är vi tvungna att handskas med det reserverade minnet via pekare. Därför börjar vi med att reservera utrymme för så många pekare till teckensträngar som vi behöver. Sedan reserverar vi så mycket utrymme som krävs för varje teckensträng och sätter de tidigare reserverade pekarna att peka till dem. Glöm inte att frigöra det reserverade minnet med funktionen free() före programmet avslutas. Annars kan operativsystemet få stora problem senare (slut på minnet, t.ex.). Exempel 2. Dynamisk minneshantering med poster (kompilerat och kört) #include <stdio.h> #include <stdlib.h> struct person char *namn; int alder; ; main() struct person *ptab; int i, ant_pers; printf("hur manga personer? "); scanf("%d", &ant_pers); ptab = (struct person *) calloc(ant_pers, sizeof(struct person)); for ( i = 0 ; i < ant_pers ; i++ ) ptab[i].namn = "Alla har samma namn"; /*Fung. kanske inte alltid!*/ ptab[i].alder = 10 + i*10; 12

for ( i = 0 ; i < ant_pers ; i++ ) printf("%s ar %d\n", ptab[i].namn, ptab[i].alder); free(ptab); Exempel 2 visar hur dynamisk minneshantering kan göras med poster. Lägg speciellt märke till linjen ptab[i].namn = "Alla har samma namn";! Det handlar här om en initialisering av en pekare till en teckensträng. I de flesta kompilatorer (om inte alla), så reserveras det automatiskt minne för alla teckensträngar som är konstanter. Det som vi gör i det här programmet är att vi sätter varje.namn -pekare att peka till samma teckensträng! 13

4. FILHANTERING I C Vid filhantering i C kan man antingen jobba med fildeskriptorer eller strömpekare. Vi kommer i denna kurs att koncentrera oss på strömpekare. 4.1. Strömpekare En strömpekare deklareras enligt följande: FILE *filpek; Det finns i C ett antal fördefinierade strömpekare: Strömpekare Betydelse stdin standard input (bl.a.. tangentbordet) stdout standard output (bl.a. bildskärmen) stderr felmeddelanden (kommer på bildskärmen) En strömpekare man själv deklarerat kan ställas in att peka till en fil (eller annan input output enhet) med fopen-kommandot: FILE *fopen(const char *path, const char *mode) path mode returnerar Teckensträng som angiver sökstigen till fil man vill skapa en filpekare till Ett tecken eller kombination av tecken som anger vad man vill göra med strömpekaren (t.ex. lagra data eller hämta data) En strömpekare Alternativ för mode : mode Betydelse r Öppna filen för läsning w Öppna filen för skrivning (skriver över gamla innehållet om filen existerar) a Öppnar en fil för utökning / appending (skriver ej över gammalt innehåll) b Läser eller skriver binärt (används i kombination med ovanstående) 4.2. Att skriva till / läsa från strömpekare För att läsa data ur en fil via en strömpekare kan bl.a. funktionerna fscanf eller fread användas. int fscanf(file *stream, const char *format,...); strream Strömpkare till en fil som man öppnat för läsning ( r ) format Samma som i vanlig scan, t.ex. %d ifall man vill läsa heltal... En lämplig variabel dit datat sparas (samma som i vanlig scanf) 14

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); ptr size nmemb stream En pekare på en datastruktur (t.ex. struct) dit data läses in Hur mycket man vill läsa in i formen bytes Hur många element av samma datastorlek man vill läsa in Strömpekare till en fil som öppnats för läsning ( r ) För att skriva data till en fil via en strömpekare kan bl.a. funktionerna fprintf eller fwrite användas. int fprintf(file *stream, const char *format,...); stream Strömpkare till en fil som man öppnat för skrivning ( r eller a ) format Samma som i vanlig printf, t.ex. %d ifall man skriva in ett heltal... Variabeln innehållande datat man vill skriva in (samma som i vanlig scanf) size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream); ptr size nmemb stream En pekare på en datastruktur (t.ex. struct) där det data finns som man vill skriva in Storleken i bytes på det data man vill skriva in Hur många element av samma datastorlek man vill skriva in Strömpekare till en fil som öppnats för skrivning ( r eller a ) Om man öppnar en fil med fopen, är det också viktigt att komma ihåg att stänga den när filpekaren inte längre behövs: fclose(file *stream); 15

5. INTRODUKTION TILL OPENGL ES 2.0 OpenGL ES (OpenGL for Embedded Systems) bygger på OpenGL 3D grafik API men är anpassad för inbyggda system såsom mobiltelefoner, PDAn, och spelkonsoler. OpenGL ES kordineras av teknologikonsortiet Khronos Group, Inc. 5.1. Versionshistoria och användningsområde OpenGL ES 1.0 och OpenGL ES 1.1. Statiskiska shaders (funktioner för effekter) och en statisk funktions-api för att använda sej av shaders. Open GL ES 1.0 används som officiellt 3D grafik-api på: - Symbian OS - Android-plattformen. - Version 1.0 med viss funktionalitet plockad ur version 2.0 fungerar som officiell grafik-api på Playstation 3 tillsammans med libgcm-biblioteket. OpenGL ES 1.1 används på: - Android 1.6 - iphone - ipod Touch - ipad - Nintendo 3DS OpenGL ES 2.0. Publicerades för offentligheten i mars 2007. Ersätter den statiska funktions- APIn i V1.0 och V1.1 med programmerbara shaders. Ger mera effektivitet i programmen och mera möjligheter ( eftersom effekter kan programmeras ) men är svårare att lära sej! Är ej bakåtkompatibel med V1.0 och V1.1. Följande plattformer stöder OGLES2: - iphone (3GS och senare) - ipod Touch (3:e genarationen och senare) - Android plattformen version 2.2 och senare - Pandora console - WebGL (OpenGL för Internet bläddrare) - Vissa Nokia telefoner: o Maemo (N900) o Meego o Symbian 3 (N8) - Flera Samsumg telefoner (bl.a. Galay X och Wave) - Archos Internet Tablets Pandora konsolen och används redan bl.a. på nyare iphone modeller och på Nokia N900. Det ryktas även att Windows Mobile 7 kommer att börja stöda OpenGL ES 2.0 OpenGL ES 2.1. Har inte ännu publicerats! Kommer att erbjuda ett texturkomprimeringschema som erbjuder bättre kvalitet än tidigare. 16

6. OPENGL ES 2.0 SDK FÖR LINUX Utvecklingsmiljöer (SDKn) för OpenGL ES 2.0 kan laddas ner från http://www.imgtec.com/powervr/insider/sdk/khronosopengles2xsgx.asp I denna kurs kommer vi att jobba med Linux-versionen eftersom det med den är lättast att utveckla 3D-program för Nokia N900 som vi kommer att använda som övningstelefon i kursen. Vi kommer också att använda C som programmeringsspråk eftersom en 3D applikation programmerad i C ger bästa portabiliteten mellan olika plattformer. Alla inbyggda plattformer har inte utvidgat stöd för C++ medan de flesta har start stöd för C. 6.1. Att komma igång med SDKn På Ubuntu-imagen, som delas ut i kursen, finns OGLES2 (OpenGL ES 2.0) SDKn installerad i katalogen /home/meego/ogles2-sdk/sdkpackage_ogles2. I katalogen /home/meego/ogles2- sdk/sdkpackage_ogles2 finns en mängd olika exempelprogram vars källkoder kan studeras, kompileras och köras. Observera att dessa exempel är skrivna i C++ och inte i C som vi kommer att använda i kursen. Användingen av API:n ser dock ändå lika ut både i C och C++ så koden är ganska lättkonverterad till C. Att bekanta sej med dessa exempel är därför ett bra sätt att komma igång med kursen. 6.2. Att skriva kod och kompilera + köra program i Emulator OGLES2 program kan emuleras i PC-miljö på det sättet att man skapar ett vanligt X11-fönster som bas för OGLES2- ritfunktionerna. Koden kan skrivas t.ex. i gedit-texteditorn (har stöd för C-syntax) och kompileras från kommandoraden m.h.a. en Makefile. Exempelprogrammen har färdiga Makefiles så de är väldigt lätta att kompilera. Efter kompilering (om kompileringen lyckas) skapas en exekverbar fil i filsystemet som man kan starta via kommandoraden. 6.3. Att kompilera OGLES2 program för Nokia N900 Eftersom Maemo plattformen har stöd för X Window System (X11) kan ett X11-fönster skapas som basfönster för OGLES2, helt på samma sätt som i emulatorn. För att få programmet kompilerat för Nokia N900 kan t.ex. en cross compiler som Scratchbox användas. 6.3.1. Scratchbox Med Scratchbox kan man kompilera program för olika processorarkitekturer och plattformer, se www.scratchbox.org. För att kompilera för Nokia N900 (Maemo 5) bör Maemo SDK först installeras i Scratchbox. I klassrummet där vi kör kursen, finns Scratchbox och Maemo SDK färdigt installerade. 17

Ett OGLES2 program för Nokia N900 kan kompileras i Scratchbox enligt följande: Flytta OGLES2-källkoden du vill kompilera till filsystemet på Scratchbox: o Öppna ett konsolfönster och flytta filerna t.ex. till katalogen /scratchbox/users/meego/home/meego (hemkatalogen för användaren meego på Scratchbox) Logga in på Scratchbox: kör /scratchbox/login från konsolfönstret (detta kommer att kasta in dej i katalogen /scratchbox/users/meego/home/meego) Kör sb-menu för att välja target device : o Välj select och N900 Paketet "libgles2-sgx-img-dev" bör vara installerat i Scratchbox för att kunna kompilera OGLES2-program. På övningsdatorerna i F365 finns detta paket färdigt installerat Skapa en Makefile för kompilering (Du kan också använda samma Makefile som vi använt för emulatorn): OPTS= -Wl,--rpath-link=/usr/lib/libEGL.so -L/usr/lib/libEGL.so -legl -lglesv2 -L/usr/X11R6/lib -lx11 -lxau oglesprog : oglesprog.o esbase.o gcc fargkub.o esbase.o $(OPTS) -o fargkub oglesprog.o : oglesprog.c gcc -c oglesprog.c $(OPTS) esbase.o : esbase.c gcc -c esbase.c $(OPTS) #esbase.c är en källkodsfil vi använt i kursen som bas för att skapa bl.a. bakgrundsfönster för OGLES2. (Kräver #att filen esbase.h finns i samma katalog). Filen oglesprog.c är ditt eget OGLES2-program. Kompilera med make Om kompileringen lyckas är det sedan bara att flytta över den exekverbara filen till Nokia N900 o Spara den exekverbara filen någonstans i din hemkatalog på penti.arcada.fi o Koppla upp Nokia N900 på Arcadas WLAN o Överför filen från din hemkatalog till telefonen med t.ex. scp-kommandot från telefonens terminalfönster: scp användarnamn@penti.arcada.fi:fullasökstigtillfil./ 18

7. STRUKTUREN AV ETT OGLES2 PROGRAM OpenGL ES 2.0 består egentligen av två väsentliga delar: OpenGL ES 2.0 API specifikationen: OpenGL ES Shading Language specifikationen (OpenGL ES SL) OpenGL ES kommandon behöver även en renderingsmiljö ( rendering context ), eller låt oss kalla det för ett sorts botten för OGLES2 som används för att lagra olika variabler odyl., och en rityta (ett fönsterbotten dit OGLES2 applikationer kan rita). OGLES2 tar inte ställning till hur renderingsmiljön skall skapas eller hur renderingsmiljön skall kopplas till den enhetsspecifika fönstermiljön. Till detta ändamål används gränssnittet EGL. EGL fungerar alltså som ett gränssnitt mellan OpenGL och fönstersystemet som finns på den enhet OpenGL programmet körs. 7.1. OpenGL ES Shading Lanugage och Shaders OpenGL ES SL används för att programmera effekter (shaders) som ska utföras på en vertex (hörnpunkt i en primitiv, t.ex. ett hörn i en triangel) och på varje pixel av en figur. Det är egentligen ett skilt programmeringsspråk men som påminner väldigt mycket om C. Shaders är egentligen små program, vars kod bäddas in och kompileras on the fly i OGLES2 programmet och som exekveras på grafikhårdvaran och som förenklat sagt transformerar om inputdata till en figur på skärmen. I ett OGLES2 program bör finnas en Vertex Shader och en Fragment Shader: Vertex Shader. En Vertex Shader exekveras på varje vertex av en figur (t.ex. varje hörn av en triangel ifall figuren är en triangel) och används för att utföra olika beräkningar på varje vertex. En vertex shader kan t.ex. utföra en transformation på en position m.h.a. matrisberäkning. Fragment Shader. Exekveras på varje pixel av en figur och används t.ex för att, beräkna färg och för beräkningar av olika ljuseffekter. Mera om shaders senare i kursen! 7.2. Grafikrörledningen graphics pipeline i OGLES2 Med grafikrörledningen menas i princip processen för hur en grafisk figur skapas. OGLES2 grafikrörledningen ser ut som i figuren nedan. 19

Vertex Arrays/Buffer Objects: Specificerar data för en vertex och är en buffer som sparas i applikationens adressutrymme. Vertex Shader: Utför beräkningar / vertex Primitive Assembly andrasterization: Fragment Shader: Utför beräkningar på varje pixel (fragment) av en figur som skapats i Rasterization-skedet Per-Fragment Operations: Mera om detta senare! Framebuffer: En buffer som innehåller den färdigt skapade bilden som är redo att visas på en datorskärm 20

7.3. Att bygga ett OGLES2 program I detta avsnitt tar vi ett program som ritar en 2D-triangel som exempel och tittar steg för steg på koden för hur programmet skall byggas upp. 7.3.1. Initialisering Innan vi kan börja rita någonting med OGLES2 behöver vi skapa en renderingsmiljö (EGL) och ett ritbotten. Renderingsmiljön EGL är plattformsoberoende och initialieringen av den ser alltid lika ut oberonde på vilken typ av enhet man kör den. Ritbottnet är däremot plattformsspecifik. När vi jobbar i Linux-miljö skapar vi ett X11-fönster för detta. Initialiseringen av ritbottnet och EGL består av otaliga rader kod, men eftersom denna process är lika i alla OGLES2 program behöver vi inte titta närmare på hur detta fungerar. Till denna kurs har skapats ett programbibliotek esbase.h med bl.a. två funktioner createnativeeglwindowtype() och initegl() som sköter om hela initialiseringen. Resultat av initialiseringen är EGL-variabler och X11-variabler som behövs senare i programmet för att få ut grafik på skärmen. Filerna esbase.h och tillhörande esbase.c kan laddas ner från kurshemsidan 7.3.2. Skapa Shaders Vi behöver nu skapa två Shaders med OpenGL SL, en shader som skall exekveras på varje vertex (vertex shader) och en som skall exekveras på varje pixel/fragmen (fragment shader). Exempel på en simpel vertex shader: attribute vec4 a_position; void main() gl_position = a_position; Vertex shadern ovan tar in en position för en vertex som parameter (av typen vektor med fyra platser). Variabeln gl_position är vertex shaderns output och kommer att innehålla vertexpositionen omformad till clip kordinater. Exempel på en simpel fragment shader: void main() gl_fragcolor = vec4(1.0, 1.0, 0.66, 1.0; Fragment shadern ovan ställer in en viss färg för varje pixel av en figur 7.3.3. Kompilera shaders och länka ihop dem till ett shader program Innan shader programmen kan användas måste de kompileras och länkas. Med följande funktioner: 21

const unsigned int shader = glcreateshader(type); glshadersource(shader, 1, (const GLchar**)&source, NULL); glcompileshader(shader); m_shaderprogram = glcreateprogram(); glattachshader(m_shaderprogram, vertexshader); glattachshader(m_shaderprogram, fragmentshader); gllinkprogram(m_shaderprogram); int success; glgetshaderiv(shader, GL_COMPILE_STATUS, &success); if (success == 0) glgetshaderinfolog(shader, sizeof(errormsg), NULL, errormsg); Innan vi kan ta ibruk shader-programmet måste vi på något sätt skapa handlers till vertex shaderns attribut så att vi kan förse shadern med input data: glbindattriblocation(programobject, vertex_array, namn på vertex shader input ); Sen är det bara att ta ibruk vårt shader-program (innehållande vertex- och fragment shadern): gluseprogram(programobject); 7.3.4. Skapa och rita ut triangeln Innan vi kan börja rita någonting måste vi så att säga tömma skärmen genom att ställa in en bakgrundsfärg: glclearcolor(red, blue, grean, alpha);//där RGBA värdena är flyttal 0-1 Sedan måste vi skapa en tabell av vertex-data för triangeln. Vertexkordinater ges som flyttal mellan -1 och 1 och kordinater skall ges för x, y och z: GLfloat afvertices[] = -0.3f,-0.3f,-0.0f, 0.3f,-0.3f,0.0f, -0.3f,0.3f,0.3f; Den vertex-tabell vi nu skapat är lagrad i datorn minne. Detta data måste kopieras till grafikkortsminnet innan vi kan anropa funktioner som ritar ut triangeln. För att göra applikationen, kopierar vi inte över datat till grafikminnet vid varje anrop av en utritningsfunktion utan i stället cachar vi datat i grafikminnet: //Vertex data (positioner för trianglen x, y, och z) //Skaper ett vertex buffer objekt (VBO) som används för att //lagra vertex-data i grafikkortsminnet glgenbuffers(1, &ui32vbo); 22

// Förbereder VBO so att vi kan fylla den med data glbindbuffer(gl_array_buffer, ui32vbo); // Sparar in data i buffern glbufferdata(gl_array_buffer, sizeof(afvertices), afvertices, GL_STATIC_DRAW); Nu är det bara att börja rita ut triangeln på skärmen: // Ritar en triangel i 800 frames for(i = 0; i < 800; ++i) // Check if the message handler finished the demo if (bdemodone) break; //Tömmer färgbuffern glclear(gl_color_buffer_bit); if (!TestEGLError("glClear")) goto cleanup; /* Tar ibruk ett vertexattribut vid index VERTEX_ARRAY. Vi band tidigare ihop detta index med input variabeln i vår vertex shader "MyVertex" */ glenablevertexattribarray(vertex_array); //Sätter vertex data till detta attributindex. Betyder i //praktiken att vi här ger över vertex data till //vertex shadern glvertexattribpointer(vertex_array, 3, GL_FLOAT, GL_FALSE, 0, 0); //Ritar en triangeln på skärmen. Sista parametern anger hur många //hörnpunkter (vertex) det finns i triangeln gldrawarrays(gl_triangles, 0, 3); if (!TestEGLError("glDrawArrays")) goto cleanup; /* Swap Buffers. Brings to the native display the current render surface. */ eglswapbuffers(e.egldisplay, e.eglsurface); 23

if (!TestEGLError("eglSwapBuffers")) goto cleanup; 24

8. ATT RITA UT PRIMITIV I OGLES2 I OGLES2 kan endast följande primitiv ritas ut: Trianglar Linjer Punkter Det finns två olika funktioner för att rita ut dessa: gldrawarrays() gldrawelements() 8.1. Funktionen gldrawarrays() void gldrawarrays(glenum mode, GLint first, GLsizei count) mode first count Definierar den primitiv man vill rita, tillåtna värden är: GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINElOOP, GL_TRIANGLES_,GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN Definierar varifrån (från vilken vertex-kordinat) i vertex-tabellen vi ska börja rita Definerar antalet hörnpunkter (vertex) som skall ritas Exempel på modes : 25

8.2. Funktionen gldrawelements() void gldrawelements(glenum mode, GLsizei count, GLenum type, const GLvoid *indices) mode Definierar den primitiv man vill rita, tillåtna värden är: GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINElOOP, GL_TRIANGLES_,GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN count type Definerar antalet hörnpunkter (vertex) som skall ritas Specificerar typen av hörnpunkterna som definieras i indices. Tillåtna värden är: GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT Indices Pekare till en plats där en tabell av hörnpunkter sparas. Idén med indices-tabellen är att definera hur givna hörnpunkter förenas och binds samman Exempel på användning av gldrawelements(): Vi vill rita en kvadrat m.h.a. gldrawelements(): GLfloat hornpunkter[] = 0,3f, 0.3f, 0.0f, //Uppe till höger -0.3f, -0.3f, 0.0f, //Nere till vänster -0.3f, 0.3f, 0.0f, //Uppe till vänster 0.3f, -0.3f, 0.0f ; //Nere till höger GLubyte indices[6] = 0, 2, 3, 2, 1, 3 ;...... gldrawelements(gl_triangles, 6, GL_UNSIGNED_BYTE, indices); 8.3. Utritningsoperationer När formatet för en figur är givet, utför OGL en del operationer och kontroller mot figurens koordinater för att bestämma om figuren skall ritas ut eller inte. Befinner sig figuren t.ex. innanför kamerans räckvidd ( clipping )? Skalla alla sidor av en figur ritas ut ( culling )? 8.3.1. Gallring ( culling ) Gallring eller culling innebär att endast en sida av en figur ritas ut. För att kunna bestämma vilken sida av en figur som ritas ut måste vi först veta vad som är fram och vad som är bak på en figur, dvs. vilken sida som är vänd mot kameran. En figurs framsida bestäms av i vilken ordning 26

hörnpunkterna definierats och roterar runt triangelns mittpunkt (medsols eller motsols). Som standard i OGLES2 är en figurs framsida den sida där hörnpunkterna sammanbinder figuren i en riktning som går motsols, se figuren nedan. En figurs orientering, dvs. vilken sida som skall vara fram och vilken sida som skall vara bak, kan också ställas in med följande funktionsanrop: void glfrontface(glenum dir) dir Definierar den orientering en figur med framsidan vänd mot kameran skall ha. Kan vara GL_CW (ClockWise = medsols) eller GL_CCW (Counter- ClockWise = motsols). GL_CCW är standard När vi vet/definierat vad som är framsidan av en figur kan vi bestämma vilken sida som inte skall ritas ut med ett anrop till glcullface: void glcullface(glenum mode) mode Specificerar vilken side som inte skall ritas ut. Alternativen är GL_FRONT, GL_BACK och GL_FRONT_AND_BACK Vi måste också till slut ge tillåtelse åt OGLES2 att använda culling : glenable(gl_cull_face); 27

9. ATT PROGRAMMERA SHADERS Shaders är små program som exekveras i grafikprocessorn. Shader-programmen kompileras och länkas ihop on the fly av OGLES2-programmet, avsnitt 6.2 och 6.3. Varje OGLES2-program måste ha en vertex shader och en fragment shader. 9.1. Vertex shaders Den simplaste vertex shadern ser ut så här: //Tar in vektor innehållande kordinater för en hörnpunkt som parameter attribute vec4 a_position; void main() //Sätter positionen för hörnpunkten till den position som gavs som parameter //i clip-kordinatsystemet gl_position = a_position; 9.1.1. Datatyper Datatyp Betydelse vec4 En vektor på 4 element mat4 En 4 x 4 matris...... attribute 9.1.2. Variabeltyper Är en inparameter till vertex shadern. Är från OGLES2 programmet åtkomligt t.ex. m.h.a följande funktion: glbindattriblocation(programobject, vertex_array_position, namnpåvariabelishadern ); Funktionen ovan måste anropas strax innan shader-programmet länkas, se 6.3.3. Efter glbindattriblocation är alltså attribute-variabeln i vertex shadern åtkomlig via det indexnummer som gavs som andra parameter (vertex_array_position). Man kan sedan ge in ett värde åt attribute variabeln med t.ex. följande funktion: 28

glvertexattrib4fv(vertex_array_position, pekare på float_tabell); vertex_array_position är alltså då det index-värde som attribute-variabeln är bunden till och pekare_på_float_tabell är pekare till en tabell som innehåller de data som man vill ge in attribute-variabeln i vertex shadern. Data man vill ge in kan t.ex. vara färgkoder eller positioner för en hörnpunkt. OBS! om man ger in positioner för hörnpunkter till vertex-shadern måste de ges in med följande funktioner: //Före frame-loopen måste vi casha vertex-positionerna i grafikkortets minne: glgenbufers(1, &handler); glbindbuffer(gl_array_buffer, handler) glbufferdata(gl_array_buffer, sizeof(tabellmedhornp), tabellmedhornp, GL_STATIC_DRAW) //I frame-loopen, fore anrop av gldrawarrays() eller gldrawelements() ger vi sedan till slut //över data till vertex-shadern glenableattribpointer(vertex_array, 3, GL_FLOAT, GL_FALSE, 0, 0); uniform En variabel som deklarers som uniform är en konstant variabel. Kan t.ex. användas för att ta in en rotationsmatris från OGLES2-programmet. En uniform variabel kan tilldelas ett värde från OGLES2-programmet m.h.a följande funktionsanrop: int loc = glgetuniformlocation(programobject, namnpåuniformvariabelishadern ); gluniformmatrix4fv(loc, 1, GL_FALSE, pekarepådatatabell) där pekarepådatatabell kan t.ex. vara en pekare på en tabell innehållande en rotationsmatris. varying Är en ut-parameter från vertex-skadern. En variabel som deklareras som varying kan tas som indata i fragment-shadern 9.1.3. Precision av variabler Precision av variabler ( precision qualifiers ) innebär att man bestämmer en noggrannhet (hur många decimaler) för t.ex. en float variabel, dvs. De olika alternativen som kan användas är: 29

lowp, mediump och highp. Om OGLES2-programmet körs i emulatorn, behöver man inte ange precision. Men om programmet körs på Nokia N900 krävs det att alla variabler förutom de variabler som är deklarerade som attribute specificeras med en precision. Exempel på vertex shader innehållande variabler med precision: const char* pszvertshader = "attribute vec4 myvertex;\n" "attribute vec4 fargkod;\n" "attribute vec2 myuv;\n" "uniform highp mat4 yrotmatris;\n" "uniform highp mat4 xrotmatris;\n" "uniform highp mat4 transmatris;\n" "varying lowp vec4 outcolor;\n" "varying mediump vec2 mytextcoord;\n" "void main()\n" "\n" "outcolor = fargkod;\n" "mytextcoord = myuv;\n" "gl_position = myvertex * yrotmatris * xrotmatris * transmatris;\n" "\n"; 9.2. Fragment shaders Exempel på en fragment shader: const char* pszfragshader = "uniform mediump sampler2d sampler2d;\n" "varying mediump vec2 mytextcoord;\n" "void main ()\n" "\n" "gl_fragcolor = texture2d(sampler2d,mytextcoord);\n" "\n"; 9.2.1. Variabeltyper Uniform Smma betydelse som i vertex-shadern, se 9.1.2 Varying Är en inparameter från vertexshadern. Bör ha samma namn och precision som motsvarande variabel i vertex-shadern Precision Samma betydelse som i vertex-shadern, se 9.1.2. 30

10. ATT PROGRAMMERA TRANSFORMATIONER I OGLES2 I OGLES2 finns inga färdiga funktioner för transformationer såsom skalning, förflyttning ( translation ) och rotation. Dessa funktioner måste skapas av användaren själv. 10.1. Rotation Ett sätt att rotera ett grafikobjekt är att ge en rotationsmatris som parameter till vertex shadern. Vertex shadern multiplicerar sedan rotationsmatrisen med vektorn som innehåller kordinaterna för hörnpunkten (gl_position). För att åstadkomma en rotation måste vi ju rotera hörnpunkterna i en figur runt x-, y-, eller z- axeln beroende på hur vi vill rotera. Rotationsmatris för rotation av en hörnpunkt runt x-axeln 1 0 0 0 0 cos(v) sin(v) 0 0 -sin(v) cos(v) 0 0 0 0 1 Rotationsmatris för rotation av en hörnpunkt runt y-axeln cos(v) 0 -sin(v) 0 0 1 0 0 sin(v) 0 cos(v) 0 0 0 0 1 Rotationsmatris för rotation av en hörnpunkt runt z-axeln cos(v) sin(v) 0 0 -sin(v) cos(v) 0 0 0 0 1 0 0 0 0 1 Där v=rotationsvinkeln angiven i radianer 10.2. Perspektiv För att kunna ställa in hur långt/djupt vi vill se på Z-axeln och för att få djupet i figurer att se realistiskt ut måste man multiplicera alla hörnpunkter med en perspektivmatris. Det vi vill åstadkomma är någonting som motsvarar följande bild: 31

Som standard klipps figurer ganska fort av på djupet och figurer transformeras inte heller automatiskt så att de ser realistiska ut. Föreställ dej t.ex. att du står på en rak landsväg och tittar rakt fram. Då borde landsvägen se smalare och smalare ut ju längre bort du ser. Denna effekt vill vi alltså bl.a. åstadkomma med en perspektivmatris. En perspektivmatris ser ut så här: 32

left = Vänstra sidan av synfältet (t.ex. -0.4) right = Högra sidan av synfältet (t.ex. 0.4) near = Avståndet från ögat/kameran till ritytans början på Z-axeln (alla figurer so ritas ut på en z-koordinat mindre än near kommer att klippas bort). Måste vara positivt. (t.ex. 1.0) far = Avståndet från ögat/kameran till ritytans slut på Z-axeln (alla figurer som ritas ut på en z-koordinat större än far kommer att klippas bort). Värdet för far kan i princip sättas hur stort som helst (t.ex. 100.0) Hur starkt figuren formar sig efter djupet ( trycks ihop ju längre bort på z-axeln man går ) är beroende av hur nära left och right är varandra. Ett near-värde på ungefär -0.4 och ett far-värde på 0.4 brukar se realistiskt ut i de flesta fall. Exempelvärden: 33

11. TEXTURER En textur är en datamängd som bestämmer en ytas utseende. I sin enklaste form består en textur av en tvådimensionell bitmap, där en bitmap består av ett rutnät av fyrkantiga bildelement (pixlar). För varje enskilt bildelement lagras information om dess färg enligt ett förutbestämt schema. En av de fundamentalaste operationerna som används vid rendering av 3D grafik är att sätta texturer till en yta. Texturer gör det möjligt att sätta till data för extra detaljer i en figur, se bilden nedan. 11.1. 2D-Texturer En 2D-textur är den enklaste och vanligaste formen av texturer som används i OpenGL ES. Tekniskt är en 2D-textur en tvådimensionell tabell av pixel-data. Pixel-data i en textur kan i OpenGL ES representeras av flera olika format: GL_RGB (Red, Green, Blue) GL_RGBA (Red, Green, Blue, Alpha) GL_LUMINANCE (Ljusstyrcka/blänkande) GL_LUMINANCE_ALPHA GL_ALPHA 34

11.2. Skapa texturobjekt och ladda texturer Det första steget är att skapa ett texturobjekt. Ett texturobjekt fungerar som ett handtag (hander) till texturdata som behövs vid rendering. I OGLES2 är ett texturobjekt en unsigned int. void glgentextures(glsizei n, GLuint *textures) n textures Antalet texturobjekt man vill skapa En tabell över unsigned int variabler som innehåller n stycken texturobjekt När ett texturobjekt skapats måste texturobjeketet bindas till applikationen så att det kan användas: void glbindtexture(glenum target, GLuint texture) target texture GL_TEXTURE_2D eller GL_TEXTURE_CUBE_MAP Handtaget (handlern) till texturobjektet som man vill binda Nästa steg är att ladda själva textur-data: void glteximage2d(glenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels) target GL_TEXTURE_2D om det är en tvådimensionell textur som används level mip-level. Vi nöjer oss med att denna kan vara 0! internalformat Formatet på texturdatat. Kan vara någåt av följande: GL_RGBA, GL_RGB, GL_LUMINANCE_ALPHA, GL_LUMINANCE eller GL_ALPHA width Bredden på texturen angiven i pixlar height Höjden på texturen angiven i pixlar border Alltid 0 i OGLES2 format Samma värde som i internalformat type Pixeldatans typ: Vi nöjer oss med att detta kan vara GL_UNSIGNED_BYTE pixels Texturdata för varje pixel för texturen / bilden 35

Exempel på en enkel textur: 1 x 1 pixels bild, 3 bytes / pixel (R, G, B) GLubyte pixels[1 * 3] = 255, 0, 0, //Röd 11.2.1. Att läsa in texturdata från bitmap-filer När man gör texturer i verkligheten skapar man i allmänhet inte texturer manuellt (som i exemplet ovan) utan i stället laddar man in texturdata från en fil och där själva texturen skapats i ett grafiskt textur-/bildhanteringsprogram. Texturer sparas ofta i Windows bitmap (.bmp) format. I OGLES2 programbiblioteket finns ingen färdig funktion för inladdning av texturer från filer. Om man jobbar i C, som vi gör i denna kurs, är man tvungen att göra en egen funktion för att ladda in en bitmap. För att kunna ladda in en bitmap måste vi först veta hur.bmp formatet ser ut. BMP filstrukturen: BITMAPFILEHEADER (14 BYTE) bftype (16bit) Filtyp bfsize (32bit) Storleken i bytes för hela bitmap-filen reserved1 (16bit) Alltid 0 reserved2 (16bit) Alltid 0 boffbits (32bit) Mellanrummet mellan bitmapfileheader och bitmap bitarna BITMAPINFOHEADER (40 BYTE) bisize (32bit) biwidth (32bit) Bredden på bildfilen i pixlar biheight (32bit) Höjden på bildfilen i pixlar biplanes (16 bit) Alltid 1 bibitcount (16 bit) Antalet bitar / pixel bicompression (32 bit) Komprimeringstyp bisizeimage (32 bit) Bildens storlek i byte bixpelspermeter (32 bit) Pixlar / meter på x-axeln biypelspermeter (32 bit) Pixlar / meter på y-axeln biclrused (32 bit) Antalet färger biclrimportant (32 bit) Antalet viktiga färger BILDDATA (RESTEN AV FILINNEHÅLLET) 36

11.3. Filtrering av texturer Vi behöver använda oss av olika filter för att få en textur att passa in i en figur. Orsaken till detta är att en texturs pixel inte nödvändigtvis behöver motsvara en pixel på skärmen (t.ex. om en figurs yta är mycket mindre än texturen som fästs på ytan). Låt oss tillsvidare nöja oss med att vi behöver definiera filtreringsmetoder för en textur men vi tar inte ännu ställning till exakt vad vår filtreringsmetoder gör. Det vi minst behöver är ett minification filter och ett magnification filter. Ett magnification filter sköter om att skala en texturs pixel på rätt sätt när en texturs pixel är större än en skärmpixel. Ett minification filter behövs om en texturs pixel är mindre än en skärmpixel. Exempel på hur ett minification filter kan definieras: gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_NEAREST); Exempel på hur ett magnification filter kan definieras: gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 11.4. Texturkordinater För att få in en textur på en yta i en figur måste man binda ihop en texturkordinat med varje vertex för den figur dit vi vill få in texturen. 2D Texturer i OGLES2 använder sig av sk. s, t kordinater: Texturkordinaterna kan definieras i samma tabell som kordinaterna för hörnpunkterna (vertex) definieras enligt följande: GLfloat hornpunkter[] = -0.3, 0.3, 0.0, //V0 Uppe till vänster 0.0, 1.0, //Kord. för texturens övre vänstra hörn -0.3, -0.3, 0.0, //V1 Nere till vänster 0.0, 0.0, //Kord. för texturens nedre vänstra hörn 0.3, -0.3, 0.0, //V2 Nere till höger 1.0, 0.0 ; //Kord. för texturens nedre högra hörn 37

11.5. Hantering av texturer i fragment shadern För att få texturen utritad i en figur måste dess kordinater föras över och behandlas i fragment shadern. Kordinaterna för en textur skickas över till fragment shadern via vertex shadern. Så i vertex shadern måste vi deklarera en inparameter och utparameter för en texturpixels kordinat: Vertex shader.. attribute vec2 a_texcoord varying vec2 v_texcoord.. void main()... v_texcoord = a_texcoord Från huvudprogrammet kan vi ge in texturkoordinaterna genom att: binda ihop en texturkoordinat till varje hörnpunkt av figuren dit vi vill ha texturen, se slutet av 10.4. binda variabeln a_textcoord i vertex shadern till en plats i vertex-tabellen (så att vi skall kunan ge in texturkordinaterna till shadern): #define TEXT_COORD 1 glbindattriblocation(uiprogramobject, TEXT_COORD, a_textcoord ); Skicka över texturkoordinaterna för varje vertex till a_textcoord: glenablevertexattribarray(text_coord); glvertexattribpointer(text_coord, 2, GL_FLOAT, GL_FALSE, sizeof(glfloat) * 5, (void *) (sizeof(glfloat) * 3)); 5:e parametern är sizeof(glfloat) * 5 eftersom vi måste hoppa exakt så långt framåt i tabellen hornpunkter för att komma till nästa hörnpunkt. Tabellan hornpunkter ser ju ut så här: 38

0 (GLfloat) Hörnpunkt 1 (GLfloat) Hörnpunkt 2 (GLfloat) Hörnpunkt 3 (GLfloat) Texturkordinat 4 (GLfloat) Texturkordinat 5 (GLfloat) Hörnpunkt 6 (GLfloat) Hörnpunkt 7 (GLfloat) Hörnpunkt 8 (GLfloat) Texturkordinat 9(GLfloat) Texturkoordinat 10 (GLfloat)... I Fragment shadern kan man sedan ta in texturkoordinaterna i formen av en varying variabel: Fragment shader.. varying vec2 texcoord uniform sampler2d texture;.. void main() gl_fragcolor = texture2d(texture, texcoord); För att sedan beräkna och rita ut färgen för en pixel i figuren på basen av en textur man tagit in kan man använda sej av funktionen texture2d: vec4 texture2d(sampler2d sampler, vec2 coord) sampler coord En sampler (en form av index) bundet till den textur som vi vill rita ut En texturkordinat för en hörnpunkt som behövs för att kunna rita ut figuren Funktionen texture2d returnerar alltså en färgvektor som är beräknad på basen av en textur Ett värde för samplern eller texture som vi kallar variabeln i exemplet ovan kan från huvudprogrammet enkelat tilldelas ett värde, enligt följande: GLint texturehandler = glgetuniformlocation(uiprogramobject, texture ); gluniformli(texturehandler, 0); Andra parametern i glgetuniformlocation() är index-värde till en textur. Texturerna får ett index-värde enligt den ordning de skapas (med glbindtexture). Så första texturen är index=0, andra texturen är index=1 osv. 39

11.6. Att rita ut flera texturer Följande exempel laddar in två olika texturer i OGLES2 från filer: BITMAPINFOHEADER stonesinfoheader, floorinfoheader; GLuint *texturdata, *texturdata2; texturdata = LoadBitmapFile("stones.bmp",&stonesInfoHeader); texturdata2 = LoadBitmapFile("floor.bmp",&floorInfoHeader); GLuint m_uitexture[2]; glgentextures(2, m_uitexture); glactivetexture(gl_texture0); glbindtexture(gl_texture_2d, m_uitexture[0]); GLuint TEX_WIDTH = (GLuint) stonesinfoheader.biwidth; GLuint TEX_HEIGHT = (GLuint) stonesinfoheader.biheight; printf("bredd: %d\n", TEX_WIDTH); glteximage2d(gl_texture_2d, 0, GL_RGB,TEX_WIDTH, TEX_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, texturdata); gltexparameterf(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); gltexparameterf(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glactivetexture(gl_texture1); glbindtexture(gl_texture_2d, m_uitexture[1]); TEX_WIDTH = (GLuint) floorinfoheader.biwidth; TEX_HEIGHT = (GLuint) floorinfoheader.biheight; printf("bredd: %d\n", TEX_WIDTH); glteximage2d(gl_texture_2d, 0, GL_RGB,TEX_WIDTH, TEX_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, texturdata2); gltexparameterf(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); gltexparameterf(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); För att kunna sätta in dessa två olika texturer på olika figurer måste figurerna ritas ut med skilda gldrawelements-anrop (eller gldrawarrays). Före varje anrop av gldrawelements måste rätt textur skickas över till textur -variabeln i fragment:... gluniform1i(glgetuniformlocation(uiprogramobject, "textur"), 0); gldrawelements(gl_triangles, 6, GL_UNSIGNED_BYTE, indices);... gluniform1i(glgetuniformlocation(uiprogramobject, "textur"), 1); gldrawelements(gl_triangles, 6, GL_UNSIGNED_BYTE, indices2); 40

Funktionen glactivetexture: glactivetexture(glenum texture) texture Den textur man vill göra aktiv. Alternativen är GL_TEXTURE0, GL_TEXTURE1 osv där GL_TEXTURE0 är den första texturen man skapat osv. 41

12. HANTERING AV TANGENTBORDS-, MUS- OCH PEKSKÄRMSHÄNDELSER Biblioteksfilen som används för att skapa ett X Windows fönster i esbase.c dvs. xlib.h includerar funktioner för avlyssning av tangentbords-, mus- och pekskärmshändelser. I esbase.c ges följande parametrar till X Windows fönstret: XSetWindowAttributes swa; swa.event_mask = StructureNotifyMask ExposureMask ButtonPressMask ButtonReleaseMask KeyPressMask KeyReleaseMask; Kodraderna ovan tar bl.a. ibruk mus-/pekskärmshändelser och tangetbordshändelser (Finns färdigt i esbase.c). Tangentbordet och pekskärmen kan sedan enkelt avlyssnas genom att placera t.ex. följande kod i OGLES2-programmet i frame-loopen: //Returnerar antalet obhanterade händelser som finns i X-serverns kö int i32nummessages = XPending( x.x11display ); //Går igenom alla händelser som finns i kön och söker efter en mus-/pekskärmstrycking for( di = 0; di < i32nummessages; di++ ) //Deklarerar en händelsevariabel XEvent event; //Sparar följande händelse som finns i kön i händelsevariabeln event XNextEvent( x.x11display, &event ); switch( event.type ) //Om händelsen är en mus-/pekskärmstryckning (ButtonPress), skriv ut x- och y- //kordinaterna case ButtonPress: printf("x-kordinat: %d\n", event.xbutton.x); printf("y-kordinat: %d\n", event.xbutton.y); break; default: break; 42