Android OpenGLES2.0开发(四):矩阵变换和相机投影
事物的本质是事物本身所固有的、深藏于现象背后并决定或支配现象的方面。
还记得我们上一篇绘制的三角形吗,我们确实能够顺利用OpenGL ES
绘制出图形了,这是一个好的开始,但这还远远不够。我们定义的坐标是正三角形,但是绘制出来三角形却拉升了(横屏显示会压缩)。
为了方便大家,我们将OpenGL ES坐标系图再次贴出。OpenGL ES的坐标系是一个正方形,他的四个顶点分别对应GLSurfaceView
的四个顶点,这个是定死的我们无法改变,那要怎么才能让我的三角形变成等边三角形呢?既然坐标系为正方形,那么我们让GLSurfaceView也为正方形是否可行呢?
1. 方式一:设置GLSurfaceView宽高相等
注意这里有个误区就是OpenGL ES坐标顶点
对应的是GLSurfaceView
的四个顶点,而不是屏幕的四个顶点。所以好多文章说变形拉升什么的,甚至是官方文档都和手机屏幕扯上关系。我现在可以明确的告诉大家的是,这和手机的屏幕一点关系也没有。
那为什么又要说和屏幕有关系,其本质是将GLSurfaceView
的宽高使用了match_parent
导致GLSurfaceView
大小和屏幕相同而已,但是变形拉升只和GLSurfaceView
的大小有关,GLSurfaceView
如果不是一个正方形,那么画出的图形就会变形。
既然我们知道了上述缘由后,最简单的方式就有了,就是设置GLSurfaceView的宽高相等即可:
<com.android.xz.opengldemo.view.TriangleGLSurfaceViewandroid:layout_width="400dp"android:layout_height="400dp"/>
运行看看效果是否可行:
不出我们所料,果然是行得通的!!!
但是世界的运行往往不是我们人为能控制的,GLSurfaceView
的宽高往往不是正方形,他要和应用相结合,他可能是游戏全屏界面,也可能是某个显示视频的预览界面,亦或是嵌到某个犄角旮旯充当不重要的视图,这个时候我们就引入了下面的方式。
2. 方式二:修改顶点坐标数据
当GLSurfaceView
的宽高不一致的时,我们该如何是好???就比如我们现在GLSurfaceView
是全屏的。
我们来分析下,GLSurfaceView
目前全屏后,视图高被拉升了,原本三角形的top顶点
到底边的垂直距离是0.866,也就是说我们按照GLSurfaceView
拉升比缩放这个距离是不是也是可行的?
// 三角形三个点的坐标,逆时针绘制static float triangleCoords[] = { // 坐标逆时针顺序0.0f, 0.616f, 0.0f, // top-0.5f, -0.25f, 0.0f, // bottom left0.5f, -0.25f, 0.0f // bottom right};
好了开始动手干,我们在Triangle
类中surfaceChanged
方法中重新计算缩放后Y的坐标点,如下:
public void surfaceChanged(int width, int height) {// 设置OpenGL ES画布大小GLES20.glViewport(0, 0, width, height);float radio = (float) width / height;triangleCoords = new float[]{ // 坐标逆时针顺序0.0f, 0.616f * radio, 0.0f, // top-0.5f, -0.25f * radio, 0.0f, // bottom left0.5f, -0.25f * radio, 0.0f // bottom right};// 初始化形状坐标的顶点字节缓冲区ByteBuffer bb = ByteBuffer.allocateDirect(// (number of coordinate values * 4 bytes per float)triangleCoords.length * 4);// use the device hardware's native byte orderbb.order(ByteOrder.nativeOrder());// create a floating point buffer from the ByteBuffervertexBuffer = bb.asFloatBuffer();// add the coordinates to the FloatBuffervertexBuffer.put(triangleCoords);// set the buffer to read the first coordinatevertexBuffer.position(0);
}
查看效果:
其实在没有看到效果时,我已经猜到最后肯定会绘制出正三角形,当然看了效果我们也就踏实了。
OpenGL ES绘图变形,其本质无非就是要解决View的宽高比和OpenGL ES正方形坐标系的一个变换;如果View是正方形无需变换,长方形该缩放缩放。
目前我们是绘制了一个三角形,顶点只有三个,修改顶点坐标倒还不是那么复杂。如果我们要绘制更加复杂的图像,顶点有几十个上百个,我们又该如何一个一个修改顶点的缩放比呢?聪明的你可能又想到了,我用for循环遍历修改啊那样就会方便很多,那我只能说你这是小聪明,OpenGL ES为我们提供了一个大聪明的方式:矩阵变换
3. 方式三:矩阵变换
我们知道OpenGL ES的世界里是三维空间,我们要对三维空间中的点进行缩放、平移、旋转实际在数学中有一个好的方式就是用矩阵来计算。而矩阵的知识是大学线性代数中的,这个基础需要读者自己去补。
空间中点缩放变换,建议看下这篇文章:【深度好文】3D坐标系下的点的转换矩阵(平移、缩放、旋转、错切)
3.1 自定义矩阵缩放方法
接下来我们使用矩阵相乘变换点的坐标,定义缩放方法如下:
public static void scale(float[] coords, int stride, float sx, float sy, float sz) {float[] scaleM = {sx, 0, 0,0, sy, 0,0, 0, sz};for (int i = 0; i < coords.length; i += stride) {float x = coords[i];float y = coords[i + 1];float z = coords[i + 2];coords[i] = scaleM[0] * x;coords[i + 1] = scaleM[4] * y;coords[i + 2] = scaleM[8] * z;}
}
修改surfaceChanged
缩放坐标代码
public void surfaceChanged(int width, int height) {// 设置OpenGL ES画布大小GLES20.glViewport(0, 0, width, height);scale(triangleCoords, 3, 1, (float) width / height, 1);// 初始化形状坐标的顶点字节缓冲区ByteBuffer bb = ByteBuffer.allocateDirect(// (number of coordinate values * 4 bytes per float)triangleCoords.length * 4);// use the device hardware's native byte orderbb.order(ByteOrder.nativeOrder());// create a floating point buffer from the ByteBuffervertexBuffer = bb.asFloatBuffer();// add the coordinates to the FloatBuffervertexBuffer.put(triangleCoords);// set the buffer to read the first coordinatevertexBuffer.position(0);
}
运行查看效果,也可得到正三角形。
3.2 使用GLSL缩放
上面方式固然可行,但是大量顶点计算都在CPU端了,如何使用GPU程序去并行计算?
OpenGL ES也提供了矩阵相乘的方式,在三维图形学中,一般使用的是4阶矩阵。在DirectX中使用的是行向量,如[xyzw],所以与矩阵相乘时,向量在前矩阵在后。OpenGL中使用的是列向量,如[xyzx]T,所以与矩阵相乘时,矩阵在前,向量在后,我们最终通过“变换矩阵”来得到我们想要的向量
。
修改顶点着色器代码并定义变换矩阵如下:
// 顶点着色器代码
private final String vertexShaderCode =// 传入变换矩阵"uniform mat4 uMVPMatrix;" +"attribute vec4 vPosition;" +"void main() {" +// 变换矩阵与顶点坐标相乘等到新的坐标" gl_Position = uMVPMatrix * vPosition;" +"}";/*** Shader程序中矩阵属性的句柄*/
private int vPMatrixHandle;// 最终变化矩阵
private final float[] mMVPMatrix = new float[16];
记得在surfaceCreated
中获取矩阵属性句柄
public void surfaceCreated() {...// 获取绘制矩阵句柄vPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
}
在surfaceChanged
中设置缩放矩阵
public void surfaceChanged(int width, int height) {// 设置OpenGL ES画布大小GLES20.glViewport(0, 0, width, height);float radio = (float) width / height;float[] scaleMatrix = new float[]{1, 0, 0, 0,0, radio, 0, 0,0, 0, 1, 0,0, 0, 0, 1};// 将缩放矩阵拷贝到变换矩阵中System.arraycopy(scaleMatrix, 0, mMVPMatrix, 0, scaleMatrix.length);}
在draw
方法中将缩放矩阵传给OpenGL ES程序
public void draw() {...// 将缩放矩阵传递给着色器程序GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mMVPMatrix, 0);// 画三角形GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);// 禁用顶点阵列GLES20.glDisableVertexAttribArray(positionHandle);
}
运行查看效果,也可得到正三角形。
本文到现在已经讲了很多种方式可以正确绘制正三角形,但是到现在还没有到正真要讲的内容。我们发现将缩放矩阵传入着色器程序貌似已经完成了最终的目的,如果我们仅仅只是为了把三角形画正那么到这里应该就结束了。世界的运行往往出乎我们的意料,现在可能要求你缩放,但是未来可能还会平移、二维旋转、三维旋转、镜像等等操作,如果要我们去定义各种矩阵,那简直是灾难,于是下面的方式就应运而生了。
4. 相机和投影
- 投影:OpenGL 中主要有两种投影模式,分别是正交投影和透视投影
- 相机:相机视图顾名思义就相当于站在相机的角度观察某个物体,相机会看到投影到近平面的物体
4.1 投影
OpenGL提供了两种投影变换矩阵如下
透视投影
学过素描的应该都知道透视图的概念,符合人眼习惯,呈现近大远小的效果。
/*** @param m 生成的投影矩阵,float[4*4]* @param mOffset 填充时候起始的偏移量* @param left 近平面left边的x坐标* @param right 近平面right边的x坐标* @param bottom 近平面bottom边的y坐标* @param top 近平面top边的y坐标* @param near 近平面距离摄像机的距离* @param far 远平面距离摄像机的距离*/
public static void frustumM(float[] m, int mOffset,float left, float right, float bottom, float top,float near, float far) {
}
正交投影
该投影方式图像大小不会随着距离变化而变化
/*** @param m 生成的投影矩阵,float[4*4]* @param mOffset 填充时候起始的偏移量* @param left 近平面left边的x坐标* @param right 近平面right边的x坐标* @param bottom 近平面bottom边的y坐标* @param top 近平面top边的y坐标* @param near 近平面距离摄像机的距离* @param far 远平面距离摄像机的距离*/
public static void orthoM(float[] m, int mOffset,float left, float right, float bottom, float top,float near, float far) {
}
- 不管是正交投影还是透视投影,最终都是将视景体内的物体投影在近平面上,这也是 3D 坐标转换到 2D 坐标的关键一步。
- 而近平面上的坐标接着也会转换成归一化设备坐标,再映射到屏幕视口上。
- 为了解决之前的图像拉伸问题,就是要保证近平面的宽高比和视口的宽高比一致,而且是以较短的那一边作为 1 的标准,让图像保持居中。
4.2 相机
相机位置设置
/**** @param rm 生成的摄像机矩阵,float[16]* @param rmOffset 填充时候的起始偏移量* @param eyeX 摄像机x坐标* @param eyeY 摄像机y坐标* @param eyeZ 摄像机z坐标* @param centerX 观察目标点的x坐标* @param centerY 观察目标点的y坐标* @param centerZ 观察目标点的z坐标* @param upX 摄像机up向量在x上的分量* @param upY 摄像机up向量在y上的分量* @param upZ 摄像机up向量在z上的分量*/
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) {
}
- eyeX,eyeY,eyeZ:摄像机坐标。
- centerX,centerY,centerZ:观察点坐标,和摄像机坐标一起决定了摄像机的观察方向,即向量(centerX - eyeX, centerY - eyeY, centerZ - eyeZ)。观察方向不朝向视景体是无法看到的。
- upX,upY,upZ:摄像机up向量。相对于人眼观察物体中,人头的朝向,头的朝向影响了最后的成像。同样以图来说明:
当up向量为Y的正方向时,正如我们头顶对着天花板,所以观察到的物体是正的,投影在近平面的样子就是正的,如右图
当up向量为X正方向时,正如我们向右90度歪着脑袋去看这个三角形,看到的三角形就会是向左旋转了90度的三角形
再比如up向量如果为Z轴正方向,就相当于仰着头去看这个三角形,但是因为我们的up向量和观察方向平行了,所以我们什么也看不到,就比如仰着头去看你身前的物体时,你什么也看不到。
所以在设置up向量时,一般总是设置为(0,1,0),这是大多数观察时头朝上的方向。注意:up向量的大小无关紧要,有意义的只有方向。
4.3 near、far的取值范围规定
- 正交投影时,摄像机可位于视景体中间,此时near < 0,far > 0,近平面位于视点后面(Z轴正方向),远平面位于视点前面(Z轴负方向)
- 正交投影时,视景体也可位于视点后面(Z轴正方向),此时near < 0, far < 0
- 正交投影时,far 和 near没有规定的大小关系,既可以far > near 也可以 far < near,只要物体在视景体内都可以被观察到。
- 透视投影时,far>near>0;我们不考虑其他情况,我们默认就在Z轴上看物体
当centerZ - eyeZ>0时:近平面nearZ坐标=eyeZ+near,远平面farZ坐标=eyeZ+far;
当centerZ - eyeZ<0时:近平面nearZ坐标=eyeZ-near,远平面farZ坐标=eyeZ-far;
我们要保证物体Z坐标在nearZ和farZ之间就能看到,也就是物体在视景里就能看到。
4.4 构造模型矩阵
根据上面的理论知识,我们不用再手动构造一个缩放矩阵了,我们定义如下三个矩阵并进行变换
public class Triangle {...// vPMatrix是“模型视图投影矩阵”的缩写// 最终变化矩阵private final float[] mMVPMatrix = new float[16];// 投影矩阵private final float[] mProjectionMatrix = new float[16];// 相机矩阵private final float[] mViewMatrix = new float[16];public void surfaceChanged(int width, int height) {// 设置OpenGL ES画布大小GLES20.glViewport(0, 0, width, height);float ratio;if (width > height) {ratio = (float) width / height;// 横屏使用// 透视投影,特点:物体离视点越远,呈现出来的越小。离视点越近,呈现出来的越大// 该投影矩阵应用于对象坐标Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);} else {ratio = (float) height / width;// 竖屏使用// 透视投影,特点:物体离视点越远,呈现出来的越小。离视点越近,呈现出来的越大// 该投影矩阵应用于对象坐标Matrix.frustumM(mProjectionMatrix, 0, -1, 1, -ratio, ratio, 3, 7);}Matrix.setLookAtM(mViewMatrix, 0,0, 0, 3f,0f, 0f, 0f,0f, 1.0f, 0.0f);// Calculate the projection and view transformationMatrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);}
}
说明:我们使用frustumM
做透视矩阵时,我们要把近平面宽高和屏幕宽高比例对应,我们以较短的一边作为1,按比例拉升即可。而far>near>0遵循此原则即可。
设置相机setLookAtM
参数,我们只需要注意eyeZ的取值,根据上面的说明正确取值。上面方法取值为3那么近平面的nearZ坐标=eyeZ-near=0和物体z坐标重合,看到的物体比例正好不会变大也不会缩小。
最后我们用multiplyMM
方法将投影矩阵和相机矩阵转换为最终的矩阵,然后传给着色器程序即可。
运行程序也可得到正三角形。
相机和投影概念实际上是为3D模型准备的,现在我们把他用在2D图形上,着实有中降维打击,大炮打苍蝇的感觉。但是我们不得不了解这个强大的工具,为将来遇到的3D场景变换做准备。
最后
我们都知道独孤九剑,剑法的最高境界是无招。上述介绍的几种方式都可谓是剑招,当我们了解了事物运行的本质后,这几种方式皆可为我所用,在适当的场景下选择合适的方式,甚至可以创造招式。
《黑客帝国》中尼奥复活后了解了虚拟世界的本质,整个世界的运行不过就是一串串数字。原来难以翻越的高山,现在也只是眼下的风景。还记的我们第一篇章吗Android OpenGLES2.0开发(一):艰难的开始,现在的我觉得脚下有路、心中有光,也期待未来会更美好。
参考:
- https://juejin.cn/post/6844903614838751240
- https://cloud.tencent.com/developer/article/1015587
- https://www.nxrte.com/jishu/13722.html
相关文章:

