Android音视频开发-OpenGL ES正交投影实现方法

本文实例为大家分享了OpenGL ES正交投影展示的具体代码,供大家参考,具体内容如下

绘制正方形

在最开始绘制的六边形里面好像看起来挺容易的,也没有出现什么问题,接下来不妨忘记前面绘制六边形的代码,让我们按照自己的理解来绘制一个简单的正方形。

按照我的理解,要想在屏幕中间显示一个正方形,效果如下图所示

image.png

应该创建的数据如下图所示

image.png

即传给渲染管线的顶点数据如下图:

float[] vertexArray = new float[] {
   (float) -0.5, (float) -0.5, 0,
   (float) 0.5, (float) -0.5, 0,
   (float) -0.5, (float) 0.5, 0,
   (float) 0.5, (float) 0.5, 0
  };

于是代码大概是这样子的,这里省略掉与主题无关的代码,颜色用纯色填充,因此在片元着色器中指定颜色,也省略掉一系列矩阵变换。顶点着色器中直接将顶点传给渲染管线,片元着色器中给片元设置固定颜色红色。

Rectangle.java

public class Rectangle {
 private FloatBuffer mVertexBuffer;
 private int mProgram;
 private int mPositionHandle;

 public Rectangle(float r) {
  initVetexData(r);
 }

 public void initVetexData(float r) {
  // 初始化顶点坐标
  float[] vertexArray = new float[] {
   (float) -0.5, (float) -0.5, 0,
   (float) 0.5, (float) -0.5, 0,
   (float) -0.5, (float) 0.5, 0,
   (float) 0.5, (float) 0.5, 0
  };

  ByteBuffer buffer = ByteBuffer.allocateDirect(vertexArray.length * 4);
  buffer.order(ByteOrder.nativeOrder());
  mVertexBuffer = buffer.asFloatBuffer();
  mVertexBuffer.put(vertexArray);
  mVertexBuffer.position(0);

  int vertexShader = loaderShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
  int fragmentShader = loaderShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

  mProgram = GLES20.glCreateProgram();
  GLES20.glAttachShader(mProgram, vertexShader);
  GLES20.glAttachShader(mProgram, fragmentShader);
  GLES20.glLinkProgram(mProgram);

  mPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
 }

 public void draw() {
  GLES20.glUseProgram(mProgram);
  // 将顶点数据传递到管线,顶点着色器
  GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, mVertexBuffer);
  GLES20.glEnableVertexAttribArray(mPositionHandle);
  // 绘制图元
  GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
 }

 private int loaderShader(int type, String shaderCode) {
  int shader = GLES20.glCreateShader(type);
  GLES20.glShaderSource(shader, shaderCode);
  GLES20.glCompileShader(shader);
  return shader;
 }

 private String vertexShaderCode = "attribute vec3 aPosition;"
   + "void main(){"
   + "gl_Position = vec4(aPosition,1);"
   + "}";

 private String fragmentShaderCode = "precision mediump float;"
   + "void main(){"
   + "gl_FragColor = vec4(1,0,0,0);"
   + "}";

}

RectangleView.java

public class RectangleView extends GLSurfaceView{

 public RectangleView(Context context) {
  super(context);
  setEGLContextClientVersion(2);
  setRenderer(new MyRender());
 }

 class MyRender implements GLSurfaceView.Renderer {
  private Rectangle rectangle;

  @Override
  public void onSurfaceCreated(GL10 gl, EGLConfig config) {
   GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1);
   rectangle = new Rectangle(0.5f);
   GLES20.glEnable(GLES20.GL_DEPTH_TEST);
  }

  @Override
  public void onSurfaceChanged(GL10 gl, int width, int height) {
   GLES20.glViewport(0, 0, width, height);
  }

  @Override
  public void onDrawFrame(GL10 gl) {
   GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
   rectangle.draw();
  }
 }

}

然后出来的效果是这样子的,实际上屏幕上的坐标并不是这样子的,后面可以知道上面画的这个样子其实只是一个归一化的设备坐标。归一化设备坐标可以通过公式映射到实际的手机屏幕,后面会学到。

image.png

咦,实际效果好像和想象中的不太一样呀。我的本意是显示一个正方形,但实际上现实的却是一个矩形了,y轴上被拉伸了,并且横屏状态下也是类似的情况。但比较巧的是,如果以屏幕中心做一个坐标轴,就会发现,这个矩形的四个顶点在这个坐标轴x、y范围为[-1,1]的中间。

实际上,要显示的所有物体映射到手机屏幕上,都是要映射到x、y、z轴上的[-1,1]范围内,这个范围内的坐标称为归一化设备坐标,独立于屏幕的实际尺寸和形状。

因此按照这样的规定,我们要创建一个正方形就非常困难了,因为要创建正方形就必须考虑手机的宽高比,传入数据的时候就比较复杂了:不能仅仅站在要绘制物体的自身角度来看了。也就是说,上面的例子中要绘制一个正方形,传入的顶点数据的y坐标要按照比例进行一点转换,比如对16:9的屏幕,将上面传入的顶点数据的y坐标都乘以9/16即可。但同时会发现当处于横屏时,又要处理传入的x坐标的值,显然这不是一个好的方案。

引入投影

实际上,对于一个物体来说它有它自身的坐标,这个空间称为物体空间,也就是设计物体的时候采用的一个坐标空间,物体的几何中心在坐标原点上,归一化后坐标范围在[-1,1]之间,x和y轴分度是一致的。

将在这个空间的物体直接往手机屏幕的归一化坐标绘制时,由于屏幕的宽高比的问题,就会出现和预料结果不一样。所以只需要对物体空间的坐标做一个映射即可。

正交投影就是为了解决这个问题的,

public static void orthoM(float[] m, int mOffset,
  float left, float right, float bottom, float top,
  float near, float far)

正交投影背后的数学

orthoM函数产生的矩阵会把所有的左右之间、上下之间,远近之间的点映射到归一化设备坐标中。

各参数的含义如图所示

image.png

正交投影是一种平行投影,投影线是平行的,其视景体是一个长方体,坐标位于视景体中的物体才有效,视景体里面的物体投影到近平面上的部分最终会显示到屏幕的视口中,关于视口后面会降到。

会产生下面的矩阵,z轴的负值会反转z坐标,这是因为归一化设备坐标是左手系统,而OpenGL ES中的坐标系统都是右手系统,这里还涉及到顶点坐标的w分量,目前暂时用不到。

image.png

利用矩阵的就可以将物体空间[-1,1]之间的坐标映射到屏幕归一化设备坐标的[-1,1]之间。归一化屏幕坐标是右手坐标系统,原点在屏幕正中心,向右为x轴正方向,向上为y轴正方向,z轴垂直屏幕向外。以竖屏为例,比如设置left=-1,right=1,bottom=-hight/width,top=hight/width,比如我的手机分辨率为1920*1080 =1.8 对上面的正方形点(0.5,0.5)坐标而言经过变化就成了(0.5,0.3)

image.png

在屏幕的归一化设备坐标中来看就是一个正方形了,因为y轴范围显然比x轴大,0.3对应的实际长度和x轴的0.5长度是一样的。

上面的代码需要做如下修改,在onSurfaceChanged里面增加如下代码

@Override
  public void onSurfaceChanged(GL10 gl, int width, int height) {
   GLES20.glViewport(0, 0, width, height);
   // 根据屏幕方向设置投影矩阵
   float ratio= width   height ? (float)width / height : (float)height / width;
   if (width   height) {
    // 横屏 
    Matrix.orthoM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 0, 5);
   } else {
    Matrix.orthoM(mProjectionMatrix, 0, -1, 1, -ratio, ratio, 0, 5);
   }
  }

接着在顶点着色器中对顶点乘以投影矩阵

private String vertexShaderCode = "uniform mat4 uProjectionMatrix;" // 增加这一行
   + "attribute vec3 aPosition;"
   + "void main(){"
   + "gl_Position = uProjectionMatrix * vec4(aPosition,1);" // 不是直接赋值而是乘以投影矩阵
   + "}";

最后增加获取着色器中uProjectionMatrix以及传入值的代码部分即可。最终的效果不论横屏还是竖屏,显示的都是我们期望的正方形。

摄像机设置

