unity build-in管线中的PBR材质Shader分析研究

article/2025/11/11 15:41:45

PBR分析

  • 前言
  • 我理解的PBR
  • PBR组成部分
    • 直接光漫反射
    • 直接光镜面反射(高光)
    • 间接光漫反射
    • 间接光镜面反射
    • 最终加和
  • 结果

前言

近来,用到了几次Surface Shader,对于其封装好的PBR计算部分,如果不是复杂的效果其实是挺方便实用的了,但是如果我们想要实现更复杂的效果,还是不能依赖Surface Shader。我终于研究了PBR,但是发现很多国内论坛的资料语焉不详,或是单纯的公式推导分析,或是单纯的源码分析。但是有一些资料还是不错的,作者亲自写了pbr Shader,但是大部分也是只写了直接光漫反射和镜面反射两个部分,意思一下就结束了,少有写全间接光照的,而且我实践下来却发现与unity自带的pbr shader有相当大的出入,这些都驱使我更加深入地研究unity PBR的具体实现,在最后,我完成了一个PBR Shader,函数都与Unity基本相同,我的宗旨是: 最终画面效果一定要与unity默认的shader分毫不差,存在一点误差也就意味着失去了意义。

我理解的PBR

或许是因为我是美术生,我喜欢用美术的思维去理解事物,例如PBR,我将它分为四个部分:直接光漫反射、直接光镜面反射、间接光漫反射和间接光镜面反射。我认为直接光漫反射就像画素描时,先铺的大的明暗关系;直接光镜面反射其实可以理解为高光;间接光漫反射类似于我们画完阴影的暗部,会给它用橡皮泥或是纸巾提亮,给它一个环境光的反射,让阴影区域不至于死黑;间接光镜面反射类似于我们画一些光洁的物体如瓷瓶,我们会最后在上面勾画一些反射上去的窗户什么的。这样理解以后,事情就变得容易一些了。
首先我阅读了很多PBR的资料,了解了BRDF,然后看了一些实现,例如一篇知乎文章,作者的思路给了我很大启发,但是遗憾的是作者将unity与unreal的实现方法杂糅在了一起,最终效果也与standard Shader有一些出入,刚好也是两位TA同事入职了,他们在写PBR,于是我决定仔细阅读源码,力求彻底搞清Untiy build-in管线下的PBR实现。

PBR组成部分

注意:Unity的PBR分三档,分别对应性能不同的机器。我这里分析的都是按照BRDF1_Unity_PBS顶配方式分析的。

直接光漫反射

这里Unity使用的是Disney漫反射模型,为什么没有用lambert模型呢?因为Disney做了实验,认为lambert模型边缘地区太暗,与真实测量值不符,所以拟合了新的漫反射模型。公式如下:
公式
实现代码:

// Note: Disney diffuse must be multiply by diffuseAlbedo / PI. This is done outside of this function.
half DisneyDiffuse(half NdotV, half NdotL, half LdotH, half perceptualRoughness)
{half fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;// Two schlick fresnel termhalf lightScatter   = (1 + (fd90 - 1) * Pow5(1 - NdotL));half viewScatter    = (1 + (fd90 - 1) * Pow5(1 - NdotV));return lightScatter * viewScatter;
}

效果(可以看到Disney漫反射模型的确要比lambert更亮一些):
对比

直接光镜面反射(高光)

直接关镜面反射由三个部分组成:D项法线分布函数、G项几何函数和F项菲涅尔项。
D项法线分布函数
这部分主要是希望得到一个漂亮的高光效果。传统的Blinn-phong高光缺乏真实度,研究发现高光是带有拖尾的,例如铬金属的高光带有显著的拖尾,GGX模型就是为了把这个拖尾模拟出来,虽然还不能完全模拟,但是比之前的模型已经好了很多。
高光
黑色曲线表示MERL 铬金属(chrome)真实的高光曲线,红色曲线表示 GGX分布(α= 0.006),绿色曲线表示Beckmann分布(m = 0.013),蓝色曲线表示 Blinn Phong(n = 12000),其中,绿色曲线和蓝色曲线基本重合。可以发现,GGX相对于传统的模型,更接近真实了。公式如下:
在这里插入图片描述
实现代码:

//D项 NDF
inline float GGXTerm (float NdotH, float roughness)
{float a2 = roughness * roughness;float d = (NdotH * a2 - NdotH) * NdotH + 1.0f; // 2 madreturn UNITY_INV_PI * a2 / (d * d + 1e-7f); 
}

效果:
高光

G项几何函数
这是由微表面模型引出的。微表面理论认为物体表面的微小凹凸也会形成微阴影,进而导致物体的受光面没有那么亮,会更暗一些。而且越粗糙的物体微阴影越多,也就越暗。
Unity没有使用常见的G项公式:G项
而是采用了这篇论文的成果:
论文公式

Unity在论文公式的基础上又简化了实现,所以最终代码如下:

inline half SmithJointGGXVisibilityTerm1 (half NdotL, half NdotV, half roughness)
{half a = roughness;half lambdaV = NdotL * (NdotV * (1 - a) + a);half lambdaL = NdotV * (NdotL * (1 - a) + a);return 0.5f / (lambdaV + lambdaL + 1e-5f);
}

结果:
微表面

F项菲涅尔项
菲涅尔项在Unity中比较特殊,分为了两个部分,一个是FresnelTerm函数控制反射占的比重,一个是FresnelLerp控制飞掠角的镜面反射强度。FresnelTerm函数非常复杂,因为每个金属对不同波段的光响应曲线也不一样,现在实时渲染使用的都是简化过的,如下:
f项
实现代码如下:

/F项
half3 FresnelTerm(half3 F0,half cosA){half t=Pow5(1-cosA);// ala Schlick interpoliationreturn F0+(1-F0)*t;
}

这里,F0的计算如下:

float3 F0 = lerp(unity_ColorSpaceDielectricSpec.rgb, Albedo, _Metallic);

因此,对于金属来讲,它的Albedo其实就是F0的颜色,对于塑料这种非金属来讲,它的F0就是unity_ColorSpaceDielectricSpec,这是一个Unity内部设置的默认值,非常暗,颜色值为float3(0.04, 0.04, 0.04),算是一个非金属的默认F0了。以下是F0更多参考:
F0
结果:
f

FresnelLerp函数代码如下:

half3 FresnelLerp(half3 F0,half3 F90,half cosA){
half t=Pow5(1-cosA);
return lerp(F0,F90,t);
}

fresnelLerp
它的结果是边缘很强,视线直视的地方很暗,符合我们对菲涅尔的认知。这个值最终是与间接光镜面反射相乘,作为其系数。

间接光漫反射

在这里插入图片描述
我们导入一张cubemap贴图进unity,unity会自动帮我们把cubemap预积分处理成这样的一张模糊的图片。这个图片就是用来做间接光漫反射用的。看到这个图我们会有两个问题:1.是怎么做的?2.为什么糊到这种程度而不是更模糊或是更清晰呢?我的回答是:
1.Unity用的基函数叫三阶的伴随勒让德多项式,将cubemap采样后滤波做出的。具体做法可以在《 Real-time Rendering》得到。
2.因为Unity使用的是三阶伴随勒让德多项式,类似傅里叶变换后只取了几个低频部分,得到的结果自然是非常糊的一张图,丢失了太多高频信息。不过对于漫反射来讲,其实也已经够了。
我们可以非常方便的取出漫反射信息,只需要在片元着色器中加一句:

half3 ambient_contrib = ShadeSH9(float4(i.normal,1));//注意输入为WorldSpace的法线

结果:
ibl漫反射

间接光镜面反射

