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

news2024/12/26 11:26:09

一.概述

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

本篇博文讲解怎样实现3D世界的代表图形:一个混色旋转的3D球体

二.球体解析

2.1 极限正多面体

如果有学习过我前几篇3D图形绘制的博文,就知道要想绘制一个3D图形,首先要做的第一步就是将要绘制的3D图形进行拆解,拆解成能够使用单位图元——三角形进行绘制的各种子图形

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

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

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

2.2 求解球体顶点坐标

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

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

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

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

2.3 球体顶点坐标公式

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

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

三.Render:变量定义

3.1 常规变量定义

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

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

//着色器程序/渲染器
private int shaderProgram;

//返回属性变量的位置
//MVP变换矩阵属性
private int mvpMatrixLoc;
//位置属性
private int aPositionLocation;
//颜色属性
private int aColorLocation;

//surface宽高比
private float ratio;

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

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

//球体顶点坐标数组
private float vertexData[];
//顶点缓冲
private FloatBuffer vertexBuffer;

3.3 定义MVP矩阵

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

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

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

4.2 着色器属性获取、赋值

4.3 缓冲内存分配

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

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

不再重复展示代码

五.Render:动态创建顶点

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

createBallPositions(0.85f);

球体渲染的关键函数: 

createBallPositions(float r):

private void createBallPositions(float r) {
	// 存放顶点坐标的ArrayList
	ArrayList<Float> alVertix = new ArrayList<Float>();
	// 将球进行单位切分的角度
	final int angleSpan = 5;
	// 纬度angleSpan度一份
	for (int wAngle = -90; wAngle < 90; wAngle = wAngle + angleSpan) {
		// 经度angleSpan度一份
		for (int jAngle = 0; jAngle <= 360; jAngle = jAngle + angleSpan) {
			// 纵向横向各到一个角度后计算对应的此点在球面上的坐标
			float x0 = (float) (r * Math.cos(Math.toRadians(wAngle)) * Math.sin(Math.toRadians(jAngle)));
			float y0 = (float) (r * Math.sin(Math.toRadians(wAngle)));
			float z0 = (float) (r * Math.cos(Math.toRadians(wAngle)) * Math.cos(Math.toRadians(jAngle)));

			float x1 = (float) (r * Math.cos(Math.toRadians(wAngle)) * Math.sin(Math.toRadians(jAngle + angleSpan)));
			float y1 = (float) (r * Math.sin(Math.toRadians(wAngle)));
			float z1 = (float) (r * Math.cos(Math.toRadians(wAngle)) * Math.cos(Math.toRadians(jAngle + angleSpan)));

			float x2 = (float) (r * Math.cos(Math.toRadians(wAngle + angleSpan)) * Math.sin(Math.toRadians(jAngle + angleSpan)));
			float y2 = (float) (r * Math.sin(Math.toRadians(wAngle + angleSpan)));
			float z2 = (float) (r * Math.cos(Math.toRadians(wAngle + angleSpan)) * Math.cos(Math.toRadians(jAngle + angleSpan)));

			float x3 = (float) (r * Math.cos(Math.toRadians(wAngle + angleSpan)) * Math.sin(Math.toRadians(jAngle)));
			float y3 = (float) (r * Math.sin(Math.toRadians(wAngle + angleSpan)));
			float z3 = (float) (r * Math.cos(Math.toRadians(wAngle + angleSpan)) * Math.cos(Math.toRadians(jAngle)));

			// 将计算出来的XYZ坐标加入存放顶点坐标的ArrayList
			alVertix.add(x1);
			alVertix.add(y1);
			alVertix.add(z1);
			alVertix.add(x0);
			alVertix.add(y0);
			alVertix.add(z0);
			alVertix.add(x2);
			alVertix.add(y2);
			alVertix.add(z2);
			alVertix.add(x3);
			alVertix.add(y3);
			alVertix.add(z3);
			/*
			2---------------3
			|             / |
			|          /    |
			|       /       |
			|    /          |
			| /             |
			1---------------0
			 */
		}
	}

	float f[] = new float[alVertix.size()];
	for (int i = 0; i < f.length; i++) {
		f[i] = alVertix.get(i);
	}
	vertexData = f;
}

六.Render:绘制

6.1 MVP矩阵

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

getBallMVPMatrix(float ratio)

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