需要补充的是,上面的参数near、far的含义指的是和视点的距离,视点貌似到目前还未接触到,它指的是摄像机的位置,和实际生活中用相机看物体一样,从不同的角度和位置拍摄同一个物体获得的照片肯定是不一样的,摄像机位置用setLookAtM函数指定。

 public static void setLookAtM(float[] rm, // 生成的摄像机矩阵
        int rmOffset,
   float eyeX, float eyeY, float eyeZ, // 摄像机的位置
   float centerX, float centerY, float centerZ, // 观察目标点的位置
               // 摄像机位置和观察目标点的位置确定了观察方向
   float upX, float upY,float upZ // up向量在x、y、z轴上的分量,我觉得一般应该是和观察方向垂直的
        )

前面提到的确定的视景体就和上面函数指定的摄像机位置和观察方向有关。摄像机默认位置在(0,0,0)处,在上面的设置下,如果将改正方形沿z轴正方向平移1个单位,屏幕上就显示不了,因为已经跑到了设置的视景体外面了。

关于摄像机的参数和投影near和far参数的设置需要注意,肯定不是胡乱设置的!摄像机的位置、方向和投影矩阵定义的视景体最终确定了视景体的位置,如果设置不当就会导致物体没有显示在屏幕上,因为物体的坐标可能位于视景体外面。

视口

前面说过在视景体中的物体最终会投影到近平面上,最终显示到视口上,正如前面在onSurfaceChanged设置的那样。

public static native void glViewport(
  int x,
  int y,
  int width,
  int height
 );

视口中各参数的含义

image.png

视口用的屏幕坐标系原点并不在屏幕左上角而是在左下角,x轴向右,y轴向上。其实还不是很准确,准确的说,视口的坐标原点位于该View的左下角,因为GLSurfaceView并不总是占据整个屏幕的。

以上就是本文的全部内容,希望对大家的学习有所帮助。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

浏览器页面操作——实时监控网页变化,读取网页内容

浏览器页面操作功能介绍 浏览器页面操作是集简云的一款免费内置应用,它可以定时监控网页变化,精准捕捉所需信息。一键设置指定网页与元素,全自动监测并即时推送通知,助您在第一时间了解网页最新情况,让您更高效便捷地…

( “树” 之 Trie) 208. 实现 Trie (前缀树) ——【Leetcode每日一题】

知识点回顾 : Trie,又称前缀树或字典树,用于判断字符串是否存在或者是否具有某种字符串前缀。 ❓208. 实现 Trie (前缀树) 难度:中等 Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构&#xff…

冲实习 or 全力准备秋招?

作者:阿秀 校招八股文学习网站:https://interviewguide.cn 这是阿秀的第「261」篇原创 小伙伴们大家好,我是阿秀。 欢迎今年参加秋招的小伙伴加入阿秀的学习圈,目前已经超过 2300 小伙伴加入!去年认真准备和走下来的基…

聚焦能源 | 赛宁网安亮相2023年中国能源网络安全大会

​​4月21日,2023年中国能源网络安全大会(以下简称“大会”)在江苏南京成功落幕!为贯彻国家网络强国战略,加强能源网络安全技术创新、成果应用、人才培养与技术交流,大会推出主旨论坛、案例交流、展览展示等…

sql优化慢查询

1.慢查询设置 慢查询设置(临时) -- 查看是否开启了慢查询日志 show variables like slow%;-- 开启慢查询日志 set global slow_query_log on;-- 更改日志路径 set global slow_query_log_file /data/mydata/app1-slow.log;-- 查看慢查询时间临界值&…

大厂齐出海:字节忙种草,网易爱社交

配图来自Canva可画 随着国内移动互联网红利逐渐触顶,互联网市场日趋饱和,国内各互联网企业之间的竞争便愈发激烈起来。在此背景下,广阔的海外市场就成为了腾讯、阿里、字节、京东、拼多多、百度、网易、快手、B站等互联网公司关注和争夺的重…

第四章(1):词向量定义与意义

第四章(1):词向量定义与意义 目录 第四章(1):词向量定义与意义前言1. 词的表示1.1 离散表示1.1.1 One-Hot独热编码1.1.2 ngram特征表示 1.2 分布式表示 2. 意义 前言 在自然语言处理的领域中,每…

成功上岸北大!总分418分,数学150分,经验贴+方法论

Datawhale干货 作者:葛云阳,杭州电子科技大学,Datawhale成员 前 言 大家好,我是北海。2023年以总分418分的成绩上岸北京大学信息工程学院计算机应用技术专业,其中初试第三,复试第五,总成绩第三…

