Köşe Dizisi Nesneleri nedir?


114

Bugün bu öğreticiden OpenGL öğrenmeye yeni başlıyorum: http://openglbook.com/the-book/
Bir üçgen çizdiğim 2. bölüme geldim ve VAO'lar dışında her şeyi anlıyorum (bu kısaltma uygun mu?). Öğreticide şu koda sahiptir:

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

Kodun gerekli olduğunu anlasam da ne işe yaradığına dair hiçbir fikrim yok. VaoId'i bu noktadan sonra hiç kullanmama rağmen (onu yok etmek dışında), kod onsuz çalışmıyor. Bunun, bağlı olması gerektiği için olduğunu varsayıyorum, ama nedenini bilmiyorum. Bu tam kodun her OpenGL programının bir parçası olması mı gerekiyor? Eğitim, VAO'ları şu şekilde açıklar:

Bir Köşe Dizisi Nesnesi (veya VAO), köşe özniteliklerinin bir Köşe Tampon Nesnesinde (veya VBO) nasıl depolandığını açıklayan bir nesnedir. Bu, VAO'nun köşe verilerini depolayan gerçek nesne olmadığı, ancak köşe verilerinin tanımlayıcısı olduğu anlamına gelir. Köşe nitelikleri, glVertexAttribPointer işlevi ve onun iki kardeş işlevi olan glVertexAttribIPointer ve glVertexAttribLPointer tarafından tanımlanabilir, bunlardan ilki aşağıda inceleyeceğiz.

VAO'nun köşe özelliklerini nasıl tanımladığını anlamıyorum. Onları hiçbir şekilde tarif etmedim. Bilgileri glVertexAttribPointer'dan alıyor mu? Sanırım bu olmalı. VAO, glVertexAttribPointer'dan gelen bilgiler için sadece bir hedef mi?

Bir yan not olarak, takip ettiğim eğitim kabul edilebilir mi? Dikkat etmem gereken bir şey veya takip etmem gereken daha iyi bir eğitim var mı?

Yanıtlar:


100

"Köşe Dizisi Nesnesi" size Aptal Adlar için OpenGL ARB Alt Komitesi tarafından sunulmaktadır.

Bunu bir geometri nesnesi olarak düşünün. (Eski zamanların SGI Performer programcısı olarak ben bunlara geosetler diyorum.) Nesnenin örnek değişkenleri / üyeleri köşe işaretçiniz, normal işaretçiniz, renk işaretleyiciniz, öznitelik N işaretçiniz, ...

Bir VAO ilk bağlandığında, bu üyeleri arayarak atarsınız.

glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer...;
glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer...;

ve bunun gibi. Hangi özniteliklerin etkinleştirildiği ve sağladığınız işaretçiler VAO'da saklanır.

Bundan sonra, VAO'yu tekrar bağladığınızda, tüm bu nitelikler ve işaretçiler de geçerli hale gelir. Dolayısıyla, bir glBindVertexArrayçağrı, tüm öznitelikleri ayarlamak için önceden gerekli olan tüm koda eşdeğerdir. Kendi yapılarınızı veya nesnelerinizi oluşturmak zorunda kalmadan geometriyi işlevler veya yöntemler arasında geçirmek için kullanışlıdır.

(Tek seferlik kurulum, çoklu kullanım, VAO'ları kullanmanın en kolay yoludur, ancak yalnızca onu bağlayarak ve daha fazla etkinleştirme / işaretçi çağrısı yaparak da öznitelikleri değiştirebilirsiniz. VAO'lar sabit değildir.)

Patrick'in sorularına yanıt olarak daha fazla bilgi:

Yeni oluşturulan bir VAO için varsayılan, boş olmasıdır (AFAIK). Hiç geometri yok, tepe noktaları bile yok, bu yüzden onu çizmeye çalışırsanız, bir OpenGL hatası alırsınız. Bu, "her şeyi False / NULL / sıfır olarak başlat" gibi oldukça mantıklıdır.

Sadece bir glEnableClientStateşeyler ayarladığınızda yapmanız gerekir . VAO, her bir işaretçi için etkinleştirme / devre dışı bırakma durumunu hatırlar.

