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

Android显示系统(04)- OpenGL ES - Shader绘制三角形

一、前言:

OpenGL 1.0采用固定管线,OpenGL 2.0以上版本重要的改变就是采用了可编程管线,Shader 编程是指使用着色器(Shader)编写代码来控制图形渲染管线中特定阶段的处理过程。在图形渲染中,着色器是在 GPU 上执行的小型程序,用于定义图形渲染管线中不同阶段的处理逻辑,以实现各种视觉效果,那么渲染管线哪个阶段可编程呢?

在这里插入图片描述

就是上图的渲染管线蓝色部分。

二、Shader介绍:

1、Vertex Shader和Fragment Shader:

在现代的图形渲染中,通常会使用两种主要类型的着色器:顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)。这两种着色器分别负责处理图形的顶点数据和片元(像素)数据,通过编写这些着色器程序,开发人员可以实现各种复杂的图形效果和渲染技术。

下面是对顶点着色器和片元着色器的简要介绍:

  • 顶点着色器
    • 顶点着色器用于处理图形的顶点数据,如位置、颜色、法线等。
    • 主要作用包括对顶点位置的变换(如模型变换、视图变换、投影变换)、法线变换、顶点着色等。
    • 顶点着色器的输出通常是裁剪空间坐标或者屏幕空间坐标。
  • 片元着色器
    • 片元着色器用于处理图元的片元(像素)数据,负责计算最终的颜色输出。
    • 在片元着色器中,开发人员可以实现光照、纹理映射、阴影、透明度等效果。
    • 片元着色器的输出通常是片元的颜色、深度值、法线等。

2、图元:

在Shader编程中,“图元”(Primitive)指的是基本的几何图形单元,通常是指在3D图形渲染中的基本几何形状,如点、线、三角形、四边形等。这些基本的几何图形单元是构成复杂场景的基础,它们通过渲染管线进行处理和转换,最终呈现在屏幕上。

在Shader编程中,图元是渲染管线处理的基本单位,Shader程序会对每个图元进行相应的处理,包括顶点变换、光照计算、纹理映射等操作,最终将图元渲染到屏幕上。常见的几何图元有以下几种:

  1. 点(Point):最简单的图元,通常用于表示粒子、光源等。
  2. 线(Line):由两个点组成的图元,可用于绘制线条、边缘等。
  3. 三角形(Triangle):由三个顶点组成的图元,是最基本的多边形,是3D图形学中最重要的图元之一,因为所有复杂的表面都可以由三角形网格构成。
  4. 四边形(Quadrilateral):由四个顶点组成的图元,通常被拆分为两个三角形处理。

Shader程序通过对这些基本图元进行处理和变换,最终形成了复杂的场景和图像。在Shader编程中,开发人员可以通过编写顶点着色器和片元着色器来对这些图元进行处理,实现各种视觉效果和渲染技术。处理图元是Shader程序中的一个重要任务,它直接影响着最终的渲染效果和性能。

3、三角形:

上面的四个图元中,其实最重要的是三角形,我们把大多数复杂的图形都可以用三角形拼起来,就像我小时候(不敢说你们00后小时候)糊灯笼一样。

比如,我之前文章提到的这个复杂的图,也都是由众多小三角形构成:

在这里插入图片描述

三、重要坐标系:

在 OpenGL ES 中,“标准设备坐标系”(Normalized Device Coordinates)和“屏幕坐标系”(Screen Coordinates)是两种不同的坐标系,它们在图形渲染过程中扮演不同的角色。

  1. 标准设备坐标系(Normalized Device Coordinates)
    • 标准设备坐标系是一个抽象的坐标系,它是一个以屏幕空间的中心为原点,范围从 -1 到 1 的立方体空间。
    • 在标准设备坐标系中,坐标 (0, 0) 表示屏幕中心,(-1, -1) 表示左下角,(1, 1) 表示右上角。
    • 所有的顶点数据在通过VertexShader处理后都会被映射到标准设备坐标系,这是 OpenGL ES 中进行图形变换和裁剪的标准坐标系。
  2. 屏幕坐标系(Screen Coordinates)
    • 屏幕坐标系是实际显示设备的坐标系,它通常以左上角为原点,向右为 x 轴正方向,向下为 y 轴正方向。
    • 屏幕坐标系的坐标值通常是以像素为单位的整数值,用来确定在屏幕上绘制图像和文本等元素的位置。

在 OpenGL ES 渲染过程中,顶点数据首先被定义在对象坐标系中,然后通过模型变换、视图变换和投影变换将其转换到标准设备坐标系中,最终在屏幕上绘制出来。

在这里插入图片描述

总结来说,标准设备坐标系是为了方便进行图形变换和裁剪而定义的坐标系,而屏幕坐标系则是实际显示设备上的坐标系,用于确定最终图像的位置。OpenGL ES 中的渲染过程涉及将顶点数据从对象坐标系转换到标准设备坐标系,最终映射到屏幕坐标系进行显示。

四、GLSL语言:

1、概念:

着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。

着色器的开头总是要声明版本,接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。

一个典型的着色器有下面的结构:

#version version_number
in type in_variable_name;
in type in_variable_name;out type out_variable_name;uniform type uniform_name;void main()
{// 处理输入并进行一些图形操作...// 输出处理过的结果到输出变量out_variable_name = weird_stuff_we_processed;
}

当我们特别谈论到顶点着色器的时候,每个输入变量也叫顶点属性(Vertex Attribute)。我们能声明的顶点属性是有上限的,它一般由硬件来决定。OpenGL确保至少有16个包含4分量的顶点属性可用,但是有些硬件或许允许更多的顶点属性,你可以查询GL_MAX_VERTEX_ATTRIBS来获取具体的上限:

int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;

通常情况下它至少会返回16个,大部分情况下是够用了。

2、数据传递:

在这里插入图片描述

  • 可以将Prog想象成一个芯片,有很多的引脚,比如这儿的1绑定的是VertextShader,2绑定的是FragmentShader。
  • 顶点着色器Vertex负责确定3D图形的三个顶点,片元着色器Fragment负责确定每个像素的颜色;
  • 数据传给1的时候,就会自动传给vPosition;看看具体怎么传递的:
    • 通过glGetAttribLocation获取vPositon的引脚;
    • glEnableVertextAttribArray传入的参数就是上一步获取的ID,这样,就可以打开这个引脚;
    • 通过glVertexAttribPointer将准备好的顶点数据Vertex Buffer Object传递给引脚1,这样vPosition就收到了;
  • 数据传给2的时候,就会自动传给vColor
    • 通过glGetUniformLocation获取引脚;
    • 通过glUniform4fv传递给vColor即可;

3、变量:

1)vertex shader变量:

在这里插入图片描述

  • 输入变量有:Attribute类型、Uniforms类型(unform变量相当于全局变量)、Samplers类型;
  • 输出变量有:Varying类型(Vertex Shader的输出,可以作为后续的Shader的输入)
  • 内部变量有:gl_Position(这个最重要);
    • 以gl开头的变量都是内部变量;
    • vertex shader需要给gl_Position赋值来确定顶点的位置;
    • 渲染管线会根据gl_Position进行图元装配;

2)fragment shader变量:

在这里插入图片描述

  • Varying类型:作为Fragment Shader的输入,和Vertex Shader的输出一一对应;
  • gl_FragColor:内部变量,是Fragment Shader的输出,保存每个像素的颜色;
    • 确定每个像素的最终颜色;
    • 可以和纹理结合,实现纹理的映射;
    • 还可以通过它实现光照、高亮等颜色特效;

五、绘制一个三角形:

1、步骤:

  • 使用GLSurfaceView创建OpenGL ES环境;

  • 定义顶点着色器和片元着色器以及OpenGL ES程序;

  • 编译顶点着色器和片元着色器;

    • 使用GLES30.glCreateShader()创建Shader对象;
    • 使用GLES30.glShaderSource()绑定Shader和其源代码;
    • 使用GLES30.glCompileShader()编译Shader;
  • 链接OpenGL ES程序;

    • 使用GLES30.glCreateProgram()创建OpenGL ES程序;
    • 使用GLES30.glAttachShader()绑定Shader到OpenGL ES程序;
    • 使用GLES30.glLinkProgram()链接整个OpenGL ES程序;
  • 使用OpenGL ES程序;

    • 调用GLES30.glUseProgram()使用OpenGL ES程序;
    • 传递顶点数据和片元数据

2、创建OpenGL ES环境:

定义GLSurfaceView

