今天我們創(chuàng)建一個(gè)球體做祝,后期會(huì)在此基礎(chǔ)上實(shí)現(xiàn)一個(gè)全景視頻播放器。之前的三篇筆記我們都是通過(guò)兩種方式來(lái)實(shí)現(xiàn)OpenGL ES的學(xué)習(xí)富蓄,即一種方式是使用GLKit另一種方式不使用GLKit像街。這是為了方便理解OpenGL ES才這樣做的筹裕,因?yàn)樘O(píng)果對(duì)OpenGL ES進(jìn)行了部分封裝使其使用起來(lái)更方便了较鼓,但如果直接用GLKit學(xué)習(xí)不利于對(duì)OpenGL ES的理解所以前面我都是在實(shí)現(xiàn)同一個(gè)功能的時(shí)候用兩種方式來(lái)實(shí)現(xiàn)椎木。兩種方式的區(qū)別基本上在前三篇的學(xué)習(xí)中已經(jīng)講解的差多了,基本沒(méi)有其他區(qū)別了博烂,所以以后的學(xué)習(xí)筆記不再使用兩種方式來(lái)實(shí)現(xiàn)而是直接用GLKit來(lái)實(shí)現(xiàn)香椎,下面進(jìn)入正題。
學(xué)習(xí)的代碼都在我的github倉(cāng)庫(kù)歡迎大家學(xué)習(xí)指教禽篱!
一畜伐、生成頂點(diǎn)數(shù)據(jù)
這次我們的代碼也是在上一次學(xué)習(xí)筆記的基礎(chǔ)之上修改來(lái)的,這里只講不同點(diǎn)躺率。上次實(shí)現(xiàn)的立方體和這次實(shí)現(xiàn)的球體已經(jīng)很像了玛界。只要傳入的頂點(diǎn)數(shù)據(jù)不一樣就可以了!其實(shí)學(xué)到這里你會(huì)發(fā)現(xiàn)OpenGL ES只是入門(mén)難悼吱,入門(mén)后里面的東西很簡(jiǎn)單慎框。有點(diǎn)跑題了,現(xiàn)在用代碼實(shí)現(xiàn)頂點(diǎn)數(shù)據(jù)舆绎。我們先要知道以下知識(shí):
- 我們可以通過(guò)三角函數(shù)實(shí)現(xiàn)一個(gè)圓的坐標(biāo)數(shù)據(jù)
- 如果我們把多個(gè)圓疊放在一起就可以實(shí)現(xiàn)一他圓柱體
- 如果我們把這多個(gè)疊放圓的半徑也按三角函數(shù)的方式實(shí)現(xiàn)就可以實(shí)現(xiàn)一個(gè)球體
通過(guò)上面的分析我們大體已經(jīng)知道怎么生成這個(gè)球體的數(shù)據(jù)了鲤脏,全靠頂點(diǎn)來(lái)實(shí)現(xiàn)球體有點(diǎn)不現(xiàn)實(shí),我們可以通過(guò)少量的頂點(diǎn)連接成的三角形來(lái)實(shí)現(xiàn)球體吕朵。頂點(diǎn)越多生成的球體越平滑但也不是沒(méi)有極限,當(dāng)頂點(diǎn)大于一定值的時(shí)候再多的頂點(diǎn)也看不出差別來(lái)反而會(huì)影響性能窥突。所以我們這里有81 x 81個(gè)頂點(diǎn)來(lái)實(shí)現(xiàn)一個(gè)球體努溃。生成頂點(diǎn)的方法如下:
#define kDevidCount 80
/**
繪制一個(gè)球的頂點(diǎn)
@param num 傳入要生成的頂點(diǎn)的一層的個(gè)數(shù)(最后生成的頂點(diǎn)個(gè)數(shù)為 num * num)
@return 返回生成后的頂點(diǎn)
*/
- (Vertex *)getBallDevidNum:(GLint) num{
if (num % 2 == 1) {
return 0;
}
GLfloat delta = 2 * M_PI / num; // 分割的份數(shù)
GLfloat ballRaduis = 0.8; // 球的半徑
GLfloat pointZ;
GLfloat pointX;
GLfloat pointY;
GLfloat textureY;
GLfloat textureX;
GLfloat textureYdelta = 1.0 / (num / 2);
GLfloat textureXdelta = 1.0 / num;
GLint layerNum = num / 2.0 + 1; // 層數(shù)
GLint perLayerNum = num + 1; // 要讓點(diǎn)再加到起點(diǎn)所以num + 1
Vertex * cirleVertex = malloc(sizeof(Vertex) * perLayerNum * layerNum);
memset(cirleVertex, 0x00, sizeof(Vertex) * perLayerNum * layerNum);
// 層數(shù)
for (int i = 0; i < layerNum; i++) {
// 每層的高度(即pointY),為負(fù)數(shù)讓其從下向上創(chuàng)建
pointY = -ballRaduis * cos(delta * i);
// 每層的半徑
GLfloat layerRaduis = ballRaduis * sin(delta * i);
// 每層圓的點(diǎn),
for (int j = 0; j < perLayerNum; j++) {
// 計(jì)算
pointX = layerRaduis * cos(delta * j);
pointZ = layerRaduis * sin(delta * j);
textureX = textureXdelta * j;
textureY = textureYdelta * i;
cirleVertex[i * perLayerNum + j] = (Vertex){pointX, pointY, pointZ, textureX, textureY};
}
}
return cirleVertex;
}
// 頂點(diǎn)數(shù)據(jù)索引
- (GLuint *)getBallVertexIndex:(GLint)num{
// 每層要多原點(diǎn)兩次
GLint sizeNum = sizeof(GLuint) * (num + 1) * (num + 1);
GLuint * ballVertexIndex = malloc(sizeNum);
memset(ballVertexIndex, 0x00, sizeNum);
GLint layerNum = num / 2 + 1;
GLint perLayerNum = num + 1; // 要讓點(diǎn)再加到起點(diǎn)所以num + 1
for (int i = 0; i < layerNum; i++) {
if (i + 1 < layerNum) {
for (int j = 0; j < perLayerNum; j++) {
// i * perLayerNum * 2每層的下標(biāo)是原來(lái)的2倍
ballVertexIndex[(i * perLayerNum * 2) + (j * 2)] = i * perLayerNum + j;
// 后一層數(shù)據(jù)
ballVertexIndex[(i * perLayerNum * 2) + (j * 2 + 1)] = (i + 1) * perLayerNum + j;
}
} else {
for (int j = 0; j < perLayerNum; j++) {
// 后最一層數(shù)據(jù)單獨(dú)處理
ballVertexIndex[i * perLayerNum * 2 + j] = i * perLayerNum + j;
}
}
}
return ballVertexIndex;
}
在原來(lái)設(shè)置VBO數(shù)據(jù)的方法里設(shè)置上面的方法來(lái)生成頂點(diǎn)數(shù)據(jù)和頂點(diǎn)索引數(shù)據(jù)阻问。代碼如下:
/**
設(shè)置頂點(diǎn)緩存VBO
*/
- (void)setupBufferVBO {
// 獲取球的頂點(diǎn)和索引
_cirleVertex = [self getBallDevidNum:kDevidCount];
_vertextIndex = [self getBallVertexIndex:kDevidCount];
// 設(shè)置VBO
glGenBuffers(1, &_bufferVBO);
glBindBuffer(GL_ARRAY_BUFFER, _bufferVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * (kDevidCount + 1) * (kDevidCount / 2 + 1), _cirleVertex, GL_STATIC_DRAW);
glGenBuffers(1, &_bufferIndexVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bufferIndexVBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * kDevidCount * (kDevidCount + 1), _vertextIndex, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid *)NULL);
// 設(shè)置紋理坐標(biāo)
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLfloat *)NULL + 3);
// 釋放頂點(diǎn)數(shù)據(jù)
free(_cirleVertex);
free(_vertextIndex);
}
二梧税、修改渲染方法
由于頂點(diǎn)數(shù)據(jù)的大小變化了,渲染方法里也得修改新的頂點(diǎn)數(shù)據(jù)個(gè)數(shù)的計(jì)算方法称近,代碼如下:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
[self.effect prepareToDraw];
// 繪制一個(gè)球
glDrawElements(GL_TRIANGLE_STRIP, kDevidCount * (kDevidCount + 1), GL_UNSIGNED_INT, 0);
}
由于這次是用的GLKit所以代碼會(huì)簡(jiǎn)單很多第队,渲染方法里的代碼也很少,其他的代碼可以看上一篇筆記∨俑眩現(xiàn)在看一下運(yùn)行結(jié)果凳谦。
球體上面的紋理可能不太合適,大家可以在網(wǎng)上搜一個(gè)地球的紋理試一下效果會(huì)比現(xiàn)在好一點(diǎn)衡未。