当前位置: 首页 > news >正文

LearnOpenGL - Android OpenGL ES 3.0 绘制纹理

系列文章目录

  • LearnOpenGL 笔记 - 入门 01 OpenGL
  • LearnOpenGL 笔记 - 入门 02 创建窗口
  • LearnOpenGL 笔记 - 入门 03 你好,窗口
  • LearnOpenGL 笔记 - 入门 04 你好,三角形
  • OpenGL - 如何理解 VAO 与 VBO 之间的关系
  • LearnOpenGL - Android OpenGL ES 3.0 绘制三角形

文章目录

  • 系列文章目录
  • 一、前言
  • 二、数据流:顶点着色器到片元着色器
    • 2.1 顶点着色器
      • 直观解释
      • 示例
      • 执行流程
    • 2.2 片元着色器
      • 片元着色器执行次数
      • 示例
      • 片元着色器代码示例
      • 渲染流程
      • 总结
    • 2.3 顶点着色器到片元着色器
  • 三、纹理绘制流程
    • 3.1 顶点着色器
    • 3.2 片元着色器
    • 3.3 验证着色器
    • 3.4 代码编写
      • `prepare`函数
        • 1. 编译着色器
        • 2. 准备顶点缓冲区
        • 3. 生成VAO、VBO和EBO
        • 4. 绑定并设置VAO
        • 5. 设置VBO数据
        • 6. 设置EBO数据
        • 7. 设置VAO属性
        • 8. 解绑VAO
        • 9. 准备纹理
        • 10. 设置纹理过滤
        • 11. 设置纹理图像数据
        • 12. 解绑纹理
        • 13. 使用着色器程序并设置纹理位置
      • `draw`函数
        • 1. 清除颜色缓冲区
        • 2. 绑定VAO
        • 3. 激活并绑定纹理
        • 4. 绘制元素
        • 5. 解绑VAO
  • 四、其他问题
  • 4.1 为什么图片填充至纹理后是颠倒的
  • 参考


一、前言

在 LearnOpenGL - Android OpenGL ES 3.0 绘制三角形 中我们学会了如何在 Android 下搭建 GLES 环境,并绘制三角形。本文我们将讨论如何绘制纹理。
本文代码在 TextureDrawer

二、数据流:顶点着色器到片元着色器

在进入具体的代码细节先,我想说明顶点着色器与片元着色器是如何工作的,它们之间是如何联系在一起的,它们的输入和输出分别是什么。

2.1 顶点着色器

顶点着色器的输入是顶点属性。这些属性通常包括顶点的位置、颜色、法线、纹理坐标等。顶点着色器处理这些输入并生成顶点的输出,这些输出通常会被传递给片元着色器。、

如果有 4 个顶点,那么顶点着色器会被执行 4 次。每次执行时,顶点着色器都会处理一个顶点的属性,并生成对应的输出。下面是一个更详细的解释:

直观解释

  1. 顶点数据: 你定义了一组顶点数据。例如,假设你有一个包含 4 个顶点的简单模型,每个顶点都有位置和颜色属性。

  2. 顶点着色器执行:

    • 当你开始渲染这个模型时,渲染管线会将顶点数据传递给顶点着色器。
    • 顶点着色器会为每个顶点单独执行一次。因此,如果有 4 个顶点,顶点着色器会执行 4 次。
    • 每次执行时,顶点着色器都会读取一个顶点的属性数据(例如位置和颜色),进行必要的处理(例如变换位置,传递颜色),并生成对应的输出。

示例

假设你有以下顶点数据(每个顶点包含位置和颜色):

GLfloat vertices[] = {// 位置        // 颜色0.0f,  0.5f, 0.0f,  1.0f, 0.0f, 0.0f, 1.0f, // 顶点 1: 红色-0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f, 1.0f, // 顶点 2: 绿色0.5f, -0.5f, 0.0f,  0.0f, 0.0f, 1.0f, 1.0f, // 顶点 3: 蓝色0.5f,  0.5f, 0.0f,  1.0f, 1.0f, 0.0f, 1.0f  // 顶点 4: 黄色
};