zookeepr 简介

简介: zookeeper是为分布式应用提供协调服务的高性能组件。zookeeper通过简单的接口暴露了一些公共服务(命名、配置管理、同步和分组服务), 因此你不需要从头开始写这些服务。你可以现成得使用zookeeper来实现共识、组管理、领导者选举和存在协议。你可以根据自己的…

iptables和firewalld防火墙

安全技术和防火墙概述 安全技术 入侵检测系统(Intrusion Detection Systems):特点是不阻断任何网络访问,量化、定位来自内外网络的威胁情况,主要以提供报警和事后监督为主,提供有针对性的指导措施和安全决…

Java核心技术 卷1-总结-16

Java核心技术 卷1-总结-16 线程属性线程优先级守护线程未捕获异常处理器 同步竞争条件的一个例子竞争条件详解锁对象 线程属性 线程的各种属性包括:线程优先级、守护线程、线程组以及处理未捕获异常的处理器。 线程优先级 在Java程序设计语言中,每一个…

OpenGL入门教程之 纹理

引言 我们已经了解到,我们可以为每个顶点添加颜色来增加图形的细节,从而创建出有趣的图像。但是,如果想让图形看起来更真实,我们就必须有足够多的顶点,从而指定足够多的颜色。这将会产生很多额外开销,因为每…

ChatGPT国内可用版-国内chatGPT哪个软件好用

国内chatGPT哪个软件最好用 国内对接ChatGPT软件,让智能的对话变得更加简单便捷!ChatGPT是由OpenAI公司开发的最新一代自然语言处理技术,为聊天机器人赋予了更加真实、流畅、智能的语言表达能力。 我们是国内一家专注于人工智能和自然语言处…

旧版VS安装 Visual Studio 2019/2017/2015官方安装教程

安装VisualStudio找不到官方版本?只能找到第三方?害怕中毒? 不要急,本文例举了VS 2019 2017 2015的官方位置,不用但心装成第三方Visual Studio 百度搜索 Visual Studio 2017,只有第三方的包,而…

大孔树脂型号,A-722,ADS500,ADS600,ADS750,ADS800

一、产品介绍 基于吸附功能的聚苯乙烯特种树脂 Tulsimer ADS-600 是一款没有离子官能基的,由交联聚苯乙烯合成的功能强大的吸附型树脂。 Tulsimer ADS-600 主要应用于水溶液中吸附酚及其化合物,氯代烃等含氯物质,表面活性剂&#xff0…

Three——二、加强对三维空间的认识

Three——二、加强对三维空间的认识 接上个例子我们接着往下看 辅助观察坐标系 THREE.AxesHelper()的参数表示坐标系坐标轴线段尺寸大小,你可以根据需要改变尺寸。 使用方法: // AxesHelper:辅助观察的坐标系 const axesHelper new THRE…

java的社区养老服务系统 ssm空巢老人

创新点: 1、根据时间、类型统计用户下单记录,形成可视化图形(饼状图) 2、根据用户爱好推荐项目 包含模块:关于我们、联系我们、外链信息、资讯类型、服务资讯、服务类型、服务项目、案例类型、服务案例、讨论类型、讨论…

【数据库】— 2NF、3NF、BCNF、最小函数依赖集例题

判断范式级别 设有关系模式W(C,P,S,G,T,R),其中各属性的含义是:C课程,P教师,S学生,G成绩,T时间,R教室,根据定义有如下数据依赖集 D{ C→P,(S,C)→G,(T,R)→C&…

2023.04.23 学习周报

文章目录 摘要文献阅读1.题目2.摘要3.介绍4.模型4.1 研究区域4.2 自相关分析4.3 LSTM 5.实验与讨论5.1 高架道路不同位置空气污染物的变化5.2 高架道路不同位置空气污染物的相关性5.3 高架道路不同位置空气污染物预测 6.结论7.展望 度规张量1.曲率2.度量张量3.代码实现4.平行四…

【go】三色标记-垃圾回收机制

垃圾回收原因 : 垃圾回收是一种内存管理技术,它的主要目的是自动管理程序中的内存分配和释放,以减少内存泄漏和野指针等问题 赋值器与回收器: 赋值器(Mutator)是指程序中的执行部分,负责创建…