技术标签: PBR Unity3D 技术专栏
先放出PBR知识体系的架构图:
这张架构图是这个系列文章的内容框架,而且会随着内容的深入,不断更新。目前是1.0版。
基于物理的渲染(Physically Based Rendering , PBR)技术,自迪士尼在SIGGRAPH 2012上提出了著名的“迪士尼原则的BRDF(Disney Principled BRDF)”之后,由于其高度的易用性以及方便的工作流,已经被电影和游戏业界广泛使用。
个人了解和研究基于物理的渲染,也已经有一段时间了。
期间看了大量的资料,基本刷完了《SIGGRAPH Course: Physically Based Shading in Theory and Practice》系列2010年到2017年的几十篇talk和note(可惜这个course 2018年没有开设),这边是整理好的链接,包含了PPT和note的下载:
也看了一些相关的著作。目前了解到的PBR相关的著作,主要有三本:
期间也记过一些笔记,已经有不少的篇幅,但内容始终比较零散。所以有了萌生将这些笔记整理成更系统的系列文章的念头。
通过将零散的笔记进行总结,集结成文章,并发布出来,既对想更系统而深入地了解PBR和实时渲染相关技术的朋友们所有帮助,对我自己而言,在总结的过程中,也应该会收获颇丰。正如已经完结的【《Real-Time Rendering 3rd》 提炼总结】系列,以及还未完结的【GPU精粹】系列一样(GPU精粹系列目前暂停,稍后会继续填完)。
而目前,国内似乎确实缺少一个较为系统、全面、深入介绍基于物理的实时渲染的系列文章。
另外,类似之前【《Real-Time Rendering 3rd》 提炼总结】的方式,在这个系列完结后,会进行整理,集结成册,成为一本电子书,暂定书名为《基于物理的渲染(PBR)白皮书》。所以此系列目前便直接命名为了【基于物理的渲染(PBR)白皮书】,便于整体认知的延续。
总之,希望这个新的系列,能对大家有所帮助。
这篇文章接下来的部分,是这个系列文章PBR知识体系的精华浓缩版。涉及八个部分的内容:
通过接下来的概览,希望能在后续的具体章节展开前,让大家对PBR的整体知识体系,有一个全面的认知,所谓的大局观的建立。
PBR核心知识体系的第一部分自然是PBR的核心理论以及相关的渲染原理。比较老生常谈,但作为基础理论,是入门级知识,还是需要仔细交代。
基于物理的渲染(Physically Based Rendering,PBR)是指使用基于物理原理和微平面理论建模的着色/光照模型,以及使用从现实中测量的表面参数来准确表示真实世界材质的渲染理念。
以下是对PBR基础理念的概括:
图 金属和非金属材质的F0范围
除了PBR的基础理论,光与非光学平坦表面的交互对理解微平面理论(Microfacet Theory)至关重要。下面进行一些说明。
光在与非光学平坦表面(Non-Optically-Flat Surfaces)的交互时,非光学平坦表面表现得像一个微小的光学平面表面的大集合。表面上的每个点都会以略微不同的方向对入射光反射,而最终的表面外观是许多具有不同表面取向的点的聚合结果。
图 来自非光学平坦表面的可见光反射是来自具有不同方向的许多表面点的反射的总体结果
在微观尺度上,表面越粗糙,反射越模糊,因为表面取向与整个宏观表面取向的偏离更强。
图 图片顶部所示的表面,表面相对光滑; 表面取向仅略有变化,导致反射光方向的微小变化,从而产生更清晰的反射。 图片底部所示的的表面较粗糙; 表面上的不同点具有广泛变化的方向取向,导致反射光方向的高度变化,并因此导致模糊的反射。 注意,两个表面在肉眼可见尺度下看起来都是光滑的,粗糙度差异仅在微观尺度上。
出于着色的目的,我们通常会去用统计方法处理这种微观几何现象,并将表面视为在每个点处在多个方向上反射(和折射)光。
图 从宏观上看,非光学平面可以被视为在多个方向上反射(和折射)光
从表面反射出的光的行为很好理解,那么,从表面折射的光会发生什么变化? 这取决于对象本身的特性:
图 在金属中,所有折射的光能立即被自由电子吸收
图 在非金属中,折射的光会进行散射,直到从表面重新射出,而这通常会在经过部分吸收之后
另外,漫反射和次表面散射其实是相同物理现象,本质都是折射光的次表面散射的结果。唯一的区别是相对于观察尺度的散射距离。散射距离相较于像素来说微不足道,次表面散射便可以近似为漫反射。也就是说,光的折射现象,建模为漫反射还是次表面散射,取决于观察的尺度,如下图。
图 在左上角,像素(带有红色边框的绿色圆形)大于光线离开表面之前所经过的距离。 在这种情况下,可以假设出射光从入口点(右上)射出,可以当做漫反射,用局部着色模型处理。 在底部,像素小于散射距离; 如果需要更真实的着色效果,则不能忽略这些距离的存在,需当做次表面散射现象进行处理。
寒霜(Frostbite)引擎在SIGGRAPH 2014的分享《Moving Frostbite to PBR》中提出,基于物理的渲染的范畴,由三部分组成:
完整的这三者,才是真正完整的基于物理的渲染系统。而很多同学一提到PBR,就说PBR就是镜面反射采用微平面Cook-Torrance模型,其实是不太严谨的。
PBR核心知识体系的第二部分是渲染方程与BxDF。渲染方程作为渲染领域中的重要理论,将BxDF代入渲染方程是求解渲染问题的一般方法。
渲染方程(The Rendering Equation)作为渲染领域中的重要理论,其描述了光能在场景中的流动,是渲染中不可感知方面的最抽象的正式表示。根据光学的物理学原理,渲染方程在理论上给出了一个完美的结果,而各种各样的渲染技术,只是这个理想结果的一个近似。
渲染方程的物理基础是能量守恒定律。在一个特定的位置和方向,出射光 Lo 是自发光 Le 与反射光线之和,反射光线本身是各个方向的入射光 Li 之和乘以表面反射率及入射角。
这个方程经过交叉点将出射光线与入射光线联系在一起,它代表了场景中全部的'光线传输。所有更加完善的算法都可以看作是这个方程的特殊形式的解。
某一点p的渲染方程,可以表示为:
其中:
而在实时渲染中,我们常用的反射方程(The Reflectance Equation),则是渲染方程的简化的版本,或者说是一个特例:
同样,其中:
BxDF一般而言是对BRDF、BTDF、BSDF、BSSRDF等几种双向分布函数的一个统一的表示。
其中,BSDF可以看做BRDF和BTDF更一般的形式,而且BSDF = BRDF + BTDF。
而BSSRDF和BRDF的不同之处在于,BSSRDF可以指定不同的光线入射位置和出射位置。
在上述这些BxDF中,BRDF最为简单,也最为常用。因为游戏和电影中的大多数物体都是不透明的,用BRDF就完全足够。而BSDF、BTDF、BSSRDF往往更多用于半透明材质和次表面散射材质。
图 BSDF:BRDF + BTDF
我们时常讨论的PBR中的BxDF,一般都为BRDF,对于进阶的一些材质的渲染,才会讨论BSDF等其他三种BxDF。
另外,BxDF即上文所示渲染方程以及反射方程中的 项。
图 BRDF的分类,来自[Montes R, Ureña C. An overview of BRDF models[J]. 2012]
PBR核心知识体系的第三部分是迪士尼原则的BxDF。迪士尼动画工作室在SIGGRAPH 2012上著名的talk《Physically-based shading at Disney》中提出了迪士尼原则的BRDF(Disney Principled BRDF),奠定了后续游戏行业和电影行业PBR的方向和标准。了解Disney Principled BxDF,是深入理解PBR的重要一环。
基于物理的渲染,其实早在20世纪就已经在图形学业界有了一些讨论,2010年在SIGGRAPH上就已经有公开讨论的Course 《SIGGRAPH 2010 Course: Physically-Based Shading Models in Film and Game Production》,而直到2012~2013年,才正式进入大众的视野,渐渐被电影和游戏业界广泛使用。
迪士尼动画工作室则是这次PBR革命的重要推动者。迪士尼的Brent Burley于SIGGRAPH 2012上进行了著名的talk《Physically-based shading at Disney》,提出了迪士尼原则的BRDF(Disney Principled BRDF), 由于其高度的通用性,将材质复杂的物理属性,用非常直观的少量变量表达了出来(如金属度metallic和粗糙度roughness),在电影业界和游戏业界引起了不小的轰动。从此,基于物理的渲染正式进入大众的视野。
图 SIGGRAPH 2012《Physically-based shading at Disney》
在2012年受到Disney的启发后,以下是主流游戏引擎从传统渲染转移到基于物理的渲染时间节点:
在2012年迪士尼原则的BRDF被提出之前,基于物理的渲染都需要大量复杂而不直观的参数,此时PBR的优势,并没有那么明显。
在2012年迪士尼提出,他们的着色模型是艺术导向(Art Directable)的,而不一定要是完全物理正确(physically correct)的,并且对微平面BRDF的各项都进行了严谨的调查,并提出了清晰明确而简单的解决方案。
迪士尼的理念是开发一种“原则性”的易用模型,而不是严格的物理模型。正因为这种艺术导向的易用性,能让美术同学用非常直观的少量参数,以及非常标准化的工作流,就能快速实现涉及大量不同材质的真实感的渲染工作。而这对于传统的着色模型来说,是不可能完成的任务。
迪士尼原则的BRDF(Disney Principled BRDF)核心理念如下:
以上五条原则,很好地保证了迪士尼原则的BRDF的易用性。
以上述理念为基础,迪士尼动画工作室对每个参数的添加进行了把关,最终得到了一个颜色参数(baseColor)和下面描述的十个标量参数:
每个参数的效果的渲染示例如下图所示。
图 Disney Principled BRDF。 每行的参数从0到1变化,其他参数保持不变
随后的2015年,迪士尼动画工作室在Disney Principled BRDF的基础上进行了修订,提出了Disney Principled BSDF [Extending the Disney BRDF to a BSDF with Integrated Subsurface Scattering, 2015]。
以下是开源三维动画软件Blender实现的Disney Principled BSDF的图示:
图 Disney Principled BSDF
为了求解渲染方程,需要分别求解Diffuse BRDF和Specular BRDF。所以PBR核心知识体系的第四部分是Diffuse BRDF。
Diffuse BRDF可以分为传统型和基于物理型两大类。其中,传统型主要是众所周知的Lambert。
而基于物理型,从1994年的Oren Nayar开始,这里一直统计到今年(2018年)。
其中较新的有GDC 2017上提出的适用于GGX+Smith的基于物理的漫反射模型(PBR diffuse for GGX+Smith),也包含了最近在SIGGRAPH2018上提出的,来自《使命召唤:二战》的多散射漫反射BRDF(MultiScattrering Diffuse BRDF):
PBR核心知识体系的第五部分是Specular BRDF。这也是基于物理的渲染领域中最活跃,最主要的部分。
上图加粗部分为目前业界较为主流的模型。
游戏业界目前最主流的基于物理的镜面反射BRDF模型是基于微平面理论(microfacet theory)的Microfacet Cook-Torrance BRDF。
而微平面理论(microfacet theory)源自将微观几何(microgeometry)建模为微平面(microfacets)的集合的思想,一般用于描述来自非光学平坦(non-optically flat)表面的表面反射。
微平面理论的基本假设是微观几何(microgeometry)的存在,微观几何的尺度小于观察尺度(例如着色分辨率),但大于可见光波长的尺度(因此应用几何光学和如衍射一样的波效应等可以忽略)。且微平面理论在2013年和以前时仅用于推导单反射(single-bounce)表面反射的表达式; 而随着领域的深入,最近几年也出现了使用microfacet理论对多次反弹表面反射的一些探讨。
由于假设微观几何尺度明显大于可见光波长,因此可以将每个表面点视为光学平坦的。 如上文所述,光学平坦表面将光线分成两个方向:反射和折射。
每个表面点将来自给定进入方向的光反射到单个出射方向,该方向取决于微观几何法线(microgeometry normal)m的方向。 在计算BRDF项时,指定光方向l和视图方向v。 这意味着所有表面点,只有那些恰好正确朝向可以将l反射到v的那些小平面可能有助于BRDF值(其他方向有正有负,积分之后,相互抵消)。
在下图中,我们可以看到这些“正确朝向”的表面点的表面法线m正好位于l和v之间的中间位置。l和v之间的矢量称为半矢量(half-vector)或半角矢量(half-angle vector); 我们将其表示为h。
图 仅m = h的表面点的朝向才会将光线l反射到视线v的方向,其他表面点对BRDF没有贡献。
并非所有m = h的表面点都会积极地对反射做出贡献;一些被l方向(阴影shadowing),v方向(掩蔽masking)或两者的其他表面区域阻挡。Microfacet理论假设所有被遮蔽的光(shadowed light)都从镜面反射项中消失;实际上,由于多次表面反射,其中一些最终将是可见的,但这在目前常见的微平面理论中一般并未去考虑,各种类型的光表面相互作用如下图所示。
图 在左侧,我们看到一些表面点从l的方向被遮挡,因此它们被遮挡并且不接收光(因此它们不能反射任何)。在中间,我们看到从视图方向v看不到一些表面点,因此当然不会看到从它们反射的任何光。在这两种情况下,这些表面点对BRDF没有贡献。实际上,虽然阴影区域没有从l接收任何直射光,但它们确实接收(并因此反射)从其他表面区域反射的光(如右图所示)。microfacet理论忽略了这些相互反射。
利用这些假设(局部光学平坦表面,没有相互反射),可以很容易推导出一个被称为Microfacet Cook-Torrance BRDF的一般形式的Specular BRDF项。此Specular BRDF具有以下形式:
其中:
关于Cook-Torrance BRDF,需要强调的两点注意事项:
下面对Microfacet Cook-Torrance BRDF中的D、F、G项分别进行简要说明。
法线分布函数(Normal Distribution Function, NDF)D的常见模型可以总结如下:
其中,业界较为主流的法线分布函数是GGX(Trowbridge-Reitz),因为具有更好的高光长尾:
另外,需要强调一点。Normal Distribution Function正确的翻译是法线分布函数,而不是正态分布函数。google翻译等翻译软件会将Normal Distribution Function翻译成正态分布函数,而不少中文资料就跟着翻译成了正态分布函数,这是错误的。
其实,一些参考文献会使用术语“法线分布(distribution of normals)”来避免与高斯正态分布(Gaussian normal distribution)混淆。
对于菲涅尔(Fresnel)项,业界方案一般都采用Schlick的Fresnel近似,因为计算成本低廉,而且精度足够:
菲涅尔项的常见模型可以总结如下:
几何项G的常见模型可以总结如下:
另外,Eric Heitz在[Heitz14]中展示了Smith几何阴影函数是正确且更准确的G项,并将其拓展为Smith联合遮蔽阴影函数(Smith Joint Masking-Shadowing Function),该函数具有四种形式:
目前较为常用的是其中最为简单的形式,分离遮蔽阴影(Separable Masking and Shadowing Function)。
该形式将几何项G分为两个独立的部分:光线方向(light)和视线方向(view),并对两者用相同的分布函数来描述。根据这种思想,结合法线分布函数(NDF)与Smith几何阴影函数,于是有了以下新的Smith几何项:
其中UE4的方案是上面列举中的“Schlick-GGX”,即基于Schlick近似,将k映射为 ,去匹配GGX Smith方程:
有了直接光部分,我们也需要环境光。所以PBR核心知识体系的第六部分是基于物理的环境光照,一般大家也直接默认环境光照的技术方案是基于图像的光照(Image Based Lighting, IBL)。这也是真正让基于物理的渲染画质提升的主要贡献者。
漫反射环境光照部分一般采用传统IBL中辉度环境映射(Irradiance Environment Mapping)技术,并不是基于物理的特有方案,这里暂不讨论。
而基于物理的镜面反射(Specular)环境光照,业界中一般会采用基于图像的光照(IBL)的方案。要将基于物理的BRDF模型与基于图像的光照(IBL)一起使用,需要求解光亮度积分(Radiance Integral),而求解光亮度积分通常会使用重要性采样(Importance Sample)。
重要性采样(Importance Sample)即通过现有的一些已知条件(分布函数),想办法集中于被积函数分布可能性较高的区域(重要的区域)进行采样,进而可高效地计算准确的估算结果的的一种策略。
基于重要性采样的思路,将蒙特卡洛积分公式代入渲染方程可得:
上式的直接求解较为复杂,进行完全的实时渲染不太现实。
目前游戏业界的主流做法是,是基于分解求和近似(Split Sum Approximation)的思路,将上式中的拆分为光亮度的均值和环境BRDF两项。即:
完成拆分后,分别对两项进行离线预计算,去匹配离线渲染参考值的渲染结果。
而在实时渲染中,分别计算分解求和近似(Split Sum Approximation)方案中几乎已经预计算好的两项,再进行组合,作为实时的IBL物理环境光照部分的渲染结果。下面分别对两项进行简单概括。
第一项为,可以理解为对光亮度 求均值。经过n= v= r的假设,仅取决于表面粗糙度(surface roughness)和反射矢量(reflection vector)。这一项,业界的做法比较统一(包括UE4和COD:Black Ops 2等),采用的方案主要借助预过滤环境贴图,用多级模糊的mipmap来存储模糊的环境高光:
也就是说,第一项直接使用cubemap 的mip级别采样输入即可。
第二项为,即镜面反射项的半球方向反射率(hemispherical-directional reflectance),可以理解为环境BRDF(Environment BRDF)。其取决于仰角θ,粗糙度α和菲涅耳项F。 通常使用Schlick近似来近似F,其仅在单个值F0上参数化,从而使Rspec成为三个参数(仰角θ(NdotV),粗糙度α、F0)的函数。
这一项的主要流派有两个,UE4的2D LUT,以及COD:OP2的解析拟合。
UE4在[Real Shading in Unreal Engine 4, 2013]中提出,第二个求和项 ,使用Schlick近似后, F0可以从积分中分出来:
上式留下了两个输入(Roughness 和 cos θv)和两个输出(缩放和向F0的偏差(a scale and bias to F0)),即把上述方程看成是F0 * Scale + Offset的形式。 我们预先计算此函数的结果并将其存储在2D查找纹理(LUT,look-up texture)中。
这张红绿色的贴图,输入roughness、cosθ,输出环境BRDF镜面反射的强度。是关于roughness、cosθ与环境BRDF镜面反射强度的固有映射关系。可以离线预计算。
具体的取出方式为:
即UE4是通过把Fresnel公式的F0提出来,组成F0 * Scale +Offset的方式,再将Scale和Offset的索引存到一张2D LUT上。靠roughness和 NdotV进行查找。
COD:Black Ops 2的做法,是通过数学工具Mathematica(http://www.wolfram.com/mathematica/) 中的数值积分拟合出曲线,即将UE4离线计算的这张2D LUT用如下函数进行了拟合:
float3 EnvironmentBRDF( float g, float NoV, float3 rf0 )
{
float4 t = float4( 1/0.96, 0.475, (0.0275 - 0.25 \* 0.04)/0.96, 0.25 );
t *= float4( g, g, g, g );
t += float4( 0, 0, (0.015 - 0.75 * 0.04)/0.96, 0.75 );
float a0 = t.x * min( t.y, exp2( -9.28 * NoV ) ) + t.z; float a1 = t.w;
return saturate( a0 + rf0 * ( a1 - a0 ) );
}
需要注意的是,上面的方程是基于Blinn-Phong分布的结果,https://knarkowicz.wordpress.com/2014/12/27/analytical-dfg-term-for-ibl/一文中提出了基于GGX分布的EnvironmentBRDF解析版本:
float3 EnvDFGLazarov( float3 specularColor, float gloss, float ndotv )
{
float4 p0 = float4( 0.5745, 1.548, -0.02397, 1.301 );
float4 p1 = float4( 0.5753, -0.2511, -0.02066, 0.4755 );
float4 t = gloss * p0 + p1;
float bias = saturate( t.x * min( t.y, exp2( -7.672 * ndotv ) ) + t.z );
float delta = saturate( t.w );
float scale = delta - bias;
bias *= saturate( 50.0 * specularColor.y );
return specularColor * scale + bias;
}
上式中的specularColor即F0。
EnvironmentBRDF函数的输入参数分别为光泽度gloss,NdotV,F0。和UE4的做法有异曲同工之妙,但COD:Black Ops 2的做法不需要额外的贴图采样,这在进行移动端优化时,是不错的选择。
Gotanda在SIGGRAPH 2010提出使用3D LUT[Practical Implementation of Physically-Based Shading Models at tri-Ace,2010]来存放环境BRDF,之后Drobot将其优化为2D LUT[Lighting Killzone:Shadow Fall , 2013]。
虽然我们目前主要关注的是实时渲染(实时光栅图形学相关,暂时不关注实时光线追踪)领域,但很多时候,实时渲染也需要涉及到预计算,尤其是IBL相关的预计算,所以或多或少会用到离线渲染相关的知识。所以PBR核心知识体系的第七部分是离线渲染相关的主题。
以下是与实时渲染结合相对紧密的离线渲染相关的核心主题以及概括总结(主要是统计学与概率相关):
与实时渲染结合相对紧密的离线渲染相关的内容,后续文章会以专题的形式详细探讨。
前面的核心PBR主题都讨论完成后,会有更多进阶的内容浮出水面,他们共同组成了PBR核心知识体系的第八部分。
以下是一个列举:
以上这些内容,作为进阶的主题,随便选取其中的一个展开来讨论,几乎都会有不小的篇幅。目前的计划是,是在前七章基础PBR内容讨论完成后,再在这些主题中选取新的内容,进行更深入的讨论。
OK,这篇文章作为这个系列的开篇,是对PBR知识体系的一个概览,相当于开了一个头,给全新的篇章描绘出了大致的轮廓。
后续的文章,会对PBR知识体系的各个章节,进行更系统深入的论述。
敬请期待。
[1] Burley B, Studios W D A. Physically-based shading at disney[C]//ACM SIGGRAPH. 2012
[2] Montes R, Ureña C. An overview of BRDF models[J]. 2012.
[3] https://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
[4] Karis B, Games E. Real shading in unreal engine 4[J]. Proc. Physically Based Shading Theory Practice, 2013
[5] Lazarov D. Getting more physical in call of duty: Black ops ii[J]. SIGGRAPH Course Notes: Physically Based Shading in Theory and Practice, 2013.
[6] Hoffman N. Background: physics and math of shading[J]. Physically Based Shading in Theory and Practice, 2013
[7] Neubelt D, Pettineo M, Studios R A D. Crafting a Next-Gen Material Pipeline for The Order: 1886[J]. Physically Based Shading in Theory and Practice, SIGGRAPH, 2013.
[8] Pharr M, Jakob W, Humphreys G. Physically based rendering: From theory to implementation[M]. Morgan Kaufmann, 2016.
[9] Akenine-Moller T, Haines E, Hoffman N. Real-time rendering[M]. AK Peters/CRC Press, 2018.
[10] Heitz E. Understanding the masking-shadowing function in microfacet-based BRDFs[J]. Journal of Computer Graphics Techniques, 2014, 3(2): 32-91.
[11] Gotanda Y. Designing Reflectance Models for New Consoles[J], 2014
[12] Lagarde S, De Rousiers C. Moving Frostbite to PBR[J]. Proc. Physically Based Shading Theory Practice, 2014.
[13] Langlands A. Physically based shader design in arnold[J]. Physically Based Shading in Theory and Practice-SIGGRAPH Courses, 2014.
[14] Burley B. Extending the Disney BRDF to a BSDF with integrated subsurface scattering[J]. Physically Based Shading in Theory and Practice'SIGGRAPH Course, 2015.
[15] Drobot M. Practical Multilayered Materials in Call of Duty: Infinite Warfare[J]. Physically Based Shading Theory Practice-SIGGRAPH Courses, 2017.
[16] Oren M, Nayar S K. Generalization of Lambert's reflectance model[C],1994
[17] Gotanda Y. Beyond a simple physically based Blinn-Phong model in real-time[M]//SIGGRAPH 2012 course. 2012.
[18] Gotanda Y. Practical Implementation of Physically-Based Shading Models at tri-Ace[J]. part of “Physically Based Shading Models in Film and Game Production,” SIGGRAPH, 2010.
[19] Hammon Jr E. PBR Diffuse Lighting for GGX+ Smith Microsurfaces[J]. 2017.
[20] https://knarkowicz.wordpress.com/2014/12/27/analytical-dfg-term-for-ibl/
[21] Drobot M. Lighting of Killzone: Shadow Fall[J]. Digital Dragons European Games Festival, 2013.
[22] Material Advances in Call of Duty: WWII, Activision Community , Advances in Real-Time Rendering , SIGGRAPH 2018
[23] 题图来自《Assassin's Creed Odyssey》
文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib
文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang
文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些
文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器
文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距
文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器
文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn
文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios
文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql
文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...
文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120
文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数