LBS 开发微课堂|通过openGL ES轻松实现建筑物渲染及动画

280 阅读5分钟

为了让广大的开发者

更深入地了解

百度地图开放平台的技术能力

轻松掌握满满的技术干货

更加简单地接入

开放平台的服务

我们特别推出了

“位置服务(LBS)开发微课堂”

系列技术案例

第五期的主题是

通过openGL ES轻松实现

建筑物渲染及动画

对于智能穿戴设备的开发者来说,在亲子守护这类应用场景下,精确展示儿童当前的位置信息尤为重要。特别是在复杂的建筑物中,需要能够详尽地指示出儿童所在的建筑物及具体位置。

1.png

那么,如何能够在地图上结合定位技术,贴合楼块轮廓,对定位所在的楼块进行自定义纹理展示,并实现当前所在楼栋内的楼层变化的动画效果呢?

今天,我们将详细介绍如何在移动端使用OpenGL ES绘制一个n面棱柱,并探讨纹理贴图、侧面索引的原理及楼层动画的实现过程。

通过代码的方式,帮助开发者更好地理解地图SDK自定义建筑物渲染的整个过程,就让我们一起来学习一下吧!

01 绘制建筑物

1.1 定义顶点与索引

绘制建筑物本质上是绘制n面棱柱体。n面棱柱的顶点数据包括底面顶点、顶面顶点和侧面顶点。

底面和顶面是n边形,侧面由底面和顶面的对应顶点连接而成。

核心代码示例:

// 示例:绘制一个六面棱柱(即长方体)GLfloat vertices[] = {
    // 底面顶点
    -0.5f, -0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,
    0.5f, -0.5f,  0.5f,
    -0.5f, -0.5f,  0.5f,
    // 顶面顶点
    -0.5f,  0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f,  0.5f,  0.5f,
    -0.5f,  0.5f,  0.5f
};

索引数据用于指定顶点的连接顺序,形成棱柱的各个面。

// 示例索引数据
GLushort indices[] = {
    // 底面    
    0, 1, 2, 2, 3, 0,    
    // 顶面    
    4, 5, 6, 6, 7, 4,    
    // 侧面    
    0, 1, 5, 5, 4, 0,    
    1, 2, 6, 6, 5, 1,    
    2, 3, 7, 7, 6, 2,    
    3, 0, 4, 4, 7, 3
};

索引数组
索引数组定义了顶点的连接顺序,从而形成了棱柱的各个面。
侧面生成
通过连接底面和顶面对应的顶点,生成棱柱的侧面。索引数组中的每个侧面由六个顶点组成(两个三角形)。

1.2 顶点着色器和片元着色器

顶点着色器负责处理顶点数据,将其转换为屏幕坐标。

片元着色器负责为每个像素着色。

const char* vertexShaderSource = R"(
attribute vec4 vPosition;
uniform mat4 uModelViewProjectionMatrix;
void main() {
    gl_Position = uModelViewProjectionMatrix * vPosition;
}
)";

const char* fragmentShaderSource = R"(
precision mediump float;
uniform vec4 vColor;
void main() {
    gl_FragColor = vColor;
})";

02 纹理贴图

2.1 纹理坐标

纹理贴图是将二维图像映射到三维模型表面的过程。因此需要定义纹理坐标,并加载纹理图像。

纹理坐标
纹理坐标是一个二维坐标系统,范围为[0, 1]。它定义了如何将纹理图像映射到三维模型的表面上。

纹理坐标(示例)
GLfloat textureCoords[] = {
    0.0f, 0.0f,
    1.0f, 0.0f,
    1.0f, 1.0f,
    0.0f, 1.0f,
    // 顶面纹理坐标(重复上述过程)
};

2.2 纹理绘制

在绘制函数中,我们需要绑定纹理、设置着色器参数、传递顶点数据和索引数据,然后调用glDrawElements进行绘制。

