glVertexAttribPointer açıklaması


94

Sadece bunu doğru anladığımdan emin olmak istiyorum (SO Chat'te sorardım, ama orada yok!)

Bir Vertex Array'ımız var, onu bağlayarak "güncel" hale getiriyoruz
,
ardından bir Hedefe bağladığımız bir Tamponumuz var ve ardından o Hedefi doldurarak o hedefe glBufferData bağlı olan her şeyi, yani Tampon'umuzu dolduruyoruz.
ve sonra glVertexAttribPointerverinin nasıl yerleştirildiğini açıklayan diyoruz - veri ne olursa olsun GL_ARRAY_BUFFER ve bu tanımlayıcı orijinal Vertex Dizimize kaydedilir.

(1) Anladığım doğru mu? Dokümantasyon her şey nasıl ögelerinden hakkında biraz seyrek.

(2) Bir tür varsayılan Köşe Dizisi var mı? Unutmuşum Çünkü / atlanmış glGenVertexArraysve glBindVertexArrayve programım onsuz cezası çalıştı.


Düzenleme: Bir adım cevapsız ... glEnableVertexAttribArray.

(3) Köşe Özniteliği glVertexAttribPointerçağrıldığında Köşe Özniteliği Vertex Dizisine bağlı mı ve sonra bu özniteliği glEnableVertexAttribArrayşu anda hangi Köşe Dizisinin bağlı olduğuna bakılmaksızın herhangi bir zamanda etkinleştirebilir / devre dışı bırakabilir miyiz ?

Veya (3b) Köşe Özniteliği o anda Köşe Dizisine bağlı mıdır glEnableVertexAttribArrayve böylece glEnableVertexAttribArrayfarklı Köşe Dizileri bağlı olduğunda farklı zamanlarda çağrı yaparak aynı Köşe Özniteliğini birden çok Köşe Dizisine ekleyebilir miyiz ?

Yanıtlar:


212

Terminolojinin bir kısmı biraz yanlış:

  • A Vertex Arraysadece float[]köşe verilerini içeren bir dizidir (tipik olarak a ). Hiçbir şeye bağlı olmasına gerek yok. A Vertex Array Objectveya VAO ile karıştırılmamalı , daha sonra buna değineceğim
  • Buffer ObjectGenellikle Vertex Buffer Objectköşeleri depolarken veya kısaca VBO olarak adlandırılan A , yalnızca a Buffer.
  • Hiçbir şey köşe dizisine geri kaydedilmez, glVertexAttribPointertam olarak aynı şekilde çalışır glVertexPointerveya glTexCoordPointerçalışır, sadece adlandırılmış öznitelikler yerine kendi özniteliğinizi belirten bir sayı sağlamanız gerekir. Bu değeri olarak geçersiniz index. Tüm glVertexAttribPointeraramalarınız bir dahaki sefere glDrawArraysveya aradığınızda sıraya alınır glDrawElements. Bağlanmış bir VAO'nuz varsa, VAO tüm öznitelikleriniz için ayarları kaydedecektir.

Buradaki ana sorun, köşe özniteliklerini VAO'larla karıştırmanızdır. Köşe nitelikleri, çizim için köşeleri, metinleri, normalleri vb. Tanımlamanın yeni yoludur. VAO'lar depo durumu. Önce çizimin köşe nitelikleriyle nasıl çalıştığını açıklayacağım, ardından VAO'larla yöntem çağrılarının sayısını nasıl azaltabileceğinizi açıklayacağım:

  1. Gölgelendiricide kullanmadan önce bir niteliği etkinleştirmelisiniz. Örneğin, köşeleri bir gölgelendiriciye göndermek istiyorsanız, büyük olasılıkla onu ilk öznitelik olarak göndereceksiniz, 0. Bu nedenle, oluşturmadan önce, ile etkinleştirmeniz gerekir glEnableVertexAttribArray(0);.
  2. Artık bir öznitelik etkinleştirildiğine göre, kullanacağı verileri tanımlamanız gerekir. Bunu yapmak için VBO'nuzu bağlamanız gerekir - glBindBuffer(GL_ARRAY_BUFFER, myBuffer);.
  3. Ve şimdi - özelliğini tanımlayabiliriz glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);. Parametre sırasına göre: 0, tanımladığınız özniteliktir, 3 her köşenin boyutudur GL_FLOAT, türdür, GL_FALSEher köşeyi normalleştirmemek anlamına gelir, son 2 sıfır, köşelerde adım veya kayma olmadığı anlamına gelir.
  4. Onunla bir şeyler çizin - glDrawArrays(GL_TRIANGLES, 0, 6);
  5. Bir sonraki çizeceğiniz şey 0 özelliğini kullanmayabilir (gerçekçi olarak kullanacaktır, ancak bu bir örnektir), böylece onu devre dışı bırakabiliriz - glDisableVertexAttribArray(0);