不同粗糙度的物体,反射的图像有的粗糙,有的清晰,难道我们需要很多张不同模糊程度的图输入进去吗?当然不需要,Unity帮我们处理好了cubemap的LOD,当物体越粗糙,就调用更高的LOD层级,这样内存占用只增加了百分之三十,但是效果非常好。
LOD
需要注意的是,粗糙度与LOD层级并非线性关系,而是一条曲线,如下:
mip
代码如下:

float mip_roughness = perceptualRoughness * (1.7 - 0.7*perceptualRoughness );

我们可以把视线方向取个负,然后根据法线把它镜像一下,去取cubemap.可以使用UNITY_SAMPLE_TEXCUBE_LOD函数。其中,unity_SpecCube0是unity内部维护的cubemap,我们可以直接取它,reflectVec就是视线方向的法线镜像,mip就是我们根据光滑度算出的mip层级。

half mip = mip_roughness * UNITY_SPECCUBE_LOD_STEPS;//得出mip层级。默认UNITY_SPECCUBE_LOD_STEPS=6(定义在UnityStandardConfig.cginc)
half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflectVec, mip);//视线方向的反射向量,去取样,同时考虑mip层级
half3 iblSpecular = DecodeHDR(rgbm, unity_SpecCube0_HDR);//使用DecodeHDR将颜色从HDR编码下解码。可以看到采样出的rgbm是一个4通道的值,
half surfaceReduction=1.0/(roughness*roughness+1.0);

需要注意的是直接取出的cubemap由于是HDR格式,颜色会超过1,导致材质非常亮,需要DecodeHDR一下,看起来才是正常的。

最终加和

最终,我们需要把上面四部分的值加和在一起。
其中直接光部分:

//漫反射系数kd
float3 kd = OneMinusReflectivityFromMetallic(_Metallic);
kd*=Albedo;
float3 specular = D * G * F ;
float3 specColor = specular * lightColor*nl*UNITY_PI;//直接光镜面反射部分。镜面反射的系数就是F。漫反射之前少除π了,所以为了保证漫反射和镜面反射的比例,这里还得乘一个π
float3 diffColor = kd * rawDiffColor;//直接光漫反射部分。
float3 directLightResult = diffColor + specColor;

间接光部分:

float3 iblDiffuseResult = iblDiffuse*kd;//乘间接光漫反射系数
half surfaceReduction=1.0/(roughness*roughness+1.0);
float oneMinusReflectivity = unity_ColorSpaceDielectricSpec.a-unity_ColorSpaceDielectricSpec.a*_Metallic;	//grazingTerm压暗非金属的边缘异常高亮
half grazingTerm=saturate(_Smoothness+(1-oneMinusReflectivity));
float3 iblSpecularResult = surfaceReduction*iblSpecular*FresnelLerp(F0,grazingTerm,nv);
float3 indirectResult = (iblDiffuseResult + iblSpecularResult)*occlusion;

值得一提的是surfaceReduction项,这是一个拟合项,如果没有它间接光镜面反射在粗糙物体上可能会过亮,导致不真实,这个我倒是没有特别找到合适的解释,按照实现代码看,应该如此。顺便一提,最终的遮蔽也要都乘上去。

最终加和:

float3 finalResult = directLightResult + indirectResult;

结果

Unity的pbr实现是非常繁琐的,往往需要套娃一样地去层层检索,如果没有VS Code这个得力工具,将更难以阅读。最终我还是实现了基础的PBR shader,不过具体的实现我不再表述,因为我已经将函数的调用直接写出来了,所以大家不必像我一样层层套娃,只需要一次就可以看到公式的实现。可以看到,效果已经和Standard Shader没有任何区别了,即便是快速替换材质,也难以发现区别,说明基本还原了Standard Shader的实现。通过层层检索unity对pbr几张贴图的处理,我添加了贴图的输入,确保它是一个实用的shader。此外,我还添加了lightProbe和reflection Probe以及烘焙lightmap的支持。
非金属材质表现:
非金属材质的表现
金属材质表现:
金属材质的表现
注意:需要在build-in管线中将gama空间改为linear空间!