Android OpenGLES2.0开发(四):矩阵变换和相机投影
事物的本质是事物本身所固有的、深藏于现象背后并决定或支配现象的方面。 还记得我们上一篇绘制的三角形吗,我们确实能够顺利用OpenGL ES绘制出图形了,这是一个好的开始,但这还远远不够。我们定义的坐标是正三角形,但是绘制出…...

快递查询软件:实现单号识别与批量物流查询的高效工具
随着网络购物的普及,快递物流行业迎来了前所未有的发展机遇,同时也面临着巨大的挑战。跟踪物流信息成为一个难题,因此,快递查询软件的核心功能之一便是单号识别。传统的快递单号输入方式繁琐且易出错在此背景下,快递查…...

nodejs与npm版本对应表
Node.js — Node.js 版本 (nodejs.org)...

Spring Boot 项目中如何使用异步任务
前置知识: 同步任务: 同步任务是在单线程中按顺序执行,每次只有一个任务在执行,不会引发线程安全和数据一致性等并发问题 同步任务需要等待任务执行完成后才能执行下一个任务,无法同时处理多个任务,响应慢…...

Scrum实战中遇到的问题与解决方法
在当今快速变化的技术环境中,IT企业面临着持续的市场压力和竞争,传统的瀑布式开发模式已经难以满足现代企业的需要。瀑布模型过于僵化,缺乏灵活性,导致项目经常延期,成本增加,最终可能无法达到预期效果。为…...

