• OpenGLES:绘制一个混色旋转的3D球体


    效果展示

    本博文会实现一个混色旋转的3D球体

    一.球体解析

    前几篇博文讲解了如何使用OpenGLES实现不同的3D图形

    这一篇讲解怎样绘制3D世界的代表图形:一个混色旋转的3D球体

    1.1 极限正多面体

    如果看过我前几篇3D图形绘制的博文,就知道要绘制一个3D图形,首先要将3D图形拆解成可以使用单位图元——三角形进行绘制的各种子图形

    然而懂点微积分的都知道,球体本身就可以看作是一个被极限分解的正多面体

    所以球面可以直接使用三角形进行绘制,并不需要拆解成其他子图形

    那么,现在要做的就是如何求解球体的顶点坐标。

    1.2 求解球体顶点坐标

    众所周知,地球上任何一个地方都能用经纬度进行标识

    以此类推,先给球体设置一个经纬度

    根据经纬度就将球体分解成四边形,再将四边形分解成三角形。

    那么求解球体的坐标,就只需要求出四边形的坐标即可。

    1.3 球体顶点坐标公式

    根据上述讲解和图示,很容易就能得出球体顶点坐标公式:

    • x0 = R * cos(a) * sin(b)
    • y0 = R * sin(a))
    • z0 = R * cos(a) * cos(b)

    二.GLRender:变量定义

    2.1 常规变量定义

    还是常见的几个变量,跟其他3D图形的常规变量并无差别

    1. //MVP矩阵
    2. private float[] mMVPMatrix = new float[16];
    3. //着色器程序/渲染器
    4. private int shaderProgram;
    5. //返回属性变量的位置
    6. //MVP变换矩阵属性
    7. private int mvpMatrixLoc;
    8. //位置属性
    9. private int aPositionLocation;
    10. //颜色属性
    11. private int aColorLocation;
    12. //surface宽高比
    13. private float ratio;

    2.2 定义顶点坐标数组和缓冲

    前文中已经讲解,对于球体,并不需要拆解出子图形,而且颜色混合我会在着色器代码中实现,并不会在Render代码中动态加载实现,因此只需要定义一个数组和缓冲,就是顶点坐标。

    1. //球体顶点坐标数组
    2. private float vertexData[];
    3. //顶点缓冲
    4. private FloatBuffer vertexBuffer;

    2.3 定义MVP矩阵

    1. //MVP矩阵
    2. private float[] mMVPMatrix = new float[16];

    三.GLRender:着色器、内存分配等

    3.1 着色器创建、链接、使用

    3.2 着色器属性获取、赋值

    3.3 缓冲内存分配

    这几个部分的代码实现2D图形绘制基本一致

    可参考以前2D绘制的相关博文,里面都有详细的代码实现

    不再重复展示代码

    四.GLRender:动态创建顶点

    创建顶点时需要传入半径:0.85f

    createBallPositions(0.85f);

    球体渲染的关键函数: 

    createBallPositions(float r):

    1. private void createBallPositions(float r) {
    2. // 存放顶点坐标的ArrayList
    3. ArrayList alVertix = new ArrayList();
    4. // 将球进行单位切分的角度
    5. final int angleSpan = 5;
    6. // 纬度angleSpan度一份
    7. for (int wAngle = -90; wAngle < 90; wAngle = wAngle + angleSpan) {
    8. // 经度angleSpan度一份
    9. for (int jAngle = 0; jAngle <= 360; jAngle = jAngle + angleSpan) {
    10. // 纵向横向各到一个角度后计算对应的此点在球面上的坐标
    11. float x0 = (float) (r * Math.cos(Math.toRadians(wAngle)) * Math.sin(Math.toRadians(jAngle)));
    12. float y0 = (float) (r * Math.sin(Math.toRadians(wAngle)));
    13. float z0 = (float) (r * Math.cos(Math.toRadians(wAngle)) * Math.cos(Math.toRadians(jAngle)));
    14. float x1 = (float) (r * Math.cos(Math.toRadians(wAngle)) * Math.sin(Math.toRadians(jAngle + angleSpan)));
    15. float y1 = (float) (r * Math.sin(Math.toRadians(wAngle)));
    16. float z1 = (float) (r * Math.cos(Math.toRadians(wAngle)) * Math.cos(Math.toRadians(jAngle + angleSpan)));
    17. float x2 = (float) (r * Math.cos(Math.toRadians(wAngle + angleSpan)) * Math.sin(Math.toRadians(jAngle + angleSpan)));
    18. float y2 = (float) (r * Math.sin(Math.toRadians(wAngle + angleSpan)));
    19. float z2 = (float) (r * Math.cos(Math.toRadians(wAngle + angleSpan)) * Math.cos(Math.toRadians(jAngle + angleSpan)));
    20. float x3 = (float) (r * Math.cos(Math.toRadians(wAngle + angleSpan)) * Math.sin(Math.toRadians(jAngle)));
    21. float y3 = (float) (r * Math.sin(Math.toRadians(wAngle + angleSpan)));
    22. float z3 = (float) (r * Math.cos(Math.toRadians(wAngle + angleSpan)) * Math.cos(Math.toRadians(jAngle)));
    23. // 将计算出来的XYZ坐标加入存放顶点坐标的ArrayList
    24. alVertix.add(x1);
    25. alVertix.add(y1);
    26. alVertix.add(z1);
    27. alVertix.add(x0);
    28. alVertix.add(y0);
    29. alVertix.add(z0);
    30. alVertix.add(x2);
    31. alVertix.add(y2);
    32. alVertix.add(z2);
    33. alVertix.add(x3);
    34. alVertix.add(y3);
    35. alVertix.add(z3);
    36. /*
    37. 2---------------3
    38. | / |
    39. | / |
    40. | / |
    41. | / |
    42. | / |
    43. 1---------------0
    44. */
    45. }
    46. }
    47. float f[] = new float[alVertix.size()];
    48. for (int i = 0; i < f.length; i++) {
    49. f[i] = alVertix.get(i);
    50. }
    51. vertexData = f;
    52. }

    五.GLRender:绘制

    5.1 MVP矩阵

    1. //MVP矩阵赋值
    2. mMVPMatrix = TransformUtils.getBallMVPMatrix(ratio);
    3. //将变换矩阵传入顶点渲染器
    4. glUniformMatrix4fv(mvpMatrixLoc, 1, false, mMVPMatrix, 0);

    getBallMVPMatrix(float ratio)

    依然采用的是视椎体透视投影:

    1. public static float[] getBallMVPMatrix(float ratio) {
    2. float[] modelMatrix = getIdentityMatrix(16, 0); //模型变换矩阵
    3. float[] modelMatrix0 = getIdentityMatrix(16, 0); //模型变换矩阵
    4. float[] viewMatrix = getIdentityMatrix(16, 0); //观测变换矩阵/相机矩阵
    5. float[] projectionMatrix = getIdentityMatrix(16, 0); //投影变换矩阵
    6. mBallRotateAgree = (mBallRotateAgree + 1.0f) % 360;
    7. Matrix.setRotateM(modelMatrix, 0, mBallRotateAgree, 1, 0, 1);
    8. Matrix.translateM(modelMatrix0,0,0.0f,0.3f,0.3f);
    9. Matrix.multiplyMM(modelMatrix, 0, modelMatrix, 0, modelMatrix0, 0);
    10. Matrix.setLookAtM(viewMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    11. Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 1, 10);
    12. float[] tmpMatrix = new float[16];
    13. float[] mvpMatrix = new float[16];
    14. Matrix.multiplyMM(tmpMatrix, 0, viewMatrix, 0, modelMatrix, 0);
    15. Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, tmpMatrix, 0);
    16. return mvpMatrix;
    17. }

    5.2 绘制球体

    1. //准备顶点坐标内存
    2. glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, false, 0, vertexBuffer);
    3. //绘制
    4. glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexData.length / 3);

    六.着色器代码

    (1).ball_vertex_shader.glsl
    1. #version 300 es
    2. layout (location = 0) in vec4 vPosition;
    3. layout (location = 1) in vec4 aColor;
    4. uniform mat4 u_Matrix;
    5. out vec4 vColor;
    6. void main() {
    7. gl_Position = u_Matrix*vPosition;
    8. float x = vPosition.x;
    9. float y = vPosition.y;
    10. float z = vPosition.z;
    11. //效果较真实
    12. vColor = vec4(x, y, z, 0.0);
    13. }
    (2).ball_fragtment_shader.glsl
    1. #version 300 es
    2. #extension GL_OES_EGL_image_external_essl3 : require
    3. precision mediump float;
    4. in vec4 vColor;
    5. out vec4 outColor;
    6. void main(){
    7. outColor = vColor;
    8. }

    八.结束语

    混色旋转3D球体的绘制过程到此讲解结束了

    最终实现出来的效果如同开头效果展示

  • 相关阅读:
    云原生中间件RocketMQ-核心原理之同步_异步刷盘,同步_异步复制解析
    C语言与内存息息相关的重要概念有哪些?
    Unity ILRuntime热更新开发原则与接口如何绑定
    Java POI 读取 大数据量的Excel 实操
    Yii实现RabbitMQ队列
    如何与ChatGPT愉快地聊天
    蓝桥杯实战应用【算法代码篇】-小朋友崇拜圈(附Java、Python和C++代码)
    【shell】限制任务并发
    指针和数组试题解析(5)二维数组部分
    RabbitMQ---Spring AMQP
  • 原文地址:https://blog.csdn.net/geyichongchujianghu/article/details/133530327