HTML

Az élet kódjai

Csináld maga. Senki nem csinálja meg helyetted.

Friss topikok

Point sprite-ok OpenGL-el

2016.03.16. 00:50 Travis.CG

Ha a háromszög túl sok, ha a vonal túl unalmas, ha a jelszavunk: zéró poligon, akkor a pontok segítségével kápráztathatjuk el a nézőket. Már a korai OpenGL verziókban is használhattuk a glBegin(GL_POINTS) hívást, de a pontok koordinátáit kénytelenek voltunk CPU-n számolni.

Ahogy fejlődtek a grafikus kártyák és nőtt az OpenGL verziószám, úgy tudtunk egyre több kódot átpakolni GPU-ra. Most egy olyan technikát akarok bemutatni, ami az összes logikát shaderekből valósítja meg. Miért jó ez? Már korábban is törekedtem arra, hogy a shadereket egyfajta szkript nyelvként használjam és úgy tudjak változtatni a jeleneteken, hogy ne kelljen újra fordítani a kódot. Ez remélhetőleg el fog vezetni egy viszonylag egyszerű demo toolhoz.

Az ötlet nem a sajátom, a vertexshaderart.com az alapja. Nagy vonalakban úgy néz ki, hogy a CPU-n lefoglaljuk a memóriát a csúcsoknak, de nem állítunk be semmit, betöltjük a shadereket, majd kirajzolunk mindent pontként. A koordinátákat a vertex shader határozza meg a csúcsok sorszáma alapján. A fragment shaderrel még játszunk a megjelenítésen, és kész.

Lássuk a kódot! Az ablaknyitástól és egyéb "unalmas" lépésektől most eltekintek. Ha hatalmas komment áradat követeli, akkor beteszem a teljes kódot, de ettől jelenleg nem tartok.

glEnable(GL_TEXTURE_2D);
glEnable(GL_PROGRAM_POINT_SIZE);

glGenVertexArrays(1, &pointsprite);
glBindVertexArray(pointsprite);

GLfloat *spritecoords;
spritecoords = (GLfloat*)malloc(POINTNUM * sizeof(GL_FLOAT));
glGenBuffers(1, &pointsprites_vba);
glBindBuffer(GL_ARRAY_BUFFER, pointsprites_vba);
glBufferData(GL_ARRAY_BUFFER, POINTNUM * sizeof(GL_FLOAT), spritecoords, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);
free(spritecoords);

Létre hozunk egy vertex arrayt, abban egy buffert, lefoglaljuk a memóriát, átmásoljuk a GPU-ra, majd a CPU-n mindent felszabadítunk. Még arra sem vesztegetjuk a drága erőforrásokat, hogy a pointjainknak textúra koordinátát tartsunk fenn. Pedig fogunk textúrázni is! A GL_PROGRAM_POINT_SPRITE bekapcsolása fontos lépés, mert a pontunk méretét is meg akarjuk változtatni a shaderben.

A kirajzolás szintén semmi újat nem tartalmaz:

glUseProgram(textsprite);
glActiveTexture(GL_TEXTURE0);

glBindTexture(GL_TEXTURE_2D, index);
GLint loc = glGetUniformLocation(textsprite, "img");
glUniform1i(loc, 0);

glBindVertexArray(pointsprite);
glDrawArrays(GL_POINTS, 0, POINTNUM / 2);

Természetesen további uniform változókat is használhatunk, hogy további érdekes effekteket hozzunk létre. Például átadhatnánk az időt, mint paramétert. A képernyő felbontást, egér pozíciót, stb. Továbbá mellőzöm a shader betöltő kódot is.

Térjünk rá a móka részére! Vertex shaderünk bemeneti változói közül így csak a gl_VertexID-t tudjuk használni. Ez nem más, mint egy egész szám 0-tól POINTNUM-ig (csak, hogy konzekvens legyek a kóddal). Ebből határozhatjuk meg a sprite-ok helyzetét. Például ha van 8000 pontunk, akkor a következő kóddal színusz hullám formájában rajzolhatjuk ki őket:

#version 410

void main(){
   float x = gl_VertexID / 8000.0 - 1.0;
   float y = sin(x) * 200.0);
   vec2 vertex = vec2(x,y);
   gl_Position = vec4(vertex, 0.0, 1.0);
}

Ez elég érdekes, de tudunk ennél jobbat is! Például textúrát húzhatunk rájuk. Egy pontra nem sok értelme van textúrát húzni, de a pontjaink méretét tudjuk változtatni a gl_PointSize változóval. Meglepő módon pixelben kell megadni a méretet, ami (legalábbis nekem) szokatlan, hiszen minden más értéket 0-1 közötti számmal kell definiálni. Ezt a felbontás független kód írásánál érdemes észben tartani.

#version 410

out vec4 FragColor;
uniform sampler2D img;

void main(){
   FragColor = texture(img, vec2(1.0, -1.0) * gl_PointCoord);
}

A fenti fragment shader használatával sprite-jaink mindegyike ugyan azzal a textúrával lesz bevonva. A gl_PointCoord változó kiszámítja nekünk a megfelelő textúra koordinátákat. Azért sok, egyforma alakzat unalmas. Adjunk egyéniséget spritejainknak! Például pozíciótól függően változtassuk a textúrát. Vertex shaderünk egy kicsit átalakul:

#version 410

out vec2 textcoord;

void main(){
  float x = gl_VertexID / 8000.0 - 1.0;
  float y = sin(x) * 200.0);
  vec2 vertex = vec2(x,y);
  textcoord = vertex;
  gl_Position = vec4(vertex, 0.0, 1.0);
}

A fragment shader is módosul, de nem sokat:

#version 410

out vec4 FragColor;
in vec2 textcoord;
uniform sampler2D img;

void main(){
  FragColor = texture(img, vec2(1.0, -1.0) * textcoord);
}

Ez már sokkal érdekesebb hatás. Kísérletezzünk bátran. Próbáljunk ki őrült ötleteket, hátha valamelyik megtetszik.

Szólj hozzá!

Címkék: programozás demoscene opengl

A bejegyzés trackback címe:

https://cybernetic.blog.hu/api/trackback/id/tr78475086

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása