Global Illumination_Exponential Variance Shadow Maps(EVSM)

最近工程中需要集成高质量阴影(效率、效果),介于项目非循环渲染所以CSM无法使用,但动态建模中还需要快速增删改场景,阴影还必须重新生成,奈何之前简单SM+PCF无法满足效率、效果要求,于是调研RVT等软件,发现其采用的为单帧EVSM算法,遂集成实现。
想要了解Exponential Variance Shadow Maps(EVSM),就需要先了解Variance Shadow Map(VSM)算法,想要了解VSM,就需要知道常规的Shadow Map(SM算法),本文便不再赘述SM了,期间也不再赘述中间优化算法Layered Variance Shadow Maps(LVSM)。
首先我们来看一下EVSM算法的优点吧,毕竟算法有优势我们才会优先选择集成到项目嘛。

先来看一张EVSM原文里的效果对比(1080卡),详见下参考文章链接:https://www.martincap.io/project_detail.php?project_id=9
在这里插入图片描述

首先VSM相对于SM的优缺点如下:

  • 优点: 在处理软阴影时效率更快;没有shadow acne现象
  • 缺点: 需要额外通道来记录深度平方;高方差区域会产生漏光

其次EVSM相对于VSM的优缺点如下:

  • 优点: 最大程度上减缓漏光问题
  • 缺点: 极端情况下,仍然会有artifacts;会占用较多带宽(EVSM4)

首先我们来看一下VSM。多说一句Demo主要参照http://mynameismjp.wordpress.com/大神介绍,某些汪汪不想看的可以出门右拐,求别再喷粪嗷嗷,感谢合作。

一、VSM

1.1 算法原理

根据SM原理我们知道,在处理软阴影的时候,我们通常需要使用PCF来处理一个区域内的多个深度比较结果来确定一个合适的软硬程度值。
简单说一下PCF的原理:把当前屏幕空间像素范围投射为阴影贴图,并对得到的区域采样,这个过程和标准的纹理过滤很相似。然后把每一个样本和一个参考的深度进行对比,产 生一个二元结果。接着,结合所有的深度对比来计算过滤区域中比参考深度更加靠近(closer)的纹理像素的百分比。这个百分比就被用来减弱光线。一般提高过滤区域的大小可以软化阴影的边缘程序。

虽然PCF的品质挺不错,但是需要大量的采样。对于标准的纹理过滤来说,阴影角部的表面还需要求大量的各向异性过滤区域。在最坏的情况下,我们必须对阴影贴图中的每一个纹理像素进行采样和对比,以计算每一个帧缓冲像素的光衰减。这样过程会很慢,而且存在shadow acne现象。

针对上边的问题于是便有了VSM算法,其主要思路是:按照一种可以线性过滤的方法来表示深度数据,这样我们就可以使用适用于颜色和其他线性数据的算法和硬件。本算法和标准的阴影贴图算法很相似,只不过我们把深度和深度平方写到一个二分量R32G32_FLOAT的差值阴影贴图中,而不是简单地把深度写入到阴影贴图中。通过对某些区域进行过滤,我们恢复了该区域在M1时刻和M2时刻的深度分布,其中M1/M2代表的是该区域的均值数据,即:
在这里插入图片描述
本质上就是对深度和深度平方贴图做一个滤波(如盒型滤波或高斯滤波等)。
在这里插入图片描述

抑或是直接采用MSAA处理一下之后采用线型采样器直接取值即可,之后算出均值(平均准)和方差(统计学经典公式)。
在这里插入图片描述
紧接着我们就可以使用这个差值,应用(Chebyshev)切比雪夫不等式来计算当前待求阴影的表面(深度为1)被遮挡的概率的上界:
在这里插入图片描述

在这里插入图片描述
Chebyshev不等式的“积差相关系数”版本只在t>μ时有效。如果t≤μ,那么Pmax=1,代表的就是这个表面被完全照亮了,虽然不准,但是就这么用就行。

以上就是全部理论,就这么简单。但是在实现中还有几点小坑,直接看代码吧。

1.2 算法实现

1.2.1 阴影生成

常规阴影算法,不再赘述,生成如下图所示:
在这里插入图片描述

1.2.2 深度与深度平方期望计算

一个DrawCall三角形将上述深度图转换存储,HLSL中PS代码如下:

