dimarts, 22 de novembre de 2011

Sistemes electorals

Bones.

Continuant amb el tema i havent parlat amb algun pirata sobre sistemes electorals, he pensat de dissenyar-ne un (no m'hi poso pas per poc).

La idea és agafar el que tenim, inspirar-me en propostes vigents en altres països (tot gira al voltant del STV).

Què tenim de bo? Poca cosa. M'agrada que hi hagi representativitat lligada al territori. Estic en contra de la circumscripció única, ja que m'agradaria que hi hagi "proximitat".

Què falta? Primer, i sobretot, proporcionalitat. No pot ser que els vots d'uns ciutadans contin més que els d'altres, fi. No pot ser que un partit amb amb el 44% dels vots tingui el 53% dels diputats, i que un altre amb el 7.1% dels vots tingui el 3.1% dels diputats (menys de la meitat).

En segon punt a millorar, parlaré d'obrir les llistes, però no totalment, doncs és difícil de fer el sistema que m'imagino amb llistes totalment obertes.

Tercer punt, important, eliminar el concepte "vot útil". Tots els vots han de ser igual d'útils.

Comencem.

Primer: Què voten les persones.

Cada persona tindrà a la seva disposició la llista de tots els partits que es presentin, que haurà de numerar en preferència (1 el més preferent, 2 el segon, etc...). Aquí hi ha 2 alternatives, la primera és limitar el màxim de nombres, i la segona és no fer-ho. Per una banda hi ha donar la llibertat total als electors, però per l'altre hi ha un problema de logística important (la complexitat del sistema és d'ordre O(n!) on "n" és el nombre d'opcions, per tant limitar-ho és interessant) i també el fet de que la segona opció té poc pes al sistema, i el tercer és ja pràcticament negligibles.

Després, l'elector tindrà la opció de reordenar els diputats de les 2 primeres llistes (sense intercanviar-los). Això és conegut com a "llistes desbloquejades".

Segon: Cambres.

Senat fora, no serveix per a res.

En quan al congrés, hi ha un límit constitucional de que hi hagi entre 300 i 400 diputats. Anem a distribuir-los.

Comencem tal i com es fa actualment, 2 per provincia, un per ceuta i un per melilla. La resta, fins a arribar a 300, es distribueixen segons la població (Podriem aquí fer un Homt o Sainte-Languë) per cada provincia. Aquests són els representants "fixes", o "mínims" que cada província aportarà al congrés.

Els 100 diputats que queden, una quarta part, serviran per re-equilibrar els vots dels ciutadans. Si, per exemple una candidatura ha rebut un suport per sota del llindar per treure un diputat en moltes províncies, aquests vots es poden "sumar" per treure'n un d'aquests 100. Això farà que el resultat final en quan a diputats sigui molt més representatiu.

Aquests 100 diputats es tornen a repartir segons la població per totes les províncies. Sumant amb el nombre anterior dóna el nombre màxim d'escons que una província pot aportar. Aquests seran el nombre mínim de candidats que una força política haurà d'incloure a la llista de cada província, no havent-hi màxim.

Tercer: Recompte

El recompte té tres fases: Primer escollir quants membres de cada partit en cada circumscripció són escollits directament per aquesta, en la segona fase es trien els diputats "d'equilibri": a quin partit pertanyen i de quina llista (circumscripció) surten. Finalment, es decideix, per cada circumscripció i partit, quins dels candidats són finalment escollits (i s'ordena la resta per fer de "suplents").

Primera fase.

Mitjançant un mètode inspirat al del "Vot únic transferible" (STV per les seves sigles en anglès) es trien els diputats de la província.

Aquí partim d'unes butlletes on hi ha numerades les preferències dels electors ( 1 - Partit dels Cargols, 2 - Partit de les Libèl·lules, etc...). De moment només ens fixem en la primera opció.

Es calcula la quota del coeficient de Droop (jo el modifico una mica, perdoneu-me):
coeficient = (vots totals vàlids) / ( (escons a escollir) + 1)
El coeficient original és enter, jo el vui fer servir com a real.

Seguidament, per cada partit que superi (estrictament) el coeficient en nombre de vots, se li resta el coeficient i se li suma un diputat, fins que cap partit tingui un nombre de "vots restants" estrictament superior al coeficient.

Ara comença lo bo:

Es comencen a eliminar partits. Si en qualsevol moment queden tants partits "vius" com escons a escollir, es reparteixen automàticament 1 a cada un.

S'elimina el partit amb menys vots, i els vots que li resten es reparteixen entre els altres. Com? Tenint en compte les segones opcions d'aquells que han votat el partit eliminat. Si s'elimina el Partit dels Cargols, amb 500 vots restants, i un 20% dels seus electors han posat el Partit de les Libèl·lules com a segona opció, aleshores se li sumen 100 vots al Partit de les Libèl·lules (el 20% de 500). D'aquests 100 vots cal compatibilitzar la tercera opció, que es suma a les segones del Partit de les Libèl·lules, i així successivament.

Si algun partit ha arribat a superar la quota, se li adjudica un escó, restant-li els vots corresponents.

Amb aquest complex algorisme, es trien quants representants de cada partit van per cada comarca. L'algorisme té les següents característiques:

  • El vot és únic, només serveix per un partit.
  • Si el partit que has escollit com a primera opció no aconsegueix cap representant, el teu vot serveix, amb el mateix pes, a la segona opció (i així successivament).
  • Si el partit que has escollit obté representació, però li "sobren vots" (té prou vots per aconseguir un escó, però no suficients per arribar al segon), aquests es reparteixen a segones opcions, havent restat el pes que representa ja haver escollit algun diputat.
  • Concretament, si la teva primera opció no obté representació, la segona obté el teu vot amb el mateix pes que qualsevol altre vot amb primera opció, si aquesta tampoc, passa a la tercera opció igual, etc... Si la teva primera opció obté un escó, el teu vot passa a la segona amb la meitat del pes, 2 diputats, 1/3, etc... La tercera opció rep el vot diluït per els escons obtinguts per les 2 primeres sumades, i així successivament.
Segona Fase

Queden 100 diputats, que cal escollir de tal manera per reequilibrar el parlament per a que recuperi la proporcionalitat que les urnes creen però les circumscripcions retallen.

Per fer-ho es torna a calcular el coeficient abans esmentat i s'agafen el nombre de vots TOTALS a tot el país de cada partit i el nombre d'escons aconseguit per cada partit. Aleshores es resta una vegada el coeficient al recompte de vots restants a cada partit per cada escó ja aconseguit (són vots que "ja ha utilitzat"). Amb el que queda es repeteix l'algorisme STV de l'apartat anterior*.

* Pot ser que quedin nombres de vots restants negatius. Amb un 12% d'escons de re-equilibri n'hi poden quedar segur, però amb un 25% no ho tinc tant clar, caldria mirar-ho. En tot cas, què s'ha de fer si això passa no ho tinc molt pensat, de moment eliminaria el partit i treuria del recompte de "vots vàlids totals" els vots que aquest partit hagués tret, i recalcularia el coeficient.

Després toca triar quina província va cada diputat extra. Aquí s'acaba el STV i passem al Sainte-Languë (un sistema similar a la llei d'Homt). Girem una mica la truita i compatibilitzem les províncies com si fossin candidates i els vots al partit que ha guanyat l'escó (o escons) extra a cada província ens serveixen per decidir a quina província va aquest diputat extra. A l'hora de calcular el coeficient a cada província necessitem comptavilitzar també els escons guanyats per aquest partit en aquella província a la primera fase.


Per exemple el Partit dels Cargols ha guanyat 2 escons a Barcelona amb 170.000 vots, però cap a Girona, Lleida ni Tarragona, on hi té 30.000, 15.000 i 16.000 vots respectivament. Aquest partit ha guanyat 2 representants extres en aquesta fase.

El coeficient és de [170.000/ (2*2 + 1) =] 34.000 per Barcelona, 30.000 per Girona, 15.000 per Lleida i 16.000 per Tarragona, per tant el primer escó va a Barcelona, que modifica el seu coeficient i el deixa a [170.000 / (3*2 + 1) =] 24.286, que com és inferior al coeficient de Girona aquesta última província és la que guanya el diputat. Ni Lleida ni Tarragona han obtingut cap escó, però els vots allà obtinguts han ajudat a aconseguir els escons extra per Barcelona i Girona.

Aquest sistema, a més de les característiques de la primera fase guanya les següents característiques:
  • Al compatibilitzar els resultats obtinguts en la primera fase, serveix per equilibrar els resultats que l'arrodoniment que el sistema de circumscripcions desequilibra. El resultat final sempre és altament representatiu i la diferència entre el % de vots obtinguts (respecte el total de vots a les forces amb representació parlamentària) i el % d'escons obtinguts mai s'allunya més d'un 1% (absolut).
  • Tota força amb més del 0.25% de vots aconsegueix un escó (1 diputat és el 0.25% del total).
  • El teu vot, si no aconsegueix donar un escó a la teva província pot ajudar a una altra província a sumar prou per obtenir escó de la força que tu has triat. O al revés, et pots ajudar de vots "orfes" d'altres províncies per tal de poder obtenir escons extra.
  • La única manera de que un vot no sigui útil és que cap de les forces que has escollit (en pots triar 5) obtingui representació, cosa que és molt complicada ja que a mesura que s'eliminin opcions minoritàries els vots a aquestes tendirien a agrupar-se a altres opcions minoritàries i eventualment alguna obtindria representació.
Última Fase.

Aquesta és la fase més senzilla de totes (especialment per qui hagi pogut seguir les anteriors). Recuperem el mètode STV per decidir quins candidats de cada força surten de cada província ara que ja sabem quants n'han de sortir. Només es poden canviar l'ordre de llista de les dues primeres opcions, per tal d'evitar "boicots" a llistes "enemigues", es podria revisar.

Conclusions

El mètode que proposo permet:
  • Mantenir representativitat territorial (Cada diputat surt d'un territori i farà bé de recordar a qui representa).
  • Equilibrar el resultat final, que no hi hagi una diferència esperpèntica entre el percentatge de vot al percentatge d'escons obtinguts.
  • Eliminar el "vot inútil". Si no ajudes a la primera força de la teva elecció a obtenir un escó, ajudaràs a la segona, o a la tercera, o...
  • Encara que no ajudis a la teva primera elecció a obtenir un escó a la teva circumscripció, és probable que l'ajudis a obtenir per una altra. Les forces polítiques farien bé de recordar la solidaritat de quins territoris l'han ajudat a aconseguir poder.
  • Desbloquejar les llistes i decidir l'ordre de cada candidat en cada partit polític.
Fi.

Gràcies a aquells que hagin tingut la santa paciència d'haver-se llegit tot això. Es nota que ara sóc un ni-ni, jejeje.... :-(

dilluns, 21 de novembre de 2011

Eleccions generals

Avui, hi ha hagut eleccions generals. Suposo que no sorprenc a ningú.

Mirant els resultats (http://www.generales2011.mir.es/99CG/DCG99999TO_L1.htm) m'han sorprès un parell de coses.

La primera, el PP ha guanyat uns 550.000 vots (+5.37%) i 32 escons (+20.75%) respecte les últimes eleccions. Sorpren.

Després destaco EQUO i PACMA. Ambdós han superat a vàries forces que han entrat al parlament en nombre de vots (EQUO n'ha superat a 5!) i cap dels 2 ha entrat.

Finalment, al PP cada escó li ha costat uns 58.200 vots, mentre que a UPyD més de 228.000. Equo té 215.000 vots i no ha aconseguit cap escó.


És per això que he decidit fer un "sondeig" del resultat electoral amb diferents sistemes:

Les columnes són:
Nom del partit (1), total vots (2 [99.9% escrutat]), % de vots (3), diputats actuals i % sobre el total (4 i 5), amb circumscripció única (6 i 7), ídem, però canviant llei d'Homt per Sainte-Laguë "light", sense el "1.4" (8 i 9), i fent el sistema "norueg", que explico després (10 i 11).

Sobre els percentatges, incloc el persentatge de diputats, i el persentatge de vots sobre el total entre les forces amb representació ( % de diputats / % de vots ).




   1           2        3      4       5       6       7       8       9      10      11 
 partit      vots      % vot    resultats      Homt            Sainte-Languë  Noruega
PP        10.830.035  44,62%  186 (53.%/47.%) 164 (48.%/46.%) 159 (45.%/46.%) 210 (52.%/46.%)
PSOE       6.973.351  28,73%  110 (31.%/30.%) 105 (30.%/30.%) 103 (29.%/29.%) 125 (31.%/30.%)
CiU        1.013.979   4,17%   16 (4.6%/4.4%)  15 (4.3%/4.3%)  15 (4.3%/4.3%)  18 (4.5%/4.3%)
IU-LV      1.680.639   6,92%   11 (3.1%/7.3%)  25 (7.1%/7.1%)  25 (7.1%/7.1%)  15 (3.7%/7.1%)
AMAIUR       333.628   1,37%    7 (2.0%/1.4%)   5 (1.4%/1.4%)   5 (1.4%/1.4%)   8 (2.0%/1.4%)
UPyD       1.140.116   4,69%    5 (1.4%/4.9%)  17 (4.9%/4.8%)  17 (4.9%/4.8%)   7 (1.7%/4.8%)
EAJ-PNV      323.517   1,33%    5 (1.4%/1.4%)   4 (1.1%/1.4%)   5 (1.4%/1.4%)   6 (1.5%/1.4%)
ESQUERRA     256.304   1,05%    3 (.86%/1.1%)   3 (.86%/1.1%)   4 (1.1%/1.1%)   4 (1.0%/1.1%)
BNG          183.279   0,75%    2 (.57%/.79%)   2 (.57%/.78%)   3 (.86%/.77%)   2 (.50%/.78%)
CC-NC-PNC    143.514   0,59%    2 (.57%/.62%)   2 (.57%/.61%)   2 (.57%/.60%)   2 (.50%/.61%)
COMPROMÍS-Q  125.150   0,51%    1 (.29%/.54%)   1 (.29%/.53%)   2 (.57%/.53%)   1 (.25%/.53%)
FAC           99.173   0,40%    1 (.29%/.43%)   1 (.29%/.42%)   1 (.29%/.42%)   1 (.25%/.42%)
GBAI          42.411   0,17%    1 (.29%/.18%)   1 (.29%/.18%)   1 (.29%/.18%)   1 (.25%/.18%)
EQUO         215.750   0,88%    0 (---------)   3 (.86%/.92%)   1 (.29%/.91%)   0 (---------)
PACMA        101.546   0,41%    0 (---------)   1 (.29%/.43%)   1 (.29%/.43%)   0 (---------)
Eb            97.685   0,40%    0 (---------)   1 (.29%/.41%)   1 (.29%/.41%)   0 (---------)
PA            76.842   0,31%    0 (---------)   0 (---------)   1 (.29%/.32%)   0 (---------)
PxC           59.778   0,24%    0 (---------)   0 (---------)   1 (.29%/.25%)   0 (---------)
P.R.C.        43.903   0,18%    0 (---------)   0 (---------)   1 (.29%/.18%)   0 (---------)



El "mètode noruec" es tracta en afegir uns quants diputats (jo n'he afegit 50, per arribar als 400 que la constitució deixa que hi hagi) i distribuir-lo via llei Sainte-Laguë en circumscripció única.

Un anàlisi profund indica que

  1. El sistema actual és una mofa. El % de vot respecte el % d'escons està desproporcionadament distant.
  2. La diferència entre la llei d'Homt i la Sainte-Laguë no és tan profunda com sembla a la teoria. La única diferència pràctica és qui arrodoneix "cap amunt" i qui "cap avall". De fet, només PP i PSOE perden escons, i tampoc en perden gaires (7 en total).
  3. El "mètode noruec" equilibra però no és la panacea. És qüestió de proporció, a més pes de la llista general, més equilibra. Jo ho he fet amb un 12%. Noruega ho fan al 22%. Pujant fins a 450 diputats la cosa comença a semblar decent, i amb 500 diputats (30% de llista general) la cosa queda ja força bé (entra el PACMA i escons en blanc). Comptant que he afegit 150 diputats i eliminant els 264 del senat (una cambra que no serveix per a res) es guanya un sistema força més equitatiu, amb menys necessitat de "vot útil" (el teu vot acaba anant a algun lloc, encara que no surti a la teva província).
P.S.: Taula amb els 450/500 diputats, mètode noruec.



 partits    vots       % vots    resultats     noruega 450     noruega 500
PP        10.830.035  44,62%  186 (53.%/47.%) 233 (52.%/46.%) 255 (51.%/46.%)
PSOE       6.973.351  28,73%  110 (31.%/30.%) 140 (31.%/30.%) 154 (30.%/30.%)
CiU        1.013.979   4,17%   16 (4.6%/4.4%)  20 (4.4%/4.3%)  22 (4.4%/4.3%)
IU-LV      1.680.639   6,92%   11 (3.1%/7.3%)  18 (4.0%/7.2%)  22 (4.4%/7.1%)
AMAIUR       333.628   1,37%    7 (2.0%/1.4%)   8 (1.8%/1.4%)   9 (1.8%/1.4%)
UPyD       1.140.116   4,69%    5 (1.4%/4.9%)  10 (2.2%/4.9%)  12 (2.4%/4.8%)
EAJ-PNV      323.517   1,33%    5 (1.4%/1.4%)   6 (1.3%/1.4%)   7 (1.4%/1.4%)
ESQUERRA     256.304   1,05%    3 (.86%/1.1%)   4 (.89%/1.1%)   5 (1.0%/1.1%)
BNG          183.279   0,75%    2 (.57%/.79%)   3 (.67%/.78%)   3 (.60%/.78%)
CC-NC-PNC    143.514   0,59%    2 (.57%/.62%)   3 (.67%/.61%)   3 (.60%/.61%)
COMPROMÍS-Q  125.150   0,51%    1 (.29%/.54%)   2 (.44%/.54%)   2 (.40%/.53%)
FAC           99.173   0,40%    1 (.29%/.43%)   1 (.22%/.42%)   2 (.40%/.42%)
GBAI          42.411   0,17%    1 (.29%/.18%)   1 (.22%/.18%)   1 (.20%/.18%)
EQUO         215.750   0,88%    0 (---------)   1 (.22%/.92%)   1 (.20%/.92%)
PACMA        101.546   0,41%    0 (---------)   0 (---------)   1 (.20%/.43%)
Eb            97.685   0,40%    0 (---------)   0 (---------)   1 (.20%/.41%)
PA            76.842   0,31%    0 (---------)   0 (---------)   0 (---------)
PxC           59.778   0,24%    0 (---------)   0 (---------)   0 (---------)
P.R.C.        43.903   0,18%    0 (---------)   0 (---------)   0 (---------)


En aquest cas, els únics partits que realment podria considerar el resultat com un atracament son UPyD i IU. PA i PxC també es podrien queixar, doncs tenint més d'un 0.2%, no tenen cap diputat.

P.P.S.: He fet un re-pensament i he tornat a calcular els resultats segons el métode norueg "repassat". Abans, repartia els 50 escons "contant des de zero", ara ho faig tenint en compte els resultats anteriors i dóna un parlament molt decent. (Últimes columnes)


 partit     vots       %vots    actuals         noruega ben fet
PP        10.830.035  44,62%  186 (53.%/47.%) 186 (46.5%/46.6%) +0
PSOE       6.973.351  28,73%  110 (31.%/30.%) 114 (28.5%/29.3%) +4
CiU        1.013.979   4,17%   16 (4.6%/4.4%)  17 (4.25%/4.27%) +1
IU-LV      1.680.639   6,92%   11 (3.1%/7.3%)  27 (6.75%/7.08%) +16
AMAIUR       333.628   1,37%    7 (2.0%/1.4%)   7 (1.75%/1.41%) +0
UPyD       1.140.116   4,69%    5 (1.4%/4.9%)  19 (4.75%/4.80%) +14
EAJ-PNV      323.517   1,33%    5 (1.4%/1.4%)   5 (1.25%/1.36%) +0
ESQUERRA     256.304   1,05%    3 (.86%/1.1%)   4 (1.00%/1.08%) +1
BNG          183.279   0,75%    2 (.57%/.79%)   3 (.750%/.772%) +1
CC-NC-PNC    143.514   0,59%    2 (.57%/.62%)   2 (.500%/.605%) +0
COMPROMÍS-Q  125.150   0,51%    1 (.29%/.54%)   2 (.500%/.527%) +1
FAC           99.173   0,40%    1 (.29%/.43%)   2 (.500%/.418%) +1
GBAI          42.411   0,17%    1 (.29%/.18%)   1 (.250%/.179%) +0
EQUO         215.750   0,88%    0 (---------)   4 (1.00%/.909%) +3
PACMA        101.546   0,41%    0 (---------)   2 (.500%/.428%) +2
Eb            97.685   0,40%    0 (---------)   2 (.500%/.411%) +2
PA            76.842   0,31%    0 (---------)   1 (.250%/.324%) +1
PxC           59.778   0,24%    0 (---------)   1 (.250%/.252%) +1
P.R.C.        43.903   0,18%    0 (---------)   1 (.250%/.185%) +1

Nota final: M'han comentat de mirar com serien les eleccions segons el métode STV. He decidit no fer-ho per una senzilla raó: STV suposa llistes obertes, i el vot seria completament diferent.

La meva proposta.

De moment, i sense ser massa revolucionari.


  1. Llistes desbloquejades (com a mínim)
  2. Eliminaria el senat (no serveix per a res) i pujaria el congrés fins a 400 membres (el màxim que permet la constitució).
  3. Actualment es donen 2 diputats per província, i la resta, fins arribar als 350, es reparteixen segons la població. Això ho deixaria més o menys igual, afegint uns diputats "de reforç" (que anirien abans dels suplents) repartits amb els 50 que falten.
  4. Els 350 primers diputats s'escollirien mitjançant el mateix sistema que fins ara, els 50 que queden es repartirien mitjançant la llei de Sainte-Languë, en circumscripció única i contant els 350 primers com si ja s'haguéssin escollit.
  5. Quan un partit polític guanya un diputat d'aquests 50 extres, aquest surt de la llista d'una província. La província s'escull també amb la llei Sainte-Languë entre les llistes d'aquest partit de cada província, contant els escons ja trets en cada una d'elles. Això afavoreix tenir més representativitat a les províncies amb més participació, i acostuma a equilibrar el "pes" dels votants. (Els diputats "de reforç" són per cobrir aquests llocs).
Per posar uns exemples, CiU i ERC haguessin guanyat un diputat extra a Barcelona, UPyD, els 14 que guanyaria serien: 1 a Alacant, 1 a Barcelona, 1 a Cádiz, 1 a Granada, 4 a Madrid, 1 a Màlaga, 1 a Múrcia, 1 a Sevilla, 1 a València, 1 a Valladolid i 1 a Zaragoza. EQUO 3 a Madrid i 1 a les Balears.

dilluns, 14 de novembre de 2011

Crear un motor 3D Multiplataforma - Motor Antagonista [ I ]

Bones.

Començo el meu "diatari" sobre com vaig fent un motor multiplataforma (Linux i Windows i, opcionalment, Android i, opcionalíssimament, iOs).

Aquest motor serà bàsicament una reescriptura del motor que hem fet al màster (mirar entrada anterior), però "ben fet" (no és que digui que el motor que hem fet està mal fet [fet, fet, fet, parèntesi, parèntesi, parèntesi], sinó que, al anar aprenent no teníem la visió de conjunt necessària per fer certes coses, que ara si que tinc).

En aquest projecte em proposo diverses coses, entre elles aprendre C++11 (la raó principal per la que no faci el joc en Java) i distreure'm mentre no trobi feina.

Els objectius principals seran tenir la mateixa funcionalitat que al master, més la capacitat de ser multiplataforma (i alguna millora gràfica gràcies a no dependre del DirectX, visca OpenGL!!).


  • Interfície amb el sistema operatius via SDL 1.3 (té llicència ZLib, la qual em permet fer-ne el que em doni la santa gana).
  • Gràfics 2D via SDL o OpenGL (ES?).
  • Gràfics 3D via OpenGL amb diferents perfils "intercanviables".
    • Perfil bàsic: 1.5 o ES 1.1
    • Perfil normal: 2.0
    • Perfil avançat: 4.2 (3.3? Qui el vol?)
  • Configuració amb fitxers YAML
  • Scripting via LUA (no he aconseguit fer funcionar Boost::Python, ja que té un memory leak gros com ell sol, així que tornem al lua).
  • So via SDL (amb l'ajuda del mixer per llegir "ogg"s. Bass costa 2.700€, així que queda descartat.
  • Física via Bullet (si hi ha temps per fer-la). Sé que n'hi ha d'altres, però ja estic una mica familiaritzat amb Bullet, així que no hi ha d'haver masses problemes al respecte.
  • DCCs Blender i Gimp.
  • D'animacions m'agradaria crear el meu pròpi sistema, però sempre hi ha Cal3D allà per si no me'n surto.
No sé si m'he deixat res, però és igual.

Com a eines uso Mercurial com a control de versions (és distribuit i mola lo suficient) i Eclipse com a IDE.

De moment simplement m'he creat una carpeta a l'Ubuntu controlada amb el Hg (mercurial) de nom "Motor Antagonista" i a dintre hi aniré creant els diferents projectes de l'Eclipse.

Començo per el base.

Nou projecte -> C++ Static Library -> Toolchain "Cross GCC"

A partir d'aqui el primer que faig és retocar les configuracions (botó dret sobre el projecte, propietats al final de tot). Dintre de "C/C++ Build" -> Settings et deixa mirar les configuracions. Per defecte n'hi ha 2: "debug" i "release"; els hi canvio el nom per "GNU|Linux Debug" i "GNU|Linux Release" (per després crear les configuracions corresponents per windows) i n'afegeixo una de nova "Production" (cópia de Release) a Release li engego les opcions de debug (així que queda optimitzada a tope i amb debug, sent una build "intermitja"). També defineixo els símbols "_DEBUG" (per la build de debug) i "NDEBUG" (per la resta), per si de cas. Tot això s'ha de afegir 2 vegades, una a la configuració del "gcc" i l'altra a la del "g++" (les 2 són a "Settings", on he dit abans). Finalment, i per totes les configuracions, afegeixo la opció "-std=c++0x" a la línea de comandes de "g++" per que em compili amb les coses del C++11 (bé, amb les coses "provisionals", ja que encara no tinc la versió de gcc que compila C++11 correctament).

 

Després de fer tot això, em dedico a afegir llibreries.

Creo una carpeta "include" i a dintre i foto tots els arxius de "GLI"[0.3.0.3] i "GLM"[0.9.2.7], trets de http://www.g-truc.net/ (nota: els 2 tenen un "dummy.cpp" que cal esborrar, aquestes llibreries són "només capçaleres" i per tant relativament fàcils de incloure | nota2: GLI té un memory leak. No és molt preocupant i ja he avisat de la seva existència i l'autor diu que a la pròxima versió estarà corregit).

Afegeixo també els includes de "yaml-cpp" i creo una carpeta nova "lib/GNULinux" on hi afegeixo la llibreria estàtica (libyaml-cpp.a). Faig el mateix amb les llibreries de LUA (5.1), de SDL (1.3) i Luabind (0.9.1)** (aquestes embruten considerablement la carpeta "include", espero que això no em passi factura després)*.

Una bona estona me la passo fent "./config; make; make install;", per cert (a veure en windows com ho faig tot això).

Afegeixo per acabar "http://sourceforge.net/projects/nvwa/" i GLEW. Finalment faig una carpeta anomenada "Licences" on vaig copiant totes les llicències per tenir-les controlades.

I de moment això és tot. Després intentaré configurar el Mercurial correctament.

*: Per lua, només cal "lua.h" "lua.hpp" "luaconf.h" "lualib.h" i "lauxlib.h"
**: Per al luabind... és més fàcil passar de "bjam"s i limitar-se a copiar tot el codi a dintre del projecte.

Final de màster.

Bé, ja he acabat el màster en creació de videojocs. Feia temps que no em passava per aqui i tinc alguna cosa a apuntar.

De fet, he interromput el que estava fent per escriure aquesta petita entrada, doncs vui tenir els videos del joc ben localitzables.







Bé, ha estat un llarg trajecte que ha comportat mooooooolta feina. El resultat final és, com no, millorable, però n'estic força satisfet.

Ja sóc un Ni-ni, a veure quan duro així.

diumenge, 29 de maig de 2011

Una de pollosos i gossos

Seré polèmic, que és el que toca.

https://www.asopol.org/2011/05/sobre-la-carga-de-los-mossos-desquadra-en-plaza-cataluna/

http://thelede.blogs.nytimes.com/2011/05/27/police-clash-with-protesters-in-barcelona/?ref=world

Un link és de la policia, i l'altre del NY Times, on veureu uns quans vídeos. Llegiu el primer i mireu (després) els videos, si no els heu vist ja.

Primer de tot una introducció sobre mi per qui no em conegui. Sóc (quasi) enginyer informàtic, i des de l'octubre sóc membre del Partit Pirata Català (Anomenat "Pirates de Catalunya" per que ja existia un "Partit Pirata" espanyol i no ens hi podiem dir sense formar part d'ells rollo PSC). Aquest partit defensa molts dels punts que han anat sortint a les acampades i que n'han estat causa, tot i que no tots. Per fer-vos una idea, un amic meu ha escrit el següent text sobre les demandes dels acampats:

https://docs.google.com/document/d/1nNplofoR06TTPYqJzY69cUHcnJR3lSsEyRms1Z6lQtY/edit?hl=en_US

Hi estic majoritàriament d'acord amb l'Aleix (l'autor dels comentaris) però ràpidament dic que posaria més emfasis a que els diners no plouen del cel i un estat amb "més de tot" és desitjable però difícil, i que toca moltes vegades prioritzar, cosa que els acampats no fan en cap moment, simplement demanen una sobreprotecció total de l'estat, i això en opinió meva acava sent negatiu (és, segons crec jo, un dels grans fracassos dels estats comunistes: la falta d'ambició individual dels seus habitants fa que el sistema no pugui aguantar-se a ell mateix). Tampoc parlaré molt d'aquest tema, que demanaria un post en si mateix.

Estava parlant de Pirates de Catalunya. Una de les coses boniques d'aquest partit és de demana democracia directa (que no participativa) i pretén aconseguir-ho usant les eines que, afortunadament, ens dóna el nostre país, que hem de recordar, les té i afortunadament no és pas cap dictadura (després hi torno). Per veure un exemple de com funciona el partit per dintre, a part de mirar la seva web pirata.cat podeu mirar el link que poso a continuació, que és on es discuteix una desició (també es discuteixen coses per les llistes de correu):

https://xifrat.pirata.cat/ideatorrent/idea/107


Molt bé, hem parlat que jo no estic en contra del que demanen els acampats. Al menys no en contra de tot, i que concretament també penso que el nostre estat democràtic té certes mancances que, sobretot ara la tecnologia i la informació global, ens poden permetre superar via democràcia directa.

Fins hi tot puc entendre que acampin, cosa il·legal, però ara diré el que més llenya em farà caura.

ETA també demana (o demanava) coses "correctes" (la independència d'euskadi, concretament), i feia servir métodes ilícits per aconseguir-ho.

És extremadament obvi que no es pot comparar matar persones amb acampar a una plaça. La diferéncia de gravetat dels dos fets els situa a ordres de magnitud de distància. I tot i així, les dos coses són demanar coses via métodes ilegals. Està clar, doncs, que és un tema d'on podem el llistor, i això no sempre és fàcil Si diem que matar està clarament fora, i ocupar un espai públic està dintre, què hi ha al mig i on marquem la barrera? Sé que em cauràn osties per haver-ho dit, però com a mínim feu-vos la pregunta.

Ara, sobre les càrregues policials. Havent llegit el text de la policia (també tenen dret a defensar-se) queden clar els motius de la càrrega policial: van demanar als indignats que s'apartessin del mig, i ells no ho van fer, i els policies van haver de recórrer a la violència per treure'ls. Assumirem (que és molt assumir) que realment el que venien a fer les brigades de neteja era correcte (seria: endur-se bombones de butà, eines de l'hort i coses perilloses en general; netejar de pixum la plaça, etc... NO seria: endur-se portàtils i apunts de les assamblees, com també van fer). I que, per tant, la feina de la policia era també correcte: garantir que els primers puguin fer la seva feina en condicions i seguretat.

Si assumim el què he dit a dalt (que hi podem discutir, i ja he dit que té matissos), aleshores arribem a un punt: impedir que les brigades de neteja facin la seva feina era (1) incorrecte i (2) buscar-se problemes amb la policia. Arribat a aquest punt era, doncs, feina de la policia, apartar als indignats del mig. Fins aquí, i si s'hagues pogut apartar-los del mig sense violència, crec que estariem tots d'acord. Per tant el problema sembla ser si la policia ha de, o pot, fer servir la violència en aquests casos.

I aqui us vui comentar un punt molt interessant, al meu parer, i és que, en el mateix comentari de l'associació professional de policies (que he enllaçat més amunt) comenta que la violència era el métode "protocolari" de resoldre la situació (gent que ocupa l'espai públic, desobeeix per activa i passiva les ordres d'un agent policial, i que guanya en nombre a aquest), però que caldria buscar una altra manera menys violenta i "espectacular" (en paraules seves) de resoldre-la. I no podria estar més d'acord. Ho remarco per que no vui que ningú oblidi que els agents que eren allà feien la seva feina, malaguanyada aquell dia però que segurament si et roben a la rambla i després t'ajuden els hi donaràs les gràcies, i que no disposaven de mitjans (o coneixements) per resoldre-la d'una altra manera.

És correcte emprar la violència? Segurament no. Mai? Ningú s'ha ficat en una baralla per res? Qui estigui lliure de culpa que tiri la primera pedra.

El que vui resumir aqui, no és si la càrrega policial va ser correcte o incorrecte. Si l'ocupació de plaça catalunya, un espai de tots, és correcte o incorrecte. El que m'agradaria transmetre, és que no tot és blanc o negre, ni és tant fàcil de dir: No, que la policia usi la violència contra gent que ha petat les rodes dels camions de les escombraries, i que no obeeixen per les bones, és dolent.

És molt fàcil dir que la policia va actuar de forma violenta, com animals, i oblidar que dos no es barallen si un no vol, i que si els manifestants haguéssin volgut haguéssin evitat tot l'espectacle. Però clar (i aqui si que em surt la meva vena anti-pollòs) si no estàs a fabor seu, estàs en contra seu.

I aquesta és la trampa que jo, i algún amic meu, hem caigut: expressar públicament que els indignats no tenen la raó absoluta i que, la policia "només" va fer la seva feina. Fent això la gent ens agafa per feixistes, o alguna cosa similar, per molt que pensem que la violència no és la solució i que el problema hagués estat millor resolt d'una altra manera.

Però sabeu què? Fins ara, ningú m'ha dit una altra solució que no sigui "la policia i els camions de la neteja haurien d'haver marxat". Mireu, aquesta solució és igual a "els manifestants haurien d'haver agafat les seves coses, apartar-se, deixar fer la seva feina a BCNeta i després tornar". És un problema de a veure qui cedeix i qui té raó. El que jo vui és que algú em proposi una solució pacífica intermitja.

P.S.: Finalment, sobre el títol del post, és per deixar clar que estic més en contra de les 2 parts que a fabor de cap d'elles. Concretament, crec que els objectius d'ambdós són generalment bons, però els métodes no ho són tant (matissos en tots els casos).

dissabte, 5 de febrer de 2011

Suficiència d'OpenGL ES 2.0 Part II (Framebuffers)

Continuant el post anterior, i una mica en la mateixa línea, avordem un tema "important", sobretot si es tracta de fer efectes abançats: Els Framebuffers.

D'acord, primer de tot, necessitem entendre una mica de teoria sobre on dibuixem les coses i quins espais de memòria ens són disponibles.

Aquests espais de memòria als que em refereixo són els buffers de renderització; allà on guardem informació de color, profunditat, etc... En l'exemple més senzill de OpenGL, disposem de 2 buffers: el front buffer i el back buffer. Aquests buffers guarden informació de color (RGB i a vegades un Alpha que indica "quantitat d'opacitat"), el front buffer es mostra per pantalla mentre que el backbuffer és on dibuixem. Un cop acavem de dibuixar un frame, intercanviem el front i el back, així no es veu mai "mentre dibuixem" sinó que veiem sempre dibuixos sencers.

Addicionalment se li poden afegir 2 buffers: el depth buffer i el stencil buffer. Sobre l'stencil no en parlarè gaire, per no dir gens; mai l'he fet servir i no sé ben bé per a què serveix, però el depth buffer és molt útil. Aquest búffer de profunditat (depth) guarda la profunditat on se suposa que cada pixel està pintat. Això ens permet un mètode senzill per renderitzar les coses que estàn més a prop: si anem a dibuixar una cosa més llunyana que la que hi ha dibuixada, no la dibuixem. Si és més propera, la dibuixem i actualitzem el depth.

Molt bé, ara veiem que per renderitzar necessitem un buffer de color i, opcionalment, un de profunditat i/o un de stencil. Per defecte normalment en tenim un de cada (més el front buffer), però a vegades ens interessa dibuixar coses "fora de la pantalla". Per exemple: Imaginem-nos que estem en un joc, en una sala amb càmeres de videovigilància. Aquestes càmeres mostren, en temps real, altres habitacions. Per a acomplir aquest efecte, podem renderitzar aquestes altres habitacions en una textura cada una i, posteriorment, renderitzar la textura a la càmera.

Això és el que es coneix com a off-screen rendering, i consisteix en crear un "Framebuffer object", que és una col·lecció de buffers de distínta índole, i dir-li a l'openGL que renderitzi sobre aquest. Els buffer poden ser de 2 maneres: o bé una textura, o bé un Renderbuffer. La diferència essencial és que no tenim fàcilment accessible la informació dels Renderbuffers, mentre que una textura la tenim sempre a mà.

Codi:

//------------------------------------------------------------

//Primer creem els Renderbuffers, que poden servir com a color,
//profunditat o stencil
GLuint renderbuffers[2];
glGenRenderbuffers(2, &(renderbuffers[0]));

//activem el renderbuffer
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[0]);

//definim com és el renderbuffer
glRenderbufferStorage(GL_RENDERBUFFER,
                      format,         //Format intern
                      width, height); //dimensions

// Formats estàndard:
// GL_RGB565, GL_RGBA4, GL_RGB5_A1,         -- Color
// GL_DEPTH_COMPONENT16, GL_STENCIL_INDEX8
//
// Formats amb l'extensió GL_OES_rgb8_rgba8
// GL_RGB8_OES, GL_RGBA8_OES
//
// Formats amb l'extensió GL_OES_depth24 / GL_OES_depth32
// GL_DEPTH_COMPONENT24_OES, GL_DEPTH_COMPONENT32_OES
//
// Formats amb l'extensió GL_OES_stencil1 / GL_OES_stencil4
// GL_STENCIL_INDEX1, GL_STENCIL_INDEX4
//
// Formats amb l'extensió GL_OES_packed_depth_stencil
// GL_DEPTH24_STENCIL8_OES

// Les dimensions cal que siguin iguals o menors a
// GL_MAX_RENDERBUFFER_SIZE
 
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[0]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 800, 600);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[1]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 800, 600);



//Crear identificadors de frambuffer,
// de forma similar a les textures:
GLuint framebufferID;
glGenFramebuffers(1,                //nombre d'id a generar
                  &framebufferID);

// Activar el framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);

// Aquesta funció ens serveix tant per especificar les propietats
//del framebuffer (punts d'anclatge) com per indicar que volem
//que es renderitzi a aquest. Per tornar a renderitzar als buffer
//"normals" hem de fer:
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// Lligar un renderbuffer al framebuffer
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           punt_d_anclatge,
                           GL_RENDERBUFFER,
                           renderbufferID); //0 per no anclar-hi res

// Punts d'anclatge:
// GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT
// Com a nota important, saber que en OpenGL estàndard hi ha més
//punts d'anclatge, concretament, els de color 1, 2, 3... Això 
//serveix per poder pintar a més d'un lloc a la vegada, i ho 
//explicarè als fragment shaders.

//Continuant amb l'exemple
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           GL_COLOR_ATTACHMENT0,
                           GL_RENDERBUFFER,
                           renderbuffers[0]);
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           GL_DEPTH_ATTACHMENT,
                           GL_RENDERBUFFER,
                           renderbuffers[1]);
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           GL_STENCIL_ATTACHMENT,
                           GL_RENDERBUFFER,
                           0);

//Comprovar errors
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);

//Result pot ser:
// GL_FRAMEBUFFER_COMPLETE -- OK
// GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT -- Algun error
//                                      -- en algun anclatge.
// GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT -- No hi ha cap
//                                              --anclatge
// GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS -- Anclatges amb
//                                      -- dimensions diferents
// GL_FRAMEBUFFER_INCOMPLETE_FORMATS -- Algun format no suportat
// GL_FRAMEBUFFER_UNSUPPORTED -- Combinació d'anclatges i formats
//                            -- no suportats.


// Com s'ha dit anteriorment, la gràcia de tot plegat és la de
//dibuixar a una textura, per això anclem una textura al 
//framebuffer com ho anclavem un renderbuffer
glFramebufferTexture2D(GL_FRAMEBUFFER,
                       punt_d_anclatge, //Només color o profunditat
                       target,
                       texture_id,
                       0); //nivell mipmap.

// Targets:
// GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP_POSITIVE_X, ...

//EXTRA: Textures 3D
glFramebufferTexture3DOES(GL_FRAMEBUFFER,
                          GL_COLOR_ATTACHMENT0, //No depth
                          GL_TEXTURE_3D_OES,
                          texture_id,
                          0, //nivell mipmap.
                          z_offset); //profunditat

// Finalment i nogensmenys important:
glDeleteRenderbuffers(2, &(renderbuffers[0])); //La pots liar si 
                           //encara està en ús en un framebuffer
glDeleteFramebuffers(1, &framebufferID);

//------------------------------------------------------------


Per altra banda, i de manera molt ràpida, m'agradaria introduir una altra funcionalitat molt similar, encara que amb un rendiment més baix (tot i que a vegades necessitem fer-la servir en front de framebuffers). Aquesta és simplement copiar el contingut de l'actual framebuffer (el back buffer en cas de no tenir un framebuffer activat) a una textura.


//------------------------------------------------------------
glCopyTexImage2D(target, // GL_TEXTURE_2D, GL_TEXTURE_CUBE...
                 0,      // nivell mipmap
                 texture_format,
                 x, y,   // On de la pantalla comencem a copiar
                 w, h,   // tamany de la textura / àrea a copiar
                 0);     // border

// El format pot ser
// GL_RGBA, GL_RGB, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_ALPHA
// A més, en OpenGL estàndard (MAI en ES) es pot copiar el depth
// buffer si s'especifica el format
// GL_DEPTH_COMPONENT


// Per copiar a una subregió d'una textura
glCopyTexSubImage2D(target, 0, texture_format,
                    xoffset, // coordenades on començar a copiar
                    yoffset, // a la textura
                    x, y, w, h, 0);

// Les 3D només poden fer servir la "SubImage"
glCopyTexSubImage3DOES(target, 0, texture_format,
                       xoffset, yoffset,
                       zoffset, // coordenada z a on es copia
                       x, y, w, h, 0);
//------------------------------------------------------------

 I fins aqui tots els temes relacionats amb renderitzar múltiples vegades un frame. Amb això es poden aconseguir efectes xulos, ja sigui:
Glow, Blur, HDR, Ombres (Shadowmap)...

divendres, 28 de gener de 2011

Guia ràpida d'OpenGL (ES)

Molts bons el que sigui.

He estat llegint un llibre d'OpenGL ES 2.0, la versió per sistemes integrats d'OpenGL. La gràcia (explicat ràpidament) de la versió ES és que és essencialment el mateix que la versió "mare" però simplificada, o sigui, amb totes les funcions "redundants" eliminades. Un programa fet doncs, en OpenGL ES és pràcticament un 100% portable a OpenGL normal (excepte que les extensions són diferens, petits detallets).

Què vui fer jo ara? Escriure'm un resumet del llibre, ja que l'he de tornar a la biblioteca eventualment.

Així que, què, saveu com va OpenGL, més o menys? El de veritat, el dels Shaders, eh!

Essencialment, el que un ha de fer perque es dibuixi alguna cosa és especificar un seguit de geometria (Triangles, línees o "punts"), que recorre un seguit de transformacions fins que són pintades. Quines transformacions?

Pipeline:
  1. Vertex Shader: Agafa paràmetres que li dónes directament i les passes a la següent fase. El paràmetre important a passar són les coordenades de "clip" (coordenades de retallada), que essencialment són les coordenades dels vertexos normalitzades.
  2. Rasterització: Es divideix en diferents fases, l'objectiu és trobar els fragments que faràn el "Fragment Shader.
    1. Divisió de perspectiva: S'agafen les coordenades de clip (xc, yc, zc, wc) i es transformen a coordenades de Device (xd, yd, zd) = (xc/wc, yc/wc, zc/wc). Aquelles coordenades fóra del rang [-1, 1] són descartades.
    2. Transformació de Viewport: Transforma les anteriors coordenades a coordenades de pantalla (les x i y) i també transforma proporcionalment les z a un rang que tu mateix has especificat.
    3. Rasterització: Un cop tens cada vèrtex situat en un pixel, "s'envien" tots els pixels intermitjos al Fragment Shader. Prèviament es fa el culling (Eliminar els triangles que estiguin mirant en contra la càmera.
  3. Fragment Shader: Agafa paràmetres interpolats del Vertex Shader i els utilitza per definir el color del fragment. A més disposa de les coordenades del fragment en pantalla, les coordenades dels point-sprites, i si la cara mira o no a càmera.
  4. Operacions per fragment: També n'hi ha vàries.
    1. Scissor Testing: Fa que només es pintin pixels definits en un rectangle definit.
    2. Stencil Test: No ho he entès mai, ni mai he vist cap exemple de ningú fent-ho servir.
    3. Depth Test: Decideix si un fragment es dibuixa o no segons la seva profunditat.
    4. Blending: Barreja el color amb el color anterior.
    5. Dithering: Tampoc ho he fet servir mai.
Coses Ràpides que cal saber de shaders:

Els Shaders tenen 3 tipus de "variables globals":
  • Uniform: Són paràmetres que comparteix tota la geometria, on la geometria és allò que dibuixes d'un cop, un model, vamos. Exemples serien les matrius de transformació, les llums, etc...
  • Attribute: Són paràmetres per vèrtex. Per exemple Coordenades-Objecte, Normals, coordenades de textura i altres.
  • Varying: Aquestes s'escriuen al Vertex Shader i es llegeixen al Fragment Shader. És la manera de passar informació de l'una a l'altre.
Cada shader es defineix amb una funció "main" de tota la vida.

Com es creen els shaders? "Fàcil"!

Primer cal crear ambdós shaders:


//---------------------------------------------------------
GLenum type; //GL_VERTEX_SHADER | GL_FRAGMENT_SHADER

GLuint shader_object = glCreateShader(type);

glShaderSource( shader_object, 
                count,   // número d'strings que passes
                strings, // array de char*, amb el codi
                lenghts);// array de int, amb la llargada de cada
                         // string. Si és NULL se suposa que
                         // acaven en '\0'. Si un element és
                         // negatiu se suposa que aquell acava
                         // en '\0'.

glCompileShader( shader_object );
glGetShaderiv( shader_object,
               param,    // GL_COMPILE_STATUS
                         // GL_DELETE_STATUS
                         // GL_INFO_LOG_LENGTH
                         // GL_SHADER_SOURCE_STATUS
                         // GL_SHADER_TYPE
               &result);

//Comprovar que el compile status sigui true, sino agafar el log
if(!result) {
  int len;
  glGetShaderiv( shader_object, GL_INFO_LOG_LENGTH, len);

  char* infoLog = new char[len];
  glGetShaderInfoLog(shader_object,
                     len,
                     NULL, //llargada, com que ja la savem, null
                     infoLog);

  printf("Log: %s", infoLog);
}

//Eventualment
glDeleteShader(shader_object);
//---------------------------------------------------------

Un cop creats els shaders (Vertex i Fragment), cal lligar-los:

//---------------------------------------------------------
GLuint vertex_shader, fragment_shader;

GLuint shader_program = glCreateProgram();

glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);

//glDetachShader( ... )
glLinkProgram(shader_program); //Fer el link de les "coses"

//glValidateProgram(shader_program) serveix per validar i agafar
//info per el log, però és lent i només s'ha d'usar per "debugar"
//i buscar errors.

glProgramiv( ... ); 
//similar al de shader objects, amb paràmetres
//GL_ACTIVE_ATTRIBUTES             --nombre d'atributs
//GL_ACTIVE_ATTRIBUTES_MAX_LENGTH  --len de l'atribut més llarg

//GL_ACTIVE_UNIFORMS               -- "
//GL_ACTIVE_UNIFORMS_MAX_LENGTH    -- "
//GL_ATTACHED_SHADERS              -- nombre de shaders afegits
//GL_DELETE_STATUS                 -- si es vol eliminar
//GL_INFO_LOG_LENGHT               --
//GL_LINK_STATUS                   -- si s'ha linkad bé
//GL_VALIDATE_STATUS               -- si ha validat

glGetProgramInfoLog( ... ); // com el dels objectes.

//Eventualment
glDeleteProgram( shader_program );
//---------------------------------------------------------

Un cop tenim els shaders compilats i tot, cal agafar les localitzacions dels "uniforms". Amb les localitzacions serem capaços d'escriure aquests valors. "Passar-los" al shader.

Hi ha una funció, glGetActiveUniform, que serveix per saber quina "uniform" hi ha en cada posició i quin tipus té, però aquesta no és la manera normal de buscar-ho (ja sabem quines "uniform" hi ha en el shader, l'hem escrit nosaltres!).

GLint uniformLocation = glGetUniformLocation( shader_program, "nom_uniform");

Així tenim la localització, que podem fer servir així:

void glUniform1f(GLint location, GLfloat v0);

Coses a tenir en compte: Per fer-ho servir el shader ha d'estar "actiu" (més, després) i tmb que n'hi ha moltes, de funcions, i es troben a [1].

Els atributs s'agafen de manera similar.

GLint attributeLocation = glGetAttributeLocation( shader_program, "nom_attribute");

Com preparar la "Geometria"

No m'estaré de métodes arcaics per definir geometria i aniré directament al métode més "eficient".

Conceptualment es tracta de pujar a memória VRAM, memòria de la targeta gràfica, totes les dades necessàries per dibuixar la geometria, i posteriorment cridar-la quan calgui. L'algoritme vé a ser el següent:
  1. En una fase de "init", definir i omplir els buffers.
  2. En una fase de "render":
    1. Activar el Shader que s'utilitza ( glUseProgram(program_id); ).
    2. Definir totes les Uniforms que fa servir el Shader
    3. Fer la crida de "Pinta la geometria".
  3. En una fase de "cleanup" eliminar els buffers.
Què són i com funcionen els buffers? Imaginem-nos que volem renderitzar el següent cuadrat:

[0] ----- [1]
 |         |
 |         |
 |         |
[2] ----- [3]

Aquest té 4 vertexos, amb coordenades [0 => (0, 5, 0)], [1 => (5, 5, 0)], [2 => (0, 0, 0)], [3 => (5, 0, 0)], i amb coordenades de textura [0 => (0, 1)], [1 => (1, 1)], [2 => (0, 0)], [3 => (1, 0)]. Podríem fer un array de la següent manera:

float vertex_data[] = {0, 5, 0, 0, 1, //primer vertex
                       5, 5, 0, 1, 1,
                       0, 0, 0, 0, 0,
                       5, 0, 0, 1, 0  //últim vertex
                      };

Per indicar com s'ha de dibuixar, es pot fer indexat. Primer de tot, cal dividir la geometria en triangles, podem fer 2 triangles en aquest cas: el {0, 2, 1} i el {1, 2, 3}. Així li podem indicar a OpenGL que dibuixi la geometria {0, 2, 1, 1, 2, 3}.

uint16 index_data[] = {0, 2, 1, 1, 2, 3};

Abdós buffers es poden guardar a VRAM i posteriorment activar i utilitzar.

Creant Buffers

Aqui expliquem com crear els buffers, o sigui, la part de "init"


//----------------------------------------------------------
GLuint bufferIds[2];

glGenBufferData(2, bufferIds); //Generem 2 buffers

glBindBuffer(GL_ARRAY_BUFFER, bufferIds[0]); //activem el buffer
                                             //com a buffer de
                                             //dades
glBufferData(GL_ARRAY_BUFFER,   
             numVert * sizeVert, //Tamany que tindran les dades
             vertex_data,        //Dades
             GL_STATIC_DRAW);    //Mode. Li dóna pistes a la gràfica
             //GL_STATIC_DRAW  -- escrit 1, usat molt
             //GL_DYNAMIC_DRAW -- escrit molt, usat molt
             //GL_STREAM_DRAW  -- escrit 1, usat poc

           //GL_ELEMENT... serveix per indexos
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIds[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, index_data, GL_STATIC_DRAW);

//----------------------------------------------------------
Com a nota adicional, sempre es pot re-crear les dades amb "glBufferData" o canviar una part amb "glBufferSubData" (tot i que si l'has creat amb "STATIC_DRAW" puteges al driver).

Emprant Buffers

Aqui expliquem com renderitzar usant buffers, o sigui la crida a "pintar geometria".


//-------------------------------------------------------------

  //Activem el buffer de vertexos que farem servir
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);

  //Activem el buffer d'indexos que farem servir
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexBufferId);


  //activem els atributs del shader
glEnableVertexAttribArray(position_attrib); 
glEnableVertexAttribArray(texcoord_attrib); 

glVertexAttribPointer(position_attrib,  // atribut que usem
                      3,         //nº de dades a l'atribut
                      GL_FLOAT,  //tipus de dades
                      GL_FALSE,  //normalització
                      vtxStride, //distancia entre un vtx
                                 //  i el següent
                      0);        //distancia entre l'inici del
                                 //  vtx i les dades en qüestió.     

     /**********************************************************\
     *   Sobre la normalització, serveix per quan usem un tipus *
     * de dades diferent a float i volem que al passar-les a    *
     * float OpenGL les transformi al rang [-1, 1] o no.        *
     \**********************************************************/

     /**********************************************************\
     *   Sobre els tipus de dades, poden ser GL_BYTE,           *
     * GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, *
     * GL_FLOAT, GL_HALF_FLOAT_OES/GL_HALF_FLOAT_ARB            *
     \**********************************************************/

glVertexAttribPointer(texcoord_attrib,

                      2,        
                      GL_FLOAT, 

                      GL_FALSE, 

                      vtxStride,
                      sizeof(float)*3);

glDrawElements(GL_TRIANGLES,      //Tipus de primitiva
               6,                 //nº de indexos
               GL_UNSIGNED_SHORT, //tipus de dades
               0);                //distància entre l'inici del 
                                  //  buffer i el primer index. 

// Tipus de primitives:
//GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP,
//GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN

// Tipus de dades als indexos
//GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT
//La darrera necessita de l'extensió OES_element_index_uint
       
glDisableVertexAttribArray(position_attrib); 
glDisableVertexAttribArray(texcoord_attrib); 
//-------------------------------------------------------------

Textures:

Les textures en OpenGL sempre han estat un petit percal. No m'extendré massa, però coses "importants":


//----------------------------------------------
glGenTextures(num, &id); //genera textures
glDeleteTextures(num, &id); //ejem

  //Activar textura
glBindTexture(tipus, //GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP
              id);

  //Definir la informació
glTexImage(capa,
           nivell,         //Nivell de mipmap. Deixeu-lo a 0
           internalFormat, //format intern
           width,         
           height,
           border,         //en OpenGL ES, 0.
           externalFormat, //cal que sigui el mateix que l'intern.
           dataType,       //tipus de dades
           data);          //dades, per "files"

// Capa:
// Per textures 2D, GL_TEXTURE_2D
// Per Cubemap s'han de definir totes 6 cares:
// GL_TEXTURE_CUBE_MAP_POSITIVE_X, ...NEGATIVE_X, ...

// Formats:
//GL_RGBA, GL_RGB, GL_LUMINANCE_ALPHA, GL_LUMINANCE, GL_ALPHA
//Pensar en la luminance com a "escala de grisos"

// Tipus de dades:
//GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_4_4_4_4,
//GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_5_6_5

  //Definir com es filtra la imatge quan es fa més gran.
glTexParameteri( tipus,
                 GL_TEXTURE_MAG_FILTER,
                 param); //GL_NEAREST / GL_LINEAR

  //Definir com es filtra la imatge quan es fa més petita.
glTexParameteri( tipus,
                 GL_TEXTURE_MIN_FILTER,
                 param); 
              //GL_NEAREST, GL_LINEAR
              //GL_NEAREST_MIPMAP_NEAREST x4 combinacions
//Definir què fa una textura quan té una coordenada fora de [0,1]
glTexParameteri( tipus,
                 GL_TEXTURE_WRAP_S, //O t
                 GL_REPEAT); //GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT



glGenerateMipmap(tipus); //Genera automàticament un mipmap per la 
                         //textura activa

//Finalment parlem de com agafar una textura des del shader.
//Per tal de fer-ho, necessitem una "uniform" especial, anomenada
//"sampler" ("sampler2D" o "samplerCube")

//Activem la textura en un la unitat 0:
glActiveTexture(GL_TEXTURE0); //GL_TEXTURE1, ...
glBindTexture(tipus, textureId);

//Fem que el sampler apunti a la unitat 0
glUniform1i(sampler_uniform, 0);

//----------------------------------------------
També hi ha un altre tipus de textura, la textura3D, que és estandard a OpenGL i a OpenGL ES necessita de l'extenció "GL_OES_texture_3D". Funciona pràcticament igual amb una funció extra per carregar-la "glTexImage3D"[OES] amb un paràmetre adicional indicant la profunditat.

Adicionalment podem crear textures de "profunditat" si l'extenció "GL_OES_depth_texture" està disponible. Això serveix essencialment amb el tema dels pBuffers (vist més endavant) i es creen amb un format "GL_DEPTH_COMPONENT" i tipus "GL_UNSIGNED_SHORT" o "GL_UNSIGNED_INT" si estàn disponibles.


Continuaré en un altre moment. Falta per fer:

  • pBuffers
  • Vertex Shaders
  • Fragment Shaders
  • Operacions post-shader
  • Exemples

[1] http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml