更新时间:2020-06-23
OpenGL绘制简介
iOS SDK开放了OpenGL绘制功能,开发者可利用OpenGL的绘制来实现更多复杂的覆盖物绘制。
BMKMapViewDelegate中的-mapView:onDrawMapFrame:,地图渲染每一帧画面过程中,以及每次需要重绘地图时(例如添加覆盖物)都会调用此接口。开发者可以在这个接口中进行opengl的绘制。
不需要用户自己创建context和buffer,V5.0.0新增OpenGL映射矩阵(getProjectionMatrix)和视图矩阵(getViewMatrix)接口,用于3D绘制场景,具体代码请参考BaiduMap_IOSSDK_Sample BMKOpenGLESPage。
下面代码以在地图上绘制3D立方体为例,介绍如何使用OpenGL绘制接口。
绘制3D立方体
1初始化着色器
- (void)init3DShader { //顶点着色器 NSString *vertexShader = @"precision highp float;\n\ attribute vec3 aVertex;\n\ attribute vec4 aColor;\n\ uniform mat4 aViewMatrix;\n\ uniform mat4 aProjectionMatrix;\n\ uniform mat4 aTransformMatrix;\n\ uniform mat4 aScaleMatrix;\n\ varying vec4 color;\n\ void main(){\n\ gl_Position = aProjectionMatrix * aViewMatrix * vec4(aVertex, 1.0);\n\ color = aColor;\n\ }"; //片段着色器 NSString *fragmentShader = @"\n\ precision highp float;\n\ varying vec4 color;\n\ void main(){\n\ gl_FragColor = color;\n\ }"; // program对象是可以附加着色器对象的对象 // 创建一个空program并返回一个可以被引用的非零值(program ID) _program3D = glCreateProgram(); GLuint vShader = glCreateShader(GL_VERTEX_SHADER); GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER); GLint vlength = (GLint)[vertexShader length]; GLint flength = (GLint)[fragmentShader length]; const GLchar *vByte = [vertexShader UTF8String]; const GLchar *fByte = [fragmentShader UTF8String]; glShaderSource(vShader, 1, &vByte, &vlength); glShaderSource(fShader, 1, &fByte, &flength); //成功编译着色器对象 glCompileShader(vShader); glCompileShader(fShader); //成功地将着色器对象附加到program 对象 glAttachShader(_program3D, vShader); glAttachShader(_program3D, fShader); //成功的链接program 对象之后 glLinkProgram(_program3D); //查询由program指定的先前链接的程序对象 _vertexLocation3D = glGetAttribLocation(_program3D, "aVertex"); //表示程序对象中特定统一变量的位置 _viewMatrixLocation3D = glGetUniformLocation(_program3D,"aViewMatrix"); //得到名字为“aProjectionMatrix”在shader中的位置 _projectionMatrixLocation3D = glGetUniformLocation(_program3D,"aProjectionMatrix"); _colorLocation3D = glGetAttribLocation(_program3D,"aColor"); }
2准备立方体数据并初始化
- (void)init3DVertext { //创建vertex float vertext[] = { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, }; for (int i = 0; i < 24; i++) { // 对标墨卡托坐标 _vertext[i] = vertext[i] * 10000; } short indices[] = { 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, 7, 2, 7, 3, 3, 7, 4, 3, 4, 0, 4, 7, 6, 4, 6, 5, 3, 0, 1, 3, 1, 2, }; for (int i = 0; i < 36; i++) { _indecies[i] = indices[i]; } float colors[] = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, }; for (int i = 0; i < 32; i++) { _color[i] = colors[i]; } }
3实现绘制方法
-(void)drawFrameFor3DCube { if (_program3D == 0) { [self init3DShader]; [self init3DVertext]; } GLboolean depthMask = 0; glGetBooleanv(GL_DEPTH_WRITEMASK,&depthMask); glUseProgram(_program3D); glEnable(GL_DEPTH_TEST); if (depthMask == GL_FALSE) { glDepthMask(GL_TRUE); } glEnableVertexAttribArray(_vertexLocation3D); glVertexAttribPointer(_vertexLocation3D, 3, GL_FLOAT, false, 0, _vertext); glEnableVertexAttribArray(_colorLocation3D); glVertexAttribPointer(_colorLocation3D, 4, GL_FLOAT, false, 0, _color); BMKMapPoint center = BMKMapPointForCoordinate(CLLocationCoordinate2DMake(39.965, 116.404)); CGPoint offsetPoint = [self.mapView glPointForMapPoint:center]; float *viewMatrix = [self.mapView getViewMatrix]; translateM(viewMatrix, 0, offsetPoint.x, offsetPoint.y, 0); float *projectionMatrix = [self.mapView getProjectionMatrix]; //传值函数,该函数的第一个参数是该变量在shader中的位置,第二个参数是被赋值的矩阵的数目。第三个参数表明在向uniform变量赋值时该矩阵是否需要转置 //vec4类型的变量赋值,我们可以使用glUniform4f或者glUniform4fv。v代表数组 glUniformMatrix4fv(_viewMatrixLocation3D, 1, false, viewMatrix); glUniformMatrix4fv(_projectionMatrixLocation3D, 1, false, projectionMatrix); glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, _indecies); glDisableVertexAttribArray(_vertexLocation3D); glDisableVertexAttribArray(_colorLocation3D); glDisable(GL_DEPTH_TEST); glDepthMask(depthMask); }
4在onDrawMapFrame调用drawFrameFor3DCube进行绘制
/** 地图渲染每一帧画面过程中,以及每次需要重新绘制地图时(例如添加覆盖物)都会调用此方法 @param mapView 地图View @param status 地图的状态 */ - (void)mapView:(BMKMapView *)mapView onDrawMapFrame:(BMKMapStatus *)status { [self drawFrameFor3DCube]; }
5运行程序
效果如图: