OpenGL ES 3. 绘制球体 实战

2024-05-10 19:32
文章标签 实战 es 绘制 opengl 球体

本文主要是介绍OpenGL ES 3. 绘制球体 实战,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

大家好,接下来将为大家介绍OpenGL ES 3. 绘制球体。

        OpenGL ES 中任何形状的 3D 物体都是用三角形而组成的, 因此,构建曲面物体最重要的就是找到将曲面恰当划分成三角形的策略。最基本的策略是首先按照一定的规则将物体按行和列两个方向进行划分,这时就可以得到很多的小四边形。然后再将每个小四边形划分成两个三角形即可。

        球面首先被按照纬度 (行)和经度(列)的方向划分成了很多的小四边形,每个小四边形又被划分成两个小三角形。这种划分方式下,三角形中每个顶点的坐标都可以用几何的公式方便地计算出来,具体情况如下。

x = R * cos(a) * cos(b) ;y = R * cos(a) * sin(b) ;z = R * sin(a) 。

上述给出的是当球的半径为 R,在经度为a,纬度为b处球面上顶点坐标的计算公式。

 

1、按照切分规则生成球面上顶点的坐标,并渲染球体的 Ball 类。

initVertexData方法用于初始化球体的顶点数据,initShader方法用于创建着色器对象:

import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.opengl.GLES30;
import android.os.Build;public class Ball {int mProgram;// 自定义渲染管线着色器程序idint muMVPMatrixHandle;//总变换矩阵引用int maPositionHandle; //顶点位置属性引用int muRHandle;//球的半径属性引用String mVertexShader;//顶点着色器代码脚本String mFragmentShader;//片元着色器代码脚本FloatBuffer mVertexBuffer;// 顶点坐标数据缓冲int vCount = 0;float yAngle = 0;// 绕y轴旋转的角度float xAngle = 0;// 绕x轴旋转的角度float zAngle = 0;// 绕z轴旋转的角度float r = 0.8f;public Ball(MySurfaceView mv) {// 初始化顶点数据的方法initVertexData();// 初始化着色器的方法initShader(mv);}// 初始化顶点数据的方法public void initVertexData() {// 顶点坐标数据的初始化================begin============================ArrayList<Float> alVertix = new ArrayList<Float>();// 存放顶点坐标的ArrayListfinal int angleSpan = 10;// 将球进行单位切分的角度for (int vAngle = -90; vAngle < 90; vAngle = vAngle + angleSpan)// 垂直方向angleSpan度一份{for (int hAngle = 0; hAngle <= 360; hAngle = hAngle + angleSpan)// 水平方向angleSpan度一份{// 纵向横向各到一个角度后计算对应的此点在球面上的坐标float x0 = (float) (r * UNIT_SIZE* Math.cos(Math.toRadians(vAngle)) * Math.cos(Math.toRadians(hAngle)));float y0 = (float) (r * UNIT_SIZE* Math.cos(Math.toRadians(vAngle)) * Math.sin(Math.toRadians(hAngle)));float z0 = (float) (r * UNIT_SIZE * Math.sin(Math.toRadians(vAngle)));float x1 = (float) (r * UNIT_SIZE* Math.cos(Math.toRadians(vAngle)) * Math.cos(Math.toRadians(hAngle + angleSpan)));float y1 = (float) (r * UNIT_SIZE* Math.cos(Math.toRadians(vAngle)) * Math.sin(Math.toRadians(hAngle + angleSpan)));float z1 = (float) (r * UNIT_SIZE * Math.sin(Math.toRadians(vAngle)));float x2 = (float) (r * UNIT_SIZE* Math.cos(Math.toRadians(vAngle + angleSpan)) * Math.cos(Math.toRadians(hAngle + angleSpan)));float y2 = (float) (r * UNIT_SIZE* Math.cos(Math.toRadians(vAngle + angleSpan)) * Math.sin(Math.toRadians(hAngle + angleSpan)));float z2 = (float) (r * UNIT_SIZE * Math.sin(Math.toRadians(vAngle + angleSpan)));float x3 = (float) (r * UNIT_SIZE* Math.cos(Math.toRadians(vAngle + angleSpan)) * Math.cos(Math.toRadians(hAngle)));float y3 = (float) (r * UNIT_SIZE* Math.cos(Math.toRadians(vAngle + angleSpan)) * Math.sin(Math.toRadians(hAngle)));float z3 = (float) (r * UNIT_SIZE * Math.sin(Math.toRadians(vAngle + angleSpan)));// 将计算出来的XYZ坐标加入存放顶点坐标的ArrayListalVertix.add(x1);alVertix.add(y1);alVertix.add(z1);alVertix.add(x3);alVertix.add(y3);alVertix.add(z3);alVertix.add(x0);alVertix.add(y0);alVertix.add(z0);alVertix.add(x1);alVertix.add(y1);alVertix.add(z1);alVertix.add(x2);alVertix.add(y2);alVertix.add(z2);alVertix.add(x3);alVertix.add(y3);alVertix.add(z3);}}vCount = alVertix.size() / 3;// 顶点的数量为坐标值数量的1/3,因为一个顶点有3个坐标// 将alVertix中的坐标值转存到一个float数组中float vertices[] = new float[vCount * 3];for (int i = 0; i < alVertix.size(); i++) {vertices[i] = alVertix.get(i);}// 创建顶点坐标数据缓冲// vertices.length*4是因为一个整数四个字节ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);vbb.order(ByteOrder.nativeOrder());// 设置字节顺序mVertexBuffer = vbb.asFloatBuffer();// 转换为float型缓冲mVertexBuffer.put(vertices);// 向缓冲区中放入顶点坐标数据mVertexBuffer.position(0);// 设置缓冲区起始位置// 特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer// 转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题}// 初始化着色器public void initShader(MySurfaceView mv) {// 加载顶点着色器的脚本内容mVertexShader = ShaderUtil.loadFromAssetsFile("vertex.sh",mv.getResources());// 加载片元着色器的脚本内容mFragmentShader = ShaderUtil.loadFromAssetsFile("frag.sh",mv.getResources());// 基于顶点着色器与片元着色器创建程序mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);// 获取程序中顶点位置属性引用maPositionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition");// 获取程序中总变换矩阵引用muMVPMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix");// 获取程序中球半径引用muRHandle = GLES30.glGetUniformLocation(mProgram, "uR");}public void drawSelf() {MatrixState.rotate(xAngle, 1, 0, 0);//绕X轴转动MatrixState.rotate(yAngle, 0, 1, 0);//绕Y轴转动MatrixState.rotate(zAngle, 0, 0, 1);//绕Z轴转动// 指定使用某套shader程序GLES30.glUseProgram(mProgram);// 将最终变换矩阵传入渲染管线GLES30.glUniformMatrix4fv(muMVPMatrixHandle, 1, false,MatrixState.getFinalMatrix(), 0);// 将半径尺寸传入渲染管线GLES30.glUniform1f(muRHandle, r * UNIT_SIZE);//将顶点位置数据送入渲染管线GLES30.glVertexAttribPointer(maPositionHandle, 3, GLES30.GL_FLOAT,false, 3 * 4, mVertexBuffer);//启用顶点位置数据数组GLES30.glEnableVertexAttribArray(maPositionHandle);//绘制球		GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vCount);}
}

 

2、自定义的MySurfaceView,创建自定义渲染器SceneRenderer,同时设置渲染模式为主动渲染:RENDERMODE_CONTINUOUSLY。

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.annotation.SuppressLint;
import android.content.Context;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;@SuppressLint("ClickableViewAccessibility")
class MySurfaceView extends GLSurfaceView 
{private final float TOUCH_SCALE_FACTOR = 180.0f/320;//角度缩放比例private SceneRenderer mRenderer;//场景渲染器	   Ball ball;//球private float mPreviousY;//上次的触控位置Y坐标private float mPreviousX;//上次的触控位置X坐标public MySurfaceView(Context context) {super(context);this.setEGLContextClientVersion(3); //设置使用OPENGL ES3.0mRenderer = new SceneRenderer();	//创建场景渲染器setRenderer(mRenderer);				//设置渲染器		        setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//设置渲染模式为主动渲染   }private class SceneRenderer implements GLSurfaceView.Renderer {public void onDrawFrame(GL10 gl) {//清除深度缓冲与颜色缓冲GLES30.glClear( GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_COLOR_BUFFER_BIT);//保护现场MatrixState.pushMatrix();//绘制球MatrixState.pushMatrix();ball.drawSelf();MatrixState.popMatrix();//恢复现场MatrixState.popMatrix();}public void onSurfaceChanged(GL10 gl, int width, int height) {//设置视窗大小及位置GLES30.glViewport(0, 0, width, height);//计算GLSurfaceView的宽高比Constant.ratio = (float) width / height;// 调用此方法计算产生透视投影矩阵MatrixState.setProjectFrustum(-Constant.ratio, Constant.ratio, -1, 1, 20, 100);// 调用此方法产生摄像机9参数位置矩阵MatrixState.setCamera(0, 0, 30, 0f, 0f, 0f, 0f, 1.0f, 0.0f);//初始化变换矩阵MatrixState.setInitStack();}public void onSurfaceCreated(GL10 gl, EGLConfig config) {//设置屏幕背景色RGBAGLES30.glClearColor(0f,0f,0f, 1.0f);  //创建球对象ball=new Ball(MySurfaceView.this);//打开深度检测GLES30.glEnable(GLES30.GL_DEPTH_TEST);//打开背面剪裁   GLES30.glEnable(GLES30.GL_CULL_FACE);}}
}

 

3、主Activity,实例化自定义的MyGLSurfaceView,并添加到setContentView。

import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;public class Sample6_1_Activity extends Activity {private MySurfaceView mGLSurfaceView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 设置为全屏requestWindowFeature(Window.FEATURE_NO_TITLE);getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);// 初始化GLSurfaceViewmGLSurfaceView = new MySurfaceView(this);// 切换到主界面setContentView(mGLSurfaceView);	}@Overrideprotected void onResume() {super.onResume();mGLSurfaceView.onResume();}@Overrideprotected void onPause() {super.onPause();mGLSurfaceView.onPause(); } 
}

 

4、shader操作工具类:加载顶点Shader与片元Shader的工具类ShaderUtil,loadShader加载制定shader的方法,createProgram创建shader程序的方法,checkGlError检查每一步操作是否有错误的方法,loadFromAssetsFile从sh脚本中加载shader内容的方法。

import android.annotation.SuppressLint;
import android.content.res.Resources;
import android.opengl.GLES30;
import android.util.Log;//加载顶点Shader与片元Shader的工具类
@SuppressLint("NewApi")
public class ShaderUtil 
{//加载制定shader的方法public static int loadShader(int shaderType, //shader的类型  GLES30.GL_VERTEX_SHADER   GLES30.GL_FRAGMENT_SHADERString source   //shader的脚本字符串) {//创建一个新shaderint shader = GLES30.glCreateShader(shaderType);//若创建成功则加载shaderif (shader != 0) {//加载shader的源代码GLES30.glShaderSource(shader, source);//编译shaderGLES30.glCompileShader(shader);//存放编译成功shader数量的数组int[] compiled = new int[1];//获取Shader的编译情况GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);if (compiled[0] == 0) {//若编译失败则显示错误日志并删除此shaderLog.e("ES30_ERROR", "Could not compile shader " + shaderType + ":");Log.e("ES30_ERROR", GLES30.glGetShaderInfoLog(shader));GLES30.glDeleteShader(shader);shader = 0;      }  }return shader;}//创建shader程序的方法public static int createProgram(String vertexSource, String fragmentSource) {//加载顶点着色器int vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexSource);if (vertexShader == 0) {return 0;}//加载片元着色器int pixelShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource);if (pixelShader == 0) {return 0;}//创建程序int program = GLES30.glCreateProgram();//若程序创建成功则向程序中加入顶点着色器与片元着色器if (program != 0) {//向程序中加入顶点着色器GLES30.glAttachShader(program, vertexShader);checkGlError("glAttachShader");//向程序中加入片元着色器GLES30.glAttachShader(program, pixelShader);checkGlError("glAttachShader");//链接程序GLES30.glLinkProgram(program);//存放链接成功program数量的数组int[] linkStatus = new int[1];//获取program的链接情况GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0);//若链接失败则报错并删除程序if (linkStatus[0] != GLES30.GL_TRUE) {Log.e("ES30_ERROR", "Could not link program: ");Log.e("ES30_ERROR", GLES30.glGetProgramInfoLog(program));GLES30.glDeleteProgram(program);program = 0;}}return program;}//检查每一步操作是否有错误的方法 public static void checkGlError(String op) {int error;while ((error = GLES30.glGetError()) != GLES30.GL_NO_ERROR) {Log.e("ES30_ERROR", op + ": glError " + error);throw new RuntimeException(op + ": glError " + error);}}//从sh脚本中加载shader内容的方法public static String loadFromAssetsFile(String fname,Resources r){String result=null;    	try{InputStream in=r.getAssets().open(fname);int ch=0;ByteArrayOutputStream baos = new ByteArrayOutputStream();while((ch=in.read())!=-1){baos.write(ch);}      byte[] buff=baos.toByteArray();baos.close();in.close();result=new String(buff,"UTF-8"); result=result.replaceAll("\\r\\n","\n");}catch(Exception e){e.printStackTrace();}   return result;}
}

 

5、shader着色器程序

#version 300 es
uniform mat4 uMVPMatrix; 
in vec3 aPosition;  
out vec3 vPosition;
void main()     
{                   gl_Position = uMVPMatrix * vec4(aPosition,1); vPosition = aPosition;
}#version 300 es
precision mediump float;
uniform float uR;
in vec2 mcLongLat;
in vec3 vPosition;
out vec4 fragColor;
void main()                         
{vec3 color;float n = 8.0;float span = 2.0*uR/n;int i = int((vPosition.x + uR)/span);int j = int((vPosition.y + uR)/span);int k = int((vPosition.z + uR)/span);int whichColor = int(mod(float(i+j+k),2.0));if(whichColor == 1) {color = vec3(0.678,0.231,0.129);}else {color = vec3(1.0,1.0,1.0);}fragColor=vec4(color,0);
}     

 

6、渲染效果示例:

 

最后,欢迎大家一起交流学习:微信:liaosy666 ; QQ:2209115372 。

 

 

 

这篇关于OpenGL ES 3. 绘制球体 实战的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/977352

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

【WebGPU Unleashed】1.1 绘制三角形

一部2024新的WebGPU教程,作者Shi Yan。内容很好,翻译过来与大家共享,内容上会有改动,加上自己的理解。更多精彩内容尽在 dt.sim3d.cn ,关注公众号【sky的数孪技术】,技术交流、源码下载请添加微信号:digital_twin123 在 3D 渲染领域,三角形是最基本的绘制元素。在这里,我们将学习如何绘制单个三角形。接下来我们将制作一个简单的着色器来定义三角形内的像素

滚雪球学Java(87):Java事务处理:JDBC的ACID属性与实战技巧!真有两下子!

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE啦,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~ 🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!! 环境说明:Windows 10

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87

springboot实战学习(1)(开发模式与环境)

目录 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 (3)前端 二、开发模式 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 Validation:做参数校验Mybatis:做数据库的操作Redis:做缓存Junit:单元测试项目部署:springboot项目部署相关的知识 (3)前端 Vite:Vue项目的脚手架Router:路由Pina:状态管理Eleme

深度学习实战:如何利用CNN实现人脸识别考勤系统

1. 何为CNN及其在人脸识别中的应用 卷积神经网络(CNN)是深度学习中的核心技术之一,擅长处理图像数据。CNN通过卷积层提取图像的局部特征,在人脸识别领域尤其适用。CNN的多个层次可以逐步提取面部的特征,最终实现精确的身份识别。对于考勤系统而言,CNN可以自动从摄像头捕捉的视频流中检测并识别出员工的面部。 我们在该项目中采用了 RetinaFace 模型,它基于CNN的结构实现高效、精准的

项目实战系列三: 家居购项目 第四部分

购物车 🌳购物车🍆显示购物车🍆更改商品数量🍆清空购物车&&删除商品 🌳生成订单 🌳购物车 需求分析 1.会员登陆后, 可以添加家居到购物车 2.完成购物车的设计和实现 3.每添加一个家居,购物车的数量+1, 并显示 程序框架图 1.新建src/com/zzw/furns/entity/CartItem.java, CartItem-家居项模型 /***