OpenGL ES 基本基本使用、绘制基本2D图形

OpenGL ES 绘制基础图形

OpenGL ES基本概念

OpenGL ES (Embedded-System) 是专为嵌入式设备(如手机、平板、VR 设备)设计的图形 API,是 OpenGL 的轻量级版本。

|下面是一个Android使用 OpenGL ES的基本框架

  • MainActivity 设置一个 GLSurfaceView 直接显示

class MainActivity : ComponentActivity() {private lateinit var glSurfaceView: GLSurfaceViewoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)glSurfaceView = GLSurfaceView(this)// 设置渲染器glSurfaceView.setRenderer(GLRender())setContentView(glSurfaceView)}override fun onResume() {super.onResume()glSurfaceView.onResume()}override fun onPause() {super.onPause()glSurfaceView.onPause()}
}
  • GLRender 着色器代码已经其内部函数含义,由于没有绘制样式,运行后显示黑色屏幕,因为onSurfaceCreated时将颜色重制为黑色。

class GLRender : Renderer {/*** 应用程序窗口创建时调用的函数* 一般处理一些全局的设置*/override fun onSurfaceCreated(gl: GL10, config: EGLConfig?) {// 设置需要对透视进行修正gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST)// 设置清理屏幕颜色 RGBA 取值范围为 0-1之间的float数 0 0 0 就是黑色gl.glClearColor(0F, 0F, 0F, 1F)// 启动深度缓存 如果不启动深度缓存效果,后绘制的东西可能会覆盖先绘制的东西。// 启动深度缓存后,按照实际Z值来处理远近效果,更符合3D效果。gl.glEnable(GL10.GL_DEPTH_TEST)}/*** 应用程序窗口改变时调用的函数*/override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {val ratio: Float = width / height.toFloat()// 设置GL场景的大小gl.glViewport(0, 0, width, height)// 设置投影矩阵为投影矩阵// 定义视锥体(View Frustum)和投影方式(透视或正交),决定物体如何从 3D 空间投影到 2D 屏幕上。// 其他矩阵类型还有模型矩阵,纹理矩阵等gl.glMatrixMode(GL10.GL_PROJECTION)// 重制投影矩阵// 作用是将当前矩阵重置为单位矩阵(Identity Matrix)。// 若不调用该函数,矩阵会保留之前的变换状态,可能导致后续变换叠加在已有变换上,产生非预期的结果gl.glLoadIdentity()// 创建一个透视投影矩阵,设置窗口大小gl.glFrustumf(-ratio, ratio, -1F, 1F, 1F, 10F)}/*** Android在图形绘制时调用的方法,除非设置手动刷新,否则会一直调用。*/override fun onDrawFrame(gl: GL10) {// 清理缓存 颜色和深度gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 设置模型视图矩阵gl.glMatrixMode(GL10.GL_MODELVIEW)// 重制矩阵gl.glLoadIdentity()// 视角变换,就是观察的方向,眼睛的位置,中心点的位置 和 视线的朝向GLU.gluLookAt(gl,0F,0F,3F,0F,0F,0F,0F,1F,0F)}
}

通过 OpenGL ES 完成 2D图形的绘制。

  • OpenGL的坐标向线如下:
    openGL 坐标

绘制三角形

private val one: Int = 0x10000// 正确创建直接缓冲区private val triggerBuffer: IntBuffer by lazy {// 创建直接缓冲区并设置本地字节序val byteBuffer = ByteBuffer.allocateDirect(3 * 3 * 4) // 3个顶点,每个3个分量,每个int占4字节byteBuffer.order(ByteOrder.nativeOrder())// 获取 IntBuffer 视图val intBuffer = byteBuffer.asIntBuffer()// 填充顶点数据intBuffer.put(intArrayOf(0, one, 0,        // 顶点1-one, -one, 0,    // 顶点2one, -one, 0      // 顶点3))// 重置位置到起始点intBuffer.position(0)intBuffer}
override fun onDrawFrame(gl: GL10) {// 清理缓存 颜色和深度gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 设置模型视图矩阵gl.glMatrixMode(GL10.GL_MODELVIEW)// 重制矩阵gl.glLoadIdentity()// 视角变换,就是观察的方向,眼睛的位置,中心点的位置 和 视线的朝向GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)// 允许设置顶点gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)// 给三角形设置顶点数组// size 含义代表每个点有 xyz三个方向数据所以size为3;// GL10.GL_FIXED 为整形数据 还可以设置 GL_FLOAT 浮点型,以及其他类型,甚至是无符号类型// 第三个参数代表相邻顶点之间的偏移量,0 表示紧密排列。即下一个顶点数据紧跟当前顶点数据。非0的话代表交替存储顶点数据额外便宜的字节。gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)// 绘制三角形 GL_TRIANGLES 代表三角形,开始0 绘制3个点。gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)// 关闭顶点设置gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)}

上述代码绘制了一个三角形如下:
在这里插入图片描述

需要注意的是创建三角形顶点使用的buffer必须使用直接缓冲区,提高数据读取的速度。

  • 绘制正方形 , 三角形右侧增加绘制正方形。
// 创建正方形直接缓冲区private val squareBuffer: IntBuffer by lazy {// 创建直接缓冲区并设置本地字节序val byteBuffer = ByteBuffer.allocateDirect(4 * 3 * 4) // 4个顶点,每个3个分量,每个int占4字节byteBuffer.order(ByteOrder.nativeOrder())// 获取 IntBuffer 视图val intBuffer = byteBuffer.asIntBuffer()// 填充顶点数据intBuffer.put(intArrayOf(one, one, 0,        // 顶点1-one, one, 0,    // 顶点2one, -one, 0,      // 顶点3-one, -one, 0      // 顶点4))// 重置位置到起始点intBuffer.position(0)intBuffer}/*** Android在图形绘制时调用的方法,除非设置手动刷新,否则会一直调用。*/override fun onDrawFrame(gl: GL10) {// 清理缓存 颜色和深度gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 设置模型视图矩阵gl.glMatrixMode(GL10.GL_MODELVIEW)// 重制矩阵gl.glLoadIdentity()// 视角变换,就是观察的方向,眼睛的位置,中心点的位置 和 视线的朝向GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)// 设置模型位置gl.glTranslatef(-2.0F, 0.0F, -4.0F)// 允许设置顶点gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)// 给三角形设置顶点数组// size 含义代表每个点有 xyz三个方向数据所以size为3;// GL10.GL_FIXED 为整形数据 还可以设置 GL_FLOAT 浮点型,以及其他类型,甚至是无符号类型// 第三个参数代表相邻顶点之间的偏移量,0 表示紧密排列。即下一个顶点数据紧跟当前顶点数据。非0的话代表交替存储顶点数据额外便宜的字节。gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)// 绘制三角形 GL_TRIANGLES 代表三角形,开始0 绘制3个点。gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)// 重制模型矩阵gl.glLoadIdentity()// 绘制正方形  两个相同的三角形组成正方形,可以采用三角形的带// 设置模型位置  为了显示效果更小所以 Z 周弄远了。Z越大看起来离我们越近显示效果越大(也可以通过坐标修改)gl.glTranslatef(1F, 0.0F, -6.0F)// 设置顶点数据gl.glVertexPointer(3, GL10.GL_FIXED, 0, squareBuffer)// 绘制正方形gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4)// 关闭顶点设置gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)}

绘制三角形以后,重制模型矩阵gl.glLoadIdentity() ,设置模型位置 gl.glTranslatef,设置顶点数据gl.glVertexPointer,绘制正方形。gl.glDrawArrays。

效果如下
在这里插入图片描述

在0penGL中绘制2D多边形常用的函数以及常量:

  • glEnableClientState/glDisableClientState:状态开关
  • glVertexPointer:设置顶点数据
  • glDrawArrays:绘制函数
  • GL_VERTEX_ARRAY:顶点数组
  • GL_BYTE/GL_SHORT/GL_FIXED/GL_FLOAT:顶点数据的类理
  • GL_LINES:线
  • GL_TRIANGLES:三角形
  • GL_TRIANGLE_STRIP:三角形带

如何绘制颜色

// 创建颜色buffer 分为 RGBA private val colorBuffer: IntBuffer by lazy {// 创建直接缓冲区并设置本地字节序val byteBuffer = ByteBuffer.allocateDirect(3 * 4 * 4)byteBuffer.order(ByteOrder.nativeOrder())// 获取 IntBuffer 视图val intBuffer = byteBuffer.asIntBuffer()// 填充顶点数据intBuffer.put(intArrayOf(one, 0, 0, one,         // 顶点10, one, 0, one,   // 顶点20, 0, one, one      // 顶点3))// 重置位置到起始点intBuffer.position(0)intBuffer}override fun onDrawFrame(gl: GL10) {// 清理缓存 颜色和深度gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 设置模型视图矩阵gl.glMatrixMode(GL10.GL_MODELVIEW)// 重制矩阵gl.glLoadIdentity()// 视角变换,就是观察的方向,眼睛的位置,中心点的位置 和 视线的朝向GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)// 设置模型位置gl.glTranslatef(-2.0F, 0.0F, -4.0F)// 允许设置顶点gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)// 允许设置颜色数组gl.glEnableClientState(GL10.GL_COLOR_ARRAY)gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer)// 给三角形设置顶点数组// size 含义代表每个点有 xyz三个方向数据所以size为3;// GL10.GL_FIXED 为整形数据 还可以设置 GL_FLOAT 浮点型,以及其他类型,甚至是无符号类型// 第三个参数代表相邻顶点之间的偏移量,0 表示紧密排列。即下一个顶点数据紧跟当前顶点数据。非0的话代表交替存储顶点数据额外便宜的字节。gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)// 绘制三角形 GL_TRIANGLES 代表三角形,开始0 绘制3个点。gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)// 绘制完成禁用颜色数组绘制gl.glDisableClientState(GL10.GL_COLOR_ARRAY)// 重制模型矩阵gl.glLoadIdentity()// 绘制正方形  两个相同的三角形组成正方形,可以采用三角形的带// 设置模型位置  为了显示效果更小所以 Z 周弄远了。Z越大看起来离我们越近显示效果越大gl.glTranslatef(1F, 0.0F, -6.0F)// 设置顶点数据gl.glVertexPointer(3, GL10.GL_FIXED, 0, squareBuffer)// 设置颜色,R G B A 的色值gl.glColor4f(1F, 0F, 0F, 1F)// 绘制正方形gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4)// 关闭顶点设置gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)}

// 设置颜色,R G B A 的色值
gl.glColor4f(1F, 0F, 0F, 1F) 这里只给红色和透明度设置成1 显示效果为红色
给正方形上色,直接采用设置RGBA的方式,单一的颜色

gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer) 给三角形绘制平滑过渡的颜色,需要开启设置颜色数组,使用结束后关闭。

效果如下:
在这里插入图片描述

  • 颜色所需要的常用函数
常用函数及常量
- glColor4f:设置单一颜色
- glColorPointer:设置颜色数组
- GL_COLOR_ARRAY:颜色数组(通过状态开关函数
- glDisableClientState 来操作)
- GL_COLOR_BUFFER_BIT:颜色缓存

图形的变换

OpenGLES有三种不同类型的变换,它们分别是:转移(Translate):在3D空间中移动物体旋转(Rotate):绕X,Y,或者 Z 轴进行旋转缩放(Scale):改变物体的大小
  • 为了防止上述三角形和正方形的位置重叠,进行了 gl.glTranslatef(1F, 0.0F, -6.0F) 这就是平移操作。

gl.glTranslatef(1F, 0.0F, -6.0F) 代表分别在 x y z上分别平移多少距离。

  • 旋转操作
 /*** 定义旋转角度*/var rotate1: Float = 0Fvar rotate2: Float = 0F/*** Android在图形绘制时调用的方法,除非设置手动刷新,否则会一直调用。*/override fun onDrawFrame(gl: GL10) {// 清理缓存 颜色和深度gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 设置模型视图矩阵gl.glMatrixMode(GL10.GL_MODELVIEW)// 重制矩阵gl.glLoadIdentity()// 视角变换,就是观察的方向,眼睛的位置,中心点的位置 和 视线的朝向GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)// 设置模型位置gl.glTranslatef(-2.0F, 0.0F, -4.0F)// 设置旋转角度 设置成了绕Y轴旋转gl.glRotatef(rotate1, 0F, 1F, 0F)// 允许设置顶点gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)// 允许设置颜色数组gl.glEnableClientState(GL10.GL_COLOR_ARRAY)gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer)// 给三角形设置顶点数组// size 含义代表每个点有 xyz三个方向数据所以size为3;// GL10.GL_FIXED 为整形数据 还可以设置 GL_FLOAT 浮点型,以及其他类型,甚至是无符号类型// 第三个参数代表相邻顶点之间的偏移量,0 表示紧密排列。即下一个顶点数据紧跟当前顶点数据。非0的话代表交替存储顶点数据额外便宜的字节。gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)// 绘制三角形 GL_TRIANGLES 代表三角形,开始0 绘制3个点。gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)// 绘制完成禁用颜色数组绘制gl.glDisableClientState(GL10.GL_COLOR_ARRAY)// 重制模型矩阵gl.glLoadIdentity()// 绘制正方形  两个相同的三角形组成正方形,可以采用三角形的带// 设置模型位置  为了显示效果更小所以 Z 周弄远了。Z越大看起来离我们越近显示效果越大gl.glTranslatef(1F, 0.0F, -6.0F)// 设置绕X轴旋转gl.glRotatef(rotate2, 1F, 0F, 0F)// 设置顶点数据gl.glVertexPointer(3, GL10.GL_FIXED, 0, squareBuffer)// 设置颜色,R G B A 的色值gl.glColor4f(1F, 0F, 0F, 1F)// 绘制正方形gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4)// 关闭顶点设置gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)// 改变旋转角度rotate1 += 0.5Frotate2 += 0.5F}

在这里插入图片描述

上端代码实际上增加了 gl.glRotatef(rotate2, 1F, 0F, 0F) ,参数依次是 角度,x轴 y轴 z轴旋转。看上图效果实际上是动旋转,三角Y轴旋转,正方形一直绕x轴旋转。然后通过改变第一个参数的角度,一直旋转。

  • 放大
// 增加如下代码即可 x y z 放大2倍 
gl.glScalef(2.0F,2.0F,2.0F)

在这里插入图片描述

总结

2D基本图形的绘制;坐标系;着色方式包括单一着色,平滑着色;图形的平移、旋转、缩放。

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

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

相关文章

JavaScript进阶(十二)

第三部分:JavaScript进阶 目录 第三部分:JavaScript进阶 十二、深浅拷贝 12.1 浅拷贝 12.2 深拷贝 1. 通过递归实现深拷贝 2. js库lodash里面cloneDeep内部实现了深拷贝 3. 通过JSON.stringify()实现 十三、异常处理 13.1 throw抛异常 13.2 try /catch捕获异常 1…

大疆制图跑飞马D2000的正射与三维模型

1 问题描述 大疆制图在跑大疆无人机飞的影像的时候,能够自动识别相机参数并且影像自带pos信息,但是用飞马无人机获取的影像pos信息与影像是分开的(飞马无人机数据处理有讲),所以在用大疆制图时需要对相机参数进行设置…

探索服务网格(Service Mesh):云原生时代的网络新范式

文章目录 一、引言二、什么是服务网格基本定义形象比喻 三、服务网格解决了哪些问题微服务通信复杂性可观察性安全性 四、常见的服务网格实现IstioLinkerdConsul Connect 五、服务网格的应用场景大型微服务架构混合云环境 六、服务网格的未来发展与其他技术的融合标准化和行业规…

Electron+vite+vue3 从0到1搭建项目,开发Win、Mac客户端

随着前端技术的发展,出现了所谓的大前端。 大前端则是指基于前端技术延伸出来的各种终端平台及应用场景,包括APP、桌面端、手表终端、服务端等。 本篇文章主要是和大家一起学习一下使用Electron 如何打包出 Windows 和 Mac 所使用的客户端APP&#xff…

【DevSecOps】从零到一:用OpenAI Codey与Trivy打造智能化安全扫描 CI/CD 流水线实战

背景与动机 核心概念:DevSecOps、OpenAI Codey 与 Trivy 什么是 DevSecOpsOpenAI Codey(Codex)概览Trivy 扫描器简介 架构设计与技术选型 流水线实战:GitHub Actions 集成示例 多平台适配:GitLab CI 与 Azure DevO…

Swagger、Springfox、Springdoc-openapi 到底是什么关系

记得刚开始想在 SpringBoot 应用中使用 Swagger 生成 API 文档时,在 Swagger 官网上想找如何在 SpringBoot 中使用的指导,结果肯定是找不到,因为当时不清楚 Swagger 的定位是什么,只知道可以用它生成 API 文档。所以就想写这篇文章…

目标检测DINO-DETR(2023)详细解读

文章目录 对比去噪训练混合查询选择look forward twice 论文全称为:DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection 提出了三个新的方法: 首先,为了改进一对一的匹配效果,提出了一种对比去噪训练方法…

深度学习-162-DeepSeek之调用远程大模型API接口参数结构分析

文章目录 1 文本对话请求1.1 请求参数1.1.1 参数说明1.1.2 参数总结1.2 返回参数1.2.1 id1.2.2 choices1.2.3 usage1.2.4 created1.2.5 model1.2.6 object1.2.7 参数总结2 应用示例2.1 调用大模型API2.2 返回结果3 参考附录分析文本对话请求v1/chat/completions的参数结构含义。…

uniapp原生插件

前言 使用uniapp开发Android app时,一些特定的需求没办法实现的时候就只能开发原生插件或者uts插件去满足需求,我的需求目前uts插件没法实现,只能靠原生插件了😆 需求:拍照不保存图片到相册,uniapp的API或…

CVE-2024-42323

漏洞名称 Apache HertzBeat SnakeYaml 反序列化远程代码执行漏洞(CVE-2024-42323) 漏洞描述 Apache HertzBeat 是一款开源的实时监控告警工具,支持对操作系统、中间件、数据库等多种对象进行监控,并提供 Web 界面进行管理。 在…

“人工智能+多学科”选题思路,2025热点AI+(180个)

给大家整理了一份超全的论文资料合集 不仅有论文创新点的干货知识、开题报告模板、文献综述模板、审稿意见回复模板、还有全套的SCI论文写作指导教程和人工智能各方向经典的论文合集以及各大热门科研和论文写作润色工具等 以及1000G人工智能资料包(如下图&#xff…

LangChain4j入门AI(八)使用RAG增加私有知识库

前言 我们通过提示词工程提升了通用LLM的专业知识水平,并结合Function Calling构建了私有业务能力。为了在实际应用中有效维护私有领域的专有数据,我们进一步采用大模型微调或RAG检索增强技术,使LLM能够充分掌握私有知识库的内容。 一、微调…

使用arXiv.org上的资源进行学术研究

arXiv.org e-Print archive arXiv.org 是一个收集物理学、数学、计算机科学、定量生物学、定量金融学和统计学等领域学术论文预印本的网站,其使命是促进科学文献的传播与交流。以下是关于该网站的详细介绍: 核心定位与功能 学术预印本平台&#xff1a…

Redis 中的缓存击穿、缓存穿透和缓存雪崩是什么?

在 Redis 中,缓存击穿、缓存穿透和缓存雪崩是常见的缓存问题,它们都会影响系统的性能和稳定性。以下是针对这三个问题的详细解释: 一、缓存击穿(Cache Breakdown) 定义:缓存击穿是指某个热点数据在缓存中…

RabbitMQ配置环境变量

一、RabbitMQ下载 1、从官网下载 在官网中下载,适合自己电脑的版本。我直接下载的windows版本。 然后下载opt这个软件这个版本适合3.6.3的RabbitMQ,点击蓝色的字体即可下载。 2、安装erlang 当你双击安装程序并进入安装向导后,会看到这样…

基于vue框架的东莞市二手相机交易管理系统5yz0u(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表 项目功能:用户,相机分类,二手相机 开题报告内容 基于FlaskVue框架的东莞市二手相机交易管理系统开题报告 一、研究背景与意义 1.1 研究背景 随着数字技术的迅猛发展和摄影文化的广泛普及,相机已成为人们记录生活、表达创意的重要工…

dify调用Streamable HTTP MCP应用

一、概述 上一篇文章,介绍了使用python开发Streamable HTTP MCP应用,链接:https://www.cnblogs.com/xiao987334176/p/18872195 接下来介绍dify如何调用MCP 二、插件 安装插件 需要安装2个插件,分别是:Agent 策略(支持 …

笔记:将一个文件服务器上的文件(一个返回文件数据的url)作为另一个http接口的请求参数

笔记:将一个文件服务器上的文件(一个返回文件数据的url)作为另一个http接口的请求参数 最近有这么个需求,需要往某一个业务的外部接口上传文件信息,但是现在没有现成的文件,只在数据库存了对应的url&#…

如何在 FastAPI 中合理使用 Pydantic 的 Alias

下面的内容是我跟 Gemini 2.5 Pro 探讨关于Pydantic 的 Alias 问题之后,让它总结的一篇技术博客。 我已经有很长一段时间没有好好写技术类的博客了,这就是原因。 可以在 https://panzhixiang.cn/ 查看更多我的博客,有技术的,也有很…

CAU人工智能class4 批次归一化

归一化 在对输入数据进行预处理时会用到归一化,将输入数据的范围收缩到0到1之间,这有利于避免纲量对模型训练产生的影响。 但当模型过深时会产生下述问题: 当一个学习系统的输入分布发生变化时,这种现象称之为“内部协变量偏移”…