全面介绍 Windows 录屏工具:开启录制新篇章
高质量的录屏工具是我们录屏的得力助手。但是日常因为侧重点的不同,比如有的喜欢录制游戏画面、有的需要录制教学视频、演示操作也需要录屏工具。这次我们就来探讨一下windows录屏工具有哪些吧。 1.福晰录屏大师 链接:www.foxitsoftware.cn/REC/ 从这…...

Maven 和 NetBeans:集成与使用
Maven 和 NetBeans:集成与使用 Maven 和 NetBeans 是两款强大的工具,常用于Java开发。Maven是一个项目管理工具,它能够帮助管理项目的构建、报告和文档。NetBeans是一个集成开发环境(IDE),它为Java开发提供了丰富的功能和友好的用户界面。将Maven集成到NetBeans中,可以…...

【系统架构设计师】目录提纲
一、绪论(TODO) 二、计算机与网络基础知识(TODO) 三、信息系统基础知识(TODO) 四、系统开发基础知识(TODO) 五、软件架构设计(TODO) 六、UML建模与架构文…...

【微服务】—SpringBoot入门
⭐⭐⭐⭐⭐⭐ Github主页👉https://github.com/A-BigTree 笔记仓库👉https://github.com/A-BigTree/tree-learning-notes 个人主页👉https://www.abigtree.top ⭐⭐⭐⭐⭐⭐ 文章目录 1 SpringBoot快速入门1.1 SpringBoot简介1.1.1 简介1.1.2…...

