Android利用OpenGLES绘制天空盒实例教程

作者:滑板上的老砒霜 时间:2023-11-19 16:07:27 

前言

天空盒这个效果最早是在腾讯的实景地图里看到的,当时觉得很牛逼,但是没有想过自己去实现以下。最近这段时间对opengl很有兴趣,顺便就搞了这个天空盒,话不多说,先上效果。

Android利用OpenGLES绘制天空盒实例教程

天空盒的原理就是在三维空间中放置一个正方体,然后将我们的相机放置在正方体内,当我们的视点转动,相机跟着转动。我们就可以看到相应的景色的变换了,天空盒本质上是一个立方体。

OpenGL

关于什么是OpenGL,什么是OpenGLES就不细说了,不了解的就自行百度吧,我们主要是关注代码。整个项目采用了Kotlin + Ndk的形式进行的开发。现在NDK的环境搭建比以前容易了,而且现在是使用CMakeList来构建C++代码的,不熟悉的可以去查看一下。整个项目就两个关键类,SkyBoxView和SkyBoxRender。下面分别来看一下。

第一步

SkyBoxView继承了GLSurfaceView,为什么要继承GLSurfaceView,因为在使用OpenGLES需要建立一个窗口和一个上下文,GLSurfaceView帮我们做了这些工作。下面是SkyBoxView的主要代码:


class SkyBoxView(context: Context, attributeSet: AttributeSet?) : GLSurfaceView(context, attributeSet)
{

private lateinit var skyBoxRender: SkyBoxRender

private var lastX=0F

private var lastY=0F

private var yaw=0f

private var pitch=0f

private var screenWidth=0

private var screenHeight=0

private var horSensity=0.03f

private var verSensity=0.03f

constructor(context: Context) : this(context, null)

init
{
// initSensor()
initSensity()
initConfig()
}

private fun initSensity()
{
screenWidth=resources.displayMetrics.widthPixels
screenHeight=resources.displayMetrics.heightPixels
horSensity= 360.0f/screenWidth
verSensity=180.0f/screenHeight
}

private fun rotate(pitch:Float,yaw:Float)
{
queueEvent {

skyBoxRender.rotate(pitch,yaw)
}
}
private fun initConfig()
{
setEGLContextClientVersion(3)
skyBoxRender=SkyBoxRender(context)
setRenderer(skyBoxRender)
renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY

}

override fun onTouchEvent(event: MotionEvent?): Boolean
{
when(event?.action)
{
MotionEvent.ACTION_DOWN->
{
 lastX=event.x
 lastY=event.y
 return true
}

MotionEvent.ACTION_MOVE->
{
 val offsetX=event.x-lastX
 val offsetY=lastY-event.y
 yaw+=offsetX*horSensity
 pitch+=offsetY*verSensity
 lastX=event.x
 lastY=event.y
 skyBoxRender.rotate(pitch,yaw)
}
}

return true
}
}

在initConfig方法里,设置了render为SkyBoxRender,真正的绘制是在这里进行的。在initSensity方法里设置了旋转精度, horSensity和verSensity,水平和数值旋转时的精度,就像你玩fps游戏设置的鼠标灵敏度一样。在onTouchEvent则根据手指滑动的距离设置俯仰角pitch和偏移脚yaw,调用skyBoxRender进行相机的旋转。另外如果你看github可能发现我注释掉了很多代码,那是用传感器旋转的尝试,但是觉得麻烦,也没继续做,有兴趣的读者可以自己搞一下。

第二步

SkyboxRender的主要工作就是加载贴在正方体表面的6个图片纹理,从文件读取着色器语言,而真正创建opengles program和绘制是用C++代码来写的,所以主要看一下这里。


#include <jni.h>
#include <string>
#include <GLUtils/GLUtils.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