Evet, VAO saklayacak glEnableVertexAttribArrayve glVertexAttrib. Eski köşe, normal, renk, ... dizileri öznitelik dizileriyle aynıdır, köşe == # 0 vb.


62
"Köşe Dizisi Nesnesi" size Aptal Adlar için OpenGL ARB Alt Komitesi tarafından sunulmuştur. " Evet, köşe dizisi bağlamalarını depolayan bir nesne için çok saçma bir isim .
Nicol Bolas

2
Ayrıca, VAO'lar ile ilgiliglVertexAttribPointer
Patrick

2
Lütfen çekirdek profili kullanan kişiler için genel köşe özniteliklerinin kullanımı hakkında bazı bilgiler ekleyin.
Oskar

8
@NicolBolas Daha iyi bir isim VertexArrayMacroveya benzeri bir şey olabilir.
bobobobo

7
@NicolBolas "Vertex Array Object" berbat bir isim. Yaklaşık olduğu nitelikler veri bağlama . Adından da anlaşılacağı gibi, köşe dizileri ile ilgili değildir. İsimde bağlanmalara veya niteliklere herhangi bir gönderme yoktur ve "köşe dizisi" kendi başına ayrı bir kavram olduğundan, anlamayı daha da zorlaştırır. IMHO, "(Vertex) Attributes Binding Object" ifadesinin anlaşılması daha kolaydır. Geometri Nesnesi bile daha iyi: Sevmiyorum, ama en azından aşırı yüklenmemiş.
AkiRoss

8

Vertex Array Nesneleri, kelime işlem programları ve benzerlerindeki makrolar gibidir. İyi bir açıklama bulunursa burada .

Makrolar , bu özniteliği etkinleştirmek, o arabelleği bağlamak, vb. Gibi yaptığınız eylemleri hatırlar . ÇağırdığınızdaglBindVertexArray( yourVAOId ) , yalnızca bu öznitelik işaretçi bağlamalarını ve arabellek bağlamalarını yeniden oynatır .

Dolayısıyla, bir sonraki çekiliş çağrınız, VAO ile sınırlandırılmış olanı kullanır.

VAO'lar tepe verilerini depolamaz . Hayır. Köşe verileri bir köşe arabelleğinde veya bir istemci belleği dizisinde saklanır .


19
-1: Makrolar gibi değiller. Öyle olsaydı, yeni VAO'nun "kaydettiği" siz bu dizileri açıkça devre dışı bırakmadığınız sürece, yeni bir VAO'nun bağlanması önceki bir VAO tarafından etkinleştirilen köşe dizilerini devre dışı bırakmaz . VAO'lar, tüm OpenGL nesneleri gibi , komutları değil , durumu korurlar . Komutlar basitçe durumu değiştirir , ancak nesneler varsayılan durum setiyle gelir. Bu nedenle, yeni oluşturulan bir VAO'yu bağlamak her zaman tüm nitelikleri devre dışı bırakır.
Nicol Bolas

6

VAO'yu her zaman OpenGL tarafından kullanılan bir dizi veri arabelleği olarak düşünüyorum. Modern OpenGL kullanarak, bir VAO ve Vertex Buffer Nesneleri oluşturacaksınız.

görüntü açıklamasını buraya girin

//vaoB is a buffer
glGenVertexArrays(1, vaoB); //creates one VAO
glBindVertexArray(vao.get(0));
glGenBuffers(vbo.length, vbo, 0); //vbo is a buffer
glBindVertexArray(vao.get(1));
glGenBuffers(vbo1.length, vbo1, 0); //vbo1 is a buffer
glBindVertexArray(vao.get(2));
glGenBuffers(vbo2.length, vbo2, 0); //vbo2 is a buffer

Bir sonraki adım, verileri bir arabelleğe bağlamaktır:

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER,vertBuf.limit()*4, vertBuf, GL_STATIC_DRAW); //vertf buf is a floatbuffer of vertices

Bu noktada OpenGL şunları görür:

görüntü açıklamasını buraya girin

Şimdi, OpenGL'ye arabellekteki verilerin neyi temsil ettiğini söylemek için glVertexAttribPointer'ı kullanabiliriz:

glBindBuffer(GL_ARRAY_BUFFER, 0); //bind VBO at 0
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); //each vertex has 3 components of size GL_FLOAT with 0 stride (space) between them and the first component starts at 0 (start of data)

görüntü açıklamasını buraya girin

OpenGL artık arabellekte verilere sahiptir ve verilerin köşeler halinde nasıl organize edildiğini bilir. Aynı işlem doku koordinatlarına vb. Uygulanabilir, ancak doku koordinatları için iki değer olacaktır.

glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER,coordBuf.limit()*4, coordBuf, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);

Daha sonra dokuyu bağlayabilir ve dizileri çizebilirsiniz, bir Vert ve Frag gölgelendiricisi oluşturmak, derlemek ve bir programa eklemek isteyeceksiniz (burada dahil değildir).

glActiveTexture(textureID); //bind our texture
glBindTexture(GL_TEXTURE_2D, textureID);
glDrawArrays(GL_TRIANGLES,0,6); //in this case 6 indices are used for two triangles forming a square

5

VAO, OpenGL ardışık düzeninin köşe getirme aşamasını temsil eden ve köşe gölgelendiricisine girdi sağlamak için kullanılan bir nesnedir.

Bunun gibi köşe dizisi nesnesi oluşturabilirsiniz

GLuint vao;
glCreateVertexArrays(1, &vao);
glBindVertexArray(vao);

Önce basit bir örnek verelim. Gölgelendirici kodunda böyle bir girdi parametresi düşünün

layout (location = 0) in vec4 offset; // input vertex attribute

Bu niteliği doldurmak için kullanabiliriz

glVertexAttrib4fv(0, attrib); // updates the value of input attribute 0

Köşe dizisi nesnesi bu statik nitelik değerlerini sizin için saklasa da çok daha fazlasını yapabilir.

Köşe dizisi nesnesini oluşturduktan sonra durumunu doldurmaya başlayabiliriz. OpenGL'den, sağladığımız bir tampon nesnesinde depolanan verileri kullanarak bunu otomatik olarak doldurmasını isteyeceğiz. Her köşe özniteliği, çeşitli köşe arabellek bağlamalarından birine bağlı bir arabellekten veri alır. Bunun için kullanıyoruz glVertexArrayAttribBinding(GLuint vao, GLuint attribindex, GLuint bindingindex). Ayrıca, glVertexArrayVertexBuffer()bir tamponu köşe tampon bağlamalarından birine bağlamak için işlevi kullanırız . glVertexArrayAttribFormat()Verinin düzenini ve biçimini tanımlamak için işlevi kullanırız ve son olarak, arayarak özniteliğin otomatik olarak doldurulmasını etkinleştiririz glEnableVertexAttribArray().

Bir köşe nitelik etkinleştirildiğinde, OpenGL Birlikte sağladık biçim ve konum bilgilerini temel alarak vertex shader veri besleyecek glVertexArrayVertexBuffer()ve glVertexArrayAttribFormat(). Öznitelik devre dışı bırakıldığında, köşe gölgelendiricisine, bir çağrı ile sağladığınız statik bilgiler sağlanır glVertexAttrib*().

// First, bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(vao, 0, buffer, 0, sizeof(vmath::vec4));

// Now, describe the data to OpenGL, tell it where it is, and turn on automatic
// vertex fetching for the specified attribute
glVertexArrayAttribFormat(vao, 0, 4, GL_FLOAT, GL_FALSE, 0);

glEnableVertexArrayAttrib(vao, 0);

Ve bir gölgelendiricide kodlama

layout (location = 0) in vec4 position;

Sonuçta aramanız gerekiyor glDeleteVertexArrays(1, &vao).


Daha iyi anlamak için OpenGL SuperBible'ı okuyabilirsiniz .


3
DSA tarzı OpenGL kullanımını teşvik eden insanları görmek güzel.
Nicol Bolas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.