struct VSOutput
{float4 Position : SV_Position;float2 TexCoord : TEXCOORD;
};
VSOutput FullScreenVS(in uint VertexID : SV_VertexID)
{VSOutput output;if(VertexID == 0){output.Position = float4(-1.0f, 1.0f, 1.0f, 1.0f);output.TexCoord = float2(0.0f, 0.0f);}else if(VertexID == 1){output.Position = float4(3.0f, 1.0f, 1.0f, 1.0f);output.TexCoord = float2(2.0f, 0.0f);}else{output.Position = float4(-1.0f, -3.0f, 1.0f, 1.0f);output.TexCoord = float2(0.0f, 2.0f);}return output;
}float4 ConvertToVSM(in VSOutput input) : SV_Target0
{float sampleWeight = 1.0f / float(MSAASamples_);uint2 coords = uint2(input.Position.xy);float4 average = float4(0.0f, 0.0f, 0.0f, 0.0f);// Sample indices to Load() must be literal, so force unroll[unroll]for(uint i = 0; i < MSAASamples_; ++i){// Convert to EVSM representation#if MSAASamples_ > 1float depth = ShadowMap.Load(coords, i);#elsefloat depth = ShadowMap[coords];#endifaverage += sampleWeight * float4(vsmDepth.xy, vsmDepth.xy * vsmDepth.xy);}return average.xzxz;
}

转换后贴图如下所示:
在这里插入图片描述
除了上述说的,直接采用MSAA和线型采样器来处理得到深度与深度平方的均值,即(M1与M2),还可以采用滤波的方式。
可参照这篇文章介绍https://graphics.stanford.edu/~mdfisher/Shadows.html,流程效果图如下:
在这里插入图片描述

滤波处理代码可参照:

struct VSOutput
{float4 Position : SV_Position;float2 TexCoord : TEXCOORD;
};
VSOutput FullScreenVS(in uint VertexID : SV_VertexID)
{VSOutput output;if(VertexID == 0){output.Position = float4(-1.0f, 1.0f, 1.0f, 1.0f);output.TexCoord = float2(0.0f, 0.0f);}else if(VertexID == 1){output.Position = float4(3.0f, 1.0f, 1.0f, 1.0f);output.TexCoord = float2(2.0f, 0.0f);}else{output.Position = float4(-1.0f, -3.0f, 1.0f, 1.0f);output.TexCoord = float2(0.0f, 2.0f);}return output;
}float4 BlurSample(in float2 screenPos, in float offset, in float2 mapSize)
{#if Vertical_float2 samplePos = screenPos;samplePos.y = clamp(screenPos.y + offset, 0, mapSize.y);return VSMMap[uint2(samplePos)];#elsefloat2 samplePos = screenPos;samplePos.x = clamp(screenPos.x + offset, 0, mapSize.x);return VSMMap[uint3(samplePos, 0)];#endif
}float4 BlurVSM(in VSOutput input) : SV_Target0
{#if Vertical_float scale = abs(CascadeScale.y);float maxFilterSize = MaxKernelSize / abs(Cascade0Scale.y);#elsefloat scale = abs(CascadeScale.x);float maxFilterSize = MaxKernelSize / abs(Cascade0Scale.x);#endifconst float KernelSize = clamp(min(FilterSize, maxFilterSize) * scale, 1.0f, MaxKernelSize);const float Radius = KernelSize / 2.0f;#if GPUSceneSubmission_[branch]if(KernelSize > 1.0f){const int SampleRadius = int(round(Radius));float4 sum = 0.0f;[loop]for(int i = -SampleRadius; i <= SampleRadius; ++i){float4 sample = BlurSample(input.Position.xy, i, ShadowMapDimensions);sample *= saturate((Radius + 0.5f) - abs(i));sum += sample;}return sum / KernelSize;}else{return BlurSample(input.Position.xy, 0, ShadowMapDimensions);}#elsefloat4 sum = 0.0f;[unroll]for(int i = -SampleRadius_; i <= SampleRadius_; ++i){float4 sample = BlurSample(input.Position.xy, i, ShadowMapDimensions);sample *= saturate((Radius + 0.5f) - abs(i));sum += sample;}return sum / KernelSize;#endif
}

总结一下:由于阴影贴图是线性可过滤的,那么我们就可以使用很多技术和算法。最明显的是,我们可以简单地使用纹理细化、三线过滤和各向异性过滤,甚至是多样本反锯齿(同时要渲染阴影贴图)。相比于使用标准的阴影贴图以及常量过滤percentage-closer过滤而言,它自己就可以极大地提高阴影贴图的质量。当然还有盒型滤波或高斯滤波等、还有就是区域求和表(SAT)算法也可以。有兴趣的可以自行尝试。

当然,为了更精确的算法深度平方的均值,还可以给其加一个偏离:
在这里插入图片描述
此处主要是把阴影贴图纹理像素当成一个局部平面分布考虑来优化(GPU Gems 3中相关优化技术),有兴趣的自行深入探索吧。

1.2.3 计算阴影

经过上边的处理我们就很容易得到深度与深度平方的期望值了,接下来就是在阴影计算的时候使用切比雪夫不等式了:


//  http://mynameismjp.wordpress.com/// 减少漏光
float Linstep(float a, float b, float v)
{return saturate((v - a) / (b - a));
}float ReduceLightBleeding(float pMax, float amount)
{// 溢出[0, amount] 的尾部并线型地缩放到[amount, 1].return Linstep(amount, 1.0f, pMax);
}float ChebyshevUpperBound(float2 moments, float mean, float minVariance,float lightBleedingReduction)
{// 计算方差float variance = moments.y - (moments.x * moments.x);variance = max(variance, minVariance);//防除0// 计算概率上界(切比雪夫不等式)float d = mean - moments.x;float pMax = variance / (variance + (d * d));//减轻漏光pMax = ReduceLightBleeding(pMax, lightBleedingReduction);// 单侧切比雪夫处理return (mean <= moments.x ? 1.0f : pMax);
}float SampleShadowMapVSM(in float3 shadowPos, in float3 shadowPosDX,in float3 shadowPosDY, uint cascadeIdx)
{float depth = shadowPos.z;float2 occluder = ShadowMap.SampleGrad(VSMSampler, float3(shadowPos.xy, cascadeIdx),shadowPosDX.xy, shadowPosDY.xy).xy;return ChebyshevUpperBound(occluder, depth, VSMBias * 0.01, LightBleedingReduction);
}float3 ShadowVisibility(in float3 positionWS, in float depthVS, in float nDotL, in float3 normal,in uint2 screenPos)
{float3 shadowVisibility = 1.0f;uint cascadeIdx = NumCascades - 1;float3 projectionPos = mul(float4(positionWS, 1.0f), ShadowMatrix).xyz;// Apply offsetfloat3 offset = GetShadowPosOffset(nDotL, normal) / abs(CascadeScales[cascadeIdx].z);// Project into shadow spacefloat3 samplePos = positionWS + offset;float3 shadowPosition = mul(float4(samplePos, 1.0f), ShadowMatrix).xyz;float3 shadowPosDX = ddx_fine(shadowPosition);float3 shadowPosDY = ddy_fine(shadowPosition);shadowVisibility = SampleShadowCascade(shadowPosition, shadowPosDX, shadowPosDY,cascadeIdx, screenPos);return shadowVisibility;
}

得到的结果如下所示:
在这里插入图片描述
看着上边代码不多,除了切比雪夫近似应用外,其实在一定程度上还解决了漏光问题(VSM最大的问题在于漏光现象),那么算法为什么会产生漏光呢?我们具体看一下(其实就是切比雪夫不等式的近似导致的,我们从数学公式的角度上分析更容易看出原因)。
在这里插入图片描述
假设三个物体从上到下标记为A、B、C,对应的深度值为a、b、c。只有物体A和B处于滤波区域,C则作为receiver被两个物体挡住本应是看不到光源的。我们假定当前着色点位于C中滤波区域的中心我们可以得到下面两个矩:
在这里插入图片描述
然后我们可以算出均值和方差:
在这里插入图片描述
上图中有Δx=b−a和Δy=c−b,因此运用切比雪夫不等式计算中间区域的可见性函数为:
在这里插入图片描述
以Δy/Δx作为变量,可以得到函数曲线为:
在这里插入图片描述
因此Δy/Δx越小,整体可见性越是接近于0.5,所以导致了漏光现象的出现。

因此解决漏光问题的算法便是上述代码中的:

