Unity Internal-DeferredShading 分析
1. 延迟渲染的原理
延迟渲染主要包含了两个Pass。在第一个Pass中,我们不进行任何光照计算,而是仅仅计算哪些片元是可见的,这主要是通过深度缓冲技术来实现,当发现一个片元是可见的,我们就把它的相关信息存储到G缓冲区中。然后,在第二个Pass中,我们利用G缓冲区的各个片元信息,例如表面法线、视角方向、漫反射系数等,进行真正的光照计算。
延迟渲染的过程大致可以用下面的伪代码来描述:
// 第一个 Pass 不进行真正的光照计算
// 仅仅把光照计算需要的信息存储到 G 缓冲中
for (each primitive in this model) {for (each fragment covered by this primitive) {if (failed in depth test) {// 如果没有通过深度测试,说明该片元是不可见的discard;} else {// 如果该片元可见// 就把需要的信息存储到 G 缓冲中writeGBuffer(materialInfo, pos, normal, lightDir, viewDir);}}
}// 利用 G 缓冲中的信息进行真正的光照计算
for (each pixel in the screen) {if (the pixel is valid) {// 如果该像素是有效的// 读取它对应的 G 缓冲中的信息readGBuffer(pixel, materialInfo, pos, normal, lightDir, viewDir);// 根据读取到的信息进行光照计算float4 color = Shading(materialInfo, pos, normal, lightDir, viewDir);// 更新帧缓冲writeFrameBuffer(pixel, color);}
}
Unity 里面的延迟渲染
一、代码结构
Shader "Hidden/Internal-DeferredShading" {Properties {// 光照贴图(用于点光源/聚光灯衰减)_LightTexture0 ("Light Texture", any) = "" {}// 光照方向贴图(用于聚光灯方向衰减)_LightTextureB0 ("Light Direction Texture", 2D) = "" {}// 阴影贴图(来自深度缓冲)_ShadowMapTexture ("Shadow Map", any) = "" {}// 混合模式参数(源/目标混合因子)_SrcBlend ("Source Blend", Float) = 1_DstBlend ("Destination Blend", Float) = 1}SubShader {Pass {// Pass 1: 延迟光照计算(叠加或减法混合)}Pass {// Pass 2: LDR模式最终解码(将对数值转为线性)}Fallback Off // 无备用着色器
}
二、第一个Pass
Pass {ZWrite Off // 关闭深度写入Blend [_SrcBlend] [_DstBlend] // 动态混合模式(HDR时为加法,LDR时为减法)CGPROGRAM#pragma target 3.0 // 要求SM3.0以上#pragma vertex vert_deferred // 使用Unity内置延迟渲染顶点着色器#pragma fragment frag // 片段着色器#pragma multi_compile_lightpass // 多编译光照传递模式#pragma multi_compile ___ UNITY_HDR_ON // 根据是否启用HDR编译不同路径#pragma exclude_renderers nomrt // 排除不支持多渲染目标的平台#include "UnityCG.cginc" // Unity基础CG库#include "UnityDeferredLibrary.cginc" // 延迟渲染专用库#include "UnityPBSLighting.cginc" // 物理渲染光照计算#include "UnityStandardUtils.cginc" // 标准着色器工具#include "UnityGBuffer.cginc" // GBuffer解包#include "UnityStandardBRDF.cginc" // BRDF计算// GBuffer纹理采样器(包含材质信息)sampler2D _CameraGBufferTexture0; // Albedo + 金属度sampler2D _CameraGBufferTexture1; // 法线 + 粗糙度sampler2D _CameraGBufferTexture2; // 前照光 + 漫反射// 计算单个光源的光照贡献half4 CalculateLight (unity_v2f_deferred i) {float3 wpos; // 世界空间坐标float2 uv; // GBuffer纹理坐标float atten, fadeDist;// 光照衰减和雾化距离UnityLight light; // 光照结构体UNITY_INITIALIZE_OUTPUT(UnityLight, light); // 初始化光照结构UnityDeferredCalculateLightParams (i, wpos, uv, light.dir, atten, fadeDist); // 计算光照参数(方向、衰减等)// 设置光照颜色(光源颜色 * 衰减)light.color = _LightColor.rgb * atten;// 从GBuffer解包材质属性 //“Unity Standard Shader 解析(六)之Deferred”里面,可以看到Gbuffer的数据来源half4 gbuffer0 = tex2D(_CameraGBufferTexture0, uv);half4 gbuffer1 = tex2D(_CameraGBufferTexture1, uv);half4 gbuffer2 = tex2D(_CameraGBufferTexture2, uv);UnityStandardData data = UnityStandardDataFromGbuffer(gbuffer0, gbuffer1, gbuffer2); // 转换为标准材质数据结构// 计算视角方向(世界空间)float3 eyeVec = normalize(wpos - _WorldSpaceCameraPos);// 计算非高光部分(1 - 反射率)half oneMinusReflectivity = 1 - SpecularStrength(data.specularColor.rgb);// 环境光(此处设为0,因为延迟渲染通常单独处理)UnityIndirect ind;UNITY_INITIALIZE_OUTPUT(UnityIndirect, ind);ind.diffuse = 0;ind.specular = 0;// 使用标准BRDF计算光照结果return UNITY_BRDF_PBS(data.diffuseColor, // 漫反射颜色data.specularColor, // 高光颜色oneMinusReflectivity, // 非高光部分data.smoothness, // 粗糙度data.normalWorld, // 法线方向-eyeVec, // 视角方向(取反)light, // 光照参数ind // 环境光(未使用));}// 主片段着色器入口#ifdef UNITY_HDR_ONhalf4 frag (unity_v2f_deferred i) : SV_Target {#elsefixed4 frag (unity_v2f_deferred i) : SV_Target {#endifhalf4 c = CalculateLight(i); // 计算光照#ifdef UNITY_HDR_ONreturn c; // HDR模式直接返回线性值#elsereturn exp2(-c); // LDR模式对数编码#endif}ENDCG}
1.顶点着色器
vert_deferred定义在UnityDeferredLibrary.cginc里面
// 顶点着色器部分
// 延迟渲染顶点着色器输出结构体
struct unity_v2f_deferred {float4 pos : SV_POSITION; // 裁剪空间顶点坐标float4 uv : TEXCOORD0; // 屏幕空间UV坐标(用于采样GBuffer)float3 ray : TEXCOORD1; // 视线方向向量(用于光照计算)
};// 控制是否使用全屏四边形模式的Shader变量
float _LightAsQuad;// 延迟渲染顶点着色器入口函数
unity_v2f_deferred vert_deferred (float4 vertex : POSITION, float3 normal : NORMAL)
{unity_v2f_deferred o;// 1. 将顶点从物体空间转换为裁剪空间o.pos = UnityObjectToClipPos(vertex);// 2. 计算屏幕空间UV坐标(范围[0,1],保留深度信息)o.uv = ComputeScreenPos(o.pos);// 3. 计算视线方向向量(视图空间下的相机到顶点方向)// UnityObjectToViewPos将顶点转换为视图空间坐标// 乘以float3(-1,-1,1)调整坐标方向(可能与坐标系转换有关)o.ray = UnityObjectToViewPos(vertex) * float3(-1, -1, 1);// 4. 根据_LightAsQuad选择ray的来源:// - 当_LightAsQuad=1时:使用传入的normal参数(实际是相机到近平面角落的向量)// - 当_LightAsQuad=0时:使用顶点计算的rayo.ray = lerp(o.ray, normal, _LightAsQuad);return o;
}
2.UnityDeferredCalculateLightParams
// --------------------------------------------------------
// 核心光照参数计算函数void UnityDeferredCalculateLightParams (unity_v2f_deferred i,out float3 outWorldPos, // 输出世界空间坐标out float2 outUV, // 输出屏幕UV坐标out half3 outLightDir, // 输出光照方向out float outAtten, // 输出光照衰减out float outFadeDist) // 输出阴影衰减距离
{// 1. 计算视线方向(归一化到近裁剪面)i.ray = i.ray * (_ProjectionParams.z / i.ray.z);float2 uv = i.uv.xy / i.uv.w;// 2. 从深度缓冲区重建世界坐标float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);depth = Linear01Depth(depth); // 将深度转换为线性0-1范围float4 vpos = float4(i.ray * depth, 1); // 视图空间坐标float3 wpos = mul(unity_CameraToWorld, vpos).xyz; // 世界空间坐标// 3. 计算阴影衰减距离float fadeDist = UnityComputeShadowFadeDistance(wpos, vpos.z);// 4. 根据光照类型计算光照参数#if defined (SPOT) // 聚光灯float3 tolight = _LightPos.xyz - wpos; // 光源到顶点向量half3 lightDir = normalize(tolight); // 光照方向float4 uvCookie = mul(unity_WorldToLight, float4(wpos,1)); // 光源空间坐标// 聚光灯衰减(带贴图)float atten = tex2Dbias(_LightTexture0, float4(uvCookie.xy / uvCookie.w, 0, -8)).w;atten *= uvCookie.w < 0; // 防止背面采样float att = dot(tolight, tolight) * _LightPos.w; // 距离衰减atten *= tex2D(_LightTextureB0, att.rr).r; // 衰减贴图采样// 计算阴影atten *= UnityDeferredComputeShadow(wpos, fadeDist, uv);#elif defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE) // 方向光half3 lightDir = -_LightDir.xyz; // 光方向(反向)float atten = 1.0;// 计算阴影atten *= UnityDeferredComputeShadow(wpos, fadeDist, uv);#if defined (DIRECTIONAL_COOKIE) // 带贴图的方向光// 采样方向光贴图(如天空盒投影)atten *= tex2Dbias(_LightTexture0, float4(mul(unity_WorldToLight, half4(wpos,1)).xy, 0, -8)).w;#endif#elif defined (POINT) || defined (POINT_COOKIE) // 点光源float3 tolight = wpos - _LightPos.xyz; // 顶点到光源向量half3 lightDir = -normalize(tolight); // 光照方向(反向)float att = dot(tolight, tolight) * _LightPos.w; // 距离衰减float atten = tex2D(_LightTextureB0, att.rr).r; // 衰减贴图// 计算阴影atten *= UnityDeferredComputeShadow(tolight, fadeDist, uv);#if defined (POINT_COOKIE) // 带贴图的点光源// 采样立方体阴影贴图atten *= texCUBEbias(_LightTexture0, float4(mul(unity_WorldToLight, half4(wpos,1)).xyz, -8)).w;#endif#else // 无效光照类型half3 lightDir = 0;float atten = 0;#endif// 输出结果outWorldPos = wpos;outUV = uv;outLightDir = lightDir;outAtten = atten;outFadeDist = fadeDist;
}
3,UnityComputeShadowFadeDistance
// 计算阴影衰减距离(用于混合实时阴影和烘焙阴影)
//float3 wpos:顶点的世界空间坐标(通过深度缓冲区重建)。
//float z:顶点的归一化设备坐标(NDC)的Z值(范围 [0,1]),表示顶点到相机的距离。
float UnityComputeShadowFadeDistance(float3 wpos, float z)
{// 1. 计算顶点到阴影衰减中心点的线性距离// unity_ShadowFadeCenterAndType.xyz 是阴影衰减球体的中心点坐标(世界空间)float sphereDist = distance(wpos, unity_ShadowFadeCenterAndType.xyz);// 2. 根据混合因子 lerp 在 z(深度值)和 sphereDist(球体距离)之间插值// unity_ShadowFadeCenterAndType.w 是混合权重(0~1):// - 当为0时,完全使用 z(深度值)作为衰减距离// - 当为1时,完全使用 sphereDist(球体距离)作为衰减距离// - 中间值则线性插值return lerp(z, sphereDist, unity_ShadowFadeCenterAndType.w);
}
4.UnityDeferredComputeShadow
// 计算混合阴影(实时+烘焙)
half UnityDeferredComputeShadow(float3 vec, float fadeDist, float2 uv)
{half fade = UnityComputeShadowFade(fadeDist); // 计算阴影衰减因子half shadowMaskAttenuation = UnityDeferredSampleShadowMask(uv); // 烘焙阴影half realtimeShadowAttenuation = UnityDeferredSampleRealtimeShadow(fade, vec, uv); // 实时阴影// 混合烘焙和实时阴影return UnityMixRealtimeAndBakedShadows(realtimeShadowAttenuation, shadowMaskAttenuation, fade);
}
5.UnityComputeShadowFade
half UnityComputeShadowFade(float fadeDist)
{// 计算衰减因子:将 fadeDist 乘以比例因子(scale),加上偏移因子(offset)// 然后通过 saturate() 将结果限制在 [0, 1] 范围内return saturate(fadeDist * _LightShadowData.z + _LightShadowData.w);
}
6.UnityDeferredSampleShadowMask
// 采样阴影掩码(烘焙阴影)
half UnityDeferredSampleShadowMask(float2 uv)
{half shadowMaskAttenuation = 1.0f;#if defined (SHADOWS_SHADOWMASK)// 从GBuffer的第四通道(_CameraGBufferTexture4)读取阴影掩码half4 shadowMask = tex2D(_CameraGBufferTexture4, uv);// 使用unity_OcclusionMaskSelector计算最终的阴影衰减shadowMaskAttenuation = saturate(dot(shadowMask, unity_OcclusionMaskSelector));#endifreturn shadowMaskAttenuation;
}
7.UnityDeferredSampleRealtimeShadow
// 采样实时阴影(动态阴影)
half UnityDeferredSampleRealtimeShadow(half fade, float3 vec, float2 uv)
{half shadowAttenuation = 1.0f;// 方向光/带贴图的方向光的阴影计算(屏幕空间阴影)#if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE)#if defined(SHADOWS_SCREEN)// 从_ShadowMapTexture采样阴影值shadowAttenuation = tex2D(_ShadowMapTexture, uv).r;#endif#endif// 快速分支优化(Unity 5.6+)#if defined(UNITY_FAST_COHERENT_DYNAMIC_BRANCHING) && defined(SHADOWS_SOFT) && !defined(LIGHTMAP_SHADOW_MIXING)// 当衰减小于阈值时,跳过计算(提升性能)UNITY_BRANCHif (fade < (1.0f - 1e-2f)){#endif// 聚光灯阴影(深度阴影)#if defined(SPOT)#if defined(SHADOWS_DEPTH)// 转换到阴影坐标系并采样float4 shadowCoord = mul(unity_WorldToShadow[0], float4(vec, 1));shadowAttenuation = UnitySampleShadowmap(shadowCoord);#endif#endif// 点光源/带贴图的点光源阴影(立方体贴图阴影)#if defined (POINT) || defined (POINT_COOKIE)#if defined(SHADOWS_CUBE)// 直接采样立方体阴影贴图shadowAttenuation = UnitySampleShadowmap(vec);#endif#endif#if defined(UNITY_FAST_COHERENT_DYNAMIC_BRANCHING) && defined(SHADOWS_SOFT) && !defined(LIGHTMAP_SHADOW_MIXING)}#endifreturn shadowAttenuation;
}
8.UnitySampleShadowmap
// ------------------------------------------------------------------
// Spot 光源阴影采样逻辑
// ------------------------------------------------------------------
// 仅在启用深度阴影(SHADOWS_DEPTH)且光源类型为 Spot(SPOT)时编译此部分
#if defined(SHADOWS_DEPTH) && defined(SPOT)// 阴影采样函数(核心逻辑)inline fixed UnitySampleShadowmap (float4 shadowCoord){#if defined(SHADOWS_SOFT)half shadow = 1; // 默认全亮(无阴影)// 情况1:不支持硬件比较采样器(如部分移动设备或 Xbox360)#if !defined(SHADOWS_NATIVE)float3 coord = shadowCoord.xyz / shadowCoord.w; // 将齐次坐标转换为归一化坐标 [0,1]// 使用四个偏移量进行软阴影采样(PCF)float4 shadowVals;shadowVals.x = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[0].xy);shadowVals.y = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[1].xy);shadowVals.z = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[2].xy);shadowVals.w = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[3].xy);// 比较采样深度与当前坐标深度,计算阴影权重(0为全黑,1为全亮)half4 shadows = (shadowVals < coord.zzzz) ? _LightShadowData.rrrr : 1.0f;shadow = dot(shadows, 0.25f); // 四个采样的平均值#else // 情况2:支持硬件比较采样器(如移动设备)// 使用硬件比较采样器进行高效采样float3 coord = shadowCoord.xyz / shadowCoord.w;half4 shadows;shadows.x = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[0]);shadows.y = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[1]);shadows.z = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[2]);shadows.w = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[3]);shadow = dot(shadows, 0.25f); // 四个采样的平均值#else // 情况3:其他平台(如PC)// 使用更复杂的3x3采样(PCF3x3)提高阴影质量float3 coord = shadowCoord.xyz / shadowCoord.w;float3 receiverPlaneDepthBias = UnityGetReceiverPlaneDepthBias(coord, 1.0f);shadow = UnitySampleShadowmap_PCF3x3(float4(coord, 1), receiverPlaneDepthBias);#endif// 根据光照阴影强度混合阴影效果shadow = lerp(_LightShadowData.r, 1.0f, shadow);#else // 情况4:硬阴影(非软阴影)// 硬阴影直接采样(1-tap)#if defined(SHADOWS_NATIVE)// 使用硬件比较采样器half shadow = UNITY_SAMPLE_SHADOW_PROJ(_ShadowMapTexture, shadowCoord);shadow = lerp(_LightShadowData.r, 1.0f, shadow);#else// 手动比较深度half shadow = SAMPLE_DEPTH_TEXTURE_PROJ(_ShadowMapTexture, UNITY_PROJ_COORD(shadowCoord)) < (shadowCoord.z / shadowCoord.w) ? _LightShadowData.r : 1.0;#endif#endifreturn shadow;}#endif // #if defined(SHADOWS_DEPTH) && defined(SPOT)
三、第二个Pass
Pass {ZTest Always // 总是通过深度测试Cull Off // 不剔除面ZWrite Off // 关闭深度写入Stencil {// 只渲染非背景区域(通过Stencil缓冲区)ref [_StencilNonBackground] // 参考值来自Unity的Stencil设置readmask [_StencilNonBackground] // 读取掩码compback equal // 后面面的比较函数compfront equal // 前面面的比较函数(修复bug,见注释)}CGPROGRAM#pragma target 3.0#pragma vertex vert#pragma fragment frag#pragma exclude_renderers nomrt#include "UnityCG.cginc"sampler2D _LightBuffer; // 光照缓冲区纹理struct v2f {float4 vertex : SV_POSITION;float2 texcoord : TEXCOORD0;};// 顶点着色器(传递屏幕坐标)v2f vert (float4 vertex : POSITION, float2 texcoord : TEXCOORD0) {v2f o;o.vertex = UnityObjectToClipPos(vertex);o.texcoord = texcoord.xy;#ifdef UNITY_SINGLE_PASS_STEREOo.texcoord = TransformStereoScreenSpaceTex(o.texcoord, 1.0f);#endifreturn o;}// 片段着色器(解码对数值)fixed4 frag (v2f i) : SV_Target {return -log2(tex2D(_LightBuffer, i.texcoord)); // 对数解码}ENDCG}
总结Unity中关于内部延迟渲染(Deferred Shading)的实现,主要包括了原理介绍、代码结构和关键函数的作用。
延迟渲染的原理
延迟渲染通过两个Pass完成:第一个Pass只计算哪些片元是可见的,并将这些片元的相关信息存储到G缓冲区中;第二个Pass利用G缓冲区的信息进行光照计算。这种方法允许在不增加额外复杂度的情况下处理大量光源。
Unity里面的延迟渲染
一、代码结构
- Shader “Hidden/Internal-DeferredShading”:定义了一个隐藏的着色器,用于内部延迟渲染。
- Properties块:定义了各种纹理属性,如光纹理、阴影贴图等。
- SubShader块:包含了具体的渲染过程,分为多个Pass来执行不同的渲染任务。
二、第一个Pass
- ZWrite Off 和 Blend:关闭深度写入并设置混合模式。
- CGPROGRAM块:包含了顶点和片段着色器的实现,使用多编译指令以适应不同平台和配置。
- CalculateLight函数:根据从GBuffer读取的信息计算单个光源对像素颜色的贡献。
- frag函数:主片段着色器入口,根据是否启用高动态范围(HDR)选择不同的返回值处理方式。
关键函数
- vert_deferred:延迟渲染使用的顶点着色器,负责转换坐标系并将必要信息传递给片段着色器。
- UnityDeferredCalculateLightParams:核心光照参数计算函数,根据当前片元的位置和其他信息计算出光照方向、衰减等因素。
- UnityComputeShadowFadeDistance:计算阴影衰减距离,用于混合实时阴影和烘焙阴影。
- UnityDeferredComputeShadow:计算混合阴影效果,结合实时阴影和烘焙阴影。
- UnityComputeShadowFade:计算阴影衰减因子。
- UnityDeferredSampleShadowMask:采样阴影掩码,即烘焙阴影。
- UnityDeferredSampleRealtimeShadow:采样实时阴影,即动态生成的阴影。
- UnitySampleShadowmap:阴影贴图采样逻辑,支持多种情况下的阴影采样,包括软阴影和硬阴影。
第二个Pass
- ZTest Always, Cull Off, ZWrite Off:总是通过深度测试,不剔除面,关闭深度写入。
- Stencil块:仅渲染非背景区域,通过模板缓冲区实现。
- CGPROGRAM块:解码对数值,主要是通过对数运算将HDR颜色值转换为线性空间颜色值。
相关文章:
Unity Internal-DeferredShading 分析
1. 延迟渲染的原理 延迟渲染主要包含了两个Pass。在第一个Pass中,我们不进行任何光照计算,而是仅仅计算哪些片元是可见的,这主要是通过深度缓冲技术来实现,当发现一个片元是可见的,我们就把它的相关信息存储到G缓冲区…...
嵌入式面试题:C 语言核心考点经典例题
引言 在嵌入式系统开发的面试中,常常会考察候选人对 C 语言基础知识的掌握程度。以下将详细分析几道常见的嵌入式面试题,包括解题步骤、涉及的知识点以及相关拓展。 题目 1 main() {unsigned char z0;unsigned char x100;unsigned char y10;z (~x)*(…...
Axure RP9教程 【数据传输】(页面值传递)| 作用域 :全局变量、局部变量
文章目录 引言作用域:全局变量作用域>局部变量作用域I 基于全局变量实现一个简单的登陆操作设置变量值的交互动作打开链接的交互动作接收并显示变量值注意点see also共享原型引言 全局变量在交互效果作用是页面值传递 作用域:全局变量作用域>局部变量作用域 全局变量…...
IBM Rational Software Architect安装感受及使用初体验
1 安装感受 最近准备用UML 2.0绘制模型图。在读UML创始人之一Grady Booch写的书《Object-Oriented Analysis and Design with Applications》(第3版)1时,发现书中用的UML工具之一为IBM Rational Software Architect(RSAÿ…...
VRRP学习
虚拟路由器冗余技术【三层技术】:网关冗余VRRP设计了VRRP组的概念,在一个 VRRP 组中,多台路由器共同构成一个虚拟路由器。这个虚拟路由器拥有一个虚拟 IP 地址【VRRP-ID默认是8位二进制,范围是0~255,用以标识和区别不同…...
C_内存 内存地址概念
1. 计算机内存的基本概念 计算机的内存(RAM,随机存取存储器)是用来存储程序运行时的数据和指令的地方。内存被划分为许多小单元,每个单元有一个唯一的地址,这些地址从0开始编号。 内存单元:每个内存单元通…...
GPT-5、o3和o4-mini即将到来
原计划有所变更: 关于我们应有何期待的一些零散想法。 深度研究(Deep Research)确实强大但成本高昂且速度较慢(当前使用o3模型)。即将推出的o4-mini在性能上可能与o3相近,但将突破这些限制,让全球用户——甚至免费用户(尽管会有速率限制)——都能用上世界顶级AI研究助…...
C#MVC项目引用Swagger的详细步骤
目录 一、安装Swagger依赖包二、配置Swagger服务三、启用XML注释四、调整启动配置五、验证与访问常见问题解决 以下是基于ASP.NET Core项目集成Swagger的详细步骤(已适配当前项目结构): 一、安装Swagger依赖包 通过NuGet安装 右键点击项目…...
golang 对象池sync.Pool
Golang中的sync.Pool是什么? sync.Pool 是 Go 标准库中提供的一个对象池(Object Pool)实现,用于缓存和复用临时对象,以减少内存分配和垃圾回收(GC)的压力。它的主要特点是: 临时对…...
聚焦AI与大模型创新,紫光云如何引领云计算行业快速演进?
【全球云观察 | 科技热点关注】 随着近年来AI与大模型的兴起,云计算行业正在发生着一场大变局。 “在2025年春节期间,DeepSeek两周火爆全球,如何进行私域部署成了企业关心的问题。”紫光云公司总裁王燕平强调指出,AI与…...
去重新闻数据中重复的正文内容(body 字段),并把唯一的新闻内容保存到一个新的 JSON 文件中
示例代码: import os import json import nltk from tqdm import tqdmdef wr_dict(filename,dic):if not os.path.isfile(filename):data []data.append(dic)with open(filename, w) as f:json.dump(data, f)else: with open(filename, r) as f:data json.l…...
解决前后端时区不一致问题
前后端时区不一致导致: 》数据不显示在前端 》页面显示时间有误 》一些对时间有要求的方法,无法正确执行,出现null值,加上我们对null值有判断/注解,程序就会报错中断,以为是业务逻辑问题,其实…...
有哪些反爬机制可能会影响Python爬取视频?如何应对这些机制?
文章目录 前言常见反爬机制及影响1. IP 封禁2. 验证码3. 请求头验证4. 动态加载5. 加密与混淆6. 行为分析 应对方法1. 应对 IP 封禁2. 应对验证码3. 应对请求头验证4. 应对动态加载5. 应对加密与混淆6. 应对行为分析 前言 在使用 Python 爬取视频时,会遇到多种反爬…...
STL之序列式容器(Vector/Deque/List)
序列式容器 序列式容器包括:静态数组 array 、动态数组 vector 、双端队列 deque 、单链表 forward_ list 、双链表 list 。这五个容器中,我们需要讲解三个 vector 、 deque 、 list 的使 用,包括:初始化、遍历、尾部插入与删除、…...
小试牛刀-抽奖程序
编写抽奖程序 需求:设计一个抽奖程序,点击抽奖按钮随机抽取一个名字作为中奖者 目标:了解项目结构,简单UI布局,属性方法、事件方法,程序运行及调试 界面原型 待抽奖: 点击抽奖按钮&#x…...
Vue 3 中 ref 与 reactive 的对比
Vue 3 中 ref 与 reactive 的对比 Vue 3 中 ref 与 reactive 的对比一、定义和基本使用refreactive 二、响应式原理refreactive 三、适用场景refreactive 四、注意事项refreactive Vue 3 中 ref 与 reactive 的对比 在 Vue 3 中,ref 和 reactive 都是用于创建响应式…...
centos-stream-9上安装nvidia驱动和cuda-toolkit
这里写目录标题 驱动安装1. 更新系统2. NVIDIA GPU安装检查系统是否安装了 NVIDIA GPU2.1 首先,使用以下命令更新 DNF 软件包存储库缓存:2.2 安装编译 NVIDIA 内核模块所需的依赖项和构建工具2.3 在 CentOS Stream 9 上添加官方 NVIDIA CUDA 软件包存储库…...
从 MySQL 切换到国产 YashanDB 数据库时,需要在数据库字段和应用连接方面进行适配 ,使用总结
YashanDB | 崖山数据库系统 - 崖山科技官网崖山数据库系统YashanDB是深圳计算科学研究院完全自主研发设计的新型数据库系统,融入原创理论,支持单机/主备、共享集群、分布式等多种部署方式,覆盖OLTP/HTAP/OLAP交易和分析混合负载场景ÿ…...
【学习笔记】头文件中定义函数出现重复定义报错
目录 错误复现原因解决方案inlinestatic 扩展参考 错误复现 现在有一个头文件 duplicate_define.h 和两个源文件 duplicate_define_1.cpp 和 duplicate_define_2.cpp。 两个源文件都引入了头文件 duplicate_define.h,且在各自的函数中调用了定义在头文件中的全局函…...
游戏开发中 C#、Python 和 C++ 的比较
🎬 Verdure陌矣:个人主页 🎉 个人专栏: 《C/C》 | 《转载or娱乐》 🌾 种完麦子往南走, 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持!❤️ 摘要: 那么哪种编程语言最适合游戏开发…...
linux上anaconda安装、卸载、及不同用户共享同个anaconda的操作
这里写目录标题 1、anaconda安装2、所有账号可以访问condastep1: 创建文件step2: 追加以下内容:step3: 赋予执行权限:step4: 生效方式: 3、anaconda3的卸载(1)删除安装文件夹(2)在当前终端会话中…...
利用持久变量绕过长度限制 + unicode特性绕过waf-- xyctf 出题人已疯12 复现
本文章附带TP(Thinking Process)! 黑盒查看网站不具有功能,需要审计代码 # 定义/attack路径的路由 bottle.route(/attack) def attack():# 从请求的查询参数中获取payloadpayload bottle.request.query.get(payload)# 检查payload是否存在,长度是否小于25ÿ…...
大数据技术与Scala
集合高级函数 过滤 通过条件筛选集合元素,返回新集合。 映射 对每个元素应用函数,生成新集集合 扁平化 将嵌套集合展平为单层集合。 扁平化映射 先映射后展平,常用于拆分字符串。 分组 按规则将元素分组为Map结构。 归约 …...
DeepSeek 都开源了哪些技术?
DeepSeek作为中国领先的人工智能企业,通过开源策略推动了全球AI技术的普及与创新。以下是其官方公布的主要开源项目及其技术内容、应用场景和社区反馈的详细分析: 1. FlashMLA 技术描述:专为Hopper架构GPU优化的高效MLA(Multi-Layer Attention)解码内核,针对可变长度序列…...
P8754 [蓝桥杯 2021 省 AB2] 完全平方数
题目描述 思路 一看就知道考数学,直接看题解试图理解(bushi) 完全平方数的质因子的指数一定为偶数。 所以 对 n 进行质因数分解,若质因子指数为偶数,对结果无影响。若质因子指数为奇数,则在 x 中乘以这个质因子,保证指…...
ADGaussian:用于自动驾驶的多模态输入泛化GS方法
25年4月来自香港中文大学和浙大的论文“ADGaussian: Generalizable Gaussian Splatting for Autonomous Driving with Multi-modal Inputs”。 提出 ADGaussian 方法,用于可泛化的街道场景重建。所提出的方法能够从单视图输入实现高质量渲染。与之前主要关注几何细…...
0501路由-react-仿低代码平台项目
文章目录 1 react路由1.1 核心库:React Router安装 1.2 基本路由配置路由入口组件定义路由 1.3 导航方式使用 <Link> 组件编程式导航 1.4 动态路由参数定义参数获取参数 1.5 嵌套路由父路由配置子路由占位符 1.6 重定向与404页面重定向404页面 1.7 路由守卫&a…...
MySQL NULL 值处理
MySQL NULL 值处理 引言 在数据库管理系统中,NULL 值是一个非常重要的概念。在 MySQL 中,NULL 值代表未知、不存在或未定义的值。正确处理 NULL 值对于保证数据的准确性和完整性至关重要。本文将详细介绍 MySQL 中 NULL 值的处理方法,包括 …...
OpenAI即将上线新一代重磅选手——GPT-4.1
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
【蓝桥杯】赛前练习
1. 排序 import os import sysn=int(input()) data=list(map(int,input().split(" "))) data.sort() for d in data:print(d,end=" ") print() for d in data[::-1]:print(d,end=" ")2. 走迷宫BFS import os import sys from collections import…...