Linux: debug: perf: report: --sort
文章目录 简介实例简介 接上回:https://mzhan017.blog.csdn.net/article/details/142689870。 这里介绍perf的这个参数,还是非常的有用,尤其是分析对整个系统做perf record的数据,而不是单个进程做perf record。-s, --sort= : Sort histogram entries by given key(s) - …...

like 模糊查询的底层算法
like 模糊查询的底层算法 全文搜索算法、模糊查询、n-gram分隔算法功能介绍 百度搜索,文心一言给出的结果: SQL模糊查询底层通常使用全文搜索算法,如LIKE操作符和全文索引通常使用的n-gram分割算法。 n-gram是一种将文本分割成固定大小的词…...

【Linux实践】实验九:Shell流程控制语句
文章目录 实验九:Shell流程控制语句实验目的:实验内容:操作步骤:1. 复制*.c文件并排序2. 计算1-10的平方 实验九:Shell流程控制语句 实验目的: 掌握条件判断语句,如if语句、case语句。掌握循环…...

YOLOv8实战TT100K中国交通标志检测【数据集+YOLOv8模型+源码+PyQt5界面】
YOLOv8实战TT100k交通标志识别 文章目录 研究背景资源获取1.前言1.1 YOLO 系列:中国交通标志检测领域的璀璨明星1.2 Transformer与注意力机制:为中国交通标志检测注入新活力1.3 中国交通标志检测技术:迎接挑战,砥砺前行1.4 YOLOv8…...

