• OpenGLES:绘制一个混色旋转的3D圆锥


    效果展示:

    本篇博文总共会实现两种混色旋转的3D圆锥:

    一.圆锥解析

    1.1 对圆锥的拆解

    上一篇博文讲解了绘制圆柱体,这一篇讲解绘制一个彩色旋转的圆锥

    在绘制圆柱体时提到过,关键点是先将圆柱进行拆解,便于创建出顶点坐标数组

    同样,绘制圆锥也先进行拆解

    圆锥的拆解很简单,有两种方式可以理解:

    • 2D圆的圆心从圆平面里抽离出来,赋予一个Z值
    • 2D的圆心和圆平面分别赋予不同的Z值

    也就是把圆锥拆成:一个2D圆 + 扇形锥面

    1.2 单位图元:三角形

    讲到这里顺带提一句:

    OpenGL的世界里,不论多么复杂图形,最终都会被拆解成使用最基础的单位图元:三角形来完成绘制

    为什么OpenGL渲染的基础单位图元是三角形呢?

    因为一个点只是点,两个点组成线,三个点能确定一个面。

    三角形是形成一个面最基础的图形单元,所以也是OpenGL的基础图元。

    二.GLRender:变量定义

    这次顶点颜色数组的定义和赋值与立方体绘制类似,在Render类中使用代码动态完成

    2.1 常规变量定义

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

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

    1. //圆锥锥顶 顶点
    2. private float vertexData[];
    3. //圆锥底部圆 顶点
    4. private float vertexData1[];
    5. //圆锥锥顶 顶点颜色
    6. private float colorData[];
    7. //圆锥底部圆 顶点颜色
    8. private float colorData1[];
    9. //对应的坐标和颜色缓冲
    10. private FloatBuffer vertexBuffer;
    11. private FloatBuffer vertexBuffer1;
    12. private FloatBuffer colorBuffer;
    13. private FloatBuffer colorBuffer1;

    2.3 定义MVP矩阵

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

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

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

    3.2 着色器属性获取、赋值

    3.3 缓冲内存分配

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

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

    不再重复展示代码

    四.GLRender:动态创建顶点

    需要传入两个参数:

    • 圆锥底部圆半径长度
    • 底部圆和圆锥扇面分割份数
    createPositions(0.6f, 60);

    函数实现:

    1. private void createPositions(float radius, int n) {
    2. ArrayList red = new ArrayList<>();
    3. ArrayList blue = new ArrayList<>();
    4. ArrayList magenta = new ArrayList<>();
    5. ArrayList totalColor1 = new ArrayList<>();
    6. ArrayList totalColor2 = new ArrayList<>();
    7. //红
    8. red.add(1.0f);
    9. red.add(0.0f);
    10. red.add(0.0f);
    11. red.add(0.0f);
    12. //蓝
    13. blue.add(0.0f);
    14. blue.add(0.0f);
    15. blue.add(1.0f);
    16. blue.add(0.0f);
    17. //粉 Magenta
    18. magenta.add(1.0f);
    19. magenta.add(0.2f);
    20. magenta.add(1.0f);
    21. magenta.add(0.0f);
    22. ArrayList data = new ArrayList<>();
    23. //设置圆心的顶点坐标
    24. data.add(0.0f);
    25. data.add(0.0f);
    26. data.add(1.0f);
    27. //设置底部圆的顶点坐标
    28. float angDegSpan = 360f / n;
    29. for (float i = 0; i < 360 + angDegSpan; i += angDegSpan) {
    30. data.add((float) (radius * Math.sin(i * Math.PI / 180f)));
    31. data.add((float) (radius * Math.cos(i * Math.PI / 180f)));
    32. //底部圆的顶点Z坐标设置为-0.5f
    33. data.add(-0.5f);
    34. }
    35. //所有顶点坐标
    36. float[] f = new float[data.size()];
    37. for (int i = 0; i < f.length; i++) {
    38. f[i] = data.get(i);
    39. }
    40. vertexData = f;
    41. //设置圆心和底部圆顶点对应的颜色数据
    42. colorData = new float[f.length * 4 / 3];
    43. for (int i = 0; i < f.length / 3; i++) {
    44. if (i == 0) {
    45. totalColor1.addAll(red);
    46. } else {
    47. totalColor1.addAll(blue);
    48. }
    49. }
    50. for (int i = 0; i < totalColor1.size(); i++) {
    51. colorData[i] = totalColor1.get(i);
    52. }
    53. //底部圆
    54. vertexData1 = new float[vertexData.length];
    55. for (int i = 0; i < vertexData.length; i++) {
    56. if (i == 2) {
    57. vertexData1[i] = -0.5f;
    58. } else {
    59. vertexData1[i] = vertexData[i];
    60. }
    61. }
    62. colorData1 = new float[f.length * 4 / 3];
    63. for (int i = 0; i < f.length / 3; i++) {
    64. totalColor2.addAll(magenta);
    65. }
    66. for (int i = 0; i < totalColor2.size(); i++) {
    67. colorData1[i] = totalColor2.get(i);
    68. }
    69. }

    五.GLRender:绘制

    5.1 MVP矩阵

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

    getConeMVPMatrix():

    采用的是透视投影方式

    1. public static float[] getConeMVPMatrix(float ratio) {
    2. float[] modelMatrix = getIdentityMatrix(16, 0); //模型变换矩阵
    3. float[] viewMatrix = getIdentityMatrix(16, 0); //观测变换矩阵/相机矩阵
    4. float[] projectionMatrix = getIdentityMatrix(16, 0); //投影变换矩阵
    5. mConeRotateAgree = (mConeRotateAgree + 1) % 360;
    6. //旋转方向xyz三个轴是相对于相机观察方向的,可以写一篇博客
    7. Matrix.rotateM(modelMatrix, 0, mConeRotateAgree, 1, 0, 1); //获取模型旋转变换矩阵
    8. //设置相机位置
    9. Matrix.setLookAtM(viewMatrix, 0, 5, 0.0f, -3.0f, 0f, 0f, 0f, 0f, 0.0f, 1.0f);
    10. //设置透视投影
    11. Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 3, 10);
    12. //计算变换矩阵
    13. float[] tmpMatrix = new float[16];
    14. Matrix.multiplyMM(tmpMatrix, 0, viewMatrix, 0, modelMatrix, 0);
    15. float[] mvpMatrix = new float[16];
    16. Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, tmpMatrix, 0);
    17. return mvpMatrix;
    18. }

    5.2 绘制圆锥的锥顶锥面、底部圆

    1. drawCenterAndSide();
    2. drawBottomCircle();

    (1).drawCenterAndSide()

    1. //准备顶点坐标和颜色数据
    2. glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, false, 0, vertexBuffer);
    3. glVertexAttribPointer(aColorLocation, 4, GL_FLOAT, false, 0, colorBuffer);
    4. //绘制
    5. glDrawArrays(GL_TRIANGLE_FAN, 0, vertexData.length / 3);

    (2).drawBottomCircle()

    1. //准备顶点坐标和颜色数据
    2. glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, false, 0, vertexBuffer1);
    3. //底部圆颜色(粉色)缓冲
    4. glVertexAttribPointer(aColorLocation, 4, GL_FLOAT, false, 0, colorBuffer1);
    5. //绘制
    6. glDrawArrays(GL_TRIANGLE_FAN, 0, vertexData1.length / 3);

    六.着色器代码

    (1).cone_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. vColor = aColor;
    9. }

    (2).cone_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. }

    七.两种效果

    最终实现出来的是锥面红蓝渐变、锥底粉色的圆锥

    个人这个效果并不太好,底部和锥面的颜色变化没有渐变,过于突兀 

    只要在绘制底部圆的函数中更改一下,就可以得到底部圆心对应锥顶颜色,圆周对应锥面底部颜色的圆锥

    注释掉底部圆的颜色缓冲代码

    1. //准备顶点坐标和颜色数据
    2. glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, false, 0, vertexBuffer1);
    3. //注释掉这句,底部圆的圆心颜色就会和圆锥锥顶颜色一样,底部圆的圆周颜色和圆锥锥面底部颜色一样
    4. //glVertexAttribPointer(aColorLocation, 4, GL_FLOAT, false, 0, colorBuffer1);
    5. //绘制
    6. glDrawArrays(GL_TRIANGLE_FAN, 0, vertexData1.length / 3);

    效果如下:

    八.结束语

    两种混色旋转的3D圆锥绘制过程到此讲解结束

    下一篇博文讲解混色旋转的3D球体绘制

  • 相关阅读:
    【C语言】break 关键字
    手撕IP核系列——Xilinx FIR IP核之一
    好用的爬取静态页面谷歌浏览器工具:Save All Resources
    网络安全实战,潜伏与Python反向连接
    itss认证条件是什么?
    Devchat-AI 编程助手:Devchat-AI 尝鲜测评+场景实践
    你还不会用数据库吗?一篇文章带你入门!!!#sql #Mysql
    科普一下:拍抖音需要什么设备,可能用到的设备合集
    C数据结构:排序
    10.7- 的报错整理(与linux、python相关)
  • 原文地址:https://blog.csdn.net/geyichongchujianghu/article/details/133519555