Bunu glUseProgram()çağrılara sarın ve gölgelendiricilerle düzgün şekilde çalışan bir işleme sistemine sahip olun. Ancak 5 farklı niteliğiniz, köşeleriniz, metin kodlarınız, normalleriniz, renk ve ışık haritası koordinatınız olduğunu varsayalım. Her şeyden önce, glVertexAttribPointerbu özelliklerin her biri için tek bir çağrı yaparsınız ve önceden tüm özellikleri etkinleştirmeniz gerekir. Diyelim ki 0-4 niteliklerini listelediğim gibi tanımladınız. Hepsini şu şekilde etkinleştirirsiniz:

for (int i = 0; i < 5; i++)
    glEnableVertexAttribArray(i);

Sonra da (eğer bir VBO ve kullanım uzaklıklar / adımlarla saklayabilir sürece) her özellik için farklı VBOS bağlamak zorunda kalacak, o zaman 5 farklı yapmak gerekir glVertexAttribPointergelen çağrıları glVertexAttribPointer(0,...);için glVertexAttribPointer(4,...);sırasıyla lightmap koordinatlara köşeler için.

Umarım bu sistem tek başına mantıklıdır. Şimdi, bu tür bir işleme yaparken yöntem çağrılarının sayısını azaltmak için onları nasıl kullanacağımı açıklamak için VAO'lara geçeceğim. Bir VAO kullanmanın gerekli olmadığını unutmayın.

A Vertex Array Objectveya VAO, tüm glVertexAttribPointeraramaların ve her glVertexAttribPointerarama yapıldığında hedeflenen VBO'ların durumunu saklamak için kullanılır .

Bir çağrı ile bir tane oluşturursunuz glGenVertexArrays. İhtiyaç duyduğunuz her şeyi bir VAO'da saklamak için, ona bağlayın glBindVertexArrayve ardından tam bir çizim çağrısı yapın . Tüm beraberlik bağlama çağrıları yakalanır ve VAO tarafından saklanır. VAO'nun bağlantısını şu şekilde çözebilirsiniz:glBindVertexArray(0);

Eğer nesne çizmek istediğinizde Şimdi, gerek yok tüm VBO bağlar veya yeniden çağrı glVertexAttribPointeraramaları, sadece birlikte vào bağlamak gerekir glBindVertexArraysonra çağrı glDrawArraysveya glDrawElementsve size sanki aynı şeyi çizim yapacak tüm bu yöntem çağrılarını yapıyorlardı. Muhtemelen daha sonra da VAO'nun bağlantısını çözmek istersiniz.

VAO’yu çözdüğünüzde, tüm durum, VAO’yu bağlamadan önceki haline döner. VAO bağlıyken yaptığınız herhangi bir değişikliğin saklanıp saklanmadığından emin değilim, ancak bu bir test programıyla kolayca anlaşılabilir. Sanırım glBindVertexArray(0);"varsayılan" VAO'ya bağlayıcı olduğunu düşünebilirsiniz ...


Güncelleme: Biri gerçek bir çekiliş çağrısı ihtiyacına dikkatimi çekti. Görünüşe göre, VAO'yu kurarken aslında bir TAM çizim çağrısı yapmanıza gerek yok, sadece tüm bağlayıcı şeyler. Neden daha önce gerekli olduğunu düşündüğümü bilmiyorum ama şimdi düzeltildi.


10
"Bir Köşe Tampon Nesnesi veya VBO (bazen sadece bir Tampon Nesnesi olarak anılır)" Bu "bazen" olarak adlandırılır çünkü aslında buna denir. Bu sadece bir arabellek nesnesidir, tek tip bloklar, piksel aktarımı, dönüşüm geri bildirimi veya başka herhangi bir kullanım için kullanabileceğiniz diğer arabellek nesnesinden farklı değildir. OpenGL spesifikasyonu hiçbir zaman "köşe arabellek nesnesi" olarak hiçbir şeye başvurmaz; orijinal uzantı özelliği bile onu asla böyle adlandırmaz.
Nicol Bolas