public static float[] getBallMVPMatrix(float ratio) {
	float[] modelMatrix = getIdentityMatrix(16, 0); //模型变换矩阵
	float[] modelMatrix0 = getIdentityMatrix(16, 0); //模型变换矩阵

	float[] viewMatrix = getIdentityMatrix(16, 0); //观测变换矩阵/相机矩阵
	float[] projectionMatrix = getIdentityMatrix(16, 0); //投影变换矩阵

	mBallRotateAgree = (mBallRotateAgree + 1.0f) % 360;
	Matrix.setRotateM(modelMatrix, 0, mBallRotateAgree, 1, 0, 1);
	Matrix.translateM(modelMatrix0,0,0.0f,0.3f,0.3f);
	Matrix.multiplyMM(modelMatrix, 0, modelMatrix, 0, modelMatrix0, 0);

	Matrix.setLookAtM(viewMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
	Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 1, 10);

	float[] tmpMatrix = new float[16];
	float[] mvpMatrix = new float[16];
	Matrix.multiplyMM(tmpMatrix, 0, viewMatrix, 0, modelMatrix, 0);
	Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, tmpMatrix, 0);

	return mvpMatrix;
}

6.2 绘制球体

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

七.着色器代码

(1).ball_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;

    float x = vPosition.x;
    float y = vPosition.y;
    float z = vPosition.z;

    //效果较真实
    vColor = vec4(x, y, z, 0.0);
}
(2).ball_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;
}

八.效果展示

混色旋转的球体:

 

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

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

相关文章

基于Java的校园二手书交易平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

挑战自己,编程你的五子棋:Python+Pygame实践经验分享

引言 五子棋&#xff0c;古老而经典&#xff0c;是一种两人对抗的策略棋类游戏。想要体验制作游戏的乐趣吗&#xff1f;本文将详细指导你如何使用Python语言和Pygame库&#xff0c;一步步打造自己的五子棋游戏&#xff01; 1. 开始之前&#xff1a;重要准备 首先&#xff0…

使用DNS查询Web服务器IP地址

浏览器并不具备访问网络的功能&#xff0c;其最终是通过操作系统实现的&#xff0c;委托操作系统访问服务器时提供的并不是浏览器里面输入的域名而是ip地址&#xff0c;因此第一步需要将域名转换为对应的ip地址 域名&#xff1a;www.baidu.com ip地址是一串数字 tcp/ip的网络结…

项目进展(六)-继续学习32位ADC芯片ADS1285

一、数据手册学习 1.1时序图 SPI时序图&#xff0c;这是很重要的一个地方&#xff0c;一定要在代码中将SPI配置成对应的模式。 先放一堆截图在这吧&#xff0c;一些引脚的功能及特性还未看到&#xff0c;等具体了解之后再详细介绍下面几张截图的时序&#xff1a; 1.2 内…

大模型RLHF算法更新换代,DeepMind提出自训练离线强化学习框架ReST

文章链接&#xff1a; https://arxiv.org/abs/2308.08998 大模型&#xff08;LLMs&#xff09;爆火的背后&#xff0c;离不开多种不同基础算法技术的支撑&#xff0c;例如基础语言架构Transformer、自回归语言建模、提示学习和指示学习等等。这些技术造就了像GPT-3、PaLM等基座…

问答雕虫1

问题&#xff1a; 现在有如下表 假设按时间顺序&#xff0c;记录中连续出现0.2 0.3 0.5 0.7四条记录记为一次有效数据组&#xff0c;统计一段时间范围内&#xff0c;有效数据组出现的次数&#xff0c;最终计算有效数据组在整个时间范围内的记录的占比。用mysql语句或者函数如何…

uniapp uni.showToast 一闪而过的问题

问题&#xff1a;在页面跳转uni.navigateBack()等操作的前或后&#xff0c;执行uni.showToast&#xff0c;即使代码中设置2000ms的显示时间&#xff0c;也会一闪而过。 解决&#xff1a;用setTimeout延后navigateBack的执行。

css自学框架之选项卡

这一节我们学习切换选项卡&#xff0c;两种切换方式&#xff0c;一种是单击切换选项&#xff0c;一种是鼠标滑动切换&#xff0c;通过参数来控制&#xff0c;切换方法。 一、参数 属性默认值描述tabBar.myth-tab-header span鼠标触发区域tabCon.myth-tab-content主体区域cla…

C语言动态内存管理

