學習OpenGL ES之繪製地形

2021-09-11 14:11:27 字數 4192 閱讀 9972

地形模型一般是由nxn的網格構成,網格的點在y軸上的座標由灰度地形圖上相應的顏色決定。顏色越亮,高度越高。顏色每個通道的取值範圍可以是0~ 255,通過公式轉換,可以很容易的控制生成模型的高度。

上篇文章中,我們使用三角帶生成圓柱體的中間部分。現在我們要用多個三角帶來生成地形。

如何生成單個三角帶我就不贅述了,上篇文章已經介紹了。下面主要介紹如何計算每個頂點的位置,法線和uv。

計算頂點位置之前,我們先要獲取到灰度地形圖的畫素資料。因為我們需要知道指定點的畫素顏色。

- (glubyte *)datafromimage:(uiimage *)img 

複製**

上面的**將畫素格式不確定的轉換成4通道rgba格式的資料。texturedata的記憶體布局是r,g,b,a,r,g,b,a,r,g,b,a,...不停重複。位置(x,y)的畫素資料在偏移量y * 寬度 * 4 + x * 4處。獲取頂點位置的**如下。

- (glkvector3)vertexposition:(int)col row:(int)row buffer:(unsigned char *)buffer bytesperrow:(size_t)bytesperrow bytesperpixel:(size_t)bytesperpixel 

複製**

bytesperrow是指一行的位元組數,也就是寬度 * 4bytesperpixel是每個畫素的位元組數,也就是4。使用red通道的值,計算出y軸上的座標glfloat y = r / 255.0 * self.terrainheight;self.terrainheight是可以配置的地形高度。self.terrainsize是地形的大小。self.heightmap.size是灰度的大小。通過計算(int)(row / self.terrainsize.height * self.heightmap.size.height)在上進行取樣。

因為我想給每個頂點指定唯一的法線,所以必須計算出頂點在每個面上的法線之和。在網格上每個頂點最多被4個面共享,也就是頂點的前後左右各有乙個頂點。假設這四個頂點是va,vb,vc,vd,中間的點為vce,那麼第乙個面的法線就是(vb - vce) 叉乘 (va - vce),以此類推,算出四個法線,相加後歸一化,就可以得到最終的法線了。因為邊緣的頂點可能只被2或3個面共享,所以需要處理一下這種特殊情況。下面是法線計算**。

- (glkvector3)vertexnormal:(glkvector3)position col:(int)col row:(int)row buffer:(unsigned char *)buffer bytesperrow:(size_t)bytesperrow bytesperpixel:(size_t)bytesperpixel 

if (row >= 1)

if (col <= self.terrainsize.width - 1)

if (row <= self.terrainsize.width - 1)

glkvector3 normal = glkvector3make(0, 0, 0);

for (int i = 0; i < sidecount; ++i)

glkvector3 vec2 = i == sidecount - 1 ? sides[0] : sides[i + 1];

normal = glkvector3add(normal, glkvector3crossproduct(vec2, vec));

}return glkvector3normalize(normal);

}複製**

glkvector3crossproduct就是用作叉乘的方法,兩個向量叉乘得出的向量將垂直於它們兩個,也就是法向量。

有了獲取位置和法線的方法,就可以很方便的構建幾何體了。

- (void)buildgeometry 

[self.terrainmeshstrips addobject:terrainmeshstrip];

}free(buffer);

}複製**

每一行構建乙個三角帶幾何體,計算uv的時候可以乘以乙個縮放係數,控制地形貼圖的重複次數。通過乘以2col / (glfloat)self.terrainsize.width * 2,uv的範圍就變成了0~2。為了使貼圖可以重複,還需要新增下面的**。

複製**在glktextureinfo建立後,使用gltexparameterf(gl_texture_2d, gl_texture_wrap_s, gl_repeat);配置它們支援重複貼圖。

為了使地形看起來更加自然,我新增了草和泥土兩種貼圖,並為地形編寫了新的fragment shader。

precision highp float;

varying vec3 fragposition;

varying vec3 fragnormal;

varying vec2 fraguv;

uniform float elapsedtime;

uniform vec3 lightdirection;

uniform mat4 normalmatrix;

uniform sampler2d grassmap;

uniform sampler2d dirtmap;

void main(void) else

if (fragposition.y > 30.0 && fragposition.y < 60.0) else

gl_fragcolor = vec4(materialcolor.rgb * finallightstrength.rgb, 1.0);

}複製**

增加了兩個紋理uniform sampler2d grassmap; uniform sampler2d dirtmap;,y座標小於30使用泥土的貼圖,30到60使用泥土和草混合,高於60使用草貼圖。地形在繪製時同時繫結這兩種貼圖。

- (void)draw:(glcontext *)glcontext 

}複製**

因為地形需要使用不一樣的fragment shader,所以在viewcontroller中為terrain生成新的glcontext

- (void)createterrain 

複製**

學習OpenGL ES之繪製圓柱體

為了更方便的進行頂點資料的管理,我建立了乙個glgeometry類。typedef enum nsuinteger glgeometrytype typedef struct glvertex inte ce glgeometry property strong,nonatomic nsmutabl...

OpenGL繪製地形

1.建立頂點緩衝區物件 vertexbuffer 2.建立索引緩衝區物件 indexbuffer 3.載入高度圖 heightmap 3.1把位影象素轉換為高度圖資料 頂點資料 高度圖在每個方向上都是1個單位寬,且其以x z平面上的位置 0,0 為中心,點陣圖的最上角將被對映到 0.5,0.5 右下...

地形繪製基礎

地形繪製基礎 高度圖其實就是乙個陣列,該陣列的每個元素都指定了地形方格中某乙個特定頂點的高度值。另外一種實現方式可能用該陣列中的每個元素來指定每個三角形柵格的高度 我們將高度圖視為乙個矩陣,這樣高度圖中的元素就與地形柵格中的頂點一一對應。高度圖被儲存在磁碟中,我們通常為其每個元素只分配乙個位元組的儲...