extern "C" {

JNIEXPORT jint JNICALL
Java_com_skateboard_skybox_SkyBoxRender_genProgram(JNIEnv *env, jobject thiz, jstring vertexPath,
     jstring fragmentPath) {
//load program
const char *cVertexPath = env->GetStringUTFChars(vertexPath, nullptr);
const char *cFragmentPath = env->GetStringUTFChars(fragmentPath, nullptr);
int program = glutils::loadProgram(cVertexPath, cFragmentPath);
return program;

}

JNIEXPORT jint JNICALL
Java_com_skateboard_skybox_SkyBoxRender_preparePos(JNIEnv *env, jobject thiz, jfloatArray pos) {
//gen vao vbo

unsigned int VAO, VBO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
int posSize = env->GetArrayLength(pos);
float* p=env->GetFloatArrayElements(pos, nullptr);
glBufferData(GL_ARRAY_BUFFER, posSize* sizeof(float), p,
 GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glBindVertexArray(0);
return VAO;
}

JNIEXPORT jint JNICALL
Java_com_skateboard_skybox_SkyBoxRender_prepareTexture(JNIEnv *env, jobject thiz) {
//gen texture
unsigned int TEXTURE;
glGenTextures(1, &TEXTURE);
glBindTexture(GL_TEXTURE_CUBE_MAP, TEXTURE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
return 1;
}

glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);

JNIEXPORT void JNICALL
Java_com_skateboard_skybox_SkyBoxRender_draw(JNIEnv *env, jobject thiz, jint program, jint VAO,
     jint texture,jfloat width,jfloat height) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.0, 1.0, 0.0, 1.0);
glUseProgram(program);

glEnable(GL_DEPTH_TEST);
glm::mat4 viewMatrix = glm::mat4(1.0f);
glm::mat4 projectionMatrix = glm::mat4(1.0f);
glm::vec3 v = glm::vec3(cameraFront.x - cameraPos.x, cameraFront.y - cameraPos.y,
  cameraFront.z - cameraPos.z);
viewMatrix = glm::lookAt(cameraPos, v, glm::vec3(0.0f, 1.0f, 0.0f));
projectionMatrix = glm::perspective(glm::radians(45.0f), width / height, 0.1f,
    100.0f);
int viewMatrixLocation = glGetUniformLocation(program, "view");
int projectMatrixLocation = glGetUniformLocation(program, "projection");
glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, &viewMatrix[0][0]);
glUniformMatrix4fv(projectMatrixLocation, 1, GL_FALSE, &projectionMatrix[0][0]);

glBindVertexArray(VAO);
glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
glDrawArrays(GL_TRIANGLES, 0, 36);
}

JNIEXPORT void JNICALL
Java_com_skateboard_skybox_SkyBoxRender_rotate(JNIEnv *env, jobject thiz,jfloat pitch,jfloat yaw) {

if(pitch>89)
{
pitch=89.0;
}
if(pitch<-89)
{
pitch=-89.0;
}
cameraFront.x=glm::cos(glm::radians(pitch))*glm::cos(glm::radians(yaw));
cameraFront.y=glm::sin(glm::radians(pitch));
cameraFront.z=glm::cos(glm::radians(pitch))*glm::sin(glm::radians(yaw));
cameraFront=glm::normalize(cameraFront);
}

}

genProgram主要是用来产生opengl es的program的,如果对这个概念不太理解请参考C++编译过程。

preparePos是将java层顶点位置数组传入进来并写入顶点着色器。

prepareTexture用来生成纹理。

draw用来进行绘制。

旋转的时候就是通过改变cameraFront的单位向量的方向来做到的。

源码下载

最后附上

github (本地下载)

来源:https://www.jianshu.com/p/a7211b6733b6

标签:android,opengles,天空盒
0
投稿

猜你喜欢

  • java中TreeMap排序的示例代码

    2023-02-17 15:39:30
  • Python实现JavaBeans流程详解

    2021-05-29 05:37:39
  • 横竖屏切换导致页面频繁重启screenLayout解析

    2021-06-14 05:16:22
  • SpringBoot的@Value注解如何设置默认值

    2023-09-03 14:32:05
  • mybatis-plus多表联查join的实现

    2023-11-24 06:49:56
  • Android实现滑动效果

    2022-09-20 15:04:24
  • C#操作XML方法详解

    2022-11-10 21:21:14
  • Unity动画混合树实例详解

    2022-06-17 14:41:27
  • java基础-数组扩容详解

    2022-05-24 00:34:58
  • Android clipChildren属性实例详解

    2022-12-29 02:35:50
  • java IO流 之 输入流 InputString()的使用

    2023-08-22 07:44:31
  • c#读取文件详谈

    2023-03-04 14:47:30
  • springboot mybatis里localdatetime序列化问题的解决

    2023-06-25 06:58:18
  • Java基础之数组详解

    2021-08-06 08:15:28
  • Java SpringBoot在RequestBody中高效的使用枚举参数原理案例详解

    2022-03-28 01:47:18
  • Spring实现一个简单的SpringIOC容器

    2023-02-06 21:03:43
  • Java5种遍历HashMap数据的写法

    2022-09-27 15:43:27
  • Android学习教程之日历控件使用(7)

    2023-01-06 23:10:46
  • java实现图片反色处理示例

    2022-07-09 20:53:34
  • Android 中ScrollView嵌套GridView,ListView的实例

    2023-06-15 15:49:56
  • asp之家 软件编程 m.aspxhome.com