&#x1f435;本篇文章将会对动态内存管理相关知识进行讲解 1. 为什么要存在动态内存管理❓ 目前我们掌握了两种开辟内存的方式&#xff0c;分别为&#xff1a; int a 10;//存放一个值 int arr[] { 1,2,3,4,5,6,7,8,9,10 };//存放一组数 这两种内存开辟方式都是静态的&#…

2023年山东省安全员C证证考试题库及山东省安全员C证试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年山东省安全员C证证考试题库及山东省安全员C证试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大…

人脸识别:FaceSDK 8.1 Crack

FaceSDK 使 Microsoft Visual C、C#、Objective C、Swift、Java、VB、Delphi 和 Python 开发人员能够为 Web、Windows、Linux、macOS、iOS 和 Android 构建具有人脸识别和基于人脸的 32 位和 64 位应用程序生物特征识别功能 FaceSDK 用于数百个应用程序&#xff0c;用于通过网络…

极大似然估计概念的理解——统计学习方法

目录 1.最大似然估计的概念的理解1 2.最大似然估计的概念的理解2 3.最大似然估计的概念的理解3 4.例子 1.最大似然估计的概念的理解1 最大似然估计是一种概率论在统计学上的概念&#xff0c;是参数估计的一种方法。给定观测数据来评估模型参数。也就是模型已知&#xff0c;参…

Flutter项目安装到Android手机一直显示在assembledebug

问题 Flutter项目安装到Android手机一直显示在assembledebug 原因 网络不好&#xff0c;gradle依赖下载不下来 解决方案 修改如下的文件 gradle-wrapper.properties 使用腾讯提供的gradle镜像下载 distributionUrlhttps://mirrors.cloud.tencent.com/gradle/gradle-7.5…

SpringBoot-Shiro安全权限框架

Apache Shiro是一个强大而灵活的开源安全框架&#xff0c;它干净利落地处理身份认证&#xff0c;授权&#xff0c;企业会话管理和加密。 官网&#xff1a; http://shiro.apache.org/ 源码&#xff1a; https://github.com/apache/shiro Subject&#xff1a;代表当前用户或…

【问题证明】矩阵方程化为特征值方程求得的特征值为什么是全部特征值?不会丢解吗?

问题 这个问题困扰了我好久&#xff0c;一直感觉如果有其他的特征值没法证伪&#xff0c;不过一直存在思想的层面&#xff0c;没有实际解决&#xff0c;今天突然想到动笔来解决&#xff0c;遂得解&#xff0c;证明如下。 证明 总结 这个证明看似证明过后很直观&#xff0c;但…

10.4 小任务

目录 QT实现TCP服务器客户端搭建的代码&#xff0c;现象 TCP服务器 .h文件 .cpp文件 现象 TCP客户端 .h文件 .cpp文件 现象 QT实现TCP服务器客户端搭建的代码&#xff0c;现象 TCP服务器 .h文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #includ…

智能合约漏洞,BEVO 代币损失 4.5 万美元攻击事件分析

智能合约漏洞&#xff0c;BEVO 代币损失 4.5 万美元攻击事件分析 一、事件背景 北京时间 2023 年 1 月 31 日&#xff0c;在 twitter 上看到这样一条消息&#xff1a; BEVO 代币被攻击&#xff0c;总共损失 45000 美元&#xff0c;导致 BEVO 代币的价格下跌了 99%。 有趣的是…

编程新手?跟着这个教程,用Python画出小猪佩奇

小猪佩奇是许多小朋友们的心头好&#xff0c;它的形象可爱、颜色鲜艳。你知道吗&#xff0c;只需要Python中的一个简单模块&#xff0c;我们就可以自己绘制出这个可爱的形象&#xff01;本文将教你如何使用Python的turtle模块&#xff0c;一步步画出小猪佩奇。 1. 准备工作&a…

当我们做后仿时我们究竟在仿些什么(四)

就像人类容易接受自然数&#xff0c;但对于负数缺乏某种直觉上的认识一样&#xff1b;后仿过程中经常出现的 Negative Delay 和 Negative Timing Check 也非常容易使人困惑。 Warning-[SDFCOM_NICD] Negative INTERCONNECT Delay encountered今天这篇首先简要分析这些 Negativ…

创建线程池

如何创建线程池及处理相应任务 目录 如何创建线程池及处理相应任务线程池定义解决的问题(需求)工作原理实现线程池创建示意图重要构造器创建线程池(ExecutorService)线程池任务处理常用API处理Runnable任务处理Callable任务 使用工具类(Executors)创建线程池常用API应用案例 拓…