// 减少漏光
float Linstep(float a, float b, float v)
{return saturate((v - a) / (b - a));
}float ReduceLightBleeding(float pMax, float amount)
{// 溢出[0, amount] 的尾部并线型地缩放到[amount, 1].return Linstep(amount, 1.0f, pMax);
}

其原理很简单:一些重要的数据观察结果是如果深度t的一个表面完全遮挡了某些平均深度为μ的过滤区域,那么t>μ。因此(t-μ)²>0,根据Chebyshev不等式,Pmax<1。简单地说就是,完全遮挡的表面上错误的半影绝对不会达到完全的亮度。因此我们可以通过修改Pmax来移除这些区域,这样所有小于某些最小亮度的值都会映射为0,然后重新调节其他的值,这样它们就映射到(0,1)区间。 也就是上边代码中的实现。

为了进一步提升精度更大程度上解决漏光问题,便衍生了LVSM、EVSM等方法,接下来我们就直接以效果最好的EVSM来看吧。

二、EVSM

2.1 算法原理

正如前面所说的,漏光现象在Δy和Δx的比值非常小的时候会特别明显。我们可以考虑使用一些对x和y的wrap来尝试提升Δy和Δx的比值。
例如我们可以使用上面e(cx)的wrapper,这里c是一个正数。然后对ecx求均值和方差,然后使用切比雪夫不等式求Pmax。
这样原来Δy/Δx就变成了e(Δy−Δx)

经过e(cx)的wrapper后,可以有效抑制漏光情况,但是随着c增大,远处场景也会出现问题,因此需要一个反向抑制,即-e(-cx)
在这里插入图片描述
这两个wrapper一起使用时,叫做EVSM4,即需要使用四通道纹理,只使用正向的叫EVSM2。由于e(cx)和-e(-cx)都是单调递增函数,这两个wrapper都可以使用切比雪夫不等式,最后取两个上限概率之中的最小值即可。这时候artifacts就会随着c值的增加而极大限度的减缓漏光问题。

2.2 算法实现

2.2.1 EVSM4阴影转换

代码如下:

static const uint SMFormat16Bit = 0;
static const uint SMFormat32Bit = 1;float2 GetEVSMExponents(in float positiveExponent, in float negativeExponent, in uint vsmFormat)
{const float maxExponent = vsmFormat == SMFormat16Bit ? 5.54f : 42.0f;float2 lightSpaceExponents = float2(positiveExponent, negativeExponent);// 逼近至fp32/fp16的最大范围,以防止溢出return min(lightSpaceExponents, maxExponent);
}// 对阴影贴图深度应用指数扭曲,输入深度应为[0,1]
float2 WarpDepth(float depth, float2 exponents)
{// Rescale depth into [-1, 1]depth = 2.0f * depth - 1.0f;float pos =  exp( exponents.x * depth);float neg = -exp(-exponents.y * depth);return float2(pos, neg);
}float4 ConvertToVSM(in VSOutput input) : SV_Target0
{float sampleWeight = 1.0f / float(MSAASamples_);uint2 coords = uint2(input.Position.xy);//40.0f,5.0ffloat2 exponents = GetEVSMExponents(PositiveExponent, NegativeExponent, SMFormat);float4 average = float4(0.0f, 0.0f, 0.0f, 0.0f);[unroll]for(uint i = 0; i < MSAASamples_; ++i){// Convert to EVSM representationfloat depth = ShadowMap[coords];float2 vsmDepth = WarpDepth(depth, exponents);average += sampleWeight * float4(vsmDepth.xy, vsmDepth.xy * vsmDepth.xy);}return average;
}

原始阴影贴图:
在这里插入图片描述
转换后的EVSM4阴影贴图:
在这里插入图片描述

2.2.2 阴影计算

