本節(jié)學(xué)習(xí)目標(biāo)
使用OpenGL繪制一個(gè)地球
上干貨
- 第一步 創(chuàng)建一個(gè)工程
- 第二步 創(chuàng)建GLKViewController類(lèi)型的控制器
- 第三步 添加OpenGL ES 2.0的頭文件
- 第四步 配置我們的GLKViewController 控制器
GLKView *glkView = (GLKView*)self.view;
glkView.drawableDepthFormat = GLKViewDrawableDepthFormat24;// 設(shè)置深度緩沖區(qū)格式
// 創(chuàng)建管理上下文
glkView.context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
// 設(shè)置當(dāng)前上下文
[EAGLContext setCurrentContext:glkView.context];
- 第五步 創(chuàng)建一個(gè)負(fù)責(zé)渲染的類(lèi)
@property(nonatomic,strong)GLKBaseEffect *baseEffect;
self.baseEffect = [[GLKBaseEffect alloc]init];
- 第六步 生成球體的頂點(diǎn)坐標(biāo)和紋理坐標(biāo)和索引
下面是生成球體坐標(biāo)C語(yǔ)言方法
#define ES_PI (3.14159265f)
int generateSphere(int numSlices, float radius, float **vertices,
float **texCoords, uint16_t **indices, int *numVertices_out) {
int i;
int j;
int numParallels = numSlices / 2;
int numVertices = (numParallels + 1) * (numSlices + 1);
int numIndices = numParallels * numSlices * 6;
float angleStep = (2.0f * ES_PI) / ((float) numSlices);
if (vertices != NULL)
*vertices = malloc(sizeof(float) * 3 * numVertices);
if (texCoords != NULL)
*texCoords = malloc(sizeof(float) * 2 * numVertices);
if (indices != NULL)
*indices = malloc(sizeof(uint16_t) * numIndices);
for (int i = 0; i < numParallels + 1; i++) {
for (int j = 0; j < numSlices + 1; j++) {
int vertex = (i * (numSlices + 1) + j) * 3;
if (vertices) {
(*vertices)[vertex + 0] = radius * sinf(angleStep * (float)i) *
sinf(angleStep * (float)j);
(*vertices)[vertex + 1] = radius * cosf(angleStep * (float)i);
(*vertices)[vertex + 2] = radius * sinf(angleStep * (float)i) *
cosf(angleStep * (float)j);
}
if (texCoords) {
int texIndex = (i * (numSlices + 1) + j) * 2;
(*texCoords)[texIndex + 0] = (float)j / (float)numSlices;
(*texCoords)[texIndex + 1] = 1.0f - ((float)i / (float)numParallels);
}
}
}
if (indices != NULL) {
uint16_t *indexBuf = (*indices);
for (i = 0; i < numParallels ; i++) {
for (j = 0; j < numSlices; j++) {
*indexBuf++ = i * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + (j + 1);
*indexBuf++ = i * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + (j + 1);
*indexBuf++ = i * (numSlices + 1) + (j + 1);
}
}
}
if (numVertices_out) {
*numVertices_out = numVertices;
}
return numIndices;
}
接下來(lái)定義上面函數(shù)需要的參數(shù)
GLfloat *_vertexData; // 頂點(diǎn)數(shù)據(jù)
GLfloat *_texCoords; // 紋理坐標(biāo)
GLushort *_indices; // 頂點(diǎn)索引
GLint _numVetex; // 頂點(diǎn)數(shù)量
GLuint _texCoordsBuffer;// 紋理坐標(biāo)內(nèi)存標(biāo)識(shí)
GLuint _numIndices; // 頂點(diǎn)索引的數(shù)量
調(diào)用上面方法生成頂點(diǎn)坐標(biāo),紋理坐標(biāo)励背,索引數(shù)組
_numIndices = generateSphere(200, 1.0, &(_vertexData), &(_texCoords), &_indices, &_numVetex);
- 第七步 將頂點(diǎn)坐標(biāo)歪玲,紋理坐標(biāo)懒闷,索引坐標(biāo)加載到GPU 中去
-(void)loadVertexData{
// 加載頂點(diǎn)坐標(biāo)數(shù)據(jù)
glGenBuffers(1, &_vertexBuffer); // 申請(qǐng)內(nèi)存
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); // 將命名的緩沖對(duì)象綁定到指定的類(lèi)型上去
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*_numVetex*3,_vertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition); // 綁定到位置上
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), NULL);
// 加載頂點(diǎn)索引數(shù)據(jù)
GLuint _indexBuffer;
glGenBuffers(1, &_indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, _numIndices*sizeof(GLushort), _indices, GL_STATIC_DRAW);
// 加載紋理坐標(biāo)
glGenBuffers(1, &_texCoordsBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _texCoordsBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*_numVetex*2, _texCoords, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), NULL);
}
- 第八步 將我們的地圖照片使用剛才創(chuàng)建的渲染類(lèi)GLKBaseEffect 加載到內(nèi)存中去
GLKTextureInfo *textureInfo =
[GLKTextureLoader textureWithCGImage:[UIImage imageNamed:@"earth-diffuse.jpg"].CGImage options:nil error:nil];
self.baseEffect.texture2d0.target = textureInfo.target;
self.baseEffect.texture2d0.name = textureInfo.name;
- 第九步 在繪制之前,我們要設(shè)置一下 世界坐標(biāo)和繪制球體的自身坐標(biāo)
// 設(shè)置世界坐標(biāo)和視角
float aspect = fabs(self.view.bounds.size.width / self.view.bounds.size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 0.1f, 100.0f);
self.baseEffect.transform.projectionMatrix = projectionMatrix;
// 設(shè)置模型坐標(biāo)
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, -1.0f, -6.5f);
self.baseEffect.transform.modelviewMatrix = modelViewMatrix;
- 第十一步 我們?cè)O(shè)置個(gè)沿著Y軸旋轉(zhuǎn)的效果
// update方法系統(tǒng)會(huì)自動(dòng)調(diào)動(dòng)
-(void)update{
self.baseEffect.transform.modelviewMatrix = GLKMatrix4Rotate(self.baseEffect.transform.modelviewMatrix, 0.1, 0, 1, 0);
}
- 第十二步 開(kāi)始繪制
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
// 清除顏色緩沖區(qū)
glClearColor(1.0, 0, 1.0, 1);
glClear(GL_COLOR_BUFFER_BIT);
// 繪制之前必須調(diào)用這個(gè)方法
[self.baseEffect prepareToDraw];
static int i =1;
if (i < _numIndices-2000){
i = i+1000;
}else{
i = _numIndices;
}
// 以畫(huà)單獨(dú)三角形的方式 開(kāi)始繪制
glDrawElements(GL_TRIANGLES, i,GL_UNSIGNED_SHORT, NULL);
}
代碼!
運(yùn)行一下:
總結(jié)
寫(xiě)這篇文章主要給初學(xué)者一個(gè)繪制球體的思路,蘋(píng)果給我們封裝的類(lèi),幫助我們簡(jiǎn)化了不少代碼,如果純OpenGL 做這樣一個(gè)練習(xí)代碼量還是挺多的乍楚。