閱讀262 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Opengl繪製數組數據與文件數據的方法與Nvidia opengl sdk輔助實現

在opengl中繪製基本的集合原語可以使用諸如
gl_begin(type)
。。。
gl_end
的方式,逐個頂點進行繪製,但是如果想繪製一個大的模型或是一個完整的場景,裏麵的頂點數目幾十上百萬,這時就不能這樣逐個頂點繪製了,為此,在opengl中有從數組繪製的方式。

數組繪製的基本思想:
就是把所所有頂點的位置、法向等信息裝入數組,並且按照一定的序列(預先排好的)繪製他們就行了,這隻需要幾步操作。這裏麵一共涉及到兩種數組,頂點數組(vertex array)與序列數組(indice array)。頂點數組就是將各頂點的位置、法向等裝入(可單獨也可聯合),序列數組就好比一個目錄,上麵記錄了先繪製哪個頂點,在繪製哪一個。圖例

opengl繪製數組數據時的數組組織圖
 
有了這個數組就可以進行繪製了
在opengl的數組繪製中,一共分三步:
第一步:用glEnableClientState(type)激活一個類型的數組type=GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_INDEX_ARRAY, GL_NORMAL_ARRAY, GL_TEXTURE_COORD_ARRAY, and GL_EDGE_FLAG_ARRAY,表示要進行那種數據的繪製
第二步: 用glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)/glNormalPointer…指定定點數組,size為分量數(位置為2或3,法向為3等),type是GL_SHORT, GL_INT, GL_FLOAT, or GL_DOUBLE的一種,為數組中數據的類型,stride是指在定點數組中兩個連續頂點的數據間的間隔(byte為單位),這隻在聯合的形式中有用,在上圖的頂點的聯合數組中,GL_VERTEX_ARRAY的stride為3*四兒總分(GLfloat),因為要跨國3個向量的數據,POINTE為指向第一個數據的指針,上圖中聯合類型中GL_VERTEX_ARRAY的為pointer,而GL_NORMAL_ARRAY的為pointer+3。
第三步:用glDrawElements(GLenum mode, GLsizei count, GLenum type, void *indices)進行繪製,其中mode為繪製的集合原語類型(三角形等),count為繪製的頂點個數,type為索引數組中數據的類型,indeces為索引數組。另外有函數glArrayElement()一次繪製一個點。

使用以上三步可以從數組中繪製圖形了,但是通常我們不直接在程序中直接定義這些長數組,而是將一個圖形的數據保存在文件中,常用的如OBJ文件,這是就需要先解析文件,然後從中得到這些數組
解析OBJ文件的過程的主要思想為:
Obj文件包含了所有頂點的信息,和所有麵片所包含的頂點的信息。

V 0.1 0.2 0.3
V 1.1 1 2.1
……
F 1 2 3
F2 3 5
首先將其中的所有的頂點信息讀入到我們的頂點數組中;
然後解讀麵片信息,將所有的麵片按照順序讀入到索引數組中,如上麵的例子在
頂點的位置數組中將是{0.1 0.2 0.3 1.1 1 2.1。。。。。。}
索引數組將是{1,2,3,2,3,5。。。。。。}
當然實際的OBJ可能還有很多其他的頂點信息,如法向、貼圖、顏色等,過稱相同
這樣構建好數組後,就可以用opengl的三步繪製了
注意,索引數組中的個數和總定點數是不等的,因為一個頂點可能會被幾個麵共有,這時,他在索引數組中會出現多次,索引數組就是繪製頂點的順序。