纹理环绕
当纹理坐标超出 [0, 1] 范围时,OpenGL提供了多种处理方式,如重复(GL_REPEAT)、镜像重复(GL_MIRRORED_REPEAT)、约束到边缘(GL_CLAMP_TO_EDGE)等。
纹理过滤
纹理过滤用于处理纹理在放大或缩小时的像素处理。常用的过滤方式有邻近过滤(GL_NEAREST)和线性过滤(GL_LINEAR)。

// 加载纹理图像(示例)
GLuint textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

绘制

void draw() {
glUseProgram(program);
   // 设置顶点属性位置    
   glEnableVertexAttribArray(positionAttribLocation);    
   glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);

   // 设置纹理坐标属性位置    
   glEnableVertexAttribArray(textureAttribLocation);    
   glVertexAttribPointer(textureAttribLocation, 2, GL_FLOAT, GL_FALSE, 0, textureCoords);

   // 绑定纹理    
   glBindTexture(GL_TEXTURE_2D, textureId);

   // 设置着色器中的矩阵参数(示例)    
   glUniformMatrix4fv(modelViewProjectionMatrixLocation, 1, GL_FALSE, modelViewProjectionMatrix);

   // 绘制棱柱    
   glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(GLushort), GL_UNSIGNED_SHORT, indices);

   glDisableVertexAttribArray(positionAttribLocation);    
   glDisableVertexAttribArray(textureAttribLocation);
}

03 楼层动画

实现楼层z轴非线性动画,通过使用顶点着色器(Vertex Shader)来动态修改顶点位置实现。

这种方法可以减少 CPU 到 GPU 的数据传输,提高性能,特别是在顶点数据较多的情况下。

3.1 定义楼层的顶点着色器

定义一个顶点着色器,其中包含一个时间变量和缓动函数,用于计算当前的 Z 坐标偏移。

// Vertex 
Shaderattribute vec3 aPosition;  // 顶点位置
uniform float uTime;  // 动画时间,从外部传入
uniform float uHeight;  // 最大高度

// 缓动函数
// 开始和结束时速度较慢,中间加速
float easeInOutCubic(float t) {    
    return t < 0.5 ? 4.0 * t * t * t : (t - 1.0) * (2.0 * t - 2.0) * (2.0 * t - 2.0) + 1.0;
}

void main() {    
    float t = easeInOutCubic(uTime);    
    float z = uHeight * t; // 计算当前高度    
    vec3 transformedPosition = vec3(aPosition.xy, aPosition.z + z); // 更新Z坐标
    gl_Position = vec4(transformedPosition, 1.0)
;}

缓动函数
缓动函数(Easing Functions)是在动画和运动图形中常用的一种技术,用于创建更加平滑和自然的动画效果。主要用于控制动画的速度变化,使得动画不是以恒定速度进行,而是可以加速或减速,或者两者结合。

3.2 更新时间和高度​​​​​​​

// 更新时间,高度并传递到着色器
float time = fmod(currentTime, animationDuration) / animationDuration; // 计算归一化时间
glUniform1f(glGetUniformLocation(shaderProgram, "uTime"), time);
glUniform1f(glGetUniformLocation(shaderProgram, "uHeight"), buildingHeight);

3.3 渲染循环​​​​​​​

void render() {    
    // 更新时间    
    updateDeltaTime();    
    // 设置时间和高度    
    glUniform1f(glGetUniformLocation(shaderProgram, "uTime"), time); 
    glUniform1f(glGetUniformLocation(shaderProgram, "uHeight"), buildingHeight); 
    // 绘制楼层    
    drawFloorBuilding();
}

04 效果展示

640.gif

怎么样,你学会了吗?登录百度地图开放平台官网,轻松实现建筑物的渲染及动画效果!

查看路径:

开发者频道>开发文档>Android地图SDK>开发指南>在地图上绘制>绘制3D建筑物(lbsyun.baidu.com/faq/api?tit…

开发者频道>开发文档>iOS地图SDK>开发指南>在地图上绘制>绘制3D建筑物(lbsyun.baidu.com/faq/api?tit…

·END·

你还想了解哪些技术内容?

快来评论区留言告诉我们吧!