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

OpenGL ES 纹理以及纹理的映射

文章目录

    • 开启纹理
    • 创建纹理
    • 绑定纹理
    • 生成纹理
    • 纹理坐标
    • 图像配置
      • 线性插值
      • 重复效果
      • 限制拉伸
    • 完整代码

在 Android OpenGL ES 中使用纹理(Texture)可以显著提升图形渲染的质量和效率。以下是使用纹理的主要好处:

  1. 增强视觉真实感
    纹理可以将复杂的图像细节(如皮肤、木纹、砖石等)映射到简单的几何模型上,避免为每个细节创建复杂的几何体,从而用较低的计算成本实现高度真实的视觉效果。

  2. 减少内存占用
    通过纹理复用,可以避免为相似的物体重复定义顶点数据。
    使用同一张纹理图渲染多个相同的物体(如树木、建筑)
    利用纹理压缩技术(如 ETC、ASTC)进一步减少内存占用

  3. 提高渲染性能
    纹理在 GPU 中以高度优化的方式存储和处理,比动态生成的图形更高效。例如:
    使用预渲染的纹理代替实时计算(如阴影、光照效果)
    利用 Mipmapping 技术优化远处物体的纹理采样

  4. 实现复杂特效
    纹理不仅可以用于静态图像,还能实现各种高级特效:
    环境映射(Environment Mapping):模拟反射效果(如镜面、金属表面)
    法线贴图(Normal Mapping):通过纹理模拟表面细节,增加立体感
    程序纹理(Procedural Textures):动态生成纹理(如火焰、烟雾)
    多重纹理(Multi-texturing):叠加多个纹理创建复合效果(如地形 + 植被)

  5. 简化模型复杂度
    使用纹理可以将细节从几何模型转移到纹理图像中,从而:
    减少顶点数量,降低 GPU 处理负担
    简化模型设计流程,提高开发效率
    支持更复杂的视觉效果,而不增加渲染负担

  6. 跨平台兼容性
    OpenGL ES 纹理机制在不同设备上具有良好的一致性,使得:
    纹理资源可以在不同 Android 设备间复用
    便于将应用移植到其他支持 OpenGL ES 的平台

  7. 灵活的纹理控制
    通过调整纹理参数,可以实现丰富的视觉变化:
    过滤模式(Filtering):控制纹理缩放时的质量(如线性插值、最近邻采样)
    环绕模式(Wrapping):定义纹理坐标超出 [0,1] 范围时的行为(重复、镜像等)
    纹理混合(Blending):将纹理与颜色或其他纹理组合

开启纹理

为了使用纹理,需要开启一些开关启动一些功能。

  • 打开2D贴图
激活 OpenGL ES 上下文的 2D 纹理功能
允许后续的纹理操作(如绑定纹理、设置纹理参数)生效
确保在绘制时使用纹理坐标对纹理进行采样gl.glEnable(GL10.GL_TEXTURE_2D)
  • 打开混色
在 Android OpenGL ES 中,gl.glEnable(GL10.GL_BLEND) 
用于启用颜色混合功能,这是实现半透明效果、粒子系统、纹理叠加等视觉效果的关键技术。gl.glEnable(GL10.GL_BLEND) 
  • 设置混色

下面方法是 Android OpenGL ES 中用于设置标准透明度混合(Alpha Blending)的核心方法。
这个设置允许你实现半透明效果,让新绘制的物体能够以透明方式与背景融合。gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA)
  • OpenGL 支持的混色方案如下
因子描述
GL_ZERO因子 = (0, 0, 0, 0)
GL_ONE因子 = (1, 1, 1, 1)
GL_SRC_COLOR因子 = (Rs, Gs, Bs, As) (源颜色)
GL_ONE_MINUS_SRC_COLOR因子 = (1-Rs, 1-Gs, 1-Bs, 1-As) (1 - 源颜色)
GL_DST_COLOR因子 = (Rd, Gd, Bd, Ad) (目标颜色)
GL_ONE_MINUS_DST_COLOR因子 = (1-Rd, 1-Gd, 1-Bd, 1-Ad) (1 - 目标颜色)
GL_SRC_ALPHA因子 = (As, As, As, As) (源 Alpha 值)
GL_ONE_MINUS_SRC_ALPHA因子 = (1-As, 1-As, 1-As, 1-As) (1 - 源 Alpha 值)
GL_DST_ALPHA因子 = (Ad, Ad, Ad, Ad) (目标 Alpha 值)
GL_ONE_MINUS_DST_ALPHA因子 = (1-Ad, 1-Ad, 1-Ad, 1-Ad) (1 - 目标 Alpha 值)

