HTML

Az élet kódjai

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

Friss topikok

Mélységi puffer

2011.10.21. 10:48 Travis.CG

Screen-space ambient occlusion, deferred rendering, shadow mapping. Mi a közös ezekben a fogalmakban? Mindegyik alapja a mélységi pufferben (depth buffer, z-buffer) tárolt információ.

Ha nem akarjuk, hogy a pouet.net-en olyan kritikát kapjunk, mint: "Ez '94-es színvonal", "2000 előtt láttam ilyen demókat", (márpedig én nem akarok :-) többet). Akkor mindenképp meg kell ismerkedni ezekkel a technikákkal. A most következő leírás OpenGL 4.1 alatt fog működni.

Áttekintés

A módszer lényege, hogy először a geometriát (3D objektumokat) rajzoljuk ki egy shaderrel (nevezzük depth shadernek), az eredményt lementjük egy vagy több textúrába. A második fázisban egy, az egész képernyőt betöltő négyzetre kirajzoljuk a textúrát.

A bevezetőben említett technikák csak abban térnek el, hogy miként használjuk pufferünket. Ha egy fényforrás felől állapítjuk meg a távolságot, akkor árnyékokat hozunk létre, ha az egymás közelében elhelyezkedő mélységeket hasonlítjuk össze, akkor SSAO-t alkalmazunk.

Inicializálás

Hozzunk létre egy framebuffert, és csatoljunk hozzá két textúrát. Más források arról írnak, hogy kell egy renderbuffer is, ami szintén tartalmazza a mélységi adatokat, de ez nem igaz. Az viszont igaz, hogy engedélyezni kell a mélységi vizsgálatot.

glEnable(GL_DEPTH_TEST);
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glGenTextures(2, textures);

/* Depth information */
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/* Add more texture parameters if You would like */
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textures[0]);

/* Color information */
glBindTexture(GL_TEXTURE_2D, textures[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/* Add more texture parameters if You would like */
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture[1], 0);

if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE){
  printf("Error\n");
}

glBindFramebuffer(0);
Már az elején létre kell hozni a "négyzetet" ami megkapja a textúrákat. A koordináták szokás szerint egy vertex arrayben lesznek.

void createSquare(){
   GLfloat v[] = {-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0};

   glGenVertexArrays(1, &square);
   glBindVertexArray(square);
   glGenBuffers(1, &squarevbo);
   glBindBuffer(GL_ARRAY_BUFFER, squarevbo);
   glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), v, GL_STATIC_DRAW);

   glBindVertexArray(0);
}
Szükségünk lesz még két shaderre. Az egyik a textúrákat fogja feltölteni. Mivel nem fog tartalmazni semmi különleges műveletet, ezért csak base_shaderkét hivatkozom rá. A másik a textúrákat fogja kirajzolni a képernyő méretű négyzetre. Ez a shader fog a mélységi pufferrel dolgozni, ezért ennek a depth_shader nevet adtam.

#version 410

/* base_shader vertex part */

in vec3 vertex;
in vec3 normal;

uniform mat4 pmatrix;
uniform mat4 mmatrix;

out float factor;
out float depth;

void main(void){
   vec4 pos = pmatrix * mmatrix * vec4(vertex, 1.0);
   gl_Position = pos;
   factor = max(0.0, dot(normal, vec3(0.0, 2.0, -15.0)));
   depth = pos.z / 15.0;
}
A base_shader rajzolja ki a modelleket, ezért itt alkalmazni kell a mátrix műveleteket, hogy a modelleket eltoljuk, forgassuk. A mélységi adatokat a vertex Z koordinátájából vesszük, miután alkalmaztuk rá az összes mátrix műveletet. A mélységi puffer 0.0-1.0 közötti értékeket vehet fel. Most az egyszerűség kedvéért vegyünk egy olyan jelenetet, ami 0 - 15 közötti koordináták között mozog. A csúcspont Z koordinátáját ezért 15-el elosztjuk. Megjegyzem, ez egy nagyon egyszerű, lineáris mélységi számítás. Realisztikusabb eredményt kapunk, ha a messzebb elhelyezkedő tárgyaknál kisebb felbontást használunk, mint a közelebb elhelyezkedőeknél.

#version 410

/* base_shader fragment part */

in float factor;
in float depth;
out vec4 gl_FragColor;

void main(void){
   gl_FragColor = vec4(1.0) * factor;
   gl_FragDepth = depth;
}
A gl_FragDepth a mélységi pufferünk, míg a gl_FragColor a színeket tartalmazza. A factor változó egy kis változatosságot csempész a színek egyhangúságába.

#version 410

/* depth_shader vertex part */

in vec2 vertex;
out vec2 texcoord;

void main(void){
   gl_Position = vec4(vertex, 0.0, 1.0);
   texcoord = (vertex + 1.0) / 2.0;
}
A négyzetünket nem forgatjuk sehova, csak kirajzoljuk. A textúra koordinátákat a shader számolja ki a csúcspontokat felhasználva.

#version 410

/* depth_shader fragment part */

out vec4 gl_FragColor;
in vec2 texcoord;

uniform sampler2D renderedtex;

void main(void){
   gl_FragColor = vec4(texture(renderedtex, texcoord).r);
}
Most csak egy textúrát kezel a shader. Később, ha majd több pufferünk is lesz, majd kiegészítjük ezt a kódot.

 Rajzolás

A rajzolás két menetben történik. Először a modelleket rajzoljuk ki, letároljuk a textúrákba, majd a második menetben a textúrákat a négyzetre rajzoljuk.

   glBindFramebuffer(GL_FRAMEBUFFER, fb);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glUseProgram(base_shader);
   uni = glGetUniformLocation(base_shader, "pmatrix");
   glUniformMatrix4fv(uni, 1, GL_FALSE, pmatrix);

   uni = glGetUniformLocation(base_shader, "mmatrix");
   glUniformMatrix4fv(uni, 1, GL_FALSE, locM);

   /* Draw the mesh */
   drawMesh3D(tree);
A kódból hiányoznak a mátrix műveletek, csak a kirajzolás váza van itt. A mátrixokról írtam egy korábbi cikkben.

   glUseProgram(depth_shader);
   glBindFramebuffer(GL_FRAMEBUFFER, 0);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   glActiveTexture(GL_TEXTURE0);
   /* Color attachment use as texture for a quad */
   glBindTexture(GL_TEXTURE_2D, textures[0]);
   uni = glGetUniformLocation(depth_shader, "renderedtex");
   glUniform1i(uni, 0);

   /* Draw a quad */
   glBindVertexArray(square);
   glBindBuffer(GL_ARRAY_BUFFER, squarevbo);
   glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
Most lebuktam! Nem is négyzetet rajzoltunk ki, hanem két háromszóget! A mélységi puffer használatra készen áll. Ne felejtsük el felszabadítani az erőforrásokat kilépésnél!

Szólj hozzá!

Címkék: programozás demoscene opengl

A bejegyzés trackback címe:

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

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