Unity是如何生成阴影的

  • 前言
  • 相关的宏
  • 投射阴影
    • v2f结构定义
      • V2F_SHADOW_CASTER
      • UNITY_VERTEX_OUTPUT_STEREO
    • 顶点函数:TRANSFER_SHADOW_CASTER_NORMALOFFSET
      • UnityClipSpaceShadowCasterPos
      • UnityApplyLinearShadowBias
    • 片元函数 SHADOW_CASTER_FRAGMENT
  • 接受阴影
    • v2f结构定义
    • 顶点处理
      • UNITY_TRANSFER_SHADOW
        • TRANSFER_SHADOW
      • 灯光位置--COMPUTE_LIGHT_COORDS
  • 片元处理
    • UNITY_SHADOW_ATTENUATION
      • 淡入淡出计算-UnityComputeShadowFadeDistance
      • 烘焙阴影计算-UnitySampleBakedOcclusion
      • 阴影采样- unitySampleShadow
      • 阴影混合- UnityMixRealtimeAndBakedShadows
  • 总结

前言

通过上一篇的总结,我们知道了阴影的产生原理。先生成一个已知的模板深度,当计算某个片元时,拿着个片元的深度和模板深度比较,得出是否在阴影。那么我们今天就分两个步骤看看unity是如何完成这两部操作的。

相关的宏

1.VERTEXLIGHT_ON:由灯光的rendermode、点光源还是平行光决定,是逐顶点还是像素光,这个不懂得可以看看shaderlab实战开发一书,这里就不多说了。
2.SHADOW_DEPTH:计算平行光照射的阴影深度图时,此关键字开启。(聚光灯不做考虑,也是我们主要讨论的对象)
3.SHADOW_CUBE:计算点光源照射的阴影深度图时,此关键字开启。
4.SHADOW_SCREEN:主光源为平行光时,此关键字开启。
5.SHADOWS_CUBE_IN_DEPTH_TEX:将点光源产生深度纹理渲染到一张深度图中,次关键字会和 关键字SHADOW_CUBE配合使用,默认情况下SHADOW_CUBE的深度纹理渲染到的是cubemap中。
6.UNITY_REVERSED_Z:近屏幕端Z的值,dx中近屏幕端z为1,远处为0
7.UNITY_NEAR_CLIP_VALUE:剪裁空间中近屏幕面的值,ndc空间z的最小值。dx中为0,opengl中为-1
8.SHDOWS_SHADOWMASK:烘焙光照贴图的LightMode模式
9.SHADOWS_NATIVE:表示硬件是否有原生shadowmap支持。如果支持可以直接使用,不需要再做距离比较。
10.LIGHTMAP_SHADOW_MIXING:灯的模式为mixed.

投射阴影

这一步就是生成深度图的步骤,所以这个操作一定是最先开始的,说到这个渲染顺序,谁知道CameraDepth、Sorting Layer、Order In Layer、RenderQueue的排序规则?年纪大了,我也总是忘.,所以我们渲染深度的相机的depth一定是最先开始的。Unity调用Pass中Tags中“LightMode”=“ShadowCaster"的通道进行深度渲染,但是有时候会发现我这个shader中没有添加这个标签为什么还是会有影子,因为unity的FallBack机制,那一定是你的shader有FallBack,并且FallBack对应的shader里有ShadowCaster这个标签。我们定位一下unity的Mobile-Diffuse.shader,发现没有ShdowCaster,但是他有个Fallback “Mobile/VertexLit” 我们打开"Mobile/VertexLit”,这里我只把ShadowCaster展示出来:

// Pass to render object as a shadow caster
Pass
{Name "ShadowCaster"//--标识用于投射阴影的通道Tags { "LightMode" = "ShadowCaster" }//深度写入开启ZWrite On //ZTest 小于等于ZTest LEqual //关闭剔除 ---如果这几个不知道啥意思的~~有个女神叫冯乐乐Cull OffCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma target 2.0#pragma multi_compile_shadowcaster#include "UnityCG.cginc"struct v2f {//定义阴影需要的数据结构,顶点向片元传递的结构V2F_SHADOW_CASTER;UNITY_VERTEX_OUTPUT_STEREO};v2f vert( appdata_base v ){v2f o;//GPUInstance使用,考虑UNITY_SETUP_INSTANCE_ID(v);UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);//顶点转换TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)return o;}float4 frag( v2f i ) : SV_Target{SHADOW_CASTER_FRAGMENT(i)}ENDCG
}

v2f结构定义

V2F_SHADOW_CASTER

// Declare all data needed for shadow caster pass output (any shadow directions/depths/distances as needed),
// plus clip space position.
#define V2F_SHADOW_CASTER V2F_SHADOW_CASTER_NOPOS UNITY_POSITION(pos)

这里有重新定义了两个宏,如果你看过之前写的那个链接: Stand源码解析,对第二个就感到很亲切了.当然,说实话要是我,我看不下去。

  1. UNITY_POSITION(pos);//float4 pos : SV_POSITION SV_POSITION和VPOS语义,均为片元使用坐标,GPU使用SV_POSITION中的值。来从剪辑空间位置计算屏幕像素位置。如果使用POSITION语义,我们还要手动处理透视触发,去除w分量。

  2. V2F_SHADOW_CASTER_NOPOS:

     //SHADOWS_CUBE--如果是点光源,并且这个点光源渲染的深度纹理渲染到cubemap中,则申请一个位置结构#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)// Rendering into point light (cubemap) shadows--申请光源到顶点的向量#define V2F_SHADOW_CASTER_NOPOS float3 vec : TEXCOORD0;#else// Rendering into directional or spot light shadows//不进行啥操作#define V2F_SHADOW_CASTER_NOPOS// 同时生命了一个标识,标识没有对位置进行记录#define V2F_SHADOW_CASTER_NOPOS_IS_EMPTY		 #endif
    

到此,我们知道这个宏定义了位置信息,如果是点光源同时点光源的深度图为cubemap时,还需要记录光源到顶点的向量信息。但是一般情况下,我们的手机有的不支持cubemap,而且手游实时点光源更是奢侈,所以从轻处理。

UNITY_VERTEX_OUTPUT_STEREO

UNITY_VERTEX_OUTPUT_STEREO来声明该顶点是否位于视线域中,来判断这个顶点是否输出到片段着色器,GPUInstance中见过,

# define UNITY_VERTEX_OUTPUT_STEREO   DEFAULT_UNITY_VERTEX_OUTPUT_STEREO
//看看是啥,尝尝鲜就行了~~再往下就跑偏了
#define DEFAULT_UNITY_VERTEX_OUTPUT_STEREO                         
uint stereoTargetEyeIndexSV :SV_RenderTargetArrayIndex;   
uint stereoTargetEyeIndex : BLENDINDICES0;

顶点函数:TRANSFER_SHADOW_CASTER_NORMALOFFSET

// Vertex shader part, with support for normal offset shadows. Requires
// position and normal to be present in the vertex input.
#define TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) TRANSFER_SHADOW_CASTER_NOPOS(o,o.pos)#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)#define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) //记录光源到顶点的位置向量o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; //顶点转换,不多说opos = UnityObjectToClipPos(v.vertex);
#else#define TRANSFER_SHADOW_CASTER_NOPOS(o,opos)         opos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); opos = UnityApplyLinearShadowBias(opos);
#endif

UnityClipSpaceShadowCasterPos

//计算顶点在裁剪空间的位置
float4 UnityClipSpaceShadowCasterPos(float4 vertex, float3 normal)
{float4 wPos = mul(unity_ObjectToWorld, vertex);	if (unity_LightShadowBias.z != 0.0){float3 wNormal = UnityObjectToWorldNormal(normal);float3 wLight = normalize(UnityWorldSpaceLightDir(wPos.xyz));// apply normal offset bias (inset position along the normal)// bias needs to be scaled by sine between normal and light direction// (http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1/)//// unity_LightShadowBias.z contains user-specified normal offset amount// scaled by world space texel size.float shadowCos = dot(wNormal, wLight);float shadowSine = sqrt(1-shadowCos*shadowCos);//上面两行呢,是为了求sin(灯光和法线夹角),因为1=sin^2+cos^2,所以sin=sqrt(1-con^2)//我们为什么要求sin,上一篇我们说过,我们的bias的偏移值要和法线以及法线和灯光的角度相关,//那么是什么关系呢?夹角越大,偏移越大。夹角为0偏移为0。所以sin函数的曲线完全符合。float normalBias = unity_LightShadowBias.z * shadowSine;	//下面就是朝着法线反方向偏移,现在我们是在做渲染深度图,在这里做了深度偏移wPos.xyz -= wNormal * normalBias;}//将偏移后的顶点,转换到投影空间	return mul(UNITY_MATRIX_VP, wPos);
}

unity_LightShadowBias:x表示阴影裁切空间中的线性偏移bias,透视相机unity是按照摄像机参数计算的这个值。-------y 对于平行光为1,对于其他光源为0,-----z 代表阴影slope depth bias scale。

UnityApplyLinearShadowBias

咋得,上面不是说都计算完了偏移了么?这咋又有偏移,不给面是不是~~上面我们做的只是法线偏移,但是我们真正做计算比较时候深度是在裁剪空间,所以这里才是真正意义上的阴影偏移,我们进行偏移的时候要避免裁剪空间中的z值造成越界,

float4 UnityApplyLinearShadowBias(float4 clipPos)	
{// For point lights that support depth cube map, the bias is applied in the fragment shader sampling the shadow map.// This is because the legacy behaviour for point light shadow map cannot be implemented by offseting the vertex position// in the vertex shader generating the shadow map.//上面的注释说的很清楚了,就是深度图是cubemap的(点光源),不会再这里处理偏移,因为这里无法实现。
#if !(defined(SHADOWS_CUBE) && defined(SHADOWS_CUBE_IN_DEPTH_TEX))//如果定义了z是反向,那就是z值的范围是[near,0],所以我们偏移是朝向近平面做的偏移,对于做了z反向的空间,//近平面偏移就是减法,没有的近平面就是加法#if defined(UNITY_REVERSED_Z)// We use max/min instead of clamp to ensure proper handling of the rare case// where both numerator and denominator are zero and the fraction becomes NaN.clipPos.z += max(-1, min(unity_LightShadowBias.x / clipPos.w, 0));#elseclipPos.z += saturate(unity_LightShadowBias.x/clipPos.w);#endif
#endif//UNITY_NEAR_CLIP_VALUE 上面有提到dx中为1,opengl中为-1,是为了不让越界//接下来的操作就是为了不让我们计算出z值越界
#if defined(UNITY_REVERSED_Z)	//----Z值范围为[near,0],是递减的,如果clipPos.z大于clipPos.w*UNITY_NEAR_CLIP_VALUE值,说明越界了float clamped = min(clipPos.z, clipPos.w*UNITY_NEAR_CLIP_VALUE);
#else//---Z值范围为[near,1],是递增的,如果范围小于了clipPos.w*UNITY_NEAR_CLIP_VALUE则越界了。float clamped = max(clipPos.z, clipPos.w*UNITY_NEAR_CLIP_VALUE);
#endif
//上面说了,点光源不做处理,unity_LightShadowBias.y=0的是时候,非平行光,为1的时候表示平行光。clipPos.z = lerp(clipPos.z, clamped, unity_LightShadowBias.y);return clipPos;
}

片元函数 SHADOW_CASTER_FRAGMENT

简单粗暴,只有一个宏:SHADOW_CASTER_FRAGMENT(i);让我们看看他的庐山真面目。
哇,好n形,平行光返回0,上面辛辛苦苦算了半天,这边就不管是啥都反回0.没错,我们这是在投射阴影,所以只需要保存深度,不需要颜色。但是使用CubeMap来做阴影缺不一样,需要把z值写入cubemap中。UnityEncodeCubeShadowDepth把浮点数编码成RGBA形式存入图中。EncodeFloatRGBA一般用于2D纹理的浮点数压缩,自定义阴影可以使用这个呦

  #if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)#define SHADOW_CASTER_FRAGMENT(i) //_LightPositionRange.w=1/rangereturn UnityEncodeCubeShadowDepth ((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w);#else#define SHADOW_CASTER_FRAGMENT(i) return 0;#endif

所以,如果我们自定义的shader也需要投影的话,就可以自定义pass来实现,或者直接callback已经有对应pass的shader。知道了原理,我们其实在顶点变换的地方做各式各样的阴影投射。

接受阴影

知道了怎么投射阴影后,接下来,我们看看,unity是如何让物体接受阴影的,为了节省只看平行光的。首先接受投影不需要对应的pass的标签。上一章我们讲到,渲染某一个物体的时候,拿自己的点跟深度缓存的数据做对比,来看看是不是在阴影中,所以我们就需要一个坐标,然后做顶点函数将这个坐标转换到灯光摄像机的裁剪空间。
说到这里,我们要想下,unity中还有什么会影响到阴影?没错,就是光照贴图,所以我们再写shader的时候要注意光照贴图,当然角色渲染不用考虑,但是场景渲染是需要考虑进去的。那投射阴影跟光照贴图有没有关系?没有 ,投射阴影只与对应的pass的标签有关,所以被烘焙的物体,如果有投射阴影的pass依然是可以投射阴影的。
变量解释:
_LightTexture0:平行光 光照衰减纹理,在灯光没有

光源类型 Cookie纹理(打开) 光照衰减纹理(无Cookie) 光照衰减纹理(有Cookie)
平行光 _LightTexture0 不需要 不需要
点光源 _LightTexture0 _LightTexture0 _LightTextureB0
聚光灯 _LightTexture0 _LightTexture0 _LightTextureB0

由于,cookie在手游项目中,使用极少,所以不做讨论,聚光灯不做讨论。主要考虑平行光

v2f结构定义

源码有太多的宏定义了,我们自己在做的时候可以根据自己的项目去写对应的代码。
1.定义_LightCoord,这个lightcoord主要是用来做衰减的,所以平行光,是不需要这个定义的(要是有杠头说平行光加了cookie就会用,那就你说的对)。至于如何计算,在顶点函数我们进行。
2.定义_ShadowCoord,这个用来获取深度信息的,通过他来计算出深度图的uv。这个一看就是都需要。

看看unity定义了多少的这些宏

	//---第一个,使用UNITY_LIGHTING_COORDS声明_LightCoord,使用DECLARE_LIGHT_COORDS声明_ShadowCoord#define UNITY_LIGHTING_COORDS(idx1, idx2) DECLARE_LIGHT_COORDS(idx1) UNITY_SHADOW_COORDS(idx2)//---第二个,跟上面的不用之处在于使用SHADOW_COORDS声明了_ShadowCoord#define LIGHTING_COORDS(idx1, idx2) DECLARE_LIGHT_COORDS(idx1) SHADOW_COORDS(idx2)

首先是DECLARE_LIGHT_COORDS的定义,

	#ifdef POINT#   define DECLARE_LIGHT_COORDS(idx) unityShadowCoord3 _LightCoord : TEXCOORD##idx;#endif#ifdef SPOT#   define DECLARE_LIGHT_COORDS(idx) unityShadowCoord4 _LightCoord : TEXCOORD##idx;#endif		//没有任何操作#ifdef DIRECTIONAL#   define DECLARE_LIGHT_COORDS(idx)#endif  

然后UNITY_SHADOW_COORDS定义,其实我们看到他的分支要么直接定义,要么就使用SHADOW_COORDS进行定义,那么为什么这么操作一下呢,主要是光照贴图做的区分。

// handles shadows in the depths of the GI function for performance reasons
//如果在GI的计算了阴影,详见stander源码解析
#if defined(HANDLE_SHADOWS_BLENDING_IN_GI) #   define UNITY_SHADOW_COORDS(idx1) SHADOW_COORDS(idx1)
//如果使用平行光,并且没有光照贴图,不是屏幕空间阴影
#elif defined(SHADOWS_SCREEN) && !defined(LIGHTMAP_ON) && !defined(UNITY_NO_SCREENSPACE_SHADOWS) // no lightmap uv thus store screenPos instead// can happen if we have two directional lights. main light gets handled in GI code, //but 2nd dir light can have shadow screen and mask.// - Disabled on ES2 because WebGL 1.0 seems to have junk in .w (even though it shouldn't)//如果有SHADOWS_SHADOWMASK,这是Lighting-MixedLighting-LightingMode-Shadowmask进行烘焙设置开启//如果是在es2.0的不使用w值了。因为WebGL 1.0的w值有问题(尽管不应该)#   if defined(SHADOWS_SHADOWMASK) && !defined(SHADER_API_GLES)#       define UNITY_SHADOW_COORDS(idx1) unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1;		#   else#       define UNITY_SHADOW_COORDS(idx1) SHADOW_COORDS(idx1)#   endif
#else#   define UNITY_SHADOW_COORDS(idx1) unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1;
#endif

我们看看SHADOW_COORDS的定义:

 //平行光,这就尬住了啊,定义一样啊
#if defined (SHADOWS_SCREEN)#define SHADOW_COORDS(idx1) unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1;#define SHADOW_ATTENUATION(a) unitySampleShadow(a._ShadowCoord)
#endif// ---- 聚光灯,一样啊
#if defined (SHADOWS_DEPTH) && defined (SPOT)#define SHADOW_COORDS(idx1) unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1;
#endif// ----点光源
#if defined (SHADOWS_CUBE)#define SHADOW_COORDS(idx1) unityShadowCoord3 _ShadowCoord : TEXCOORD##idx1;	
#endif// ---- 关闭阴影,
#if !defined (SHADOWS_SCREEN) && !defined (SHADOWS_DEPTH) && !defined (SHADOWS_CUBE)#define SHADOW_COORDS(idx1)		
#endif

看来看去,除了点光源和定义数据类型的不一样和关闭时不需要定义外,其他的定义都是一样的,好费劲。是为了条例更加清晰么?才写这么绕。。。

顶点处理

计算顶点操作,其实从上面定义数据的处理就可以看出来,主要是区分了是否使用光照贴图,以及光源类型进行的区分。所以,顶点计算也是如此,代码如下:

#define UNITY_TRANSFER_LIGHTING(a, coord) COMPUTE_LIGHT_COORDS(a) //这里a是结构v2f,但是这coord是什么?是顶点的uv信息UNITY_TRANSFER_SHADOW(a, coord)
#define TRANSFER_VERTEX_TO_FRAGMENT(a) COMPUTE_LIGHT_COORDS(a)TRANSFER_SHADOW(a)

很简单,两句话,上面的数据结构定义有两个,_LightCoords和_ShadowCoords,所以这两句从名字也能看出是给这俩东西赋值的,如果我们再自己写shader的时候,明确不需要哪些东西就可以选择的时候,毕竟变体太多不是一般项目可以承受的,

UNITY_TRANSFER_SHADOW

UNITY_TRANSFER_SHADOW计算深度图的uv,由此我们看出对于使用了SHADOWS_SHADOWMASK的物体,是不需要深度图的,而是直接从光照贴图中获取阴影信息:

	// handles shadows in the depths of the GI function for performance reasons#if defined(HANDLE_SHADOWS_BLENDING_IN_GI) #   define UNITY_TRANSFER_SHADOW(a, coord) TRANSFER_SHADOW(a)		#elif defined(SHADOWS_SCREEN) && !defined(LIGHTMAP_ON) && !defined(UNITY_NO_SCREENSPACE_SHADOWS) //如果有SHADOWS_SHADOWMASK,这是Lighting-MixedLighting-LightingMode-Shadowmask进行烘焙设置开启//如果是在es2.0的不使用w值了。因为WebGL 1.0的w值有问题(尽管不应该)#   if defined(SHADOWS_SHADOWMASK) && !defined(SHADER_API_GLES)#       define UNITY_TRANSFER_SHADOW(a, coord) //xy记录了光照贴图的uv信息,zw则记录了屏幕空间xy的位置信息,这里与我们上一张介绍的//不太一致,//但是这里ComputeScreenPos是获取的当前的场景摄像机{a._ShadowCoord.xy = coord * unity_LightmapST.xy + unity_LightmapST.zw;a._ShadowCoord.zw = ComputeScreenPos(a.pos).xy;}	#   else		#       define UNITY_TRANSFER_SHADOW(a, coord) TRANSFER_SHADOW(a)#   endif#else		#   if defined(SHADOWS_SHADOWMASK)#       define UNITY_TRANSFER_SHADOW(a, coord) //只获取光照贴图的uva._ShadowCoord.xy = coord.xy * unity_LightmapST.xy + unity_LightmapST.zw;	#   else//---记住这个耻辱---高精度片元处理器,不配在顶点处理做阴影坐标转换,真男人就去片元做。。。#       if !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)#           define UNITY_TRANSFER_SHADOW(a, coord)#       else#           define UNITY_TRANSFER_SHADOW(a, coord) TRANSFER_SHADOW(a)#       endif	#   endif#endif

TRANSFER_SHADOW

	//平行光#if defined (SHADOWS_SCREEN)		#if defined(UNITY_NO_SCREENSPACE_SHADOWS)#define TRANSFER_SHADOW(a) a._ShadowCoord = mul( unity_WorldToShadow[0], mul( unity_ObjectToWorld, v.vertex ) );#else // 屏幕空间阴影--这里记录的也是场景相机的ndc空间坐标啊??#define TRANSFER_SHADOW(a) a._ShadowCoord = ComputeScreenPos(a.pos);		 #endif#endif// ---- Spot light shadows#if defined (SHADOWS_DEPTH) && defined (SPOT)#define TRANSFER_SHADOW(a) a._ShadowCoord = mul (unity_WorldToShadow[0], mul(unity_ObjectToWorld,v.vertex));#endif// ---- Point light shadows#if defined (SHADOWS_CUBE)#define TRANSFER_SHADOW(a)a._ShadowCoord.xyz = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz;#endif// ---- Shadows off#if !defined (SHADOWS_SCREEN) && !defined (SHADOWS_DEPTH) && !defined (SHADOWS_CUBE)		#define TRANSFER_SHADOW(a)#endif

这里我们有了四种情况,第一种是使用阴影蒙版,要记录光照贴图uv信息,以及屏幕空间坐标。第二种是屏幕空间阴影记录做了屏幕空间坐标。第三种是点光源,记录的是灯光到顶点的向量信息(不考虑)。第四种才是我们上一讲说的灯光空间的位置变换unity_WorldToShadow[0]为世界空间–灯光摄像机裁剪空间的转换矩阵。

灯光位置–COMPUTE_LIGHT_COORDS

这个位置主要是计算距离灯光的相对坐标,用于聚光灯和点光源,平行光不需要,因为平行光只有方向

	#ifdef SPOT#   define COMPUTE_LIGHT_COORDS(a)a._LightCoord = mul(unity_WorldToLight, mul(unity_ObjectToWorld, v.vertex));	#endif#ifdef DIRECTIONAL#   define COMPUTE_LIGHT_COORDS(a)#endif#ifdef POINT#   define COMPUTE_LIGHT_COORDS(a) a._LightCoord = mul(unity_WorldToLight, mul(unity_ObjectToWorld, v.vertex)).xyz;#endif

这里用到的变换矩阵为unity_WorldToLight。

片元处理

UNITY_SHADOW_ATTENUATION

计算衰减,前面都是数据准备。
在看这里之前,了解一下unity烘焙系统还是很关键的,因为阴影处理不可能一直都是实时的,不然的话,下面的各个宏的关系会让你头皮发麻,生活不能自理,走你:光照贴图LightMode.

//如果在GI中处理的阴影,则使用shadow_attenuation进行处理
#if defined(HANDLE_SHADOWS_BLENDING_IN_GI) 
#   define UNITY_SHADOW_ATTENUATION(a, worldPos) SHADOW_ATTENUATION(a)
//平行光,非光照贴图,非屏幕空间阴影。
#elif defined(SHADOWS_SCREEN) && !defined(LIGHTMAP_ON) && !defined(UNITY_NO_SCREENSPACE_SHADOWS)	
//----不开启光照贴图,怎么开启SHADOWS_SHADOWMASK?--官方翻译我没试出来。。
//当有两个有向平行光时,主有向平行光在全局照明的相关代码中进行处理,第二个有向平行光在屏幕空间中进行
//阴影计算,如果使用了阴影蒙版,且不是在D3D9和OpenGLES平台下,那就从烘焙出来的光照贴图中取得阴影数据。
#   if defined(SHADOWS_SHADOWMASK) && !defined(SHADER_API_GLES)
#       define UNITY_SHADOW_ATTENUATION(a, worldPos) //_ShadowCoord.xy为光照图uv,zw为屏幕空间xy坐标UnityComputeForwardShadows(a._ShadowCoord.xy, worldPos, float4(a._ShadowCoord.zw, 0.0, UNITY_SHADOW_W(a.pos.w)));
#   else
#       define UNITY_SHADOW_ATTENUATION(a, worldPos) //实时计算-平行光,无lightmap,无shadowmaskUnityComputeForwardShadows(0, worldPos, a._ShadowCoord)
#   endif
#else
#   if defined(SHADOWS_SHADOWMASK)   //开启了shadowmask模式
#       define UNITY_TRANSFER_SHADOW(a, coord) a._ShadowCoord.xy = coord.xy * unity_LightmapST.xy + unity_LightmapST.zw;
#       if (defined(SHADOWS_DEPTH) || defined(SHADOWS_SCREEN) || defined(SHADOWS_CUBE) || UNITY_LIGHT_PROBE_PROXY_VOLUME)//这些宏都是说明需要进行实时阴影的计算,也是就既要shadowmask也要实时阴影
#           define UNITY_SHADOW_ATTENUATION(a, worldPos) UnityComputeForwardShadows(a._ShadowCoord.xy, worldPos, UNITY_READ_SHADOW_COORDS(a))
#       else  //--不需要实时阴影计算
#           define UNITY_SHADOW_ATTENUATION(a, worldPos) UnityComputeForwardShadows(a._ShadowCoord.xy, 0, 0)
#       endif
#   else
#       if (defined(SHADOWS_DEPTH) || defined(SHADOWS_SCREEN) || defined(SHADOWS_CUBE))
#           define UNITY_SHADOW_ATTENUATION(a, worldPos)  //---实时计算,没有烘焙阴影UnityComputeForwardShadows(0, worldPos, UNITY_READ_SHADOW_COORDS(a))
#       else
#           if UNITY_LIGHT_PROBE_PROXY_VOLUME
#               define UNITY_SHADOW_ATTENUATION(a, worldPos) //---实时计算,没有烘焙阴影UnityComputeForwardShadows(0, worldPos, UNITY_READ_SHADOW_COORDS(a))
#           else//--没有影子计算
#               define UNITY_SHADOW_ATTENUATION(a, worldPos) UnityComputeForwardShadows(0, 0, 0)
#           endif
#       endif
#   endif
#endif// -----------------------------
//  Shadow helpers (5.6+ version)
// -----------------------------
// This version depends on having worldPos available in the fragment shader and using that to compute light coordinates.
// if also supports ShadowMask (separately baked shadows for lightmapped objects)
//-----支持计算实时阴影,以及烘焙的阴影。  
//第一个参数lightmapUV用来获取烘焙阴影。
//第二个参数worldPos世界坐标,1.计算淡入淡出需要距离计算。2.烘焙阴影计算衰减 3.spot和shadowcube计算需要
//第三个参数screenPos屏幕空间的位置,用于实时计算。1.获取深度图的uv坐标,以及屏幕空间阴影的shadow的uv坐标。
half UnityComputeForwardShadows(float2 lightmapUV, float3 worldPos, float4 screenPos)
{//fade value----ZDist表示当前点在视空间的深度float zDist = dot(_WorldSpaceCameraPos - worldPos, UNITY_MATRIX_V[2].xyz);//实时转烘焙?马萨卡?没错,就是淡入淡入。如果你看了上面的知识点,就知道,通过shadowdistance//我们会完成实时阴影和烘焙阴影的切换,但是直接切会显得不够高大尚,所以这里就是淡入淡出float fadeDist = UnityComputeShadowFadeDistance(worldPos, zDist);half  realtimeToBakedShadowFade = UnityComputeShadowFade(fadeDist);//读取烘焙的阴影数据half shadowMaskAttenuation = UnitySampleBakedOcclusion(lightmapUV, worldPos);//实时阴影计算,只有平行光half realtimeShadowAttenuation = 1.0f;//directional realtime shadow#if defined (SHADOWS_SCREEN)#if defined(UNITY_NO_SCREENSPACE_SHADOWS) && !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)//非屏幕空间阴影,并且是高精度片元处理器,什么高精度?对,就顶点可以啥都不用处理的那个,在这处理了realtimeShadowAttenuation = unitySampleShadow(mul(unity_WorldToShadow[0], unityShadowCoord4(worldPos, 1)));#else//Only reached when LIGHTMAP_ON is NOT defined (and thus we use interpolator for screenPos rather than lightmap UVs). See HANDLE_SHADOWS_BLENDING_IN_GI below.//只有当没有开启LightMap时执行,因此我们使用的是sreeenpos而不是lightmapuv.realtimeShadowAttenuation = unitySampleShadow(screenPos);#endif#endif#if defined(UNITY_FAST_COHERENT_DYNAMIC_BRANCHING) && defined(SHADOWS_SOFT) && !defined(LIGHTMAP_SHADOW_MIXING)//avoid expensive shadows fetches in the distance where coherency will be goodUNITY_BRANCHif (realtimeToBakedShadowFade < (1.0f - 1e-2f)){#endif	//spot realtime shadow--聚光灯是实时计算#if (defined (SHADOWS_DEPTH) && defined (SPOT))#if !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)unityShadowCoord4 spotShadowCoord = mul(unity_WorldToShadow[0], unityShadowCoord4(worldPos, 1));#elseunityShadowCoord4 spotShadowCoord = screenPos;#endifrealtimeShadowAttenuation = UnitySampleShadowmap(spotShadowCoord);#endif//point realtime shadow--点光源实时计算#if defined (SHADOWS_CUBE)realtimeShadowAttenuation = UnitySampleShadowmap(worldPos - _LightPositionRange.xyz);#endif#if defined(UNITY_FAST_COHERENT_DYNAMIC_BRANCHING) && defined(SHADOWS_SOFT) && !defined(LIGHTMAP_SHADOW_MIXING)}#endif//混合实时阴影衰减,烘焙阴影衰减,淡入淡入阴影衰减return UnityMixRealtimeAndBakedShadows(realtimeShadowAttenuation, shadowMaskAttenuation, realtimeToBakedShadowFade);
}

淡入淡出计算-UnityComputeShadowFadeDistance

这个没啥说的,就是根据距离,算出当前的衰减值,主要是下面变量的意义
超链接:下面用到的一个变量解析unity_ShadowFadeCenterAndType.

	float UnityComputeShadowFadeDistance(float3 wpos, float z){float sphereDist = distance(wpos, unity_ShadowFadeCenterAndType.xyz);return lerp(z, sphereDist, unity_ShadowFadeCenterAndType.w);}		// ------------------------------------------------------------------half UnityComputeShadowFade(float fadeDist){return saturate(fadeDist * _LightShadowData.z + _LightShadowData.w);}

烘焙阴影计算-UnitySampleBakedOcclusion

看不懂就走你:光照贴图LightMode.

fixed UnitySampleBakedOcclusion (float2 lightmapUV, float3 worldPos)
{//shadowmask模式#if defined (SHADOWS_SHADOWMASK)#if defined(LIGHTMAP_ON)//直接采样fixed4 rawOcclusionMask = UNITY_SAMPLE_TEX2D(unity_ShadowMask, lightmapUV.xy);#elsefixed4 rawOcclusionMask = fixed4(1.0, 1.0, 1.0, 1.0);#if UNITY_LIGHT_PROBE_PROXY_VOLUME//x分量为1时表示启用本光照体代理体,为0时表示不启用,使用光照探针计算阴影,反之使用蒙版图if (unity_ProbeVolumeParams.x == 1.0)rawOcclusionMask = LPPV_SampleProbeOcclusion(worldPos);elserawOcclusionMask = UNITY_SAMPLE_TEX2D(unity_ShadowMask, lightmapUV.xy);#elserawOcclusionMask = UNITY_SAMPLE_TEX2D(unity_ShadowMask, lightmapUV.xy);#endif#endifreturn saturate(dot(rawOcclusionMask, unity_OcclusionMaskSelector));#else	//In forward dynamic objects can only get baked occlusion from LPPV, light probe occlusion is done on the CPU by attenuating the light color.//LPPV 是Light Prober Proxy Volume,动态物体如果要获取烘焙的阴影,只能通过LPPV,lightprobe已经//通过灯光颜色衰减做了处理fixed atten = 1.0f;#if defined(UNITY_INSTANCING_ENABLED) && defined(UNITY_USE_SHCOEFFS_ARRAYS)// ...unless we are doing instancing, and the attenuation is packed into SHC array's .w component.//如果我们在用instancing,并且衰减保存在了SHC array`s.w	        atten = unity_SHC.w;#endif#if UNITY_LIGHT_PROBE_PROXY_VOLUME && !defined(LIGHTMAP_ON) && !UNITY_STANDARD_SIMPLEfixed4 rawOcclusionMask = atten.xxxx;//x分量为1时表示启用本光照体代理体,为0时表示不启用,使用光照探针计算阴影if (unity_ProbeVolumeParams.x == 1.0)rawOcclusionMask = LPPV_SampleProbeOcclusion(worldPos);return saturate(dot(rawOcclusionMask, unity_OcclusionMaskSelector));#endifreturn atten;#endif
}

阴影采样- unitySampleShadow

// ---- Screen space direction light shadows helpers (any version)
#if defined (SHADOWS_SCREEN)	#if defined(UNITY_NO_SCREENSPACE_SHADOWS)UNITY_DECLARE_SHADOWMAP(_ShadowMapTexture);#define TRANSFER_SHADOW(a) a._ShadowCoord = mul( unity_WorldToShadow[0], mul( unity_ObjectToWorld, v.vertex ) );inline fixed unitySampleShadow (unityShadowCoord4 shadowCoord){//支持原生lightmap,采样后获取数值,然后进行衰减计算#if defined(SHADOWS_NATIVE)fixed shadow = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, shadowCoord.xyz);shadow = _LightShadowData.r + shadow * (1-_LightShadowData.r);return shadow;#else//不支持原生lightmap,比如es2.0,取出深度信息之后自己比较distunityShadowCoord dist = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, shadowCoord.xy);// tegra is confused if we use _LightShadowData.x directly// with "ambiguous overloaded function reference max(mediump float, float)"unityShadowCoord lightShadowDataX = _LightShadowData.x;unityShadowCoord threshold = shadowCoord.z;return max(dist > threshold, lightShadowDataX);#endif}#else //屏幕空间阴影,需要采样的是屏幕--设置屏幕空间阴影UNITY_DECLARE_SCREENSPACE_SHADOWMAP(_ShadowMapTexture);#define TRANSFER_SHADOW(a) a._ShadowCoord = ComputeScreenPos(a.pos);inline fixed unitySampleShadow (unityShadowCoord4 shadowCoord){//---一开始看这个采样真的是把我吓出了翔,怎么shadowmap用的场景相机的屏幕空间坐标,往上看,//你会发现,在处理阴影的时候,已经将此事的_ShadowMapTexture设置成屏幕空间了fixed shadow = UNITY_SAMPLE_SCREEN_SHADOW(_ShadowMapTexture, shadowCoord);return shadow;}#endif	#define SHADOW_COORDS(idx1) unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1;#define SHADOW_ATTENUATION(a) unitySampleShadow(a._ShadowCoord)
#endif

阴影混合- UnityMixRealtimeAndBakedShadows

这个函数是将计算出的实时阴影,烘焙阴影,以及淡入淡入的阴影就行混合,都是根据一些宏定义,代码已经很清楚了

// ------------------------------------------------------------------
// Used by both the forward and the deferred rendering path
half UnityMixRealtimeAndBakedShadows(half realtimeShadowAttenuation, half bakedShadowAttenuation, half fade)
{// -- Static objects --// FWD BASE PASS// ShadowMask mode          = LIGHTMAP_ON + SHADOWS_SHADOWMASK + LIGHTMAP_SHADOW_MIXING// Distance shadowmask mode = LIGHTMAP_ON + SHADOWS_SHADOWMASK// Subtractive mode         = LIGHTMAP_ON + LIGHTMAP_SHADOW_MIXING// Pure realtime direct lit = LIGHTMAP_ON// FWD ADD PASS// ShadowMask mode          = SHADOWS_SHADOWMASK + LIGHTMAP_SHADOW_MIXING// Distance shadowmask mode = SHADOWS_SHADOWMASK// Pure realtime direct lit = LIGHTMAP_ON// DEFERRED LIGHTING PASS// ShadowMask mode          = LIGHTMAP_ON + SHADOWS_SHADOWMASK + LIGHTMAP_SHADOW_MIXING// Distance shadowmask mode = LIGHTMAP_ON + SHADOWS_SHADOWMASK// Pure realtime direct lit = LIGHTMAP_ON// -- Dynamic objects --// FWD BASE PASS + FWD ADD ASS// ShadowMask mode          = LIGHTMAP_SHADOW_MIXING// Distance shadowmask mode = N/A// Subtractive mode         = LIGHTMAP_SHADOW_MIXING (only matter for LPPV. Light probes occlusion being done on CPU)// Pure realtime direct lit = N/A// DEFERRED LIGHTING PASS// ShadowMask mode          = SHADOWS_SHADOWMASK + LIGHTMAP_SHADOW_MIXING// Distance shadowmask mode = SHADOWS_SHADOWMASK// Pure realtime direct lit = N/A#if !defined(SHADOWS_DEPTH) && !defined(SHADOWS_SCREEN) && !defined(SHADOWS_CUBE)#if defined(LIGHTMAP_ON) && defined (LIGHTMAP_SHADOW_MIXING) && !defined (SHADOWS_SHADOWMASK)//In subtractive mode when there is no shadow we kill the light contribution as direct as been baked in the lightmap.return 0.0;#elsereturn bakedShadowAttenuation;#endif#endif#if (SHADER_TARGET <= 20) || UNITY_STANDARD_SIMPLE//no fading nor blending on SM 2.0 because of instruction count limit.#if defined(SHADOWS_SHADOWMASK) || defined(LIGHTMAP_SHADOW_MIXING)return min(realtimeShadowAttenuation, bakedShadowAttenuation);#elsereturn realtimeShadowAttenuation;#endif#endif#if defined(LIGHTMAP_SHADOW_MIXING)//Subtractive or shadowmask moderealtimeShadowAttenuation = saturate(realtimeShadowAttenuation + fade);return min(realtimeShadowAttenuation, bakedShadowAttenuation);#endif//In distance shadowmask or realtime shadow fadeout we lerp toward the baked shadows (bakedShadowAttenuation will be 1 if no baked shadows)return lerp(realtimeShadowAttenuation, bakedShadowAttenuation, fade);
}

总结

太恶心了,又是这么长的东西。不过unity对阴影的计算还是比较全面的,在我们进行角色与场景进行交互时还是很有用的,根据自己项目需求去选择定制的阴影处理还是很带感的,尤其是lightprob的应用。

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

相关文章

  1. 架构大型网站应当考虑哪些问题?

    分层:分层是处理任何复杂系统最常见的手段之一,将系统横向切分成若干个层面,每个层面只承担单一的职责,然后通过下层为上层提供的基础设施和服务以及上层对下层的调用来形成一个完整的复杂的系统。计算机网络的开放系统互联参考模型(OSI/RM)和Internet的TCP/IP模型都是分…...

    2024/4/16 2:38:19
  2. 11.6Java多线程之线程同步

    Java中线程同步是利用了加锁解锁方式实现的。加锁解锁之间的代码块为临界区,任何时候临界区只能有一个线程执行。 Java中使用了一个关键字对一个对象加锁:synchronized 以下例子证明了锁的应用: package MyThread;public class MyLock {public static void main(String[] ar…...

    2024/4/16 2:38:34
  3. 绿盟日志审计系统

    绿盟日志审计系统 LINUX系统连接agent步骤 - -华龙在飞翔 1.对应虚拟化环境中连接的Linux服务器打开输入用户名和密码 2.ifconfig来查找Linux服务器对应的网段ip地址3.使用finalshell等工具用ip地址连接对应的Linux服务器4.cd /opt对于文件夹上传,也可以直接对应桌面文件复制…...

    2024/4/16 2:37:48
  4. mybatis 缓存问题详解

    https://tech.meituan.com/2018/01/19/mybatis-cache.html...

    2024/4/16 2:38:49
  5. 特征金字塔-Feature Pyramid Networks(FPN)

    什么是特征金字塔目标检测任务和语义分割任务里面常常需要检测小目标,但是小目标比较小呀,可能在原图里面只有几十个像素点。就像这个样子。 我不检测这个猫,我就检测这一片片落叶,是不是每个落叶所占的像素点特别少呢。答案肯定是的。最关键的问题就是,像素点少会对目标检…...

    2024/4/16 2:38:24
  6. editwang 添加样式

    editwang链接: https://www.kancloud.cn/wangfupeng/wangeditor3/335775...

    2024/4/16 2:38:44
  7. 寻找n*n有序二维数组中第K小的数

    题目:给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素举例:matrix = [[ 1, 5, 9],[10, 11, 13],[12, 13, 15] ], k = 8返回 13看到题目首先想到将二维数组转为一维数组,再进行排序,取出第k个元素,这样的解法没有问题,可以正确得到结果。排…...

    2024/4/16 2:38:24
  8. 云计算基础概念

    ...

    2024/4/1 3:09:24
  9. 以进化论的观点认识自己

    作者是学习了现代生物进化理论(人教版生物必修II)后写的这篇文章。可能会有落后之处。 进化的基本单位是种群(一定区域内的所有同种生物)。也就是说,一切生物的性状的发展都是为了种群的繁衍。从这个角度说,生物与生俱来的自然的根本的无上的意义,是为了种群的繁衍。那么…...

    2024/4/20 8:49:13
  10. vue项目引入不符合ES6模块化标准的JS文件

    vue项目引入不符合ES6模块化标准的JS文件实现方式实现原理 实现方式 要引入的文件放在public目录下,在index.html中通过script标签引入实现原理 参考vue官网–处理静态资源 https://cli.vuejs.org/zh/guide/html-and-static-assets.html...

    2024/4/16 2:38:49
  11. 通讯录、定位、推送、相机权限校验;(在类方法中使用全局变量方法)

    Support_PowerCheck.h文件 #import <Foundation/Foundation.h>/**权限授权状态- PowerStateAllow: 允许- PowerStateRefuse: 不允许- PowerStateUndefined: 未申请,未定义*/ typedef NS_ENUM(NSInteger,PowerState) {PowerStateAllow = 1,PowerStateRefuse = 2,PowerSta…...

    2024/3/28 18:15:41
  12. openresty1.15.8替换nginx1.14.2

    openresty说明 OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。 OpenResty通过汇聚各种设计精良的 Nginx 模块(主要由…...

    2024/4/19 13:43:25
  13. STM32学习笔记——位带操作(GPIO端口输入/输出)

    STM32学习笔记——位带操作(GPIO端口输入/输出)位带操作公式代码表示 位带操作公式 (addr&0xF0000000)+0x02000000+((addr&0x00FFFFFF)<<5)+(n<<2) addr:要操作的位所在的寄存器 n:要操作的位号 代码表示 #define GPIOB_ODR_Addr (GPIOB_BASE + 0x0C) #d…...

    2024/4/16 2:37:43
  14. JWT 简明教程

    转载自:https://www.zuojl.com/jwt/叙述JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,本文介绍它的原理和用法。应用程序或者客户端向服务器请求授权,授权服务器向客户端返回一个 JWT。之后将 JWT 放入到请求里(通常放在 HTTP 的 Authorization 头里)。服务…...

    2024/4/16 2:38:44
  15. sqlite--SQL语句宏定义

    //SQL语句宏定义 #define SQLLITE_DROP_TABLE_USER "DROP TABLE IF EXIST tb_user;" #define SQLLITE_CREATE_TABLE_USER "CREATE TABLE IF NOT EXISTS tb_user( "\"user_id varchar(16),"\"name varchar(32),"\"gender i…...

    2024/4/16 2:38:29
  16. VirtualBox安装CentOS7(一)——虚拟机安装及网络配置

    虚拟机安装 在VirtualBox安装CentOS前,首先安装好VirtualBox虚拟机软件(傻瓜式安装方法,这里我就不做介绍了)和备好centos的iso镜像文件(从centos的官网下载),本次我选的VirtualBox版本是VirtualBox-6.0.20-Win版本,CentOS版本是CentOS7版本,其他版本都是一样的安装步…...

    2024/4/24 21:02:10
  17. Kafka Tool 连接带加密的Kafka服务

    Kafk_Tool 下载地址https://www.kafkatool.com/download.html1、配置zookeeper现在kafka都内置了zookeeper,所以没有一般我们host和port可以填第一个节点,注意:Kafka版本对应为自己的2、设置Security设置Type为SASL PlainText,下面有提示如何配置JASS Config3、设计Advance…...

    2024/4/23 13:26:20
  18. 如何回放脑电数据?

    建议用.edf格式保存数据,然后下载个edfbrowser就可以读取数据,并进行回放,还可以设置滤波之类的。 edfbrowser可以在下面公众号菜单栏选择’资料下载’进入网盘下载。BCIduino脑机接口社区由来自北京航空航天大学、康奈尔大学、北京大学、首都医科大学等硕博发起成立,欢迎加…...

    2024/4/19 4:20:11
  19. javascript——面向对象(4、命名空间)

    Json方式的面向对象(命名空间)—— 把方法包在一个Json里 命名空间的意义及写法 var miaov={};miaov.common={getByClass: function (){},myAddEvent: function (){} };miaov.fx={startMove: function (){},drag: function (){} };miaov.common.getByClass()优点:比较构造函…...

    2024/4/1 3:09:19
  20. 编程语言那么多,为什么初学者都先学C语言?

    有人说:“C生万物,编程之本”,这一点都没有错!首先来说说编程语言这个概念。 编程,其实就是让计算机听懂自己的话,让计算机帮自己想干的事情。编程语言,就是让你能够和计算机进行交流的一种语言。说白了就是让你的软件按你的命令干活。 所以首次入门选择哪一门派系的话语…...

    2024/4/16 2:39:15

最新文章

  1. pycharm使用ssh连接服务器

    1、具体流程 打开pycharm – File – Setting 输入服务器的IP地址&#xff0c;端口号、登录账号名 输入登陆账号的密码 下一步 一些初级设置 2、一些需要注意的小问题 2.1 更改代码地址 2.2 本地代码上传到服务器 首先在需要上传文件右键 2.3 在服务器的环境中上新安装库&am…...

    2024/4/27 21:36:08
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. LeetCode 热题 100 题解(二):双指针部分(2)| 滑动窗口部分(1)

    题目四&#xff1a;接雨水&#xff08;No. 43&#xff09; 题目链接&#xff1a;https://leetcode.cn/problems/trapping-rain-water/description/?envTypestudy-plan-v2&envIdtop-100-liked 难度&#xff1a;困难 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&am…...

    2024/4/27 12:27:40
  4. 蓝桥杯第十五届抱佛脚(十)贪心算法

    蓝桥杯第十五届抱佛脚&#xff08;十&#xff09;贪心算法 贪心算法基本概念 贪心算法是一种在算法设计中常用的方法&#xff0c;它在每一步选择中都采取在当前状态下最好或最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望导致结果是最好或最优的算法。 贪…...

    2024/4/19 0:49:59
  5. 备战蓝桥杯Day37 - 真题 - 特殊日期

    一、题目描述 思路&#xff1a; 1、统计2000年到2000000年的日期&#xff0c;肯定是需要遍历 2、闰年的2月是29天&#xff0c;非闰年的2月是28天。我们需要判断这一年是否是闰年。 1、3、5、7、8、10、12月是31天&#xff0c;4、6、9、11月是30天。 3、年份yy是月份mm的倍数…...

    2024/4/24 9:33:16
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/26 18:09:39
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/26 20:12:18
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/26 23:05:52
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/27 4:00:35
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/26 21:56:58
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/27 9:01:45
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/25 18:39:16
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/26 22:01:59
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/25 2:10:52
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/25 18:39:00
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/27 11:43:08
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/27 8:32:30
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57