// 文件路径:com/example/glsurfaceviewdemo/GLSurfaceViewTest.java
public class GLSurfaceViewTest extends GLSurfaceView {public GLSurfaceViewTest(Context context) {super(context);// 设置OpenGL ES版本(由于3.0兼容2.0,我们使用3.0)setEGLContextClientVersion(3);// 设置渲染器Renderer,函数调用后,里面会启动一个新线程构造EGL环境setRenderer(new GLRenderTest());}
}

MainActivity使用GLSurfaceView:

// 文件路径:com/example/glsurfaceviewdemo/MainActivity.java
public class MainActivity extends AppCompatActivity {private GLSurfaceViewTest mGlSurfaceViewTest;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mGlSurfaceViewTest = new GLSurfaceViewTest(this);setContentView(mGlSurfaceViewTest);}
}

3、定义顶点着色器和片元着色器:

// 文件路径:com/example/glsurfaceviewdemo/Triangle.java
// 定义的顶点着色器代码private final String mVertexShaderCode ="attribute vec4 vPosition;" +"void main() {" +"  gl_Position = vPosition;" +"}";// 定义的片段着色器代码private final String mFragmentShaderCode ="precision mediump float;" +"uniform vec4 vColor;" +"void main() {" +"  gl_FragColor = vColor;" +"}";// 定义的三角形顶点坐标数组private final float[] mTriangleCoords = new float[]{0.0f, 0.2f, 0.0f,   // 顶部-0.5f, -0.5f, 0.0f, // 左下角0.5f, -0.5f, 0.0f   // 右下角};// 定义的fragment的颜色数组,表示每个像素的颜色private final float[] mColor = new float[]{0.0f, 1.0f, 0.0f, 1.0f};

4、定义OpenGL ES程序:

// 文件路径:com/example/glsurfaceviewdemo/Triangle.java
private int mProgram;
mProgram = GLES30.glCreateProgram();

5、编译着色器:

// 文件路径:com/example/glsurfaceviewdemo/Triangle.javapublic Triangle() {// ...// 2.加载并编译vertexShader和fragmentShaderint vertexShader = Companion.compileShader(GLES30.GL_VERTEX_SHADER, mVertexShaderCode);int fragmentShader = Companion.compileShader(GLES30.GL_FRAGMENT_SHADER, mFragmentShaderCode);// ...}// 定义静态内部类public static class Companion {// 创建并编译着色器public static int compileShader(int type, String shaderCode) {// 创建一个着色器int shader = GLES30.glCreateShader(type);// 将着色器代码设置到着色器对象中GLES30.glShaderSource(shader, shaderCode);// 编译着色器GLES30.glCompileShader(shader);return shader;}}

6、链接OpenGL ES程序:

// 文件路径:com/example/glsurfaceviewdemo/Triangle.javapublic Triangle() {// ...// 4.attach两个编译好的着色器到program当中GLES30.glAttachShader(mProgram, vertexShader);GLES30.glAttachShader(mProgram, fragmentShader);// 5.链接整个programGLES30.glLinkProgram(mProgram);}

7、使用OpenGL ES程序:

// 文件路径:com/example/glsurfaceviewdemo/Triangle.javapublic void draw() {// 使用programGLES30.glUseProgram(mProgram);// 获取顶点着色器的位置句柄mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");// 启用顶点属性数组GLES30.glEnableVertexAttribArray(mPositionHandle);// 准备三角形坐标数据// 重置缓冲区位置mVertexBuffer.position(0);// 指定顶点属性数据的格式和位置GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES30.GL_FLOAT, false, 0, mVertexBuffer);// 获取片元着色器的颜色句柄mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");// 设置绘制三角形的颜色GLES30.glUniform4fv(mColorHandle, 1, mColor, 0);// 绘制三角形GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, mTriangleCoords.length / COORDS_PER_VERTEX);// 禁用顶点属性数组GLES30.glDisableVertexAttribArray(mPositionHandle);}

这就是一个绘制三角形的函数;

8、渲染器调用:

在渲染器中周期性地调用上面的绘制函数。

// 文件路径:com/example/glsurfaceviewdemo/GLRenderTest.java
public class GLRenderTest implements GLSurfaceView.Renderer {private Triangle mTriangle;@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {GLES30.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);mTriangle = new Triangle();}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {GLES30.glViewport(0, 0,  width, height);}@Overridepublic void onDrawFrame(GL10 gl){GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);mTriangle.draw();}
}

9、运行结果:

在这里插入图片描述

10、附:全部代码:

文件路径:com/example/glsurfaceviewdemo/MainActivity.java