static const uint SMFormat16Bit = 0;
static const uint SMFormat32Bit = 1;float2 GetEVSMExponents(in float positiveExponent, in float negativeExponent, in uint vsmFormat)
{const float maxExponent = vsmFormat == SMFormat16Bit ? 5.54f : 42.0f;float2 lightSpaceExponents = float2(positiveExponent, negativeExponent);// 逼近至fp32/fp16的最大范围,以防止溢出return min(lightSpaceExponents, maxExponent);
}// 对阴影贴图深度应用指数扭曲,输入深度应为[0,1]
float2 WarpDepth(float depth, float2 exponents)
{// Rescale depth into [-1, 1]depth = 2.0f * depth - 1.0f;float pos =  exp( exponents.x * depth);float neg = -exp(-exponents.y * depth);return float2(pos, neg);
}float SampleShadowMapEVSM(in float3 shadowPos, in float3 shadowPosDX,in float3 shadowPosDY)
{float2 exponents = GetEVSMExponents(PositiveExponent, NegativeExponent, SMFormat);float2 warpedDepth = WarpDepth(shadowPos.z, exponents);float4 occluder = ShadowMap.SampleGrad(VSMSampler, float3(shadowPos.xy, 0.),shadowPosDX.xy, shadowPosDY.xy);// 采样深度翘曲float2 depthScale = VSMBias * 0.01f * exponents * warpedDepth;float2 minVariance = depthScale * depthScale;#if ShadowMode_ == ShadowModeEVSM4_float posContrib = ChebyshevUpperBound(occluder.xz, warpedDepth.x, minVariance.x, LightBleedingReduction);float negContrib = ChebyshevUpperBound(occluder.yw, warpedDepth.y, minVariance.y, LightBleedingReduction);return min(posContrib, negContrib);#else// Positive onlyreturn ChebyshevUpperBound(occluder.xy, warpedDepth.x, minVariance.x, LightBleedingReduction);#endif
}float ShadowVisibility(in float3 positionWS, in float depthVS, in float nDotL, in float3 normal,in uint2 screenPos)
{float shadowVisibility = 1.0f;// Project into shadow spacefloat3 samplePos = positionWS;float3 shadowPosition = mul(float4(samplePos, 1.0f), ShadowMatrix).xyz;float3 shadowPosDX = ddx_fine(shadowPosition);float3 shadowPosDY = ddy_fine(shadowPosition);shadowVisibility = SampleShadowMapEVSM(shadowPosition, shadowPosDX, shadowPosDY, screenPos);return shadowVisibility;
}

生成及如果如下:
在这里插入图片描述

2.2.3 高斯滤波(软阴影生成)

最后说一下无论是VSM、LVSM、ESM还是EVSM都是没办法直接产生软阴影的,但是它相对于SM单点采样直接非0即1的结果来说已经很好了,对比如下图:

单次SampleCmpLevelZero采样阴影边缘:

在这里插入图片描述
EVSM单次采样阴影边缘:
在这里插入图片描述

所以一般还需要一个高斯滤波使其能够模糊边缘区域,实现软阴影。

简单放一个5*5高斯算法示意如下:

// Kernel from: https://computergraphics.stackexchange.com/questions/39/how-is-gaussian-blur-implemented
// I presume it is approximate using the Pascal pyramid
//const float blurKernel[25] = float[](
//    1.0 / 256.0,  4.0 / 256.0,  6.0 / 256.0,  4.0 / 256.0, 1.0 / 256.0,
//	4.0 / 256.0, 16.0 / 256.0, 24.0 / 256.0, 16.0 / 256.0, 4.0 / 256.0,
//	6.0 / 256.0, 24.0 / 256.0, 36.0 / 256.0, 24.0 / 256.0, 6.0 / 256.0,
//	4.0 / 256.0, 16.0 / 256.0, 24.0 / 256.0, 16.0 / 256.0, 4.0 / 256.0,
//    1.0 / 256.0,  4.0 / 256.0,  6.0 / 256.0,  4.0 / 256.0, 1.0 / 256.0
//);// Kernel generated at: http://dev.theomader.com/gaussian-kernel-calculator/
const float blurKernel[25] = float[](
0.023528,	0.033969,	0.038393,	0.033969,	0.023528,
0.033969,	0.049045,	0.055432,	0.049045,	0.033969,
0.038393,	0.055432,	0.062651,	0.055432,	0.038393,
0.033969,	0.049045,	0.055432,	0.049045,	0.033969,
0.023528,	0.033969,	0.038393,	0.033969,	0.023528
);float4 main(in VSOutput input) : SV_Target0
{float3 finalColor = float3 (0.0);float2 u_TexelSize=input.texelSize;for (int x = -2; x <= 2; x++) {for (int y = -2; y <= 2; y++) {finalColor += InputTexture.Sample(sampler, float2(v_TexCoords.x + u_TexelSize.x * x, v_TexCoords.y + u_TexelSize.y * y)).rgb * blurKernel[x + 2 + (y + 2) * 5];}}FragColor = float4 (finalColor, 1.0);
}