对于这些顶点数据,顶点着色器类似于以下内容:

#version 300 es
layout(location = 0) in vec3 aPosition; // 顶点位置
layout(location = 1) in vec4 aColor;    // 顶点颜色out vec4 vColor; // 传递给片元着色器的输出变量void main()
{gl_Position = vec4(aPosition, 1.0); // 将顶点位置转换为齐次坐标vColor = aColor; // 将顶点颜色传递给片元着色器
}

执行流程

  • 第一次执行: 处理第一个顶点,输入 aPosition = vec3(0.0, 0.5, 0.0)aColor = vec4(1.0, 0.0, 0.0, 1.0)
  • 第二次执行: 处理第二个顶点,输入 aPosition = vec3(-0.5, -0.5, 0.0)aColor = vec4(0.0, 1.0, 0.0, 1.0)
  • 第三次执行: 处理第三个顶点,输入 aPosition = vec3(0.5, -0.5, 0.0)aColor = vec4(0.0, 0.0, 1.0, 1.0)
  • 第四次执行: 处理第四个顶点,输入 aPosition = vec3(0.5, 0.5, 0.0)aColor = vec4(1.0, 1.0, 0.0, 1.0)

每次执行时,顶点着色器根据输入的顶点属性计算输出的 gl_PositionvColor。这些输出随后传递给后续的渲染管线阶段,例如图元装配、光栅化和片元着色器。

2.2 片元着色器

片元着色器的执行次数取决于最终渲染到屏幕上的像素数量,而不是输入的顶点数量。片元着色器会为每个生成的片元(像素)执行一次。具体执行次数取决于渲染的几何图形覆盖的屏幕区域。以下是详细的解释:

片元着色器执行次数

  1. 图元的类型:

    • 图元类型可以是点、线、三角形等。
    • 通常情况下,渲染的是三角形。
  2. 图元覆盖的屏幕区域:

    • 图元被光栅化(rasterized)成片元。
    • 每个片元对应屏幕上的一个像素。
  3. 片元着色器执行:

    • 片元着色器会为每个片元(像素)执行一次。
    • 如果一个三角形覆盖了 100 个像素,片元着色器就会执行 100 次。

示例

假设我们有一个包含 4 个顶点的四边形,由两个三角形组成,渲染到屏幕上覆盖一定区域。

GLfloat vertices[] = {// 位置         // 颜色-0.5f,  0.5f, 0.0f,  1.0f, 0.0f, 0.0f, 1.0f, // 顶点 1: 红色-0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f, 1.0f, // 顶点 2: 绿色0.5f, -0.5f, 0.0f,  0.0f, 0.0f, 1.0f, 1.0f, // 顶点 3: 蓝色0.5f,  0.5f, 0.0f,  1.0f, 1.0f, 0.0f, 1.0f  // 顶点 4: 黄色
};GLuint indices[] = {0, 1, 2, // 第一个三角形0, 2, 3  // 第二个三角形
};

片元着色器代码示例

#version 300 es
precision mediump float;in vec4 vColor; // 从顶点着色器传递过来的颜色
out vec4 FragColor; // 输出的片元颜色void main()
{FragColor = vColor; // 将颜色输出
}

渲染流程

  1. 顶点着色器阶段:

    • 对每个顶点执行一次,共执行 4 次。
    • 顶点着色器输出顶点的齐次坐标和颜色。
  2. 图元装配和光栅化阶段:

    • 将顶点装配成两个三角形。
    • 将两个三角形转换为片元(像素)。
  3. 片元着色器阶段:

    • 每个片元调用一次片元着色器。
    • 如果两个三角形覆盖了 200 个像素,片元着色器会执行 200 次。

总结

  • 顶点着色器: 执行次数与顶点数量相同,每个顶点执行一次。
  • 片元着色器: 执行次数与片元(像素)数量相同,每个片元执行一次。

因此,片元着色器的执行次数通常远多于顶点着色器的执行次数,因为片元数量通常大于顶点数量。具体执行次数取决于渲染的几何图形在屏幕上的覆盖范围。

2.3 顶点着色器到片元着色器