Nvidia opengl sdk輔助
應用中有很多外部的庫實現了對obj文件的解析,其中NVIDIA opengl sdk是很好的一個opengl輔助庫,他其中實現了很多類,都是較有用的工具。其中的nv::Model類就是一個可以解析obj文件的類。
Nv::Mode類
該類描述了一個模型的信息。使用該類的過程通常是這樣的
首先用loadModel從obj文件讀入一個模型信息,此時裏麵包含的信息是最原始的obj中的數據,如果obj文件中未定義法向等,可以調用computeNormals()進行計算。
讀入後就可以用opengl的三個步驟繪製了,如這段代碼
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glVertexPointer(3,GL_FLOAT,0,model->getPositions());
glNormalPointer(GL_FLOAT,0,model->getNormals());
glDrawElements(GL_TRIANGLES, model->getIndexCount(), GL_UNSIGNED_INT, model->getPositionIndices());
glDrawElements(GL_TRIANGLES, model->getIndexCount(), GL_UNSIGNED_INT, model-> getNormals());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
這隻是一種繪製方法,這種方法中,頂點的位置、發相等分別在不同的數組中,各自的索引也可能是不同的,該類有另一種方法可以將所有的數據都歸結到一個數組中(也就是聯合形式),然後索引也是唯一的。
這種方式更加推薦
首先調用compileModel()將數組編輯歸結到一起,然後就可以用下麵代碼繪製
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
int stride=model->getCompiledVertexSize()*sizeof(GLfloat);
glVertexPointer(3,GL_FLOAT,stride,model->getCompiledVertices());
glNormalPointer(GL_FLOAT,stride,model->getCompiledVertices()+model->getCompiledNormalOffset());
glDrawElements(GL_TRIANGLES, model->getCompiledIndexCount(), GL_UNSIGNED_INT, model->getCompiledIndices());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
這裏麵stride是大數組中兩個同類型數據間的跨度,所有類型的都是相等的
然後在glVertexPointer/ glNormalPointer時,就要加上一個位移就可以了。
(我在用這個類時發現最後進行delete時會出問題,而且nvidia的demo中也隻new,不delete,不知其中是否有其他機製在裏麵)

歸結一下nv::Model類的函數
初始化:
讀文件
loadModelFromFile

查詢是否有法向(。。。)特性和計算:
NVSDKENTRY bool hasNormals() const;
NVSDKENTRY bool hasTexCoords() const;
NVSDKENTRY bool hasTangents() const;
NVSDKENTRY bool hasColors() const;
NVSDKENTRY void computeTangents();
NVSDKENTRY void computeNormals();
用原始OBJ繪製:
得到頂點數組
  NVSDKENTRY const float* getPositions() const;
  NVSDKENTRY const float* getNormals() const;
  NVSDKENTRY const float* getTexCoords() const;
  NVSDKENTRY const float* getTangents() const;
  NVSDKENTRY const float* getColors() const;
  得到序列數組
  NVSDKENTRY const GLuint* getPositionIndices() const;
  NVSDKENTRY const GLuint* getNormalIndices() const;
  NVSDKENTRY const GLuint* getTexCoordIndices() const;
  NVSDKENTRY const GLuint* getTangentIndices() const;
  NVSDKENTRY const GLuint* getColorIndices() const;
得到數組的數目
  NVSDKENTRY int getPositionCount() const;
  NVSDKENTRY int getNormalCount() const;
  NVSDKENTRY int getTexCoordCount() const;
  NVSDKENTRY int getTangentCount() const;
  NVSDKENTRY int getColorCount() const;
  NVSDKENTRY int getIndexCount() const;
用聯合的大數組進行繪製:
首先重編數組
  compileModel()
  得到聯合的頂點數組:
  NVSDKENTRY const float* getCompiledVertices() const;
  得到聯合的索引數組
  NVSDKENTRY const GLuint* getCompiledIndices( PrimType prim = eptTriangles) const;
  得到在聯合數組中各特性數據的起始位移
  NVSDKENTRY int getCompiledPositionOffset() const;
  NVSDKENTRY int getCompiledNormalOffset() const;
  NVSDKENTRY int getCompiledTexCoordOffset() const;
  NVSDKENTRY int getCompiledTangentOffset() const;
  NVSDKENTRY int getCompiledColorOffset() const;
  得到聯合數組中一個頂點所包含的分量數
  // returns the size of the merged vertex in # of floats
  NVSDKENTRY int getCompiledVertexSize() const;
  得到頂點數和索引數
  NVSDKENTRY int getCompiledVertexCount() const;
  NVSDKENTRY intgetCompiledIndexCount( PrimType prim = eptTriangles) const;

最後更新:2017-04-02 00:06:42

  上一篇:go Errata in Effective Java Second Edition
  下一篇:go 2009年IT就業形勢的分析