通过合理选择混色方案,你可以在 Android OpenGL ES 应用中实现从简单半透明到复杂特效的各种视觉效果。

创建纹理

	// 存储纹理IDprivate val texture = IntArray(1)// 用于生成纹理ID是临时存储val textureArray = IntArray(1)// 生成纹理ID,存储到textureArray 中。gl.glGenTextures(1, textureArray, 0)// 保存ID到成员变量,后续使用texture[0] = textureArray[0]

生成纹理:glGenTextures 是 OpenGL 的核心函数,用于创建纹理对象
第一个参数 1:表示生成 1 个纹理
第二个参数 textureArray:存储生成的纹理 ID
第三个参数 0:表示从数组的第 0 个位置开始存储

绑定纹理

  • 纹理绑定的基本概念

      纹理绑定是指将一个纹理对象(通过 glGenTextures 生成)关联到当前 OpenGL 上下文的过程。绑定后,所有对纹理的操作(如设置参数、上传数据)都将作用于这个纹理对象。OpenGL ES 使用纹理单元(Texture Unit)机制来支持同时使用多个纹理。每个纹理单元可以绑定一个纹理对象,通过 glActiveTexture 切换当前使用的纹理单元。
    

纹理绑定本质

  • 将一个纹理对象(由 textureId 标识)关联到当前的纹理目标(如 GL_TEXTURE_2D)
  • 状态覆盖:每次调用 glBindTexture 都会覆盖之前的绑定状态
  • 最终生效:在绘制时,OpenGL 使用的是最后一次绑定的纹理
  • 绑定纹理函数
gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0])

第一个参数:纹理目标:指定纹理的类型,常见的有:
GL_TEXTURE_2D:二维纹理(最常用)
GL_TEXTURE_CUBE_MAP:立方体贴图(用于天空盒、反射等)
GL_TEXTURE_3D:三维纹理(OpenGL ES 3.0+ 支持)

第二个参数:纹理 ID:通过 glGenTextures 生成的唯一标识符,代表一个纹理对象

  • 为什么需要绑定纹理?
    OpenGL 使用状态机模型,所有操作都作用于当前绑定的对象。

绑定纹理的主要目的是:

  • 设置纹理参数:在绑定时设置过滤模式、环绕模式等
  • 上传纹理数据:将图像数据(如 Bitmap)加载到纹理中
  • 在绘制时使用纹理:确保正确的纹理被应用到几何体上

生成纹理

GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmapTexture, 0)

该方法参数如下:

public static void texImage2D(int target,     // 纹理目标类型int level,      // Mipmap 级别Bitmap bitmap,  // 源 Bitmap 对象int border      // 边框宽度(必须为 0)
)
  • 对bitmap的尺寸要求

2 的幂尺寸:传统 OpenGL ES 1.x 和部分设备要求纹理尺寸必须是 2 的幂(如 64×64、256×512)。若使用非 2 的幂(NPOT)纹理,可能需要额外设置纹理参数.
OpenGL ES 2.0+ 支持:现代设备通常支持任意尺寸(需检查 GL_MAX_TEXTURE_SIZE),但为兼容性建议使用 2 的幂尺寸。

纹理坐标

在 OpenGL ES 中,纹理坐标(Texture Coordinates)用于将 2D 图像(纹理)映射到 3D 模型的表面。理解纹理坐标的工作原理对实现正确的纹理映射至关重要。

  • 坐标系范围:纹理坐标使用标准化的 (s, t) 坐标系统,范围从 (0, 0) 到 (1, 1)。

    • s:水平方向(类似屏幕的 X 轴)。
    • t:垂直方向(类似屏幕的 Y 轴)。
  • 原点位置:(0, 0) 位于纹理的左下角,(1, 1) 位于右上角。

  • 与屏幕坐标的差异:屏幕坐标通常以左上角为原点,而 OpenGL 纹理坐标以左下角为原点,这可能导致纹理显示时上下颠倒(需特别处理)。

  • 纹理坐标与顶点坐标的映射

    • 纹理坐标需要与顶点坐标一一对应,以确定纹理如何被拉伸或扭曲到模型表面。以下是常见形状的映射示例:
      图中是一个正方形,对应原点为中心点,长度为1,对应的纹理坐标,可见左下角顶点坐标(-1,-1,0) 对应的纹理坐标的(0,0)
