计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 08.阴影
阴影
没有阴影的渲染效果如下,看起来不真实:
有阴影的渲染效果如下,看起来真实:
显示阴影有两种方式,一种是原书中的方式,另一种是采用光线追踪技术,该技术可以参考ShaderToy学习笔记 08.阴影
投影阴影
将物体上的点(xW, yW, zW)变换为相应阴影在平面上的点(xS, 0, zS)。之后将其生成的“阴影多边形”绘制出来,通常使用暗色物体与地平面纹理混合作为其纹理。通常,投影阴影会使用暗色的多边形与地平面的纹理混合,以模拟真实的阴影效果。这种方法简单高效,适用于静态或简单的动态场景,但在复杂场景中可能会显得不够真实。
阴影体
阴影体(Shadow Volume)是一种用于生成阴影的技术,它通过几何体的扩展来定义阴影的体积。具体来说,阴影体是从光源出发,通过物体的几何形状向外延伸,形成一个三维的体积,这个体积代表了光线被遮挡的区域。
在渲染时,阴影体会与场景中的其他几何体进行交互,通过计算哪些像素位于阴影体内,从而确定哪些区域应该被渲染为阴影。这种方法可以生成精确的阴影效果,尤其适用于动态场景。
阴影体的优点是可以生成清晰且准确的阴影边界,但其计算量较大,尤其是在处理复杂几何体时,会影响性能。
阴影贴图
阴影贴图(Shadow Mapping)是一种基于纹理的阴影生成技术,用于在3D场景中模拟光源投射的阴影。其基本原理是通过两次渲染过程来确定场景中哪些区域被遮挡。
阴影贴图基于一个非常简明的想法:光线无法“看到”的任何东西都在阴影中。也就是说,如果对象 1 阻挡光线到达对象 2,等同于光线不能“看到”对象 2。
-
第一步:深度图生成
从光源的视角渲染场景,并记录每个像素到光源的深度值,生成一张深度纹理(称为阴影贴图)。这张贴图表示光源视角下场景中最接近光源的表面。这一步不需要实际渲染场景,可以禁用颜色输出。
OpenGL 提供了两种将深度缓冲区深度数据放入纹理单元的方法。- 生成空阴影纹理,然后使用命令 glCopyTexImage2D()将活动的深度缓冲区复制到阴影纹理中。
- 构建一个“自定义帧缓冲区”(而不是使用默认的深度缓冲区),并使用命令 glFrameBufferTexture()将阴影纹理附加到它上面。OpenGL 在 3.0 版中引入该命令,以进一步支持阴影纹理。使用这种方法时,无须将深度缓冲区“复制”到纹理中,因为缓冲区已经附加了纹理,深度信息由 OpenGL 自动放入纹理中。我们采用这种方法。
-
第二步:阴影检测
在实际渲染时,从摄像机的视角渲染场景。对于每个像素,将其转换到光源的视角,并与阴影贴图中的深度值进行比较。如果像素的深度值大于阴影贴图中的值,则说明该像素被遮挡,应渲染为阴影。
优点:
- 支持动态场景,适合实时渲染。
- 实现相对简单,适用于各种光源类型。
缺点:
- 可能出现锯齿或伪影(如阴影边缘的锯齿),需要使用抗锯齿技术(如PCF)。
- 受分辨率限制,高分辨率阴影贴图会占用更多内存和计算资源。
阴影贴图中的深度值可以用Z-Buffer(深度缓冲区)来表示。
Z-buffer(深度缓冲区)是一种用于处理3D图形渲染中深度信息的技术。它通过记录场景中每个像素的深度值(即距离摄像机的距离)来确定哪些物体或部分应该被显示,哪些应该被遮挡。
工作原理:
- 初始化深度缓冲区:在开始渲染时,Z-buffer 会被初始化为一个默认值(通常是最大深度值)。
- 逐像素深度比较:
- 在渲染每个像素时,计算该像素的深度值。
- 将该深度值与 Z-buffer 中对应位置的值进行比较。
- 如果当前像素的深度值更小(即更靠近摄像机),更新 Z-buffer,并渲染该像素。
- 如果当前像素的深度值更大,则忽略该像素(被遮挡)。
- 最终结果:通过这种逐像素的深度比较,Z-buffer 确保了场景中最近的物体被正确渲染,而远处的物体被遮挡。
优点:
- 高效:适合实时渲染,广泛应用于现代图形硬件。
- 通用性:支持复杂的场景和任意几何形状。
- 自动遮挡处理:无需手动排序物体。
缺点:
- 精度问题:Z-buffer 的精度取决于其位深(如 16 位、24 位等)。在远近平面距离较大时,可能会出现深度冲突(Z-fighting)。
- 内存占用:需要额外的内存来存储深度缓冲区。
策略
- 第一轮渲染:从光源的视角渲染场景,生成深度贴图。
- 第二轮渲染:从摄像机的视角渲染场景,使用深度贴图进行阴影检测。如果某个像素的深度值大于阴影贴图中的值,则该像素被遮挡,渲染为阴影。可以通过仅渲染其环境光,来模拟阴影效果。
阴影贴图 第一轮渲染:从光源的视角渲染场景,生成深度贴图
- 将相机移动到光源的位置
- 配置缓冲区和阴影贴图
- 禁用颜色输出
- 构建LookAt矩阵
- 使用LookAt矩阵和投影矩阵构建模型视图投影矩阵,称为shadowMVP
- 每个顶点无需法线,光照,贴图等uniform变量
- 渲染场景,将深度信息写入阴影贴图。无颜色输出
顶点着色器
#version 430layout (location =0 ) in vec3 position;uniform mat4 shadowMVP; // 变换矩阵
void main()
{gl_Position = shadowMVP*vec4(position,1.0);
}
片段着色器
无需输出颜色。由渲染管线自动完成深度信息写入深度缓冲区的步骤。
#version 430void main()
{}
渲染cpp代码
//shadowMVP 矩阵shadowMVP = lightPmatrix * lightVmatrix * mMat;// 从光源的视角渲染场景,生成深度贴图,相较于前面章节代码glClear(GL_DEPTH_BUFFER_BIT);glEnable(GL_DEPTH_TEST);glEnable(GL_CULL_FACE);glFrontFace(GL_CCW);glDepthFunc(GL_LEQUAL);
第二轮渲染:从摄像机的视角渲染场景,使用深度贴图进行阴影检测
从摄像机的视角渲染场景,使用深度贴图进行阴影检测。如果某个像素的深度值大于阴影贴图中的值,则该像素被遮挡,渲染为阴影。可以通过仅渲染其环境光,来模拟阴影效果。
这中间核心要素是拿到阴影贴图中每个像素的深度值,由于OpenGL相机使用[-1,1]的坐标系,而深度贴图使用[0,1]的坐标系,所以需要采用B 矩阵将摄像机空间转换为纹理空间,即先缩放为1/2,再平移1/2
B = [ 0.5 0 0 0.5 0 0.5 0 0.5 0 0 0.5 0.5 0 0 0 1 ] B=\begin{bmatrix} 0.5& 0 & 0 & 0.5\\ 0 & 0.5& 0 & 0.5\\ 0 & 0 & 0.5& 0.5\\ 0 & 0 & 0 & 1 \end{bmatrix} B= 0.500000.500000.500.50.50.51
即shadowMVP2 = B * shadowMVP
步骤
- 构建B 矩阵
- 启用阴影纹理以进行查找
- 启用颜色输出
- 启用GLSL进行第二轮渲染
顶点着色器
#version 430
#version 430// 点光源结构体定义
struct PositionalLight {vec4 ambient; // 环境光分量vec4 diffuse; // 漫反射分量vec4 specular; // 镜面反射分量vec3 position; // 光源位置
};// 材质结构体定义
struct Material {vec4 ambient; // 材质的环境光反射系数vec4 diffuse; // 材质的漫反射系数vec4 specular; // 材质的镜面反射系数float shininess; // 镜面反射的光泽度
};layout(location = 0) in vec3 position;
layout(location = 1) in vec2 tex_coord;
layout(location = 2) in vec3 vertNormal;
out vec2 tc;out vec3 varyingNormal;
out vec3 varyingLightDir;
out vec3 varyingVertPos;uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 norm_matrix;uniform vec4 globalAmbient; // 全局环境光uniform PositionalLight light; // 点光源属性
uniform Material material; // 物体材质属性uniform mat4 shadowMVP; // 光源变换矩阵
layout(binding = 0) uniform sampler2DShadow s;out vec4 shadow_coord;void main(void) {gl_Position = proj_matrix * mv_matrix * vec4(position, 1.0);varyingVertPos = (mv_matrix * vec4(position, 1.0)).xyz;varyingNormal = (norm_matrix * vec4(vertNormal, 1.0)).xyz;varyingLightDir = light.position - varyingVertPos;tc = tex_coord;shadow_coord = shadowMVP * vec4(position, 1.0);
}
片段着色器
其中我们采用textureProj
函数来计算阴影值,该函数计算的是投影纹理坐标,其原理如下:
#version 430// 点光源结构体定义
struct PositionalLight {vec4 ambient; // 环境光分量vec4 diffuse; // 漫反射分量vec4 specular; // 镜面反射分量vec3 position; // 光源位置
};// 材质结构体定义
struct Material {vec4 ambient; // 材质的环境光反射系数vec4 diffuse; // 材质的漫反射系数vec4 specular; // 材质的镜面反射系数float shininess; // 镜面反射的光泽度
};in vec3 varyingNormal;
in vec3 varyingLightDir;
in vec3 varyingVertPos;in vec2 tc;
in vec4 shadow_coord; // 传入的阴影坐标
out vec4 color;uniform mat4 mv_matrix;
uniform mat4 proj_matrix;uniform vec4 globalAmbient; // 全局环境光uniform PositionalLight light; // 点光源属性
uniform Material material; // 物体材质属性uniform mat4 norm_matrix; // 法线变换矩阵
uniform mat4 shadowMVP; // 光源变换矩阵
layout(binding = 0) uniform sampler2DShadow s;void main(void) {//vec4 texColor = texture(s,tc);// 标准化光照计算所需的向量vec3 N = normalize(varyingNormal);vec3 L = normalize(varyingLightDir);vec3 V = normalize(-varyingVertPos);// 计算漫反射分量float lambertTerm = max(0, dot(N, L));vec4 Id = light.diffuse * material.diffuse * lambertTerm;// 计算镜面反射分量//vec3 R = reflect(-L,N);vec3 halfVector = normalize(L + V);float specular = pow(max(dot(N, halfVector), 0), material.shininess);vec4 Is = light.specular * material.specular * specular;// 计算环境光分量vec4 Ia = globalAmbient * material.ambient;// 计算最终颜色值 //采用纹理贴图//color = texColor*(globalAmbient+light.ambient + light.diffuse*(max(dot(N,L),0.0f))) +light.specular*pow(max(dot(N,halfVector),0.0f),material.shininess);float notInShadow = 1.0;notInShadow = textureProj(s, shadow_coord);//采用物体材质if(notInShadow > 0.50) {color = Ia + Id + Is;} else {color = Ia;//color=Ia+Id+Is;}}
左图是没有阴影的,右图是添加阴影的。
伪影
上图中有明显的伪影,或称为阴影痤疮,这是由于深度测试的精度问题导致的。在阴影纹理中查找深度信息时计算的纹理坐标通常与实际坐标不完全一至,因此从阴影纹理中查找到的深度值可能与实际深度值存在一定的误差,当误差超过一定阈值时,就会导致阴影痤疮。
阴影痤疮通常发生在没有阴影的表面,常见的一种解决办法是在第1轮中将每个像素稍微移向光源,之后在第2轮中再移回来,这样就可以消除阴影痤疮。
//减少阴影痤疮glEnable(GL_POLYGON_OFFSET_FILL);glPolygonOffset(2.0f, 4.0f);
这是改善后的效果:
关于阴影贴图
有一个好用的插件可以查看图片文件的16进制数据,Hex Editor
我们可以用以下代码将阴影贴图保存为图片文件,方便我们查看。
void saveShadowMapToFile(const char *filename, GLuint shadowTexture, int width, int height)
{glBindTexture(GL_TEXTURE_2D, shadowTexture);GLfloat *data = new GLfloat[width * height];glGetTexImage(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GL_FLOAT, data);unsigned char *data2 = new unsigned char[width * height * 3];for (int i = 0; i < width * height; i++){data2[i * 3 + 0] = (unsigned char)(data[i] * 255);data2[i * 3 + 1] = (unsigned char)(data[i] * 255);data2[i * 3 + 2] = (unsigned char)(data[i] * 255);}int success = SOIL_save_image(filename, SOIL_SAVE_TYPE_BMP, width, height, 3, data2);if (success == 0){std::cout << "Error saving shadow map to file: " << SOIL_last_result() << std::endl;}else{std::cout << "Shadow map saved to file: " << filename << std::endl;}delete[] data;
}
下图是保存的阴影贴图:
柔和阴影
柔和阴影是阴影的一种,它不是完全硬的,而是有过渡的,过渡区域是阴影和光照区域的混合区域。
上图是现实中的柔和阴影
上图是柔和阴影的半影效果 ,可以理解为半影区域是柔和阴影的过渡区域,半影区域越宽,过渡越柔和。
生成柔和阴影 -百分比邻近滤波(PCF)
PCF的核心思想是对阴影贴图中的多个相邻像素进行采样并平均其结果,而不是仅仅采样单个像素点。具体来说:
-
多点采样:在进行阴影测试时,不仅仅对当前像素对应的阴影贴图位置进行采样,还会对其周围的多个点进行采样
-
深度比较:对每个采样点进行深度比较,判断是否在阴影中
-
结果平均:将所有采样点的阴影测试结果(0表示阴影,1表示非阴影)进行加权平均
这种方法产生的结果是阴影边缘不再是二元的(要么完全在阴影中,要么完全不在),而是有一个平滑的过渡区域,模拟了现实世界中的半影效果。
上图中黄色点是当前像素,其不在阴影中,其周围的8个点中(含自身总计9个点),有3个点在阴影中,因此该像素的颜色可以是半影。
一种用于实现 PCF 的常见算法是对每个像素附近的 4 个纹元进行采样, 其中样本通过像素对应纹元的特定偏移量选择。 对于每个像素, 我们都需要改变偏移量,并用新的偏移量确定采样的 4 个纹元。使用交错的方式改变偏移量的方法被称为抖动,它旨在使柔和阴影的边界不会由于采样点不足而看起来“结块”。
一种常见的方法是假设有 4 种不同偏移模式,每次取其中一种——我们可以通过计算像素的glFragCoord mod 2 值来选择当前像素的偏移模式。之前有提到, glFragCoord 是 vec2 类型,包含像素位置的 x、 y 坐标。因此, mod 计算的结果有 4 种可能的值: (0,0)、 (0,1)、 (1,0)或(1,1)。我们使用glFragCoord mod 2 的结果来从纹元空间(即阴影贴图)的 4 种不同偏移模式中选择一种。
偏移模式通常在 x 和 y 方向上指定,具有−1.5、 −0.5、 +0.5 和+1.5 的不同组合(也可以根据需要进行缩放)。
sx 和 sy 指与正在渲染的像素对应的阴影贴图中的位置(sx,sy),在本章的代码示例中标识为shadow_coord
抖动的4像素PCF采样示例,图中白色的点即为当前(sx,sy)
我们可以在片段着色器中实现 PCF,代码如下:
片段着色器
#version 430// 点光源结构体定义
struct PositionalLight {vec4 ambient; // 环境光分量vec4 diffuse; // 漫反射分量vec4 specular; // 镜面反射分量vec3 position; // 光源位置
};// 材质结构体定义
struct Material {vec4 ambient; // 材质的环境光反射系数vec4 diffuse; // 材质的漫反射系数vec4 specular; // 材质的镜面反射系数float shininess; // 镜面反射的光泽度
};in vec3 varyingNormal;
in vec3 varyingLightDir;
in vec3 varyingVertPos;in vec2 tc;
in vec4 shadow_coord; // 传入的阴影坐标
out vec4 color;uniform mat4 mv_matrix;
uniform mat4 proj_matrix;uniform vec4 globalAmbient; // 全局环境光uniform PositionalLight light; // 点光源属性
uniform Material material; // 物体材质属性uniform mat4 norm_matrix; // 法线变换矩阵
uniform mat4 shadowMVP; // 光源变换矩阵
layout(binding = 0) uniform sampler2DShadow s;float lookup(float ox,float oy) {// 计算阴影贴图坐标// 注意:此处的0.001 是1/windowSize.x ,目前的windowSize.x=1000 。目前屏幕的大小为1000*1000// 第三个参数-0.01 可用于消除阴影痤疮的偏移量float t=textureProj(s,shadow_coord+vec4(ox*0.001*shadow_coord.w,oy*0.001*shadow_coord.w,-0.01,0.0));return t;
}void main(void) {float shadowFactor = 0.0;//vec4 texColor = texture(s,tc);// 标准化光照计算所需的向量vec3 N = normalize(varyingNormal);vec3 L = normalize(varyingLightDir);vec3 V = normalize(-varyingVertPos);// 计算漫反射分量float lambertTerm = max(0, dot(N, L));vec4 Id = light.diffuse * material.diffuse * lambertTerm;// 计算镜面反射分量//vec3 R = reflect(-L,N);vec3 halfVector = normalize(L + V);float specular = pow(max(dot(N, halfVector), 0), material.shininess);vec4 Is = light.specular * material.specular * specular;// 计算环境光分量vec4 Ia = globalAmbient * material.ambient;// 计算最终颜色值 //采用纹理贴图//color = texColor*(globalAmbient+light.ambient + light.diffuse*(max(dot(N,L),0.0f))) +light.specular*pow(max(dot(N,halfVector),0.0f),material.shininess);// PCF float swidth=2.5; //阴影扩散量的宽度vec2 offset=mod(floor(gl_FragCoord.xy),2.0)*swidth;shadowFactor+=lookup(-1.5*swidth+offset.x,1.5*swidth-offset.y);shadowFactor+=lookup(-1.5*swidth+offset.x,-0.5*swidth-offset.y);shadowFactor+=lookup(0.5*swidth+offset.x,1.5*swidth-offset.y);shadowFactor+=lookup(0.5*swidth+offset.x,-0.5*swidth-offset.y);shadowFactor=shadowFactor/4.0;vec4 shadowColor=Ia;vec4 lightdColor=Id+Is;color=vec4(shadowColor.xyz+lightdColor.xyz*shadowFactor,1.0f);}
重点:
lookup
函数,该函数用于计算阴影贴图中的采样点。- shadowFactor,该变量用于存储阴影贴图中的采样结果,用于计算阴影因子。
运行结果 : 能看到左边的阴影要比右边的阴影更柔和,更自然。
疑问:
- 为什么左边有一块区域是有亮光,但其位置在右边是阴影?
- torus左边是有光亮,这块区域是否应有阴影?即其是否被遮挡?还是因光源位置原因造成torus左边是没有被遮挡且有光亮?
参考
- 学习笔记完整代码下载
- ShaderToy学习笔记 08.阴影
相关文章:
计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 08.阴影
阴影 没有阴影的渲染效果如下,看起来不真实: 有阴影的渲染效果如下,看起来真实: 显示阴影有两种方式,一种是原书中的方式,另一种是采用光线追踪技术,该技术可以参考ShaderToy学习笔记 08.阴…...
[面试]SoC验证工程师面试常见问题(七)低速接口篇
SoC验证工程师面试常见问题(七)低速接口篇 摘要:低速接口是嵌入式系统和 SoC (System on Chip) 中常用的通信接口,主要用于设备间的短距离、低带宽数据传输。相比高速接口(如 PCIe、USB 3.0),低速接口的传输速率较低(通常在 kbps 到几 Mbps 范围),但具有简单…...
算法训练营第十三天|226.翻转二叉树、101. 对称二叉树、 104.二叉树的最大深度、111.二叉树的最小深度
递归 递归三部曲: 1.确定参数和返回值2.确定终止条件3.确定单层逻辑 226.翻转二叉树 题目 思路与解法 第一想法: 递归,对每个结点进行反转 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, le…...
电子电器架构 --- 车载网关的设计
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界…...
`C_PiperInterface` 类接口功能列表
C_PiperInterface 类接口功能列表 C_PiperInterface 提供了全面的接口,用于控制 Piper 机械臂的运动、查询状态、设置参数以及管理 SDK 限制。 官仓链接 以下是 C_PiperInterface 类中所有接口的功能总结: 1. 初始化与连接相关接口 __new__: 实现单例…...
D. Apple Tree Traversing 【Codeforces Round 1023 (Div. 2)】
D. Apple Tree Traversing 题目大意 有一个包含 n n n 个节点的苹果树,初始时每个节点上有一个苹果。你有一张纸,初始时纸上没有任何内容。 你需要通过以下操作遍历苹果树,直到所有苹果都被移除: • 选择一个苹果路径 ( u , v…...
Docker镜像搬运工:save与load命令的实战指南
在日常的容器化开发中,镜像的搬运和部署是每个开发者必须掌握的技能。今天我们将深入探讨Docker的"save"和"load"这对黄金搭档,揭秘它们在镜像管理中的妙用。 一、基础认知:镜像的打包与解包 docker save 和 docker loa…...
查看Electron 应用的调试端口
以下是一些可以知道已发布第三方 Electron 应用调试端口的方法: * **通过命令行参数查看** : * 如果该 Electron 应用在启动时添加了类似 --remote-debugging-portxxxx 或 --inspectxxxx 的参数,那么其调试端口就是该参数指定的端口号。比…...
各种环境测试
加载测试专用属性 当在测试时想要加入某些配置且对其他测试类不产生影响是可以用Import注释添加配置 测试类中启动web环境 默认为none不开启...
腾讯云低代码实战:零基础搭建家政维修平台
目录 1. 欢迎与项目概览1.1 教程目的与受众1.2 项目愿景与目标:我们要搭建一个怎样的平台?1.3 平台核心构成与架构解析1.4 技术栈选择与考量1.5 如何高效阅读本教程 欢迎来到“腾讯云云开发低代码实战:从零搭建家政维修服务平台”开发教程&am…...
居然智家亮相全零售AI火花大会 AI大模型赋能家居新零售的进阶之路
当人工智能技术以摧枯拉朽之势重构商业世界时,零售业正在经历一场静默而深刻的革命。在这场变革中,居然智家作为新零售领域的创新标杆,凭借其在AI技术应用上的超前布局和持续深耕,已悄然构建起从消费场景到产业生态的智能化闭环。…...
微服务6大拆分原则
微服务6大拆分原则 微服务拆分是指将一个大型应用程序拆分成独立服务的过程,在微服务拆分时,需要考虑以下6大微服务拆分原则 一、单一职责原则 微服务单一职责原则,是指每个微服务应该专注于解决一个明确定义的业务领域或功能,…...
进程间通信--管道【Linux操作系统】
文章目录 进程间通信(IPC)进程间通信的目的1. 数据交换2. 资源共享3. 进程协同4. 系统解耦5. 分布式计算IPC 的典型方式对比总结 进程间通信的前提 匿名管道匿名管道的原理创建匿名管道的过程如果不关闭不需要的读写端会怎样?为什么父进程要同…...
模型实时自主训练系统设计
模型实时自主训练系统设计 一、系统架构 #mermaid-svg-MLuTBuo7ehvStoqS {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-MLuTBuo7ehvStoqS .error-icon{fill:#552222;}#mermaid-svg-MLuTBuo7ehvStoqS .error-text{f…...
5.1 神经网络: 层和块
1 层(Layer) 1.1 定义 层是深度学习模型中的基本构建单元,它由一组神经元组成,负责对输入数据进行特定的数学运算和变换,以提取数据的某种特征或表示。每一层可以看作是一个函数,它接收输入数据ÿ…...
鸿蒙系统使用ArkTS开发语言支持身份证阅读器、社保卡读卡器等调用二次开发SDK
har库导入: { "license": "", "devDependencies": {}, "author": "", "name": "entry", "description": "Please describe the basic information.", &qu…...
【Bootstrap V4系列】学习入门教程之 组件-输入组(Input group)
Bootstrap V4系列 学习入门教程之 组件-输入组(Input group) 输入组(Input group)Basic example一、Wrapping 包装二、Sizing 尺寸三、Multiple inputs 多输入四、Multiple addons 多个插件五、Button addons 按钮插件六、Buttons with dropdowns 带下拉按钮七、Custom for…...
图像处理篇--- HTTP|RTSP|MJPEG视频流格式
文章目录 前言一、MJPEG (Motion JPEG)基本概念技术特点编码方式传输协议数据格式 优势实现简单低延迟兼容性好容错性强 劣势带宽效率低不支持音频缺乏标准控制 典型应用 二、RTSP (Real Time Streaming Protocol)基本概念技术特点协议栈工作流程传输模式 优势专业流媒体支持高…...
`RotationTransition` 是 Flutter 中的一个动画组件,用于实现旋转动画效果
RotationTransition 是 Flutter 中的一个动画组件,用于实现旋转动画效果。它允许你对子组件进行动态的旋转变换,从而实现平滑的动画效果。RotationTransition 通常与 AnimationController 和 Tween 一起使用,以控制动画的开始、结束和过渡效果…...
养生:开启健康生活的密钥
在快节奏的现代生活中,养生已成为追求健康的重要方式。从饮食、运动到生活习惯,每一个细节都关乎身体的健康。以下为你介绍科学养生的实用方法,助你打造健康生活。 饮食养生:均衡营养,滋养身体 合理的饮食是养生的基…...
大模型微调算法原理:从通用到专用的桥梁
前言 本文聚焦大模型落地中的核心矛盾——理论快速发展与实际应用需求之间的脱节,并系统探讨微调技术作为解决这一矛盾的关键手段。尽管大模型展现出强大的通用能力,但其在垂直领域的直接应用仍面临适配性不足、计算成本高等挑战。微调通过在预训练模型基础上进行针对性优化,…...
引言:Client Hello 为何是 HTTPS 安全的核心?
当用户在浏览器中输入 https:// 时,看似简单的操作背后,隐藏着一场加密通信的“暗战”。Client Hello 作为 TLS 握手的首个消息,不仅决定了后续通信的加密强度,还可能成为攻击者的突破口。据统计,超过 35% 的网站因 TL…...
深度学习中的目标检测:从 PR 曲线到 AP
深度学习中的目标检测:从 PR 曲线到 AP 在目标检测任务中,评估模型的性能是非常重要的。通过使用不同的评估指标和标准,我们可以量化模型的准确性与效果。今天我们将重点讨论 PR 曲线(Precision-Recall Curve)、平均精…...
测试左移系列-产品经理实战-实战认知1
课程:B站大学 记录产品经理实战项目系统性学习,从产品思维,用户画像,用户体验,增长数据驱动等不同方向理解产品,从0到1去理解产品从需求到落地的全过程,测试左移方向(靠近需求、设计…...
数据集-目标检测系列- 烟雾 检测数据集 smoke >> DataBall
数据集-目标检测系列- 消防 浓烟 检测数据集 smoke>> DataBall 数据集-目标检测系列- 烟雾 检测数据集 smoke >> DataBall * 相关项目 1)数据集可视化项目:gitcode: https://gitcode.com/DataBall/DataBall-detections-10…...
概率论与数理统计基础学习大纲
📅 课程规划 阶段一:基础入门(第1-3周) 目标:掌握概率基础和基本分布 核心知识点: 概率论的基本概念:随机事件、样本空间、概率公理条件概率与全概率公式:贝叶斯公式、事件独立性随机变量与分布:离散型和连续型随机变量常见分布: 离散:二项分布、泊松分布连续:…...
5大B2B数字营销社群营销标杆案例TOB企业数字化营销内容营销AI营销培训讲师培训师专家顾问唐兴通分享
全球B2B数字营销领域的企业社区(或BBS)标杆案例 在全球TOB(企业对企业)和B2B数字营销实践中,构建企业社区或在线论坛(BBS的现代演变)已成为增强客户关系、驱动产品采用、获取市场洞察和 genera…...
OC语言学习——Foundation框架(上)
一、字符串 NSString代表字符序列不可变的字符串,而NSMutable代表字符序列可变的字符串。 1.1 NSString字符串及功能 通过NSString,我们可以: 1、创建字符串。2、读取文件或网络URL来初始化字符串,或者将字符串写入文件或URL。3…...
【Linux】基础 IO(一)
📝前言: 这篇文章我们来讲讲Linux——基础IO主要包括: 文件基本概念回顾 C文件的操作介绍系统关于文件的基本操作 🎬个人简介:努力学习ing 📋个人专栏:Linux 🎀CSDN主页 愚润求学 …...
ODA服务器计算节点本地硬盘状态异常的处理
近期,在系统巡检过程中发现一个客户的ODA服务器本地硬盘节点出现告警,ODAX8 X9等,本地硬盘是使用的240GB M.2接口的SSD盘(卡式)的,这个没有外置的指示灯可以从服务器前面板查看,打开服务器机箱盖…...
图像处理篇---opencv实现坐姿检测
文章目录 前言一、方法概述使用OpenCV和MediaPipe关键点检测角度计算姿态评估 二、完整代码实现三、代码说明PostureDetector类find_pose()get_landmarks()cakculate_angle()evaluate_posture() 坐姿评估标准(可进行参数调整):可视化功能&…...
微调ModernBERT为大型语言模型打造高效“过滤器”
ModernBERT(2024 年 12 月)是最近发布的小型语言模型,由 Answer.AI、LightOn 和 HuggingFace 共同开发。它利用了现代优化技术,如用于 8,192 token 上下文窗口的 RoPE 和 GeGLU layers,在保持效率的同时提升性能。jina…...
【C++指南】STL容器的安全革命:如何封装Vector杜绝越界访问与迭代器失效?
🌟 各位看官好,我是egoist2023! 🌍 种一棵树最好是十年前,其次是现在! 🚀 使用STL的三个境界:能用,明理,能扩展 👍 如果觉得这篇文章有帮助&#…...
Linux在web下http加密和配置虚拟主机及动态页面发布
web服务器的数据加密 1.简介:由于http协议以明文方式发送,不提供任何方式的数据加密,也不适合传输一些重要的信息,如银行卡号、密码等,解决该缺陷设计了安全套接字层超文本传输协议https; 2.https的握手流…...
8.2.CICD自动化
目录 一、持续集成(CI)核心实践 代码质量管理 • 静态代码分析:SonarQube规则定制(安全漏洞、代码异味检测) • 单元测试覆盖率:Jacoco报告生成与阈值控制(覆盖率≥80%) • 代码风格…...
解密数据结构之位图和布隆过滤器
位图和布隆过滤器 前言:笔者在前面分享过哈希的知识,但是笔者在哈希那篇博客中并没有给出哈希的应用场景,今天笔者分享的知识是关于哈希的应用,也就是大名鼎鼎的位图和布隆过滤器。1.位图的定义位图解决 2.位图实现1.先使用命名空间封装&…...
《从零构建大模型》PDF下载(中文版、英文版)
内容简介 本书是关于如何从零开始构建大模型的指南,由畅销书作家塞巴斯蒂安• 拉施卡撰写,通过清晰的文字、图表和实例,逐步指导读者创建自己的大模型。在本书中,读者将学习如何规划和编写大模型的各个组成部分、为大模型训练准备…...
Linux的web服务器的部署和优化
http中访问请求中I/O结构 在HTTP协议中,I/O(输入/输出)结构主要涉及客户端与服务器之间的请求和响应交互。以下是HTTP请求和响应的基本结构及其关键组成部分: HTTP请求结构 HTTP请求由请求行、请求头和请求体三部分组成 请求行…...
vue2 上传pdf,拖拽盖章,下载图片
效果图片: 不多废话上代码: <template><div class"pdf-stamp" onbeforecopyreturn false onselectdocument.selection.empty() ondragstartreturn false onselectstart return false ><div class"scroll-box" scro…...
MindSpore框架学习项目-ResNet药物分类-模型训练
目录 3.模型训练 3.1模型训练 3.1.1 定义优化器和损失函数 定义优化器和损失函数代码解析 3.1.2定义训练、推理函数 定义训练、推理函数代码解释 3.2模型保存 模型保存代码说明 3.3绘制acc和loss的曲线 参考内容: 昇思MindSpore | 全场景AI框架 | 昇思Mind…...
Jsp技术入门指南【十二】自定义标签
Jsp技术入门指南【十二】自定义标签 前言一、什么是标签二、标签的类型有哪些?1. 空标签2. 带有属性的标签3. 带主体的标签 三、自定义标签的部件3.1 自定义标签的四步骤3.2 标签处理程序3.3 自定义标签的开发及使用步骤第一步:创建标签助手类第二步&…...
数据库故障排查指南:从连接问题和性能优化
数据库作为现代应用程序的核心组件,其稳定性和性能直接影响整个系统的运行。然而,数据库在运行过程中常常会遇到各种故障,如连接失败、性能下降、数据不一致等问题。本文将从实际问题出发,结合代码示例和工具使用,系统…...
材料创新与工艺升级——猎板PCB引领高频阻抗板制造革命
在5G通信、AI服务器和自动驾驶的推动下,高频电路对信号完整性的要求日益严苛。猎板PCB作为国内高端PCB制造的标杆企业,通过材料创新与工艺革新,实现了阻抗控制的突破性进展,为行业树立了新标杆。 1. 高频材料的突破 传统FR-4基材…...
GitHub 趋势日报 (2025年05月09日)
本日报由 TrendForge 系统生成 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日整体趋势 Top 10 排名项目名称项目描述今日获星总星数语言1voideditor/void⭐ 1879⭐ 15214TypeScript2ruanyf/weekly科技爱好者周刊&…...
SaaS场快订平台项目说明【持续更新】
一、项目介绍 SaaS场快订平台是一个高效、便捷的体育场馆在线预订平台。本项目采用SaaS方式开发,用户不需要安装软件,直接通过互联网访问在线程序即可使用。本项目主要构建了一个体育馆预订系统,项目的功能主要包括:用户注册与登…...
PyTorch API 7 - TorchScript、hub、矩阵、打包、profile
文章目录 torch.hub发布模型如何实现入口点?重要通知 从Hub加载模型运行加载的模型:下载的模型保存在哪里?缓存逻辑已知限制: TorchScript创建 TorchScript 代码混合使用追踪与脚本化TorchScript 语言内置函数与模块PyTorch 函数与…...
OpenVLA:开源的视觉-语言-动作模型
1. 简介 让我们先来介绍一下什么是OpenVLA,在这里: https://openvla.github.io/ 可以看到他们的论文、数据、模型。 OpenVLA 是一个拥有 70亿参数的开源 **视觉-语言-动作(VLA)**模型。它是在 Open X-Embodiment 数据集 中的 97万…...
android HashMap和List该如何选择
目录 一,ArrayList 1.1 数组 1.2 扩容 1.3 查询 1.4 插入,删除 1.5 小结 二,LinkedList 2.1 链表 2.2 查找 2.3 插入 2.4 小结 三,HashMap 扩容 四,SparseArray 五,ArrayMap 一,ArrayList 1.…...
Visual Studio 2022 远程调试
Visual Studio 2022 远程调试全过程 这篇文章尽可能地精细记录如何使用 Visual Studio 2022 完成 Windows 系统上的远程调试。适用场景包括本地编译,远程运行,两台机器分工合作调试。 一、设备网络通信环境 1.1 确保两台设备通信 本地设备: 安装 Visu…...
Linux:线程同步与互斥
目录 线程互斥 锁 初始化 销毁 加锁 解锁 线程同步 条件变量 初始化 销毁 等待条件满足 唤醒等待 pthread_cond_signal pthread_cond_broadcast 生产者消费者模型 3种关系 2种角色 1个交易场所 POSIX信号量 初始化 销毁 等待 发布 线程互斥 互斥相关…...