天天看點

Opengl繪制數組資料與檔案資料的方法與Nvidia opengl sdk輔助實作

在opengl中繪制基本的集合原語可以使用諸如

gl_begin(type)

。。。

gl_end

的方式,逐個頂點進行繪制,但是如果想繪制一個大的模型或是一個完整的場景,裡面的頂點數目幾十上百萬,這時就不能這樣逐個頂點繪制了,為此,在opengl中有從數組繪制的方式。

數組繪制的基本思想:

就是把所所有頂點的位置、法向等資訊裝入數組,并且按照一定的序列(預先排好的)繪制他們就行了,這隻需要幾步操作。這裡面一共涉及到兩種數組,頂點數組(vertex array)與序列數組(indice array)。頂點數組就是将各頂點的位置、法向等裝入(可單獨也可聯合),序列數組就好比一個目錄,上面記錄了先繪制哪個頂點,在繪制哪一個。圖例

Opengl繪制數組資料與檔案資料的方法與Nvidia opengl sdk輔助實作

有了這個數組就可以進行繪制了

在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()将數組編輯歸結到一起,然後就可以用下面代碼繪制

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());

這裡面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;