至此结束,至于如何运用到CSM(一般我们叫级联阴影,也有一个阴影算法叫Convolution Shadow Maps,也俗称CSM,仁者见仁吧)上,很简单,每级阴影皆执行EVSM即可。

参考文章:
http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf
https://learn.microsoft.com/zh-cn/windows/win32/dxtecharts/cascaded-shadow-maps?redirectedfrom=MSDN
https://graphics.stanford.edu/~mdfisher/Shadows.html
https://www.martincap.io/project_detail.php?project_id=9
https://graphics.stanford.edu/~mdfisher/Shadows.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/96989.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql数据传输到mssql

一、找开Navicat Premium 12 此时目标数据库会创建一个同名的表

深度学习环境配置教程(保姆教程)

深度学习环境配置教程(保姆教程&#xff09; 目录1.Anaconda安装2.Anaconda环境操作相关1.显示所有环境2.新建虚拟环境3.激活虚拟环境4.在对应的虚拟环境中安装库&#xff08;tensorflow与torch的安装&#xff09;1. Tensorflow的CPU与GPU安装示例如下&#xff1a;2. pytorch的…

(七)Unity VR项目升级至Vision Pro需要做的工作

Vision Pro 概述 定位为混合现实眼镜&#xff0c;对AR支持更友好 无手柄&#xff0c;支持手&#xff08;手势&#xff09;、眼&#xff08;注视&#xff09;、语音交互 支持空间音频&#xff0c;相比立体声、环绕声更有沉浸感和空间感 支持VR/AR应用&#xff0c;支持多种应用模…

AWS复制EC2文件到S3,g4dn.2xlarge没有NVIDIA GPU 驱动问题

1、给instances权限 action > Security > modify IAM role 把提前创建好的role给这个instance即可 2、复制到bucket aws s3 cp gogo.tar.gz s3://ee547finalbucket不需要手动安装GPU驱动 如果要自己安装&#xff0c;参考https://docs.aws.amazon.com/AWSEC2/latest/U…

八、Linux下,grep/wc/管道符/echo/重定向符/tail如何使用?

1、grep命令 &#xff08;1&#xff09;主要用于文件 &#xff08;2&#xff09;主要作用是“通过关键字&#xff0c;过滤文件行” &#xff08;3&#xff09;示例&#xff1a; 2、wc命令 &#xff08;1&#xff09;统计文件的行数、单词数等 &#xff08;2&#xff09;示例…

Ansys Zemax | 手机镜头设计 - 第 1 部分:光学设计

本文是 3 篇系列文章的一部分&#xff0c;该系列文章将讨论智能手机镜头模组设计的挑战&#xff0c;从概念、设计到制造和结构变形的分析。本文是三部分系列的第一部分&#xff0c;将专注于OpticStudio中镜头模组的设计、分析和可制造性评估。&#xff08;联系我们获取文章附件…

基于Python的微博大数据舆情分析,舆论情感分析可视化系统,可作为Python毕业设计

运行效果图 基于Python的微博大数据舆情分析&#xff0c;舆论情感分析可视化系统 系统介绍 微博舆情分析系统&#xff0c;项目后端分爬虫模块、数据分析模块、数据存储模块、业务逻辑模块组成。 先后进行了数据获取和筛选存储&#xff0c;对存储后的数据库数据进行提取分析处…

C语言:深度学习知识储备

目录 数据类型 每种类型的大小是多少呢&#xff1f; 变量 变量的命名&#xff1a; 变量的分类&#xff1a; 变量的作用域和生命周期 作用域&#xff1a; 生命周期&#xff1a; 常量 字符串转义字符注释 字符串&#xff1a; 转义字符 操作符&#xff1a; 算术操作符…

SAP ME2L/ME2M/ME3M报表增强添加字段(包含:LMEREPI02、SE18:ES_BADI_ME_REPORTING)

ME2L、ME2M、ME3M这三个报表的字段增强&#xff0c;核心点都在同一个结构里 SE11:MEREP_OUTTAB_PURCHDOC 在这里加字段&#xff0c;如果要加的字段是EKKO、EKPO里的数据&#xff0c;直接加进去&#xff0c;啥都不用做&#xff0c;就完成了 如果要加的字段不在EKKO和EKPO这两个…

基于Echarts的大数据可视化模板:智慧门店管理

目录 引言智慧门店管理的重要性Echarts在智慧门店管理中的应用智慧门店概述定义智慧门店的概念和核心智慧门店的关键技术智慧门店的发展趋势与方向智慧门店管理的作用Echarts与大数据可视化Echarts库以及其在大数据可视化领域的应用优势开发过程和所选设计方案模板如何满足管理…

[保研/考研机试] KY43 全排列 北京大学复试上机题 C++实现

题目链接&#xff1a; 全排列https://www.nowcoder.com/share/jump/437195121692001512368 描述 给定一个由不同的小写字母组成的字符串&#xff0c;输出这个字符串的所有全排列。 我们假设对于小写字母有a < b < ... < y < z&#xff0c;而且给定的字符串中的字…

从零实战SLAM-第四课(相机成像及常用视觉传感器)

在七月算法报的班&#xff0c;老师讲的蛮好。好记性不如烂笔头&#xff0c;关键内容还是记录一下吧&#xff0c;课程入口&#xff0c;感兴趣的同学可以学习一下。 --------------------------------------------------------------------------------------------------------…

[保研/考研机试] 杨辉三角形 西北工业大学复试上机题 C++实现

题目描述 Time Limit: 1000 ms Memory Limit: 256 mb 输入n值&#xff0c;使用递归函数&#xff0c;求杨辉三角形中各个位置上的值。 输入描述: 一个大于等于2的整型数n 输出描述: 题目可能有多组不同的测试数据&#xff0c;对于每组输入数据&#xff0c; 按题目的要求输…

符号随机梯度下降算法SIGNSGD

考虑随机优化问题&#xff1a; 符号随机梯度下降(SIGNSGD)算法&#xff1a; 假设基础&#xff1a; 收敛定理&#xff1a; 联邦优化&#xff1a;

08-微信小程序视图层

08-微信小程序视图层 文章目录 视图层 ViewWXML数据绑定列表渲染条件渲染模板引用importimport 的作用域include WXSS尺寸单位样式导入内联样式选择器全局样式与局部样式 WXS注意事项页面渲染数据处理 视图层 View 框架的视图层由 WXML 与 WXSS 编写&#xff0c;由组件来进行…

国产32位单片机XL32F001,带1 路 12bit ADC,I2C、SPI、USART 等外设

XL32F001 系列单片机采用高性能的 32 位 ARM Cortex-M0内核&#xff0c;宽电压工作范围的 MCU。嵌入 24KbytesFlash 和 3Kbytes SRAM 存储器&#xff0c;最高工作频率 24MHz。包含多种不同封装类型多款产品。芯片集成 I2C、SPI、USART 等通讯外设&#xff0c;1 路 12bit ADC&am…

idea中Maven报错Unable to import maven project: See logs for details问题的解决方法

idea中Maven报错Unable to import maven project: See logs for details问题的解决方法。 在查看maven的环境配置和idea的maven配置后&#xff0c;发现是idea 2020版本和maven 3.9.3版本的兼容性问题。在更改为Idea自带的maven 3.6.1版本后问题解决&#xff0c;能成功下载jar包…

如何修复损坏的DOC和DOCX格式Word文件?

我们日常办公中&#xff0c;经常用到Word文档。但是有时会遇到word文件损坏、无法打开的情况。这时该怎么办&#xff1f;接着往下看&#xff0c;小编在这里就给大家带来最简单的Word文件修复方法&#xff01; 很多时候DOC和DOCX Word文件会无缘无故的损坏无法打开&#xff0c;一…

Aurix TC3xx系列MCU ResourceM模块配置(多核资源分配)

文章目录 1 前言2 配置方法 >>返回总目录<< 1 前言 为减轻主核的负载率或者平衡各个核的资源分配&#xff0c;通常需要把一些MCU内部资源分配到从核上&#xff0c;在EB tresos工具中&#xff0c;通过ResourceM模块实现多核资源分配。 2 配置方法 ResourceMMaste…

16.5.4 【Linux】SELinux 政策内的规则管理

SELinux 各个规则的布林值查询 getsebool 如果想要查询系统上面全部规则的启动与否 &#xff08;on/off&#xff0c;亦即布林值&#xff09;&#xff0c;很简单的通过 sestatus-b 或 getsebool -a 均可&#xff01; SELinux 各个规则规范的主体程序能够读取的文件 SELinux typ…