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

news2025/1/11 11:16:25

一.概述

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);
	//粉 Magenta
	magenta.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.5f
		data.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 es

layout (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球体绘制

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BGP(Border Gateway Protocol)

目录 BGP报文类型 BGP状态机 BGP邻居 BGP同步规则 Transit AS中的IBGP路由传递 IBGP水平分割原则 路由通告规则 表 计时器 配置命令 维护BGP BGP表 路径属性 WEIGHT LOCAL PREFERENCE AS-path Origin MED NEXT_HOP NEXT_HOP on shared Media COMMUNIT…

Eevee引擎与渲染原理

操作视频&#xff1a; 02-Blender的Eevee渲染器_哔哩哔哩_bilibiliy 技术原理&#xff1a; How 3D Game Rendering Works, A Deeper Dive: Rasterization and Ray Tracing | TechSpot A guide to Blender Eevee render settings - Artisticrender.com 笔记&#xff1a; …

安装Vue脚手架图文详解教程

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 预备工作 在安装Vue脚手架之前&#xff0c;请确保您已经正确安装了npm&#xff1b;假若还尚未安装npm&#xff0c;请你参考 Node.js安装教程图文详解。 安装Vue脚手架 请…

STM32HAL库CRC学习及测试记录

STM32HAL库CRC学习及测试记录 1.CRC的校验原理2.基本原理3.几个基本概念13.1.1 CRC检验码的计算13.1.2 错误检测13.2 STM32中的CRC 4.CRC功能描述5.STM32Cube生成工程6.看官方说如何使用这个驱动程序7.实验现象 1.CRC的校验原理 循环冗余校验(CRC)计算单元是根据固定的生成多项…

实用的嵌入式 C 程序!建议收藏

在学习和工作开发的时候&#xff0c;经常需要使用到各种各样不太常用的操作&#xff0c;这种情况一般是自己手动写一些小程序来处理。因为它们不太常用&#xff0c;所以经常用了又没保存&#xff0c;等到下一次在使用的时候又需要重写&#xff0c;这样的非常浪费时间和精力。 …

力扣练习——链表在线OJ

目录 提示&#xff1a; 一、移除链表元素 题目&#xff1a; 解答&#xff1a; 二、反转链表 题目&#xff1a; 解答&#xff1a; 三、找到链表的中间结点 题目&#xff1a; 解答&#xff1a; 四、合并两个有序链表&#xff08;经典&#xff09; 题目&#xff1a; 解…

c++-vector

文章目录 前言一、vector介绍二、vector使用1、构造函数2、vector 元素访问3、vector iterator 的使用4、vector 空间增长问题5、vector 增删查改6、理解vector<vector< int >>7、电话号码的字母组合练习题 三、模拟实现vector1、查看STL库源码中怎样实现的vector2…

(四)激光线扫描-光平面标定

在上一章节,已经实现了对激光线条的中心线提取,并且在最开始已经实现了对相机的标定,那么相机标定的作用是什么呢? 就是将图像二维点和空间三维点之间进行互相转换。 1. 什么是光平面 激光发射器投射出一条线,形成的一个扇形区域平面就是光平面,也叫光刀面,与物体相交…

linux下查找文件的相关命令

linux下查找文件的相关命令 运行环境&#xff1a;centos7 参考来源&#xff1a;man、鸟哥入门书籍 一、脚本文件查找&#xff1a;which/type 1. which man手册描述&#xff1a; 返回当前环境可以被执行的文件&#xff08;或链接&#xff09;的路径。搜索PATH变量匹配参数中…

vuejs开发环境搭建

Vuejs是一个前端页面应用开发框架&#xff0c;它基于标准 HTML、CSS 和JavaScript 构建&#xff0c;支持不同的JavaScript开发规范&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面&#xff0c;本文主要描述Vuejs开发环境的搭建。Vuej…

大规模语言模型--训练成本

目前&#xff0c;基于 Transformers 架构的大型语言模型 (LLM)&#xff0c;如 GPT、T5 和 BERT&#xff0c;已经在各种自然语言处理 (NLP) 任务中取得了 SOTA 结果。将预训练好的语言模型(LM) 在下游任务上进行微调已成为处理 NLP 任务的一种 范式。与使用开箱即用的预训练 LLM…

基于SSM的视频点播系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

C++语言GDAL批量裁剪多波段栅格图像:基于像元个数裁剪

本文介绍基于C 语言的GDAL模块&#xff0c;按照给定的像元行数与列数&#xff0c;批量裁剪大量多波段栅格遥感影像文件&#xff0c;并将所得到的裁剪后新的多波段遥感影像文件保存在指定路径中的方法。 在之前的文章中&#xff0c;我们多次介绍了在不同平台&#xff0c;或基于不…

TouchGFX之后端通信

在大多数应用中&#xff0c;UI需以某种方式连接到系统的其余部分&#xff0c;并发送和接收数据。 它可能会与硬件外设&#xff08;传感器数据、模数转换和串行通信等&#xff09;或其他软件模块进行交互通讯。 Model类​ 所有TouchGFX应用都有Model类&#xff0c;Model类除了存…

色彩一致性自动处理方法在遥感图像中的应用

前言 在获取卫星遥感影像时&#xff0c;由于受不均匀的光照、不同的大气条件和不同的传感器设备等因素的影响&#xff0c;遥感影像中会存在局部亮度和色彩分布不均匀的现象&#xff0c;下面是在BigMap地图下载器中收集的几幅谷歌卫星影像&#xff0c;像下面这种都是拼接好的影像…

从零开始的C++(四)

上篇链接&#xff1a;http://t.csdnimg.cn/3nyT9 1.拷贝构造函数&#xff1a; 上篇中介绍了析构函数&#xff0c;即在对象销毁时自动调用的函数&#xff0c;常用于含有malloc、fopen等成员变量的对象。然而&#xff0c;在将对象做函数实参进行值传递的时候&#xff0c;可能会…

Unity宣布自2024年起将根据游戏安装量收费,你对此有何看法?

文章目录 每日一句正能量前言Unity的来历Unity的应用对于收费的看法个人角度&#xff1a;公司角度&#xff1a; 后记 每日一句正能量 水与水之间有距离&#xff0c;但地心下直相牵&#xff0c;人与人之间有距离&#xff0c;但心里时刻挂念&#xff0c;发条短信道声晚安&#xf…

【强化学习】05 —— 基于无模型的强化学习(Prediction)

文章目录 简介蒙特卡洛算法时序差分方法Example1 MC和TD的对比偏差&#xff08;Bias&#xff09;/方差&#xff08;Variance&#xff09;的权衡Example2 Random WalkExample3 AB 反向传播(backup)Monte-Carlo BackupTemporal-Difference BackupDynamic Programming Backup Boot…

深入浅出,SpringBoot整合Quartz实现定时任务与Redis健康检测(一)

目录 前言 环境配置 Quartz 什么是Quartz&#xff1f; 应用场景 核心组件 Job JobDetail Trigger CronTrigger SimpleTrigger Scheduler 任务存储 RAM JDBC 导入依赖 定时任务 销量统计 Redis检测 使用 ​编辑 注意事项 前言 在悦享校园1.0中引入了Quart…

番外3:下载+安装VMware(前期准备)

step1: 查看自己笔记本电脑配置&#xff1b; step2: 下载并安装VMware&#xff08;下载地址www..kkx.net/soft/16841.html&#xff09;这里选择本地普通下载&#xff1b; step3: 安装VMware过程中需要填写密钥&#xff08;本人用的最后一个&#xff09;; #UU54R-FVD91-488PP-7N…