用 WebGL编程指南 中一张图片来总结上面提到的内容
在这里插入图片描述
在这里插入图片描述

三、纹理绘制流程

先从着色器出发,思考如何编写着色器,然后根据着色器来编写 OpenGL ES 代码

3.1 顶点着色器

输入:为了绘制一张矩形的图片,我们需要 4 个顶点,每个顶点的属性包括顶点位置和纹理坐标。因此,需要定义两个输入。
输出:将纹理坐标传递给片元着色器,以便在片元着色器中进行纹理采样和其他操作。因此需要定义一个输出。

#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texcoord;
out vec2 v_texcoord;
void main()
{gl_Position = a_position;v_texcoord = a_texcoord;
}

3.2 片元着色器

输入:绘制纹理,那么自然需要一张纹理;此外,还有纹理坐标(已经被插值过了)
输出:片元的颜色

#version 300 es
uniform sampler2D texture0;
in vec2 v_texcoord;
out vec4 fragColor;
void main(void)
{fragColor = texture(texture0, v_texcoord);
}

3.3 验证着色器

在编写 OpenGL ES 代码前,让我们先验证下 shader 是否正确,这里推荐使用 KodeLife,它支持顶点着色器和片元着色器,网上很多在线的 shader 网站只支持片元着色器。

本人在 Mac 运行 KodeLife ,想要运行上面代码需要做一些修改,你需要将 version 信息修改为 “#version 330”,接着在 KodeLife 上导入一张纹理即可。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到我们的 shader 能够正常的渲染出图片,只是图片颠倒了,但这个问题可以在 Android 加载图片的时候进行处理。

3.4 代码编写

完整代码在 TextureDrawer

代码分为两个部分:prepare函数和draw函数。

prepare函数

prepare函数用于初始化和准备所有需要的OpenGL资源,包括着色器、VAO、VBO、EBO以及纹理。

1. 编译着色器
sharer.prepareShaders()
checkGlError("compile shader")

调用sharer.prepareShaders()编译着色器,并使用checkGlError检查是否有任何OpenGL错误。

2. 准备顶点缓冲区
val vertexBuffer = ByteBuffer.allocateDirect(vertices.size * Float.SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer().apply {put(vertices)position(0)}

创建一个直接字节缓冲区,并将顶点数据放入缓冲区中。

3. 生成VAO、VBO和EBO
GLES30.glGenVertexArrays(1, vaos)
GLES30.glGenBuffers(1, vbos)
GLES30.glGenBuffers(1, ebo)
checkGlError("gen vertex array and buffer")

生成一个VAO,一个VBO和一个EBO,并检查是否有任何OpenGL错误。

4. 绑定并设置VAO
GLES30.glBindVertexArray(vaos[0])

绑定生成的VAO。

5. 设置VBO数据
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbos[0])
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,Float.SIZE_BYTES * vertices.size,vertexBuffer,GLES30.GL_STATIC_DRAW
)
checkGlError("glBufferData")

绑定VBO并将顶点数据传递给缓冲区。

6. 设置EBO数据
val indexBuffer = ByteBuffer.allocateDirect(indices.size * Int.SIZE_BYTES).order(ByteOrder.nativeOrder()).asIntBuffer().apply {put(indices)position(0)}
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, ebo[0])
GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER,Int.SIZE_BYTES * indices.size,indexBuffer,GLES30.GL_STATIC_DRAW
)
checkGlError("glBufferData for indices")

创建索引缓冲区并将索引数据传递给EBO。

7. 设置VAO属性
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 5 * Float.SIZE_BYTES, 0)
GLES30.glEnableVertexAttribArray(0)
GLES30.glVertexAttribPointer(1,2,GLES30.GL_FLOAT,false,5 * Float.SIZE_BYTES,3 * Float.SIZE_BYTES
)
GLES30.glEnableVertexAttribArray(1)

设置顶点属性指针和启用顶点属性。这里有两个属性:位置(3个float)和纹理坐标(2个float)。

8. 解绑VAO
GLES30.glBindVertexArray(0)

解绑VAO。

