HTML

Az élet kódjai

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

Friss topikok

Ismétlődő mozgások vertex shaderrel

2011.08.15. 22:54 Travis.CG


Organikus térhálókat élethűbbé tehetünk apró, periodikus mozgásokkal. Ilyen
volt például az Aeronautism című demónkban a repülő szörny, aminek a csápjai
és a farokúszója ritmikusan lengett.

A vertex shaderek elsősorban a térhálók csúcsponti koordinátáit és a normál
vektorokat kapják bemenetnek, de a programozó bizonyos számú saját attribútumot
is megadhat. Ezt fogjuk mi is használni, hogy kijelöljük azokat a csúcspontokat,
amelyeket mozgatni akarunk.

A csúcspontok kijelöléséhez kezdetben azt a trükköt használtam, hogy a betöltöttem a modellt egy szerkesztő programba és azokat a csúcsokat, amelyeket nem kívántam mozgatni, kitöröltem. Exportáltam OBJ formátumba a csonka modellt, majd a Linux parancssor és Perl scriptek segítségével összehasonlítottam az eredeti modellel. Végül a különbségükből készítettem egy fájlt, ami minden egyes csúcspontra tartalmazott egy 0 vagy 1 értéket, attól függően, hogy akartam-e mozgatni az adott csúcsot vagy nem. Az Aeronautism-ban volt egy 0.5 is, mert a csápokat máshogy mozgattam, mint a farokúszót.

Szerencsére Blender segítségével egyszerűsíthető a folyamat. Ezt fogom részletezni.

Indítsuk el a Blendert, töltsük be a modellünket. Váltsunk Vertex Paint módba, majd fessük be a kívánt színűre a csúcspontokat. A be nem festett részek fehérek lesznek. Ha többféle mozgást akarunk, akkor használjunk különböző színeket. Ebben a példában pirosat használtam. A mozgások finomhangolására használhatjuk a színek intenzitását is, amit a Paint fülön állíthatunk be.



Ha elküszültünk, akkor egy Python script segítségével könnyedén exportálhatjuk a csúcspontok színeit. A szkript forráskódja a következő:

#!BPY
"""
Name: 'Vertex Color Extract'
Blender: 243
Group: 'Export'
"""
import bpy
import Blender
from Blender import *

scene = bpy.data.scenes.active
for o in scene.objects:
        if o.type == 'Mesh':
                mesh = o
bf = open("vertexcolor.txt", "w")

for face in mesh.data.faces:
        for i, vert in enumerate(face):
                bf.write("%f %f %f %d\n" % (face.col[i].r, face.col[i].g, face.col[i].b, vert.index))
bf.close()
A szkriptet másoljuk a .blender/scripts könyvtárba. Ha már fut a Blender, a Script -> Update Menu menüponttal frissítsünk. Ez a szkript egy nyers adatot ad vissza egy előre definiált fájlba. Minden egyes poligon csúcspontjának a színét és a csúcspont sorszámát adja vissza. Mivel a poligonok közös csúcspontot adnak vissza, ezért az eredmény fájl redundáns. Még mindig nem értek eléggé a Python nyelvhez, ezért az ismétlődő szakaszokat a parancssor segítségével távolítom el:
sort -nk 4 vertexcolor.txt | uniq >eredmeny.txtMinden egyes csúcspont színe megvan. A demónkba töltsük be ezt a fájlt és tároljuk le egy tömbbe. A példában ez a tömb az attrib nevet kapja.
   GLfloat attrib[4432]; /* Tudom, mennyi csúcspontom van a térhálóban */

   file = fopen("vertexcolor.txt", "r");
   while(fgets(line, 200, file) != NULL){
      r = atof(strtok(line, " "));
      g = atof(strtok(NULL, " "));
      b = atof(strtok(NULL, " "));
      index = atoi(strtok(NULL, " \n"));
      if(g == 0.0 && b == 0.0){
         attrib[index] = 1.0;
      }
      else{
         attrib[index] = 0.0;
      }
   }
   fclose(file);
A példaprogram elég egyszerű. Mivel egy demóban a programozó tudja, hogy az adott modell mennyi csúcspontból áll, ezért bocsánatos bűn, ha ezt beégetem a kódba. Ha újra akarjuk használni a kódot, akkor természetesen át kell tervezni azt.

Miután beolvastuk a fájlból az adatokat, készítünk egy vertex buffert a tárolására:
   glGenBuffers(1, &vbo);
   glBindBuffer(GL_ARRAY_BUFFER, vbo);
   glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * vertexcount, attrib, GL_STATIC_DRAW);
Majd a shader megfelelő változójához kötjük a vertex buffert. Ez a lépés nagyban hasonlít a korábban bemutatott csúcspont és normál vektornál használt módszerhez.
  glBindVertexArray(vao);
  uni = glGetAttribLocation(shader, "sniff");
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glVertexAttribPointer(uni, 1, GL_FLOAT, GL_FALSE, 0, 0);
  glEnableVertexAttribArray(uni);
Az OpenGL kirajzoló rutin a következő képpen fest:

   glUseProgram(shaderprg);
   time = getTimeInterval();
   uni = glGetUniformLocation(snooze, "time");
   glUniform1f(uni, time);
Végezetül lássuk a shader kódot:

#version 410
in vec3 vertex;
in vec3 normal;
in float sniff;

uniform mat4 pmatrix;
uniform mat4 modelmatrix;
uniform float time;

out vec3 color;

void main(void){
   vec3 pos = vertex;

   if(sniff == 1.0) pos.y = pos.y + sin(time * 40.0) / 300.0;

   gl_Position = pmatrix * modelmatrix * vec4(pos, 1.0);
   color = normal * vec3(1.0);
}
Amely csúcspontnál az attribútum 1.0, ott a pozíció egy kis színusz függvény segítségével oszcillál, amitől élőbbnek tűnik a háló. Még élethűbb lehet a modell, ha zaj segítségével kiszámíthatatlanabbá tesszük a mozgást. A példafájlban a modellt Grass készítette. A Function-os demónkban is látható lesz. (A program teljes forráskódját hamarosan elérhetővé teszem)
 

Szólj hozzá!

Címkék: programozás demoscene

A bejegyzés trackback címe:

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

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