3
Mükemmel cevap. Bunu yazmaya zaman ayırdığınız için teşekkürler! Yine de birkaç soru soruyor: (1) "Oluşturmadan önce ve özniteliği tanımlamadan önce, onu glEnableVertexAttribArray (0) ile etkinleştirmeniz gerekiyor" dediniz - çağrılmadan önce etkinleştirilmesi gerektiğinden emin misiniz glVertexAttribPointer? Testlerimde sıra önemli görünmüyor. (2) Sizi doğru anlarsam, Köşe Nitelikleri geneldir ve yalnızca etkin / devre dışı durumları şu anda bağlı olan VAO'ya kaydedilir.
mpen

1
(1) Daha önce glDrawArraysveya etkinleştirdiğiniz sürece siparişin önemli olduğunu düşünmüyorum glDrawElements. Gönderiyi bunu yansıtacak şekilde güncelleyeceğim (2) Evet, ancak depolanan yalnızca etkinleştirme / devre dışı bırakma durumu değil, bu aramalarla ilgili her şey - o sırada GL_ARRAY_BUFFER'a bağlı olan şey, tür, adım ve fark. Esasen, tüm Vertex Niteliklerini VAO ile kurduğunuz şekle geri döndürmek için yeterince depoluyor.
Robert Rouhani

2
evet, VAO'lar, çoğu çizim yöntemini bir VAO'yu bağlamayla değiştirmenize izin verecek şekilde tasarlanmıştır. Her varlığın ayrı bir VAO'su olabilir ve bu yine de iyi çalışacaktır. Yine de üniformaları güncellemeniz ve kendi dokularınızı bağlamanız gerekecek. Ve gölgelendiricinizin özniteliklerini öznitelik dizinleri ile bağlamanız gerektiği gibi aynı öznitelik dizinlerini de kullanmanız gerekir, ister layout(location = x)gölgelendiriciden ister gölgelendiriciyi glBindAttributeLocationderlerken. Örnek
Robert Rouhani

8
Modern OpenGL'de artık varsayılan bir köşe dizisi nesnesi olmadığını, kendiniz bir tane oluşturmanız gerektiğini veya uygulamanızın ileriye uyumlu bir bağlamda çalışmayacağını unutmayın.
2012'de

3

API'lerin terminolojisi ve dizisi aslında oldukça kafa karıştırıcıdır. Daha da kafa karıştırıcı olan şey, çeşitli yönlerin - arabellek, genel köşe niteliği ve gölgelendirici özellik değişkeninin nasıl ilişkilendirildiği. Oldukça iyi bir açıklama için OpenGL-Terminolojisine bakın .

Ayrıca OpenGL-VBO, shader, VAO bağlantısı , gerekli API çağrıları ile basit bir örnek gösterir. Acil moddan programlanabilir boru hattına geçiş yapanlar için özellikle iyidir.

Umarım yardımcı olur.

Düzenleme: Aşağıdaki yorumlardan da görebileceğiniz gibi, insanlar varsayımlarda bulunabilir ve sonuçlara atlayabilir. Gerçek şu ki, yeni başlayanlar için oldukça kafa karıştırıcı.


" Oldukça iyi bir açıklama için OpenGL-Terminolojisine bakın. " 1 dakikadan daha kısa bir süre içinde, zaten bir yanlış bilgi buldum: "Bunlar, bir gölgelendirici değişkeniyle ( özelliği işleyen koordinatlar, renk vb.) " Bunlar "endeks" olarak adlandırılmazlar; onlar "konumlar". Bu çok önemli bir ayrımdır çünkü köşe özelliklerinin aynı zamanda konumlardan çok farklı olan "indisleri" vardır . Bu çok berbat bir web sitesi.
Nicol Bolas

2
Bu adil bir yorum, ancak tamamen doğru değil. Eğer genel bir özelliği tanımlamak için OpenGL API bakarsak glVertexAttribPointer , void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer), tanımlayıcı olarak adlandırılır index. Program bağlamındaki aynı tanımlayıcı, glGetAttribLocationlocation API'sinde çağrılır .
ap-osd
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.