9. 准备纹理
GLES30.glGenTextures(texIds.capacity(), texIds)
checkGlError("glGenTextures")
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texIds[0])
checkGlError("glBindTexture")

生成纹理ID并绑定纹理。

10. 设置纹理过滤
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MIN_FILTER,GLES30.GL_NEAREST
)
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)
checkGlError("glTexParameteri")

设置纹理过滤参数。

11. 设置纹理图像数据
val options = BitmapFactory.Options()
options.inScaled = false
var bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.lye, options)
val matrix = android.graphics.Matrix()
matrix.preScale(1.0f, -1.0f)
bitmap = android.graphics.Bitmap.createBitmap(bitmap,0,0,bitmap.width,bitmap.height,matrix,false
)
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0)
checkGlError("texImage2D")
bitmap.recycle()

解码资源中的图片,并垂直翻转,然后将图像数据传递给纹理。

12. 解绑纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)

解绑纹理。

13. 使用着色器程序并设置纹理位置
sharer.use()
sharer.setInt("texture1", 0)

使用着色器程序,并设置纹理单元。

draw函数

draw函数用于实际绘制帧。

1. 清除颜色缓冲区
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)

清除颜色缓冲区。

2. 绑定VAO
GLES30.glBindVertexArray(vaos[0])

绑定VAO。

3. 激活并绑定纹理
GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texIds[0])

激活纹理单元并绑定纹理。

4. 绘制元素
GLES30.glDrawElements(GLES30.GL_TRIANGLES, indices.size, GLES30.GL_UNSIGNED_INT, 0)

绘制元素。

5. 解绑VAO
GLES30.glBindVertexArray(0)

解绑VAO。

四、其他问题

4.1 为什么图片填充至纹理后是颠倒的

我是这么想的,图片填充时使用的内存起始地址是图片的左上角,然后一行一行地从上到下将数据拷贝到 GPU 纹理上,而纹理的起始地址是右下角,因为这种差异导致了颠倒。
在这里插入图片描述
因此为了让图片正确显示,我们可以

  1. 在图像加载阶段进行调整:在将图像数据传递给OpenGL之前,将图像上下翻转。这种方法在代码中已经实现,通过Matrix.preScale(1.0f, -1.0f)完成。
// Flip the bitmap vertically
val matrix = android.graphics.Matrix()
matrix.preScale(1.0f, -1.0f)
bitmap = android.graphics.Bitmap.createBitmap(bitmap,0,0,bitmap.width,bitmap.height,matrix,false
)
  1. 在着色器阶段进行调整:在顶点着色器或片段着色器中,翻转纹理坐标。
#version 300 eslayout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texcoord;out vec2 v_texcoord;void main()
{gl_Position = a_position;v_texcoord = vec2(a_texcoord.x, 1.0 - a_texcoord.y); // Flip y axis
}

参考

  • NDK OpenGLES 3.0 开发(二):纹理映射

相关文章:

LearnOpenGL - Android OpenGL ES 3.0 绘制纹理

系列文章目录 LearnOpenGL 笔记 - 入门 01 OpenGLLearnOpenGL 笔记 - 入门 02 创建窗口LearnOpenGL 笔记 - 入门 03 你好,窗口LearnOpenGL 笔记 - 入门 04 你好,三角形OpenGL - 如何理解 VAO 与 VBO 之间的关系LearnOpenGL - Android OpenGL ES 3.0 绘制…...

山东济南最出名的起名大师颜廷利:二十一世纪哲学的领航者

山东济南最出名的起名大师颜廷利教授:二十一世纪哲学的领航者 在哲学的天空中,颜廷利教授犹如一颗璀璨的星辰,被无数求知者誉为21世纪最杰出的思想家之一。他的理论既深邃又广博,巧妙地将东方的儒家与道家哲学与西方的思辨传统交织…...

Nginx 负载均衡实现上游服务健康检查

Nginx 负载均衡实现上游服务健康检查 Author:Arsen Date:2024/06/20 目录 Nginx 负载均衡实现上游服务健康检查 前言一、Nginx 部署并新增模块二、健康检查配置2.1 准备 nodeJS 应用程序2.2 Nginx 配置负载均衡健康检查 小结 前言 如果你使用云负载均衡…...

小程序使用接口wx.getLocation配置

开通时需详细描述业务,否则可能审核不通过 可能需要绑定腾讯位置服务,新建应该,绑定到小程序 配置 权限声明:在使用wx.getLocation前,需要在app.json的permission字段中声明对用户位置信息的使用权限,并提…...

Protobuf安装配置--附带每一步截图

Protobuf Protobuf(Protocol Buffers)协议是一种由 Google 开发的二进制序列化格式和相关的技术,它用于高效地序列化和反序列化结构化数据,通常用于网络通信、数据存储等场景。 为什么要使用Protobuf Protobuf 在许多领域都得到…...

力扣1019.链表中的下一个更大节点

力扣1019.链表中的下一个更大节点 从左到右 每个数确定下一个更大节点后 弹出栈中存下标 即res.size() class Solution {public:vector<int> nextLargerNodes(ListNode* head) {vector<int> res;stack<int> st;for(auto ihead;i;ii->next){while(!st.e…...

查询mysql库表的几个语句

1、查询某个数据库的所有表 SELECTtable_name FROMinformation_schema.TABLES WHEREtable_schema database_namedatabase_name替换成你需要查询的数据库名称 2、查询某张表的所有字段名称 SELECTCOLUMN_NAME,column_comment FROMinformation_schema.COLUMNS WHEREtable…...

【CT】LeetCode手撕—103. 二叉树的锯齿形层序遍历

目录 题目1- 思路2- 实现⭐103. 二叉树的锯齿形层序遍历——题解思路 2- ACM实现 题目 原题连接&#xff1a;103. 二叉树的锯齿形层序遍历 1- 思路 二叉树的层序遍历&#xff0c;遇到奇数时&#xff0c;利用 Collections.reverse() 翻转即可 2- 实现 ⭐103. 二叉树的锯齿形层…...

1958springboot VUE宿舍管理系统开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot VUE宿舍管理系统是一套完善的完整信息管理类型系统&#xff0c;结合springboot框架和VUE完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09; &#xff0c;系统具有完整的源代码和数…...

LVS DR模式

Linux Virtual Server&#xff08;LVS&#xff09;是一个由Linux内核支持的负载均衡解决方案&#xff0c;旨在通过集群技术来提高服务器的可扩展性、可靠性和高可用性。LVS通过将客户端的请求分发到多个服务器上&#xff0c;从而实现负载均衡和容错。 目录 LVS的工作模式 DR模…...

myslql事务示例

在 MySQL 中&#xff0c;事务&#xff08;Transaction&#xff09;是一组要么全部执行&#xff0c;要么全部不执行的SQL语句。这可以确保数据的一致性和完整性。事务管理的核心包括四个属性&#xff0c;即原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consiste…...

解决Flutter应用程序的兼容性问题

哈喽呀&#xff0c;大家好呀&#xff0c;淼淼又来和大家见面啦&#xff0c;Flutter作为一个跨平台的移动应用开发框架&#xff0c;极大地简化了开发者同时在Android和iOS平台上构建应用的难度。然而&#xff0c;由于不同设备、操作系统版本以及Flutter框架本身的变化&#xff0…...

整合微信支付一篇就够了

需要的工具 微信开发小程序工具 需要的材料 关键步骤 postman获取微信access_token https://api.weixin.qq.com/cgi-bin/token?appid=wxfssafa629021&grant_type=client_credential&secret=701d213dsfsdfsfdss4fb274生成h5跳转小程序的链接 https://api.weixin.…...

视创云展为企业虚拟展厅搭建,提供哪些功能?

在当下数字化浪潮中&#xff0c;如何为用户创造更富生动性和真实感的展示体验&#xff0c;已成为企业营销策略的核心。借助视创云展的线上虚拟3D企业展厅搭建服务&#xff0c;利用3D空间漫游和VR技术的融合&#xff0c;可以为用户呈现出一个既真实又充满想象力的全景图或三维模…...

c++ 常用的锁及用法介绍和示例

2024/6/21 14:20:10 在 C++ 中,常用的锁主要包括以下几种:std::mutex、std::recursive_mutex、std::timed_mutex 和 std::shared_mutex。这些锁可以帮助我们在多线程编程中保护共享数据,避免竞争条件。以下是每种锁的介绍及其用法示例: std::mutex std::mutex 是最基本的互…...

PostgreSQL源码分析——口令认证

认证机制 对于数据库系统来说&#xff0c;其作为服务端&#xff0c;接受来自客户端的请求。对此&#xff0c;必须有对客户端的认证机制&#xff0c;只有通过身份认证的客户端才可以访问数据库资源&#xff0c;防止非法用户连接数据库。PostgreSQL支持认证方法有很多&#xff1…...

Stability-AI(图片生成视频)

1.项目地址 GitHub - Stability-AI/generative-models: Generative Models by Stability AI 2.模型地址 魔搭社区 3.克隆项目后&#xff0c;按照教程安装 conda create --name Stability python3.10 conda activate Stability pip3 install -r requirements/pt2.txt py…...

Linux机器通过Docker-Compose安装Jenkins发送Allure报告

目录 一、安装Docker 二、安装Docker Compose 三、准备测试用例 四、配置docker-compose.yml 五、启动Jenkins 六、配置Jenkins和Allure插件 七、创建含pytest的Jenkins任务 八、项目结果通知 1.通过企业微信通知 2.通过邮件通知 九、配置域名DNS解析 最近小编接到一…...

基于Gunicorn+Flask+Docker模型高并发部署

关于猫头虎 大家好&#xff0c;我是猫头虎&#xff0c;别名猫头虎博主&#xff0c;擅长的技术领域包括云原生、前端、后端、运维和AI。我的博客主要分享技术教程、bug解决思路、开发工具教程、前沿科技资讯、产品评测图文、产品使用体验图文、产品优点推广文稿、产品横测对比文…...

java:类型变量(TypeVariable)解析--基于TypeResolver实现将类型变量替换为实际类型

上一篇博客《java:类型变量(TypeVariable)解析–获取泛型类(Generic Class)所有的类型变量(TypeVariable)的实际映射类型》中介绍如何如何正确解析泛型类的类型变量(TypeVariable)&#xff0c;获取对应的实际类型。 有了类型变量(TypeVariable)–实际类型的映射&#xff0c;我们…...

ru俄罗斯域名如何申请SSL证书?

我们日常看到的都是com这种国际域名比较普遍&#xff0c;尤其是主流网站&#xff0c;主要原因考虑的其通用性&#xff0c;那么对于地方性的域名大家很少看到&#xff0c;比如俄罗斯国家域名.ru大家还是有些陌生的&#xff0c;但要说中国.CN域名那你就很熟悉了。 有用户在申请过…...

python实现购物车的功能

模拟购物车&#xff0c;准备一个列表 goodList [{name:笔记本电脑,price:8000}, {name:鼠标, price:100}] 5个函数 1.加入购物车 2.收藏商品 3.去结算 4.删除购物车商品 5.清空购物车 购物车 cartList [] 收藏列表 collectSet {笔记本电脑,鼠标} 数据示例 去结算计算出总价…...

日元预计明年开始上涨

被称为“日元先生”的前大藏省&#xff08;现财务省&#xff09;财务官榊原英资预测&#xff0c;美元兑日元汇率将在今年底或2025年初逐步升至130。他认为&#xff0c;通缩时代已经过去&#xff0c;通货膨胀即将来临。 《日本经济新闻》6月5日报道&#xff0c;日本财务省于5月3…...

8、PHP 实现二进制中1的个数、数值的整数次方

题目&#xff1a; 二进制中1的个数 描述&#xff1a; 输入一个整数&#xff0c;输出该数二进制表示中1的个数。其中负数用补码表示。 <?phpfunction NumberOf1($n) {$count 0;if($n < 0){$n $n & 0x7FFFFFFF;$count;}while($n ! 0){$count;$n $n & ($n - 1…...

linux git凭证管理

linux git 凭证管理 解决命令行git登录github的问题&#xff0c;支持两步验证 同样适用于Azure Devops, Bitbucket 官网&#xff1a; https://github.com/git-ecosystem/git-credential-manager https://github.com/git-ecosystem/git-credential-manager/blob/release/docs/…...

WIC 图像处理初体验——读取像素的值

先放上运行结果&#xff1a; 可以发现红绿蓝是从后往前的。 必须以C方式编译代码&#xff01; #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <wincodec.h>int main(void) {CoInitialize(nullptr);IWICImagingFactory* fac;CoCreateInstance(CLS…...

使用Server-Sent Events (SSE),并获取message里面的内容

什么是Server-Sent Events (SSE)? Server-Sent Events (SSE)是一种服务器推送技术&#xff0c;允许服务器向客户端&#xff08;浏览器&#xff09;发送实时消息。与WebSocket不同&#xff0c;SSE是单向通信&#xff0c;只能从服务器到客户端。SSE在HTML5中作为标准实现&#…...

LabVIEW项目管理中如何平衡成本、时间和质量

在LabVIEW项目管理中&#xff0c;平衡成本、时间和质量是实现项目成功的关键。通过制定详细的项目计划、合理分配资源、严格控制进度、进行质量保证和灵活应对变化&#xff0c;项目管理者可以有效地协调这三者的关系&#xff0c;确保项目按时、按质、按预算完成。 1. 制定详细…...

如何检查 Kubernetes 网络配置

简介 Kubernetes 是一个容器编排系统&#xff0c;可以管理集群中的容器化应用程序。在集群中保持所有容器之间的网络连接需要一些高级网络技术。在本文中&#xff0c;我们将简要介绍一些工具和技术&#xff0c;用于检查这种网络设置。 如果您正在调试连接问题&#xff0c;调查…...

如何将网站封装成App:小猪APP分发助你实现

你有没有想过&#xff0c;将你的网站变成一个App会是什么样子&#xff1f;想象一下&#xff0c;用户只需点击一下图标&#xff0c;就能立刻访问你的内容&#xff0c;而不是在浏览器中输入网址。这不仅提升了用户体验&#xff0c;还能增加用户粘性。这一切都可以通过将网站封装成…...

做电子商务网站建设工资多少/seo泛目录培训

java交流群&#xff1a;扫码加群群满100人后需要群主邀请的&#xff0c;请加下方微信号&#xff0c;备注“入群”关注微信公众号【非典型互联网】&#xff0c;回复“入群”&#xff0c;可进所有互联网交流群&#xff1b;java干货资源下载&#xff1a;扫码关注微信公众号&#x…...

做啤酒纸箱包装的网站/太原seo关键词排名优化

原创链接如下&#xff1a; ​​​​​​http://www.yuanxiangzhixin.com/?p5...

上海做网站的公司是什么/永久免费跨境浏览app

sleep()和wait()的区别 : sleep是线程thread类的静态方法&#xff0c;wait是一个实例化对象的线程控制方法&#xff1b;sleep是thread线程类操作内的方法&#xff0c;wait是任意对象内的方法&#xff0c;范围不同&#xff1b;sleep方法会保留synchronized内的lock锁&#xff0…...

设计网站页面要多少钱/电工培训机构

一、总概 对于微信小程序整个开发框架而言&#xff0c;其程序中包含一个描述整体程序 App 和多个描述各自页面的 pages 对于微信小程序来说&#xff0c;一个小程序主体部分由三个文件构成如下表所示 页面的开发都放在文件夹pages中&#xff0c;log文件夹是微信小程序自动生成的…...

网站规划问题/百度排名规则

快速排序算法 快速排序的操作步骤是什么样的,为什么能做到时间复杂度是O(nlogn),占用空间么? 快速排序的操作步骤是怎样的? 快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列。步骤为: 从序列中挑出一个元素,作为基准(pivot)把所有比基准值小的元素…...

中天建设集团有限公司第九建设公司/德州seo优化

对列的顺序进行处理 order [date, time, open, high] df df[order]会按照新的列规则来进行排列...