package com.example.glsurfaceviewdemo;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;public class MainActivity extends AppCompatActivity {private GLSurfaceViewTest mGlSurfaceViewTest;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mGlSurfaceViewTest = new GLSurfaceViewTest(this);setContentView(mGlSurfaceViewTest);}
}

文件路径:com/example/glsurfaceviewdemo/GLSurfaceViewTest.java

package com.example.glsurfaceviewdemo;import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;public class GLSurfaceViewTest extends GLSurfaceView {public GLSurfaceViewTest(Context context) {super(context);// 设置OpenGL ES版本(由于3.0兼容2.0,我们使用3.0)setEGLContextClientVersion(3);// 设置渲染器Renderer,函数调用后,里面会启动一个新线程构造EGL环境setRenderer(new GLRenderTest());}
}

文件路径:com/example/glsurfaceviewdemo/GLRenderTest.java

package com.example.glsurfaceviewdemo;import android.opengl.GLES30;
import android.opengl.GLSurfaceView;import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;public class GLRenderTest implements GLSurfaceView.Renderer {private Triangle mTriangle;@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {GLES30.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);mTriangle = new Triangle();}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {GLES30.glViewport(0, 0,  width, height);}@Overridepublic void onDrawFrame(GL10 gl){GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);mTriangle.draw();}
}

文件路径:com/example/glsurfaceviewdemo/Triangle.java

