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

效果展示:

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

一.圆锥解析

1.1 对圆锥的拆解

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

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

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

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

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

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

1.2 单位图元:三角形

讲到这里顺带提一句:

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

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

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

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

二.Render:变量定义

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

2.1 常规变量定义

//MVP矩阵
private float[] mMVPMatrix = new float[16];//着色器程序/渲染器
private int shaderProgram;//mvp变换矩阵属性
private int mvpMatrixLoc;
//位置属性
private int aPositionLocation;
//颜色属性
private int aColorLocation;//surface宽高比率
private float ratio;

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

//圆锥锥顶 顶点
private float vertexData[];
//圆锥底部圆 顶点
private float vertexData1[];
//圆锥锥顶 顶点颜色
private float colorData[];
//圆锥底部圆 顶点颜色
private float colorData1[];//对应的坐标和颜色缓冲
private FloatBuffer vertexBuffer;
private FloatBuffer vertexBuffer1;
private FloatBuffer colorBuffer;
private FloatBuffer colorBuffer1;

2.3 定义MVP矩阵

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

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

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

3.2 着色器属性获取、赋值

3.3 缓冲内存分配

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

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

不再重复展示代码

四.Render:动态创建顶点

需要传入两个参数:

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

函数实现:

private void createPositions(float radius, int n) {ArrayList<Float> red = new ArrayList<>();ArrayList<Float> blue = new ArrayList<>();ArrayList<Float> magenta = new ArrayList<>();ArrayList<Float> totalColor1 = new ArrayList<>();ArrayList<Float> totalColor2 = new ArrayList<>();//红red.add(1.0f);red.add(0.0f);red.add(0.0f);red.add(0.0f);//蓝blue.add(0.0f);blue.add(0.0f);blue.add(1.0f);blue.add(0.0f);//粉 Magentamagenta.add(1.0f);magenta.add(0.2f);magenta.add(1.0f);magenta.add(0.0f);ArrayList<Float> data = new ArrayList<>();//设置圆心的顶点坐标data.add(0.0f);data.add(0.0f);data.add(1.0f);//设置底部圆的顶点坐标float angDegSpan = 360f / n;for (float i = 0; i < 360 + angDegSpan; i += angDegSpan) {data.add((float) (radius * Math.sin(i * Math.PI / 180f)));data.add((float) (radius * Math.cos(i * Math.PI / 180f)));//底部圆的顶点Z坐标设置为-0.5fdata.add(-0.5f);}//所有顶点坐标float[] f = new float[data.size()];for (int i = 0; i < f.length; i++) {f[i] = data.get(i);}vertexData = f;//设置圆心和底部圆顶点对应的颜色数据colorData = new float[f.length * 4 / 3];for (int i = 0; i < f.length / 3; i++) {if (i == 0) {totalColor1.addAll(red);} else {totalColor1.addAll(blue);}}for (int i = 0; i < totalColor1.size(); i++) {colorData[i] = totalColor1.get(i);}//底部圆vertexData1 = new float[vertexData.length];for (int i = 0; i < vertexData.length; i++) {if (i == 2) {vertexData1[i] = -0.5f;} else {vertexData1[i] = vertexData[i];}}colorData1 = new float[f.length * 4 / 3];for (int i = 0; i < f.length / 3; i++) {totalColor2.addAll(magenta);}for (int i = 0; i < totalColor2.size(); i++) {colorData1[i] = totalColor2.get(i);}
}

五.Render:绘制

5.1 MVP矩阵

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

getConeMVPMatrix():

采用的是透视投影方式

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

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

drawCenterAndSide();
drawBottomCircle();

(1).drawCenterAndSide()

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

(2).drawBottomCircle()

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

六.着色器代码

(1).cone_vertex_shader.glsl

#version 300 eslayout (location = 0) in vec4 vPosition;
layout (location = 1) in vec4 aColor;uniform mat4 u_Matrix;out vec4 vColor;void main() {gl_Position  = u_Matrix*vPosition;vColor = aColor;
}

(2).cone_fragtment_shader.glsl

#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;in vec4 vColor;out vec4 outColor;void main(){outColor = vColor;
}

七.效果展示

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

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

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

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

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

效果如下:

八.结束语

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/148067.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络(五):运输层

参考引用 计算机网络微课堂-湖科大教书匠计算机网络&#xff08;第7版&#xff09;-谢希仁 1. 运输层概述 之前所介绍的计算机网络体系结构中的物理层、数据链路层以及网络层它们共同解决了将主机通过异构网络互联起来所面临的问题&#xff0c;实现了主机到主机的通信&#xff…

Ubuntu安装samba服务器

为了window系统下能够像访问本地目录一样访问ubuntu系统下的目录&#xff0c;这里我通过安装samba服务器&#xff0c;将ubuntu系统的文件目录通过网络挂载的方式共享出来&#xff0c;以便在window下就能够对ubuntu系统的文件进行读写等访问操作&#xff0c;这里记录一下samba服…

ESLint自动修复代码规范错误

基于 vscode 插件 ESLint 高亮错误&#xff0c;并通过配置 自动 帮助我们修复错误 在设置中 settings.json添加这段代码就自动修复错误 // 当保存的时候&#xff0c;eslint自动帮我们修复错误 "editor.codeActionsOnSave": { "source.fixAll": true }, /…

如何使用 AI与人工智能的定义、研究价值、发展阶段

目录 一、什么是人工智能 二、人工智能的研究价值 三、人工智能的发展阶段 一、什么是人工智能 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是一门研究如何使计算机能够模拟和执行人类智能活动的科学与技术。人工智能旨在开发智能代理&…

Kaggle - LLM Science Exam上:赛事概述、数据收集、BERT Baseline