SQLite3
文章目录 SQLite3 C/CAPI介绍SQLite3 C/C API 使⽤ SQLite3 C/CAPI介绍 C/C API是SQLite3数据库的⼀个客⼾端,提供⼀种⽤C/C操作数据库的⽅法。 SQLite3 C/C API 使⽤ 下⾯我们将这⼏个接⼝封装成⼀个类,快速上⼿这⼏个接口 创建/打开数据库文件针对打开…...

我的创作纪念日一年
目录 机缘 收获 日常 成就 憧憬 机缘 我之所以开始写CSDN博客,源于一段特殊的时光。去年此时,我独自待在实验室,周围的世界仿佛与我无关。没有旅游,没有与朋友的欢聚,情感的挫折和学业的压力如潮水般袭来。在这样的…...

Docker基本操作命令(一)
Docker 是一个开源的应用容器引擎,允许开发者打包应用以及其依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。主要功能是为开发者提供一个简单…...

PGMP-02项目集管理绩效域
目录 1.概要 2.defintions定义 3.Program Management Performance Domain interactions 交互 4. Organizational Strategy, Portfolio Management, and Program Management Linkage 5. Portfolio and Program Distinctions 区别 6. Program and Project Distinctions区别 …...

CAN(Controller Area Network)总线的仲裁机制
CAN(Controller Area Network)总线的仲裁机制是其核心特性之一,它确保了在多节点环境中数据能够高效、公正地传输。以下是对CAN仲裁机制的详细解释和介绍: 一、仲裁机制概述 在CAN总线网络中,各个节点地位平等&#…...

计算机毕业设计 | SpringBoot 房屋租赁网 租房买房卖房平台(附源码)
1,绪论 1.1 背景调研 在房地产行业持续火热的当今环境下,房地产行业和互联网行业协同发展,互相促进融合已经成为一种趋势和潮流。本项目实现了在线房产平台的功能,多种技术的灵活运用使得项目具备很好的用户体验感。 这个项目的…...

OJ在线评测系统 微服务高级 Gateway网关接口路由和聚合文档 引入knife4j库集中查看管理并且调试网关项目
Gateway微服务网关接口路由 各个服务之间已经能相互调用了 为什么需要网关 因为我们的不同服务是放在不同的端口上面的 如果前端调用服务 需要不同的端口 8101 8102 8103 8104 我们最好提供一个唯一的 给前端去调用的路径 我们学习技术的时候必须要去思考 1.为什么要用&am…...

腾讯云上传pushdocker镜像到镜像仓库
文章目录 腾讯云上传docker镜像 腾讯云上传docker镜像 >docker login ccr.ccs.tencentyun.com --usernameXXXXXX用户名>sudo docker tag mynginx:1.0 ccr.ccs.tencentyun.com/crfkitty/mynginx:1.0>docker push ccr.ccs.tencentyun.com/crfkitty/mynginx:1.0 The pu…...

sqli-labs靶场第二关less-2
sqli-labs靶场第二关less-2 本次测试在虚拟机搭建靶场,从主机测试 1、输入?id1和?id2发现有不同的页面回显 2、判断注入类型 http://192.168.128.3/sq/Less-2/?id1’ 从回显判断多一个‘ ,预测可能是数字型注入 输入 http://192.168.128.3/sq/Less…...

Ruby XML, XSLT 和 XPath 教程
Ruby XML, XSLT 和 XPath 教程 1. 引言 Ruby 是一种动态、开放源代码的编程语言,广泛用于网页开发、数据分析和各种自动化任务。在处理 XML(可扩展标记语言)时,Ruby 提供了强大的库和工具,使得解析、转换和提取 XML 数据变得简单高效。本教程将介绍如何使用 Ruby 来处理…...

attain和obtain区别
一、语法问题解答 attain:主要用作及物动词,表示“达到,得到,实现”,通常用于指经过努力达到某种目标、水平或状态,其宾语多为抽象名词,如目标、理想、成就等。它强调通过不懈努力实现某种…...

◇【code】PPO: Proximal Policy Optimization
整理的代码库:https://github.com/Gaoshu-root/Code-related-courses/tree/main/RL2024/PPO OpenAI 文档 —— PPO-Clip OpenAI 文档 界面链接 PPO: on-policy 算法、适用于 离散 或 连续动作空间。可能局部最优 PPO 的动机与 TRPO 一样:…...

Spring Boot 进阶-浅析SpringBoot中如何完成数据校验
在实际开发中,我们经常会遇到接口数据校验的问题。例如在用户输入手机号、或者是身份证号的时候,我们就需要校验手机号或者身份证号是否输入正确。当然这样的校验在前端页面输入的时候就可以完成。 但是对于直接调用接口的情况则不能通过调用方来进行判断,这就需要我们在Spr…...

◇【论文_20181020 v6】广义优势估计器 (generalized advantage estimator, GAE)
https://arxiv.org/abs/1506.02438 ICLR 2016 加州伯克利 电子工程与计算机科学系 High-Dimensional Continuous Control Using Generalized Advantage Estimation 文章目录 摘要1 引言2 预备知识3 优势函数估计4 解释为 奖励设计reward shaping5 价值函数估计6 实验6.1 策略优…...

JAVA后端项目须知
Eureka服务发现框架 Eureka是一个服务发现框架,由Netflix开发,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。12 Eureka是Spring Cloud Netflix的一个子模块,也是核心模块之一…...

Java设计模式——适配器模式
目录 模式动机 模式定义 模式结构 代码分析 模式分析 实例 优点 缺点 适用环境 模式应用 模式扩展 总结 模式动机 在软件开发过程中,经常会遇到需要使用已有类的功能,但这些类的接口不符合当前系统的需求,或者需要将不同的类的接…...

docker compose入门6—如何挂载卷
在 Docker Compose 中,可以通过 volumes 字段将宿主机的文件或目录挂载到容器中。这样可以实现数据持久化、共享数据或配置等。以下是一些常见的挂载方式和示例。 1. 挂载单个文件 如果你想将宿主机上的一个特定文件挂载到容器中,可以使用以下格式&…...