package com.example.glsurfaceviewdemo;import android.opengl.GLES30;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;import javax.microedition.khronos.opengles.GL;public class Triangle {public Triangle() {// 1.初始化顶点缓冲区,存储三角形坐标// 为顶点坐标分配DMA内存空间ByteBuffer byteBuffer = ByteBuffer.allocateDirect(mTriangleCoords.length * 4);// 设置字节顺序为本地字节顺序(会根据硬件架构自适应大小端)byteBuffer.order(ByteOrder.nativeOrder());// 将字节缓冲区转换为浮点缓冲区mVertexBuffer = byteBuffer.asFloatBuffer();// 将顶点三角形坐标放入缓冲区mVertexBuffer.put(mTriangleCoords);// 设置缓冲区的位置指针到起始位置mVertexBuffer.position(0);// 2.加载并编译vertexShader和fragmentShaderint vertexShader = Companion.compileShader(GLES30.GL_VERTEX_SHADER, mVertexShaderCode);int fragmentShader = Companion.compileShader(GLES30.GL_FRAGMENT_SHADER, mFragmentShaderCode);// 3.创建一个OpenGL程序mProgram = GLES30.glCreateProgram();// 4.attach两个编译好的着色器到program当中GLES30.glAttachShader(mProgram, vertexShader);GLES30.glAttachShader(mProgram, fragmentShader);// 5.链接整个programGLES30.glLinkProgram(mProgram);}// 顶点数据是float类型,因此,使用这个存储private FloatBuffer mVertexBuffer;private int mProgram;// 定义的顶点着色器代码private final String mVertexShaderCode ="attribute vec4 vPosition;" +"void main() {" +"  gl_Position = vPosition;" +"}";// 定义的片段着色器代码private final String mFragmentShaderCode ="precision mediump float;" +"uniform vec4 vColor;" +"void main() {" +"  gl_FragColor = vColor;" +"}";// 定义的三角形顶点坐标数组private final float[] mTriangleCoords = new float[]{0.0f, 0.2f, 0.0f,   // 顶部-0.5f, -0.5f, 0.0f, // 左下角0.5f, -0.5f, 0.0f   // 右下角};// 定义的fragment的颜色数组,表示每个像素的颜色private final float[] mColor = new float[]{0.0f, 1.0f, 0.0f, 1.0f};// 顶点着色器的位置句柄private int mPositionHandle = 0;// 片元着色器的位置句柄private int mColorHandle = 0;private final int COORDS_PER_VERTEX = 3;// 定义静态内部类public static class Companion {// 创建并编译着色器public static int compileShader(int type, String shaderCode) {// 创建一个着色器int shader = GLES30.glCreateShader(type);// 将着色器代码设置到着色器对象中GLES30.glShaderSource(shader, shaderCode);// 编译着色器GLES30.glCompileShader(shader);return shader;}}public void draw() {// 使用programGLES30.glUseProgram(mProgram);// 获取顶点着色器的位置句柄mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");// 启用顶点属性数组GLES30.glEnableVertexAttribArray(mPositionHandle);// 准备三角形坐标数据// 重置缓冲区位置mVertexBuffer.position(0);// 指定顶点属性数据的格式和位置GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES30.GL_FLOAT, false, 0, mVertexBuffer);// 获取片元着色器的颜色句柄mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");// 设置绘制三角形的颜色GLES30.glUniform4fv(mColorHandle, 1, mColor, 0);// 绘制三角形GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, mTriangleCoords.length / COORDS_PER_VERTEX);// 禁用顶点属性数组GLES30.glDisableVertexAttribArray(mPositionHandle);}
}

六、总结:

本文主要介绍了Shader以及GLSL语言,同时画出了一个三角形,但是一般工程中GLSL语言会用单独的文件写,后续我们改进下!

相关文章:

Android显示系统(04)- OpenGL ES - Shader绘制三角形

一、前言&#xff1a; OpenGL 1.0采用固定管线&#xff0c;OpenGL 2.0以上版本重要的改变就是采用了可编程管线&#xff0c;Shader 编程是指使用着色器&#xff08;Shader&#xff09;编写代码来控制图形渲染管线中特定阶段的处理过程。在图形渲染中&#xff0c;着色器是在 GP…...

微信 创建小程序码-有数量限制

获取小程序码&#xff1a;小程序码为圆图&#xff0c;有数量限制。 目录 文档 接口地址 功能描述 注意事项 请求参数 对接 获取小程序码 调用获取 小程序码示例 总结 文档 接口地址 https://api.weixin.qq.com/wxa/getwxacode?access_tokenaccess_token 功能描述 …...

重生之我在异世界学编程之C语言:操作符篇

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 引言正文1. 算术操作符2. 关系&#xff0…...

365天深度学习训练营-第P7周:马铃薯病害识别(VGG-16复现)

文为「365天深度学习训练营」内部文章 参考本文所写记录性文章&#xff0c;请在文章开头带上「&#x1f449;声明」 &#x1f37a; 要求&#xff1a; 自己搭建VGG-16网络框架【达成√】调用官方的VGG-16网络框架【达成√】如何查看模型的参数量以及相关指标【达成√】 &#…...

解密时序数据库的未来:TDengine Open Day技术沙龙精彩回顾

在数字化时代&#xff0c;开源已成为推动技术创新和知识共享的核心力量&#xff0c;尤其在数据领域&#xff0c;开源技术的涌现不仅促进了行业的快速发展&#xff0c;也让更多的开发者和技术爱好者得以参与其中。随着物联网、工业互联网等技术的广泛应用&#xff0c;时序数据库…...

Kubernetes 告警标签规范与最佳实践

1. 前言 在现代化的 Kubernetes 运维环境中,规范的告警标签系统对于快速定位和解决问题至关重要。本文将详细介绍告警标签的设计规范和最佳实践,帮助团队建立高效的告警处理流程。 © ivwdcwso (ID: u012172506) 2. 标签体系设计 2.1 基本概念 告警标签(Labels)是一…...

前端开发 之 15个页面加载特效中【附完整源码】

前端开发 之 15个页面加载特效中【附完整源码】 文章目录 前端开发 之 15个页面加载特效中【附完整源码】八&#xff1a;圆环百分比加载特效1.效果展示2.HTML完整代码 九&#xff1a;毒药罐加载特效1.效果展示2.HTML完整代码 十&#xff1a;无限圆环加载特效1.效果展示2.HTML完…...

rsync+nfs+lrsync服务部署流程

rsyncnfslrsync服务 主机信息 主机角色外网IP内网IP主机名nfs、lsync10.0.0.31176.16.1.31nfs客户端10.0.0.7176.16.1.7web01rsync、nfs10.0.0.41172.16.1.41backup 部署流程 1.backup服务器部署rsync --下载rsync服务 [rootbackup ~]# yum install -y rsync --配置rsync服…...

基于SpringBoot+Vue的宠物咖啡馆系统-无偿分享 (附源码+LW+调试)

目录 1. 项目技术 2. 功能菜单 3. 部分功能截图 4. 研究背景 5. 研究目的 6. 可行性分析 6.1 技术可行性 6.2 经济可行性 6.3 操作可行性 7. 系统设计 7.1 概述 7.2 系统流程和逻辑 7.3 系统结构 8. 数据库设计 8.1 数据库ER图 &#xff08;1&#xff09;宠物订…...

SQLServer 服务器只接受 TLS1.0,但是客户端给的是 TLS1.2

Caused by: javax.net.ssl.SSLHandshakeException: the server selected protocol version TLS10 is not accepted by client preferences [TLS12] 原因描述&#xff1a;SQLServer 服务器只接受 TLS1.0&#xff0c;但是客户端给的是 TLS1.2 解决方法如下&#xff1a; 打开文件…...

Golang内存模型总结1(mspan、mcache、mcentral、mheap)

1.内存模型 1.1 操作系统存储模型 从上到下分别是寄存器、高速缓存、内存、磁盘&#xff0c;其中越往上速度越快&#xff0c;空间越小&#xff0c;价格越高。 关键词是多级模型和动态切换 1.2 虚拟内存与物理内存 虚拟内存是一种内存管理技术&#xff0c;允许计算机使用比…...

lobeChat安装

一、安装Node.js version > v18.17.0 二、下载 cd F:\AITOOLS\LobeChat git clone https://github.com/lobehub/lobe-chat.git &#xff08;下载要是失败就手动下&#xff1a;https://codeload.github.com/lobehub/lobe-chat/zip/refs/heads/main&#xff09; npm install …...

Android学习8 -- NDK2--练习2(Opencv)

以下是一个简单的安卓项目示例&#xff0c;通过NDK调用OpenCV来处理图像&#xff08;例如&#xff0c;将彩色图像转换为灰度图像&#xff09;。 开发环境 安装 Android Studio&#xff08;支持NDK开发&#xff09;。配置NDK和CMake&#xff08;通过Android Studio的SDK Manage…...

nodejs循环导出多个word表格文档

文章目录 nodejs循环导出多个word表格文档一、文档模板编辑二、安装依赖三、创建导出工具类exportWord.js四、调用五、效果图nodejs循环导出多个word表格文档 结果案例: 一、文档模板编辑 二、安装依赖 // 实现word下载的主要依赖 npm install docxtemplater pizzip --save/…...

elasticsearch-如何给文档新增/更新的字段

文章目录 前言elasticsearch-如何给文档新增/更新的字段1. 如何给某些文档新增/更新的字段2. 给所有文档添加/更新一个新的字段3. 测试 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且…...

https/http访问接口工具类,附带ssl忽略证书验证,以及head头部的添加-java版

复制即用 package utils;import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;import javax.net.ssl.*; import java.io.BufferedReader; import java.io.IOException; impo…...

node.js基础学习-express框架-静态资源中间件express.static(十一)

前言 在 Node.js 应用中&#xff0c;静态资源是指那些不需要服务器动态处理&#xff0c;直接发送给客户端的文件。常见的静态资源包括 HTML 文件、CSS 样式表、JavaScript 脚本、图片&#xff08;如 JPEG、PNG 等&#xff09;、字体文件和音频、视频文件等。这些文件在服务器端…...

Python语法基础---正则表达式

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 我们这个文章所讲述的&#xff0c;也是数据分析的基础文章&#xff0c;正则表达式 首先&#xff0c;我们在开始之前&#xff0c;引出一个问题。也是我们接下来想要解决的问题。…...

Uniapp 微信小程序分享 - 自定义绘制分享图片

技术栈&#xff1a; Uniapp Vue3 简介 因实际业务需求&#xff0c;需要实现微信小程序自定义分享&#xff0c;根据当前数据动态生成&#xff08;绘制&#xff09;分享卡片的图片。 基础分享使用 配置此处不在赘述&#xff0c;可查看上篇博客&#xff1a;Uniapp 微信小程序分…...

鸿蒙技术分享:Navigation页面容器封装-鸿蒙@fw/router框架源码解析(三)

本文是系列文章&#xff0c;其他文章见&#xff1a;鸿蒙fw/router框架源码解析&#xff08;一&#xff09;-router页面管理鸿蒙fw/router框架源码解析&#xff08;二&#xff09;-Navigation页面管理鸿蒙fw/router框架源码解析&#xff08;四&#xff09;-路由Hvigor插件实现原…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

9-Oracle 23 ai Vector Search 特性 知识准备

很多小伙伴是不是参加了 免费认证课程&#xff08;限时至2025/5/15&#xff09; Oracle AI Vector Search 1Z0-184-25考试&#xff0c;都顺利拿到certified了没。 各行各业的AI 大模型的到来&#xff0c;传统的数据库中的SQL还能不能打&#xff0c;结构化和非结构的话数据如何和…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...