文章目录 一、赛事概述1.1 OpenBookQA Dataset1.2 比赛背景1.3 评估方法和代码要求1.4 比赛数据集1.5 优秀notebook 二、BERT Baseline2.1 数据预处理2.2 定义data_collator2.3 加载模型&#xff0c;配置trainer并训练2.4 预测结果并提交2.5 相关优化 前言&#xff1a;国庆期间…

常见web信息泄露

一、源码(备份文件)泄露 1、git泄露 Git是一个开源的分布式版本控制系统&#xff0c;在执行git init初始化目录的时候&#xff0c;会在当前目录下自动创建一个.git目录&#xff0c;用来记录代码的变更记录等。发布代码的时候&#xff0c;如果没有把.git这个目录删除&#xff…

前后端通信到底是怎样一个过程

前后端通信是怎样 前言&#xff1a;Http协议 超文本传输协议 规定&#xff1a;每一次前后端通信&#xff0c;前端需要主动向后端发出请求&#xff0c;后端接收到前端的请求后&#xff0c;可以给出响应 1、Http报文 浏览器向服务器发送请求时&#xff0c;请求本身就是信息&…

玩转快速排序(C语言版)

W...Y的主页 &#x1f60a; 代码仓库分享 &#x1f495; &#x1f354;前言&#xff1a; 本篇文章&#xff0c;我们来讲解一下神秘的快速排序。对于快速排序我相信大家都已经有所耳闻&#xff0c;但是快速排序是有很多的版本的。我们这次的目的就是快排的所有内容搞懂&#…

什么是GraphQL?它与传统的REST API有什么不同?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是GraphQL&#xff1f;⭐ 与传统的REST API 的不同⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣…

JavaScript处理点击事件

在介绍点击事件之前&#xff0c;先给它们讲一些概念 事件监听 在JavaScript中&#xff0c;可以使用事件监听&#xff08;Event Listener&#xff09;来响应和处理各种事件。事件监听器是一种能够捕捉特定事件并执行相应代码的机制。事件监听器允许您在特定事件发生时执行自定…

Eclipse MAT解析headp dump,total size小于file size

1. 问题描述 使用Eclipse MAT分析20GB的heap dump文件 最后解析出来dump size只有1GB 2. 原因&#xff1a;heap dump中包含许多unreachable objects Eclipse MAT的官方文档&#xff0c;《Basic Tutorial》章节&#xff0c;有对上图的Overview page做介绍 针对total size小…

Qt扩展-QCustomPlot 用户交互

QCustomPlot 用户交互 一、概述二、操作范围三、选择机制1. 控制Graph的可选择性和选择状态2. 所选对象的外观3. 多部分对象4. 对选择变化做出反应 四、用户交互信号 一、概述 QCustomPlot提供了多个内置的用户交互。它们大致可以分为 通过用鼠标拖动和滚动鼠标滚轮进行范围操…

typescript映射类型

ts映射类型简介 TypeScript中的映射类型&#xff08;Mapped Type&#xff09;是一种高级类型&#xff0c;它允许我们基于现有类型创建新的类型&#xff0c;同时对新类型的每个属性应用一个转换函数。通过使用映射类型&#xff0c;我们可以方便地对对象的属性进行批量操作&…

华为数通方向HCIP-DataCom H12-831题库(单选题:181-200)

第181题 以下关于OSPF的5类LSA中的转发地址(ForwardingAddress,FA) 的描述,正确的是哪一项? A、当FA地址为0.0.0.0时,收到该LSA的路由器认为到达目的网段的数据包应该发往对应的ABR,因此将到达ABR的下一跳地址作为这条外部路由的下一跳 B、当FA地址为0.0.0.0时,收到该LS…

C++算法 —— 动态规划(12)两道小题

文章目录 1、动规思路简介2、组合总和Ⅳ3、卡特兰数 背包问题需要读者先明白动态规划是什么&#xff0c;理解动规的思路&#xff0c;并不能给刚接触动规的人学习。所以最好是看了之前的动规博客&#xff0c;以及背包博客&#xff0c;或者你本人就已经懂得动规了。 1、动规思路简…

(二)激光线扫描-相机标定

1. 何为相机标定? 当相机拍摄照片时,我们看到的图像通常与我们实际看到的不完全相同。这是由相机镜头引起的,而且发生的频率比我们想象的要高。 这种图像的改变就是我们所说的畸变。一般来说,畸变是指直线在图像中出现弯曲或弯曲。 这种畸变我们可以通过相机标定来进行解…

华为云云耀云服务器L实例评测|Huawei Cloud EulerOS 自动化环境部署

[toc] Huawei Cloud EulerOS 自动化环境部署 云耀云服务器L实例【Huawei Cloud EulerOS 2.0 64bit】 Python Git Google Chrome Chromedriver Selenium More… 1. Python 镜像创建后自带。 2.Git 拉取项目。 sudo yum install git3. Google Chrome 使用root权限或sudo权…

【算法|动态规划No.11】leetcode53. 最大子数组和

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

【image captioning】CaMEL: Mean Teacher Learning for Image Captioning(实现流程)

CaMEL: Mean Teacher Learning for Image Captioning(实现流程) 作者:安静到无声 个人主页 目录 CaMEL: Mean Teacher Learning for Image Captioning(实现流程)环境设置数据准备Evaluation训练程序推荐专栏参考代码: CaMEL: Mean Teacher Learning for Image Captioning.…

C++_pen_静态与常量

成员 常成员、常对象&#xff08;C推荐使用 const 而不用#define,mutable&#xff09; const 数据成员只在某个对象生存周期内是常量&#xff0c;而对于整个类而言却是可变的&#xff08;static除外&#xff09; 1.常数据成员&#xff08;构造函数初始化表赋值&#xff09; c…