顶点坐标                 纹理坐标
(-1, 1, 0) ------------ (0, 1)|                       ||                       ||                       |
(-1,-1, 0) ------------ (0, 0)------------(1,-1, 0)   (1, 0)|||------------(1, 1, 0)   (1, 1)

对应代码如下:

// 顶点坐标(x, y, z)
float[] vertices = {-1f,  1f, 0f,  // 左上-1f, -1f, 0f,  // 左下1f, -1f, 0f,  // 右下1f,  1f, 0f   // 右上
};// 纹理坐标(s, t)
float[] texCoords = {0f, 1f,  // 左上0f, 0f,  // 左下1f, 0f,  // 右下1f, 1f   // 右上
};

图像配置

线性插值

在 OpenGL ES 中,纹理的线性插值(Linear Interpolation) 是一种纹理过滤技术,用于在纹理被缩放时生成平滑的视觉效果。与最近邻采样(GL_NEAREST)相比,线性插值通过计算相邻纹理像素(纹素)的加权平均值来减少锯齿和马赛克现象,尤其适用于纹理被缩小或旋转的场景。

  • 最近邻采样(Nearest Neighbor):

    • 直接选择离采样点最近的单个纹素值。
    • 优点:性能高,适合像素艺术风格。
    • 缺点:纹理缩放时会产生明显锯齿或马赛克。
  • 线性插值(Bilinear Filtering):

    • 在水平和垂直方向各取 2×2 个纹素,计算采样点周围纹素的加权平均值。
    • 优点:平滑过渡,减少锯齿。
    • 缺点:模糊化(尤其在大幅缩小时),性能略低于最近邻。
  • 启用线性插值

    • 通过 glTexParameteri() 设置纹理过滤模式:
// 绑定纹理后设置过滤模式
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);// 设置缩小过滤为线性插值(纹理被缩小时)
gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);// 设置放大过滤为线性插值(纹理被放大时)
gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
**GL_LINEAR 代表OpenGL采用简单的线性插值方式调整图像。**

为了将图标贴到正方体的一个面,绘制时根据面来设置纹理,最后会贴出全部代码。增加纹理部分如下:

private fun setTexture(gl: GL10) {gl.glEnable(GL10.GL_TEXTURE_2D)gl.glEnable(GL10.GL_BLEND)gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA)val textureArray = IntArray(1)gl.glGenTextures(1, textureArray, 0)texture[0] = textureArray[0]// 绑定纹理gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0])// 当需要缩小时采用线性插值方式gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR)// 当需要放大时采用线性插值的方式gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR)GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmapTexture, 0)}

绘制时采用纹理的代码如下(只针对其中一个面):

				// 目标面启用纹理gl.glEnable(GL10.GL_TEXTURE_2D)gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0])gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY)gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textCoordsSquare)
public void glTexCoordPointer(int size,        // 每个纹理坐标的分量数int type,        // 数据类型int stride,      // 相邻两个纹理坐标之间的字节偏移量java.nio.Buffer pointer  // 数据缓冲区
)
  • size(每个纹理坐标的分量数)取值:必须为 2、3 或 4。

    • 2表示 (s, t) 坐标(最常用)。
    • 3表示 (s, t, r) 坐标(用于 3D 纹理,OpenGL ES 中较少使用)。
    • 4表示 (s, t, r, q) 坐标(用于齐次坐标,OpenGL ES 中极少使用)。
  • pointer(数据缓冲区)

    • 类型:java.nio.Buffer(通常为 FloatBuffer)。
    • 含义:存储纹理坐标数据的缓冲区,必须为直接缓冲区(Direct Buffer)。

绘制效果如下:Android的Logo。

在这里插入图片描述

备注:如果一个四边形的图片通过纹理的方式贴到三角形上,则选取正方形的三个角的纹理点,对应三角形坐标点。选取的三角形部分会绘制到对应三角形上。

重复效果

先将纹理坐标超过 1.0

// 纹理坐标(左下角为(0,0),右上角为(1,1))private val textureCoords = floatArrayOf(0.0f, 2f,  // 左下 V02f, 2f,  // 右下 V12f, 0.0f,  // 右上 V20.0f, 0.0f   // 左上 V3)

然后增加设置重复设置

		// 设置纹理环绕模式(重复)gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT)gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT)

效果如下:

在这里插入图片描述
GL_REPEAT:纹理会在超出部分重复平铺。例如,纹理坐标为 1.5 时,会使用 0.5 处的纹理数据,从而实现纹理在整个表面上重复铺设的效果,常用于制作无缝纹理背景。

限制拉伸

OpenGLES可以设置简单地将纹理坐标超过1.0的值限制为1.0,任何低于0.0的值限制为0.0。这实际会引起边沿像素重复。
将重复设置修改为限制拉伸设置

        gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE)gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE)

效果如下:只显示了左上角一个图标。
在这里插入图片描述

完整代码

class SquareWithTextureRenderer(private val context: Context) : GLSurfaceView.Renderer {private val vertexBuffer: FloatBufferprivate val indexBuffer: ShortBufferprivate val textureCoordsBuffer: FloatBufferprivate val normalsBuffer: FloatBufferprivate val texture: IntArrayprivate val mBitmapTexture: Bitmapprivate var angle = 0f// 正方形的4个顶点坐标(中心在原点,边长为1)private val vertices = floatArrayOf(-0.5f, -0.5f, 0.0f,  // 左下 V00.5f, -0.5f, 0.0f,   // 右下 V10.5f, 0.5f, 0.0f,    // 右上 V2-0.5f, 0.5f, 0.0f    // 左上 V3)// 两个三角形组成正方形的顶点索引private val indices = shortArrayOf(0, 1, 2,  // 第一个三角形2, 3, 0   // 第二个三角形)// 正方形顶点的法线(全部朝向屏幕外,即Z轴正方向)private val normals = floatArrayOf(0.0f, 0.0f, 1.0f,  // 所有顶点法线相同0.0f, 0.0f, 1.0f,0.0f, 0.0f, 1.0f,0.0f, 0.0f, 1.0f)// 纹理坐标(左下角为(0,0),右上角为(1,1))private val textureCoords = floatArrayOf(0.0f, 2f,  // 左下 V02f, 2f,  // 右下 V12f, 0.0f,  // 右上 V20.0f, 0.0f   // 左上 V3)init {// 初始化顶点缓冲区val vbb = ByteBuffer.allocateDirect(vertices.size * 4)vbb.order(ByteOrder.nativeOrder())vertexBuffer = vbb.asFloatBuffer()vertexBuffer.put(vertices)vertexBuffer.position(0)// 初始化索引缓冲区val ibb = ByteBuffer.allocateDirect(indices.size * 2)ibb.order(ByteOrder.nativeOrder())indexBuffer = ibb.asShortBuffer()indexBuffer.put(indices)indexBuffer.position(0)// 初始化纹理坐标缓冲区val tcb = ByteBuffer.allocateDirect(textureCoords.size * 4)tcb.order(ByteOrder.nativeOrder())textureCoordsBuffer = tcb.asFloatBuffer()textureCoordsBuffer.put(textureCoords)textureCoordsBuffer.position(0)// 初始化法线缓冲区val nb = ByteBuffer.allocateDirect(normals.size * 4)nb.order(ByteOrder.nativeOrder())normalsBuffer = nb.asFloatBuffer()normalsBuffer.put(normals)normalsBuffer.position(0)// 加载纹理图片texture = IntArray(1)mBitmapTexture = BitmapFactory.decodeResource(context.resources, R.drawable.ic_l)}override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f)gl.glEnable(GL10.GL_DEPTH_TEST)gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)gl.glEnableClientState(GL10.GL_NORMAL_ARRAY)setupLight(gl)setMaterial(gl)setTexture(gl)}private fun setTexture(gl: GL10) {gl.glEnable(GL10.GL_TEXTURE_2D)gl.glEnable(GL10.GL_BLEND)gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA)val textureArray = IntArray(1)gl.glGenTextures(1, textureArray, 0)texture[0] = textureArray[0]// 绑定纹理gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0])// 设置纹理过滤gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR)gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR)// 设置纹理环绕模式
//        gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT)
//        gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT)gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE)gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE)// 加载纹理GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmapTexture, 0)mBitmapTexture.recycle() // 纹理加载后回收Bitmap}override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {gl.glViewport(0, 0, width, height)gl.glMatrixMode(GL10.GL_PROJECTION)gl.glLoadIdentity()val aspectRatio = width.toFloat() / heightGLU.gluPerspective(gl, 45.0f, aspectRatio, 0.1f, 1000.0f)gl.glMatrixMode(GL10.GL_MODELVIEW)gl.glLoadIdentity()}override fun onDrawFrame(gl: GL10) {gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)gl.glLoadIdentity()gl.glShadeModel(GL10.GL_SMOOTH)gl.glTranslatef(0.0f, 0f, -3.0f) // 拉近正方形,使其更清晰
//        gl.glRotatef(angle, 0.0f, 0.0f, 1.0f) // 绕Z轴旋转//        angle += 1.0f // 旋转角度递增// 设置顶点、法线和纹理坐标gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer)gl.glNormalPointer(GL10.GL_FLOAT, 0, normalsBuffer)gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY)gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureCoordsBuffer)// 启用纹理gl.glEnable(GL10.GL_TEXTURE_2D)gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0])// 绘制正方形(两个三角形)gl.glDrawElements(GL10.GL_TRIANGLES, indices.size,GL10.GL_UNSIGNED_SHORT, indexBuffer)// 禁用纹理和缓冲区gl.glDisable(GL10.GL_TEXTURE_2D)gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY)}/*** 设置光效*/private fun setupLight(gl: GL10) {// 启用光照gl.glEnable(GL10.GL_LIGHTING)gl.glEnable(GL10.GL_LIGHT0)gl.glEnable(GL10.GL_SPECULAR)// 设置环境光val ambientLight = floatArrayOf(0.2f, 0.2f, 0.2f, 1.0f)gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, ambientLight, 0)// 设置漫反射光val diffuseLight = floatArrayOf(0.8f, 0.8f, 0.8f, 1.0f)gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, diffuseLight, 0)// 设置高光val specularLight = floatArrayOf(1.0f, 1.0f, 1.0f, 1.0f)gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, specularLight, 0)// 设置光源位置(位于正方形前方)val lightPosition = floatArrayOf(0.0f, 0.0f, 1.0f, 0.0f)gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPosition, 0)}private fun setMaterial(gl: GL10) {// 环境元素val ambientLight = floatArrayOf(0f, 0.1f, 0.9f, 1.0f)gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, ambientLight, 0)// 散射元素val diffuseLight = floatArrayOf(0.9f, 0f, 0.1f, 1.0f)gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, diffuseLight, 0)// 高光元素val specularLight = floatArrayOf(0.9f, 0.9f, 0f, 1.0f)gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, specularLight, 0)// 反光度gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 25F)// 自发光val emissionLight = floatArrayOf(0.0f, 0.4f, 0f, 1.0f)gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_EMISSION, emissionLight, 0)}
}
http://www.dtcms.com/a/267923.html