Shader "Custom/myPBR"
{Properties{_Tint("Tint",Color)=(1,1,1,1)_MainTex ("Texture", 2D) = "white" {}//金属度要经过gama,否则即便是linear空间下渲染,unity也不会对一个滑条做操作的[Gamma]_Metallic("Metallic",Range(0,1))=0_MetallicGlossMap("Metallic", 2D) = "white" {}_Smoothness("Smoothness(Metallic.a)",Range(0,1))=0.5_BumpMap("Normal Map", 2D) = "bump" {}_Parallax ("Height Scale", Range (0.00, 0.08)) = 0.0_ParallaxMap ("Height Map", 2D) = "black" {}_OcclusionMap("Occlusion", 2D) = "white" {}}SubShader{Pass{Tags{"LightMode"="ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag//添加lightmap支持#pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON#include "UnityStandardBRDF.cginc"struct appdata{float4 vertex : POSITION;float3 normal:NORMAL;float2 uv : TEXCOORD0;float2 uv1:TEXCOORD1;fixed4 tangent : TANGENT;};struct v2f{float2 uv : TEXCOORD0;#ifndef LIGHTMAP_OFF half2 uv1:TEXCOORD1;#endiffloat4 vertex : SV_POSITION;float3 normal:TEXCOORD2;float3 worldPos:TEXCOORD3;float4 tangent:TEXCOORD4;float3x3 tangentToWorld : TEXCOORD5; float3 viewDir:COLOR1;float3x3 tangentMatrix: TEXCOORD8; float3 objectspaceViewdir:COLOR2;};sampler2D _MainTex;float4 _Tint;float _Metallic;float _Smoothness;float4 _MainTex_ST;sampler2D _MetallicGlossMap;sampler2D _BumpMap;sampler2D _OcclusionMap;float _Parallax;sampler2D _ParallaxMap;inline half OneMinusReflectivityFromMetallic(half metallic)
{// We'll need oneMinusReflectivity, so//   1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)// store (1-dielectricSpec) in unity_ColorSpaceDielectricSpec.a, then//   1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =//                  = alpha - metallic * alphahalf oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a;return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}inline half3 DiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity)
{specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);oneMinusReflectivity = OneMinusReflectivityFromMetallic(metallic);return albedo * oneMinusReflectivity;
}v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.worldPos = mul(unity_ObjectToWorld, v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.normal = UnityObjectToWorldNormal(v.normal);o.normal = normalize(o.normal);o.tangent=v.tangent;float3 normalWorld = UnityObjectToWorldNormal(v.normal);float4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);// 对于奇怪的负缩放,我们需要sign取反(flip the sign)half sign = tangentWorld.w * unity_WorldTransformParams.w;half3 binormal = cross(normalWorld, tangentWorld) * sign;float3x3 tangentToWorld = half3x3(tangentWorld.xyz, binormal, normalWorld);o.tangentToWorld=tangentToWorld;o.viewDir=normalize(UnityWorldSpaceViewDir(o.worldPos));//Parallax viewDir need to changed from ObjectSpace to Tangentfixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(o.worldPos));fixed3 objectspaceViewdir= mul(unity_WorldToObject, worldViewDir);o.objectspaceViewdir =normalize(objectspaceViewdir);float3 objectSpaceBinormal = normalize(cross(v.normal,v.tangent.xyz) * v.tangent.w);float3x3 tangentMatrix = float3x3(v.tangent.xyz, objectSpaceBinormal, v.normal);o.tangentMatrix = tangentMatrix;#ifndef LIGHTMAP_OFFo.uv1 = v.uv1.xy*unity_LightmapST.xy + unity_LightmapST.zw;#endifreturn o;}fixed4 frag (v2f i) : SV_Target{#ifndef LIGHTMAP_OFFfixed3 lm = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap,i.uv1));float3 albedo = _Tint*tex2D(_MainTex,i.uv);float3 finalRes = albedo * lm;return float4( finalRes,1);#endifhalf height = tex2D(_ParallaxMap, i.uv).g;float3 tangentspaceViewDir =normalize( mul(i.tangentMatrix, i.objectspaceViewdir));i.uv += ParallaxOffset(height,_Parallax,tangentspaceViewDir);_Metallic=tex2D(_MetallicGlossMap,i.uv).r*_Metallic;_Smoothness=tex2D(_MetallicGlossMap,i.uv).a*_Smoothness;float occlusion=tex2D(_OcclusionMap,i.uv).r;float3 normal = normalize(i.normal);//没有加normalize操作,导致其还是取的顶点法线,没有进行插值//	#ifdef _NORMALMAPhalf3 tangent1 = i.tangentToWorld[0].xyz;half3 binormal1 = i.tangentToWorld[1].xyz;half3 normal1 = i.tangentToWorld[2].xyz;float3 normalTangent =UnpackNormal(tex2D(_BumpMap,i.uv));//return float4(1,0,0,1);//			normal= normalize(float3(dot(i.TtoW0.xyz, normalTangent), dot(i.TtoW1.xyz, normalTangent), dot(i.TtoW2.xyz, normalTangent)));//矩阵变换normal=normalize((float3)(tangent1 * normalTangent.x + binormal1 * normalTangent.y + normal1 * normalTangent.z));// #endiffloat3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);//		float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));float3 viewDir=i.viewDir;float3 lightColor = _LightColor0.rgb;float3 halfVector = normalize(lightDir + viewDir);//roughness相关float perceptualRoughness = 1 - _Smoothness;float roughness = perceptualRoughness * perceptualRoughness;roughness=max(roughness,0.002);//即便smoothness为1,也要有点高光在float squareRoughness = roughness * roughness;float nl = max(saturate(dot(normal , lightDir ) ) , 0.0000001);//防止除0float nv = max(saturate(dot(normal, viewDir)), 0.0000001);float vh = max(saturate(dot(viewDir, halfVector)), 0.0000001);float lh = max(saturate(dot(lightDir, halfVector)), 0.0000001);float nh = max(saturate(dot(normal, halfVector)), 0.0000001);//1.1直接光漫反射部分.兰伯特光照。没有除以π,是因为会显得太暗。float3 Albedo = _Tint*tex2D(_MainTex,i.uv);float3 rawDiffColor = DisneyDiffuse(nv,nl,lh,perceptualRoughness)*nl*lightColor;//1.2.直接光镜面反射部分// 1.2.1 D项(GGX)float D=GGXTerm(nh,roughness);// 1.2.2 G项 几何函数,遮蔽变暗一些//      直接光照和间接光照时的k都在逼近二分之一,只不过直接光照时这个值最小为八分之一而不是0。这是为了保证在表面绝对光滑时//      也会吸收一部分光线,毕竟完全不吸收光线的物体在现实中不存在float G=SmithJointGGXVisibilityTerm(nl,nv,roughness);//1.2.3 F项 菲涅尔反射 金属反射强边缘反射强float3 F0 = lerp(unity_ColorSpaceDielectricSpec.rgb, Albedo, _Metallic);float3 F=FresnelTerm(F0,lh);//漫反射系数kdfloat3 kd = OneMinusReflectivityFromMetallic(_Metallic);kd*=Albedo;float3 specular = D * G * F ;float3 specColor = specular * lightColor*nl*UNITY_PI;//直接光镜面反射部分。镜面反射的系数就是F。漫反射之前少除π了,所以为了保证漫反射和镜面反射的比例,这里还得乘一个πfloat3 diffColor = kd * rawDiffColor;//直接光漫反射部分。float3 directLightResult = diffColor + specColor;//至此,直接光部分结束//2.开始间接光部分//	2.1间接光漫反射half3 iblDiffuse = ShadeSH9(float4(normal,1));float3 iblDiffuseResult = iblDiffuse*kd;//乘间接光漫反射系数//	2.2间接光镜面反射float mip_roughness = perceptualRoughness * (1.7 - 0.7*perceptualRoughness );float3 reflectVec = reflect(-viewDir, normal);half mip = mip_roughness * UNITY_SPECCUBE_LOD_STEPS;//得出mip层级。默认UNITY_SPECCUBE_LOD_STEPS=6(定义在UnityStandardConfig.cginc)half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflectVec, mip);//视线方向的反射向量,去取样,同时考虑mip层级half3 iblSpecular = DecodeHDR(rgbm, unity_SpecCube0_HDR);//使用DecodeHDR将颜色从HDR编码下解码。可以看到采样出的rgbm是一个4通道的值,half surfaceReduction=1.0/(roughness*roughness+1.0);float oneMinusReflectivity = unity_ColorSpaceDielectricSpec.a-unity_ColorSpaceDielectricSpec.a*_Metallic;	//grazingTerm压暗非金属的边缘异常高亮half grazingTerm=saturate(_Smoothness+(1-oneMinusReflectivity));float3 iblSpecularResult = surfaceReduction*iblSpecular*FresnelLerp(F0,grazingTerm,nv);float3 indirectResult = (iblDiffuseResult + iblSpecularResult)*occlusion;//至此,结束间接光部分//最终加和float3 finalResult = directLightResult + indirectResult;return float4(finalResult,1);}ENDCG}}FallBack"Diffuse"
}

不足之处:本次研究只是侧重了Unity对于pbr的实现,对于Unity和标准pbr的实现的一些不同,源码中没有更多的解释,因此还有待于厘清,知其然还要知其所以然,基础才能足够扎实。
如果有帮到你的话,请点个赞吧!☺

参考资料:如何在Unity中造一个PBR Shader轮子


http://chatgpt.dhexx.cn/article/r0VJ6DXK.shtml

相关文章

UE4 虚幻引擎,处理PBR材质

处理PBR(Physically Based Rendering基于物理的渲染)材质 虚幻引擎可以提供非常真实的基于物理的材质和渲染系统,基于物理的材质是一种以三种材质核心输入为基础的实时算法,十分方便美术师使用。这三种输入分别是Mtallic金属感&a…

【LearnOpenGL】-PBR材质

PBR,或者用更通俗一些的称呼是指基于物理的渲染(Physically Based Rendering),它指的是一些在不同程度上都基于与现实世界的物理原理更相符的基本理论所构成的渲染技术的集合。正因为基于物理的渲染目的便是为了使用一种更符合物理学规律的方式来模拟光线…

源码分析学习记录(9)——PBR材质

2021SCSDUSC Dust3D中的材质采用PBR模型。PBR就是Physically-Based Rendering的缩写,意为基于物理的渲染。它提供了一种光照和渲染方法,能够更精确的描绘光和表面之间的作用。由于PBR基于物理的渲染旨在以物理上合理的方式模拟光,因此与我们…

UE4 PBR材质使用记录

参考文章:https://www.bilibili.com/video/BV1Dv411w7x6 参考文章:https://www.bilibili.com/video/BV1TQ4y167sG 引擎:4.26 初试两种纹理混合 纹理来源于【初学者内容包】 首先,新建一个材质文件。 然后将纹理拖入材质中。 …

01_ue4进阶_PBR材质

PBR材质,在不同的角度,通过不同的图层,来虚拟出一些物理效果。 用一个砖墙的例子来演示一下不同的图层。 这些图共同组成一个材质。 建立两个文件夹 将这些图层导入 这是基本颜色图片。 法线图:给材质一种凹凸不平的立体感。 标…

PBR物理材质

本人笔记,不喜勿喷 PBR在英文里面是 Physically Based Rendering 翻译成中文就是基于物理的渲染。 反射 散射 生成diffuse 漫反射 反射 生成Specular 镜面反射 进入物体生成透射 实现pbr渲染,抽象出来了相应的渲染方程 BSDF 双向散射表面分布函数&…

UE4材质(二):PBR材质

摘自并整理自虚幻官方教程:https://learn.unrealengine.com/course/2449699 课程中的工程项目文件下载: 链接:https://pan.baidu.com/s/1o7m3pR7BvaCYAmlx57B9HQ 提取码:uenb 一、PBR材质介绍 Physical Based Rendering&#xf…

Unity PBR材质

unity两种pbr材质工作流 Standard材质(metallic工作流) 通过metallic参数(金属度)和smoothness(光滑度)影响反射率和强度 非金属sRGB[50-243]金属sRGB[186-255]metallic金属度(灰度图,R通道值)smoothness光滑度(金属…

【数学】Frobenius范数

Frobenius范数简称F范数,这个范数是针对矩阵而言的,具体定义可以类比向量的L2范数。 简单来说就是矩阵的每个元素的平方和的开方。

日志-Frobenius norm,共轭矩阵

Frobenius norm 就是矩阵各个元素平方和,然后开平方根。 如果这个算出来的difference太大,就说明backpropagation出现了错误。 可以使用numpy算出两个矩阵之间frobenius norm numpy.linalg.norm import numpy as np print(np.linalg.norm(np.array([1…

Perron-Frobenius定理和一些相关定理的证明

图片来源:非负矩阵之Perron–Frobenius定理 - 纯粹的文章 - 知乎 Oskar Perron 在1907年发表了关于正矩阵的一些基本发现称之为Perron定理,后来Frobenius将其推广到非负矩阵上,称为Perron-Frobenius定理。 下面先证明一些预备定理&#xff0…

基于Frobenius范数的标准NMF更新公式推导

目标函数 在标准非负矩阵分解中,其目标函数很简单,形式为,其中V为观测矩阵,W为基矩阵,H为系数矩阵, 这里假设V为mn维的,W为ml维的,H为ln维的。 更新公式推导 其更新公式是基于梯度下降法&…

线性代数笔记 Frobenius 范数和矩阵的迹之间的关系

线性代数笔记:Frobenius 范数_UQI-LIUWJ的博客-CSDN博客 先给出结论: 举个例子: 任取22的矩阵A 它的 Frobenius 范数为: 而 所以

Frobenius标准型与Jordan标准型总结

1.数域不同 Frobenius标准型:任意数域P Jordan标准型:复数域 Jordan标准型,可以形式化理解为把Frobenius标准型中的d(λ)继续分解,进而细化到一次因式的乘机,因此Frobenius标准型为任意数域,Jordan标准型为复数域 2…

弗罗贝尼乌斯范数 matlab,【Frobenius norm(弗罗贝尼乌斯-范数)(F-范数)】

(1)Frobenius 范数(F-范数) 一种矩阵范数,记为:。 即矩阵中每项数的平方和的开方值。 这个范数是针对矩阵而言的,具体定义可以类比 向量的L2范数。 可用于 利用低秩矩阵来近似单一数据矩阵。 用数学表示就是去找一个秩为k的矩阵B,使得矩阵B与原始数据矩阵A的差的F范数尽可…

矩阵的 Frobenius 范数与 trace (迹)的关系及其求偏导法则

目录 1. 矩阵的迹求导法则 2. x is a column vector, A is a matrix 3. Practice: 4. 矩阵求导计算法则 一些结论: 1. 矩阵的迹求导法则 2. x is a column vector, A is a matrix 3. Practice: 4. 矩阵求导计算法则 求导公式(撇号为转置)&#xff1…

弗罗贝尼乌斯范数(Frobenius norm)

向量范数是很常见的,在很多教科书里都能见到。矩阵范数是对向量范数的一种推广。下面转载一篇讲解矩阵范数的文章,里面有对弗罗贝尼乌斯范数的定义,比较适合扫盲。原文如下: 矩阵范数(matrix norm)是数学上…