相关文章:

  • 【一起来学AI大模型】数据处理核心:NumPy/Pandas/Matplotlib 精要指南
  • HarmonyOS开发实战:鸿蒙分布式生态构建与多设备协同发布全流程详解
  • Flink ClickHouse 连接器数据写入源码深度解析
  • Qt实战:使用QSqlDatabase连接MySQL,并实现增删改查
  • JavaFX项目的搭建【授课用】
  • Qt:QWidget常用属性
  • NV205NV209美光固态闪存NV210NV215
  • QT并发机制
  • Qt实现外网双向音视频通话/支持嵌入式板子/实时性好延迟低/可以加水印
  • Linux系统移植(7.4)
  • C#实现CAN通讯接口
  • python-if结构、三目运算符
  • 一个简单的脚本,让pdf开启夜间模式
  • 【IOS】XCode创建firstapp并运行(成为IOS开发者)
  • Maixcam的使用3程序打包
  • 【机器学习笔记Ⅰ】13 正则化代价函数
  • 2025年6月AIGC发展全景:技术轻量化、Agent产业化与伦理新挑战
  • bottles安装网易云出现的问题01中文出现乱码问题
  • 等保测评-Apache Tomcat中间件
  • SpringMVC参数接收与数据返回详解
  • MySQL 8.0 主从复制原理分析与实战
  • 传统微商困境与开源链动2+1模式、AI智能名片及S2B2C商城小程序的转型破局
  • 数据挖掘:从理论到实践的深度探索
  • 基于腾讯云开发与“人·事·财·物”架构理念的家政预约小程序设计与实现
  • 【PyTorch】PyTorch中torch.nn模块的卷积层
  • 10.1《3步用ChatGPT+LangChain打造高质量私有数据集,模型效果提升200%》
  • Java多线程知识小结:Synchronized
  • Flink ClickHouse 连接器数据读取源码深度解析
  • G-sensor运动检测功能开源:打破技术壁垒,加速智能硬件开发!
  • Java JDBC的初步了解