当前位置: 首页 > news >正文

Unity 内置Standard Shader UNITY_BRDF_PBS函数分析 (二)

四、BRDF1_Unity_PBS

// 主物理基BRDF实现
// 基于Disney工作并以Torrance-Sparrow微面模型为基础
// 公式:
//   BRDF = kD / π + kS * (D * V * F) / 4
//   I = BRDF * (N · L)
// 
// * NDF(法线分布函数)可根据 UNITY_BRDF_GGX 选择:
//    a) 归一化 BlinnPhong
//    b) GGX
// * 可见性项采用Smith模型
// * Fresnel采用Schlick近似
half4 BRDF1_Unity_PBS (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,float3 normal, float3 viewDir,UnityLight light, UnityIndirect gi)
{// 将smoothness转换为感知粗糙度float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness);// 计算半向量 H = normalize(light.dir + viewDir)float3 halfDir = Unity_SafeNormalize(float3(light.dir) + viewDir);// 处理 N·V(法线与视线的点积)负值问题// 理论上对于可见像素 NdotV 不应该为负,但透视投影和法线贴图可能会导致负值// 如果为负,应调整法线使其面向摄像机,以免产生异常,但这会增加少量ALU开销// 可通过宏 UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 控制是否进行校正,默认禁用(0)#define UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 0#if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV// 根据 dot(normal, viewDir) 计算偏移量,当小于0时将法线向视线方向平移half shiftAmount = dot(normal, viewDir);normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal;// 注意:理论上应对normal重新归一化,但为了节省ALU开销,此处略去float nv = saturate(dot(normal, viewDir)); // TODO: 可能不需要saturate
#else// 简单采用绝对值处理,虽然不完全正确但能抑制伪影half nv = abs(dot(normal, viewDir));
#endif// 计算法线与光源方向的点积 N·L,饱和到 [0,1]float nl = saturate(dot(normal, light.dir));// 计算法线与半向量的点积 N·H,饱和到 [0,1]float nh = saturate(dot(normal, halfDir));// 计算光源方向与视线之间的点积 L·V,饱和到 [0,1]half lv = saturate(dot(light.dir, viewDir));// 计算光源方向与半向量之间的点积 L·H,饱和到 [0,1]half lh = saturate(dot(light.dir, halfDir));// ----------------- 漫反射项 -----------------// 使用DisneyDiffuse函数计算漫反射项,传入 nv, nl, lh 和感知粗糙度// 最后乘以 nl 模拟光照强度随光线入射角衰减half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl;// ----------------- 镜面反射项 -----------------// 注意:理论上应将 diffuseTerm 除以π,但为了与Legacy Shader一致并考虑非重要光源的处理,采用乘法// 将感知粗糙度转换为实际粗糙度float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
#if UNITY_BRDF_GGX// 对于GGX分支:为了防止粗糙度为0导致无镜面反射,取 roughness 的最小值0.002roughness = max(roughness, 0.002);// 计算可见性项 V 使用 SmithJointGGXVisibilityTerm,输入 N·L、N·V和粗糙度float V = SmithJointGGXVisibilityTerm(nl, nv, roughness);// 计算法线分布函数 D 使用 GGXTerm,输入 N·H 和粗糙度float D = GGXTerm(nh, roughness);
#else// Legacy 分支:采用SmithBeckmannVisibilityTerm和归一化 BlinnPhong NDF计算half V = SmithBeckmannVisibilityTerm(nl, nv, roughness);half D = NDFBlinnPhongNormalizedTerm(nh, PerceptualRoughnessToSpecPower(perceptualRoughness));
#endif// 根据 Torrance-Sparrow 模型,镜面反射项 = V * D * πfloat specularTerm = V * D * UNITY_PI;#   ifdef UNITY_COLORSPACE_GAMMA// 如果处于Gamma空间下,对 specularTerm 取平方根以调整亮度specularTerm = sqrt(max(1e-4h, specularTerm));
#   endif// 为防止在Metal平台上 specularTerm * nl 出现 NaN,取最大值确保合理性specularTerm = max(0, specularTerm * nl);
#if defined(_SPECULARHIGHLIGHTS_OFF)// 如果禁用了 specular highlights,则将 specularTerm 置为0specularTerm = 0.0;
#endif// ----------------- 表面缩减因子 -----------------// surfaceReduction = 1 / (roughness^2 + 1)// 在 Gamma 空间下,用 1 - 0.28 * roughness * perceptualRoughness 作为近似
#   ifdef UNITY_COLORSPACE_GAMMAhalf surfaceReduction = 1.0 - 0.28 * roughness * perceptualRoughness;      // 近似 1-0.28*x^3
#   elsehalf surfaceReduction = 1.0 / (roughness * roughness + 1.0);                 // 使数值在 [0.5,1] 范围内渐变
#   endif// 为了获得真正的Lambert光照,需要能够完全消除 specular 部分// 如果 specColor 全为0,则 specularTerm 乘以0specularTerm *= any(specColor) ? 1.0 : 0.0;// ----------------- 边缘高光项 -----------------// grazingTerm 用于模拟在低视角(接近边缘)下镜面反射的增强效果half grazingTerm = saturate(smoothness + (1 - oneMinusReflectivity));// ----------------- 综合颜色计算 -----------------// 最终颜色由三部分构成:// 1. 漫反射部分:diffColor 乘以 (间接漫反射 gi.diffuse + 主光 diffuseTerm * light.color)// 2. 镜面反射部分:specularTerm 乘以 light.color 和经过 FresnelTerm 调整后的 specColor// 3. 间接镜面反射部分:surfaceReduction 乘以 gi.specular 和经过 FresnelLerp 插值的 specColor(基于 grazingTerm 和 nv)half3 color = diffColor * (gi.diffuse + light.color * diffuseTerm)+ specularTerm * light.color * FresnelTerm(specColor, lh)+ surfaceReduction * gi.specular * FresnelLerp(specColor, grazingTerm, nv);// 返回最终颜色,alpha 固定为1(完全不透明)return half4(color, 1);
}

1.SmoothnessToPerceptualRoughness

float SmoothnessToPerceptualRoughness(float smoothness)
{return (1 - smoothness);
}

2.DisneyDiffuse

// 注意:Disney Diffuse 的结果必须在外部乘以 (diffuseAlbedo / π)
// 这个函数仅计算用于漫反射的散射系数
half DisneyDiffuse(half NdotV, half NdotL, half LdotH, half perceptualRoughness)
{// 计算 fd90,代表当视角或光线与法线呈90度时的散射因子// fd90 = 0.5 + 2 * (LdotH)^2 * perceptualRoughness// 这里 LdotH 越大(光与半向量接近),fd90 越大;同时粗糙度越大,fd90 越高half fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;// 下面使用 Schlick 近似计算 Fresnel 散射项// 对于光线方向(light scatter):// 计算公式:1 + (fd90 - 1) * Pow5(1 - NdotL)// 当 NdotL 接近 1 时,Pow5(1 - NdotL) 约等于 0,lightScatter 接近 1// 当 NdotL 接近 0 时,Pow5(1 - NdotL) 接近 1,lightScatter 接近 fd90half lightScatter = (1 + (fd90 - 1) * Pow5(1 - NdotL));// 对于视线方向(view scatter):// 计算公式:1 + (fd90 - 1) * Pow5(1 - NdotV)// 同理,当 NdotV 较大时,viewScatter 约等于 1;当 NdotV 较小时,viewScatter 约等于 fd90half viewScatter = (1 + (fd90 - 1) * Pow5(1 - NdotV));// 最终返回的散射系数为两个方向散射项的乘积// 这个结果反映了漫反射部分对光照散射的综合响应return lightScatter * viewScatter;
}
公式如下

Disney Diffuse模型通过粗糙度和视角/光源方向的散射增强项计算漫反射反射率:
f diffuse = diffColor π ⋅ ScatteringFactor f_{\text{diffuse}} = \frac{\text{diffColor}}{\pi} \cdot \text{ScatteringFactor} fdiffuse=πdiffColorScatteringFactor
其中:

  • ScatteringFactor(散射系数):
    ScatteringFactor = lightScatter ∗ viewScatter  = [ 1 + ( F D 90 − 1 ) ⋅ ( 1 − N ⋅ L ) 5 ] ⋅ [ 1 + ( F D 90 − 1 ) ⋅ ( 1 − N ⋅ V ) 5 ] \text{ScatteringFactor} = \text{lightScatter} * \text{viewScatter } = \left[1 + (F_{D90} - 1) \cdot (1 - N \cdot L)^5\right] \cdot \left[1 + (F_{D90} - 1) \cdot (1 - N \cdot V)^5\right] ScatteringFactor=lightScatterviewScatter =[1+(FD901)(1NL)5][1+(FD901)(1NV)5]
  • F D 90 F_{D90} FD90(散射因子在90度视角时的值):
    F D 90 = 0.5 + 2 ⋅ ( L ⋅ H ) 2 ⋅ perceptualRoughness F_{D90} = 0.5 + 2 \cdot (L \cdot H)^2 \cdot \text{perceptualRoughness} FD90=0.5+2(LH)2perceptualRoughness
    • L ⋅ H L \cdot H LH:光源方向 L L L 与半角向量 H H H 的夹角余弦( H = L + V ∣ L + V ∣ H = \frac{L + V}{|L + V|} H=L+VL+V)。
    • perceptualRoughness = 1 − smoothness \text{perceptualRoughness} = 1 - \text{smoothness} perceptualRoughness=1smoothness(光滑度转换为感知粗糙度)。

下图来自Unity shader 入门精要
在这里插入图片描述

3.PerceptualRoughnessToRoughness

float PerceptualRoughnessToRoughness(float perceptualRoughness)
{return perceptualRoughness * perceptualRoughness;
}

4.SmithJointGGXVisibilityTerm

// 参考文献: http://jcgt.org/published/0003/02/03/paper.pdf
inline float SmithJointGGXVisibilityTerm(float NdotL, float NdotV, float roughness)
{
#if 0// 原始公式:// lambda_v = (-1 + sqrt(a2 * (1 - NdotL2) / NdotL2 + 1)) * 0.5f;// lambda_l = (-1 + sqrt(a2 * (1 - NdotV2) / NdotV2 + 1)) * 0.5f;// G = 1 / (1 + lambda_v + lambda_l);// 重新排序代码以提高效率half a = roughness;  // 粗糙度参数half a2 = a * a;     // 粗糙度平方// 计算视角方向和光源方向的几何阴影因子half lambdaV = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2);half lambdaL = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2);// 可见性项简化为:(2.0f * NdotL * NdotV) / ((4.0f * NdotL * NdotV) * (lambda_v + lambda_l + 1e-5f));// 返回值,注意这里的epsilon是为了避免除零错误,由于不在移动设备上运行,所以可以使用较小的epsilon值return 0.5f / (lambdaV + lambdaL + 1e-5f);  
#else// 上述公式的近似形式(简化了sqrt计算,虽然不完全数学准确但足够接近)float a = roughness;  // 粗糙度参数// 近似计算视角方向的几何阴影因子float lambdaV = NdotL * (NdotV * (1 - a) + a);// 近似计算光源方向的几何阴影因子float lambdaL = NdotV * (NdotL * (1 - a) + a);// 根据不同的Shader API选择合适的epsilon值
#if defined(SHADER_API_SWITCH)// 如果是Switch平台,则使用UNITY_HALF_MIN作为epsilon值return 0.5f / (lambdaV + lambdaL + UNITY_HALF_MIN);
#else// 其他平台使用1e-5f作为epsilon值,防止除零错误return 0.5f / (lambdaV + lambdaL + 1e-5f);
#endif#endif
}
1.Smith-Joint GGX 原始公式:

λ V = ( − 1 + a 2 ⋅ ( 1 − ( N ⋅ L ) 2 ) ( N ⋅ L ) 2 + 1 ) ⋅ 1 2 \lambda_V = \left( -1 + \sqrt{ \frac{a2 \cdot \left(1 - (N \cdot L)^2\right)}{(N \cdot L)^2} + 1 } \right) \cdot \frac{1}{2} λV=(1+(NL)2a2(1(NL)2)+1 )21

λ L = ( − 1 + a 2 ⋅ ( 1 − ( N ⋅ V ) 2 ) ( N ⋅ V ) 2 + 1 ) ⋅ 1 2 \lambda_L = \left( -1 + \sqrt{ \frac{a2 \cdot \left(1 - (N \cdot V)^2\right)}{(N \cdot V)^2} + 1 } \right) \cdot \frac{1}{2} λL=(1+(NV)2a2(1(NV)2)+1 )21

G = 1 1 + λ V + λ L G = \frac{1}{1 + \lambda_V + \lambda_L} G=1+λV+λL1


其中:

  • a 2 a2 a2 表示粗糙度平方( α 2 \alpha^2 α2)。
  • N d o t L NdotL NdotL N d o t V NdotV NdotV 分别表示法线与光源、视角方向的点积(即 cos ⁡ θ L \cos\theta_L cosθL cos ⁡ θ V \cos\theta_V cosθV)。
2.简化后的表达式(代码中给出的中间步骤):

λ V = N ⋅ L ⋅ [ ( 1 − α 2 ) ( N ⋅ V ) 2 + α 2 ] , λ L = N ⋅ V ⋅ [ ( 1 − α 2 ) ( N ⋅ L ) 2 + α 2 ] . \begin{aligned} \lambda_V &= N \cdot L \cdot \sqrt{ \left[ (1 - \alpha^2) (N \cdot V)^2 + \alpha^2 \right] }, \\ \lambda_L &= N \cdot V \cdot \sqrt{ \left[ (1 - \alpha^2) (N \cdot L)^2 + \alpha^2 \right] }. \end{aligned} λVλL=NL[(1α2)(NV)2+α2] ,=NV[(1α2)(NL)2+α2] .

G = 0.5 λ V + λ L + ϵ , G = \frac{0.5}{\lambda_V + \lambda_L + \epsilon}, G=λV+λL+ϵ0.5,
其中 ϵ \epsilon ϵ 是防止除零的小常数(如 1 × 1 0 − 5 1 \times 10^{-5} 1×105)。

3.近似公式(实际使用的高效实现)

代码中实际使用的近似公式通过简化平方根运算,以提高计算效率,同时保持视觉效果的接近:

λ V ≈ N ⋅ L ⋅ ( ( N ⋅ V ) ⋅ ( 1 − α ) + α ) , λ L ≈ N ⋅ V ⋅ ( ( N ⋅ L ) ⋅ ( 1 − α ) + α ) . \begin{aligned} \lambda_V &\approx N \cdot L \cdot \left( (N \cdot V) \cdot (1 - \alpha) + \alpha \right), \\ \lambda_L &\approx N \cdot V \cdot \left( (N \cdot L) \cdot (1 - \alpha) + \alpha \right). \end{aligned} λVλLNL((NV)(1α)+α),NV((NL)(1α)+α).

5.GGXTerm

inline float GGXTerm(float NdotH, float roughness)
{// 粗糙度的平方float a2 = roughness * roughness;// 计算GGX分布函数中的分母部分// 这里使用了两个乘加运算(MAD: Multiply-Add)// d = (NdotH * a2 - NdotH) * NdotH + 1.0f;// 其中NdotH是法线与半角向量的点积,a2是粗糙度的平方float d = (NdotH * a2 - NdotH) * NdotH + 1.0f;// 返回GGX分布函数的结果// UNITY_INV_PI是一个预定义的常数,等于1/π,用于归一化// 分母加上一个很小的值(epsilon),防止除零错误// 注意:此函数不适合在移动设备上运行,因此这里的epsilon值比half类型能表示的最小值还要小return UNITY_INV_PI * a2 / (d * d + 1e-7f);
}
数学表达式

D G G X ( N ⋅ H , α ) = α 2 π ( ( α 2 − 1 ) ( N ⋅ H ) 2 + 1 ) 2 D_{GGX}( \mathbf{N} \cdot \mathbf{H}, \alpha) = \frac{\alpha^2}{\pi \left( (\alpha^2 - 1)(\mathbf{N} \cdot \mathbf{H})^2 + 1 \right)^2} DGGX(NH,α)=π((α21)(NH)2+1)2α2

其中:

  • N \mathbf{N} N 是表面法线,
  • H \mathbf{H} H 是半角向量(即视线方向和光源方向的中间方向),
  • α \alpha α 是粗糙度参数,等于代码中的 roughness 的平方 (a2),
  • N ⋅ H \mathbf{N} \cdot \mathbf{H} NH 表示法线与半角向量的点积,对应于代码中的 NdotH
  • ϵ \epsilon ϵ (在这里是 1e-7f)是一个小值,被加到分母上以避免除零错误。

下图来自Unity shader 入门精要
在这里插入图片描述
图中少了一个括号

6.SmithBeckmannVisibilityTerm

// 基于 Smith-Schlick 模型推导出的用于 Beckmann 分布的可见性项
inline half SmithBeckmannVisibilityTerm(half NdotL, half NdotV, half roughness)
{// 常数 c = sqrt(2 / Pi),用于后续计算// 这个常数是从 Beckmann 分布推导出来的,用于调整粗糙度参数half c = 0.797884560802865h; // 精确值为 sqrt(2 / Pi)// 计算 k,k 是一个调整后的粗糙度参数// 使用 c 对原始粗糙度进行缩放,使得其适应 Beckmann 分布half k = roughness * c;// 调用 SmithVisibilityTerm 函数计算可见性项,并乘以 0.25f// 这里的 0.25f 是因为 Smith 可见性项通常需要除以 4 来归一化return SmithVisibilityTerm(NdotL, NdotV, k) * 0.25f;
}// 通用的 Smith-Schlick 可见性项
inline half SmithVisibilityTerm(half NdotL, half NdotV, half k)
{// 计算视角方向的几何阴影因子 gL// 公式: gL = NdotL * (1 - k) + k// 其中 NdotL 是法线与光源方向的点积,k 是一个调整参数,通常与粗糙度相关half gL = NdotL * (1 - k) + k;// 计算光源方向的几何阴影因子 gV// 公式: gV = NdotV * (1 - k) + k// 其中 NdotV 是法线与视角方向的点积,k 同样是一个调整参数half gV = NdotV * (1 - k) + k;// 返回可见性项的倒数,加上一个小的 epsilon 值以防止除零错误// 这里的 epsilon 值较小,因为此函数不适合在移动设备上运行return 1.0 / (gL * gV + 1e-5f);
}
数学表达式

G S c h l i c k ( N ⋅ L , N ⋅ V , k ) = 1 G L ( N ⋅ L , k ) × G V ( N ⋅ V , k ) G_{Schlick}( \mathbf{N} \cdot \mathbf{L}, \mathbf{N} \cdot \mathbf{V}, k ) = \frac{1}{G_L(\mathbf{N} \cdot \mathbf{L}, k) \times G_V(\mathbf{N} \cdot \mathbf{V}, k)} GSchlick(NL,NV,k)=GL(NL,k)×GV(NV,k)1

其中:

  • N \mathbf{N} N 是表面法线,
  • L \mathbf{L} L 是光源方向,
  • V \mathbf{V} V 是视角方向,
  • N ⋅ L \mathbf{N} \cdot \mathbf{L} NL 表示法线与光源方向的点积,对应于代码中的 NdotL
  • N ⋅ V \mathbf{N} \cdot \mathbf{V} NV 表示法线与视角方向的点积,对应于代码中的 NdotV
  • k k k 是一个调整参数,通常与粗糙度相关联,
  • G L G_L GL G V G_V GV 分别是从视角和光源方向考虑的几何阴影因子。

具体到每个几何阴影因子 G L G_L GL G V G_V GV,它们按照Schlick的近似公式定义如下:

G L = N ⋅ L ( N ⋅ L ) ( 1 − k ) + k G_L = \frac{\mathbf{N} \cdot \mathbf{L}}{(\mathbf{N} \cdot \mathbf{L})(1-k) + k} GL=(NL)(1k)+kNL
G V = N ⋅ V ( N ⋅ V ) ( 1 − k ) + k G_V = \frac{\mathbf{N} \cdot \mathbf{V}}{(\mathbf{N} \cdot \mathbf{V})(1-k) + k} GV=(NV)(1k)+kNV

近似公式(实际使用的高效实现)

g L = ( N ⋅ L ) × ( 1 − k ) + k gL = (\mathbf{N} \cdot \mathbf{L}) \times (1 - k) + k gL=(NL)×(1k)+k
g V = ( N ⋅ V ) × ( 1 − k ) + k gV = (\mathbf{N} \cdot \mathbf{V}) \times (1 - k) + k gV=(NV)×(1k)+k

最终的可见性项 G S c h l i c k G_{Schlick} GSchlick 通过以下方式计算:

G S c h l i c k = 1 g L × g V + ϵ G_{Schlick} = \frac{1}{gL \times gV + \epsilon} GSchlick=gL×gV+ϵ1

这里加上了一个很小的值 ϵ \epsilon ϵ (在代码中是 1e-5f)以防止除零错误。

综合表达式

S m i t h B e c k m a n n V i s i b i l i t y T e r m ( N ⋅ L , N ⋅ V , r o u g h n e s s ) = 0.25 ( ( N ⋅ L ) × ( 1 − k ) + k ) × ( ( N ⋅ V ) × ( 1 − k ) + k ) + 1 e − 5 SmithBeckmannVisibilityTerm(\mathbf{N} \cdot \mathbf{L}, \mathbf{N} \cdot \mathbf{V}, roughness) = \frac{0.25}{\left( (\mathbf{N} \cdot \mathbf{L}) \times (1 - k) + k \right) \times \left( (\mathbf{N} \cdot \mathbf{V}) \times (1 - k) + k \right) + 1e-5} SmithBeckmannVisibilityTerm(NL,NV,roughness)=((NL)×(1k)+k)×((NV)×(1k)+k)+1e50.25
其中
k = r o u g h n e s s × 2 π k = roughness \times \sqrt{\frac{2}{\pi}} k=roughness×π2

ϵ 以防止除零错误 \epsilon以防止除零错误 ϵ以防止除零错误

下图来自Unity shader 入门精要

在这里插入图片描述
可以发现K值不一样,我用的是Unity2021.3.23f1

8.NDFBlinnPhongNormalizedTerm

// 使用归一化的 Blinn-Phong 作为法线分布函数 (NDF)
// 在微表面模型中的使用:spec = D * G * F
// 参考文献: https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdf 中的公式 19
inline half NDFBlinnPhongNormalizedTerm(half NdotH, half n)
{// 归一化因子 norm = (n + 2) / (2 * Pi)// 这个因子确保 Blinn-Phong 分布函数在整个半球上的积分等于 1half normTerm = (n + 2.0) * (0.5 / UNITY_PI);// 计算 Blinn-Phong 高光项,其中 NdotH 是法线与半角向量的点积,n 是光泽度参数half specTerm = pow(NdotH, n);// 返回归一化的高光项return specTerm * normTerm;
}
数学表达式

归一化的Blinn-Phong NDF可以表示为:

D B l i n n − P h o n g ( N ⋅ H , n ) = n + 2 2 π ( N ⋅ H ) n D_{Blinn-Phong}( \mathbf{N} \cdot \mathbf{H}, n ) = \frac{n + 2}{2\pi} (\mathbf{N} \cdot \mathbf{H})^n DBlinnPhong(NH,n)=2πn+2(NH)n

其中:

  • N \mathbf{N} N 是表面法线,
  • H \mathbf{H} H 是半角向量(即视线方向和光源方向的中间方向),
  • N ⋅ H \mathbf{N} \cdot \mathbf{H} NH 表示法线与半角向量的点积,对应于代码中的 NdotH
  • n n n 是光泽度参数,控制高光的锐利度,
  • n + 2 2 π \frac{n + 2}{2\pi} 2πn+2 是归一化因子,确保分布函数在整个半球上的积分等于1。

下图来自Unity shader 入门精要
在这里插入图片描述

9.PerceptualRoughnessToSpecPower

// 将感知粗糙度转换为光泽度参数
inline half PerceptualRoughnessToSpecPower(half perceptualRoughness)
{// 调用 PerceptualRoughnessToRoughness 函数,将感知粗糙度转换为实际学术上的粗糙度 m// 感知粗糙度是为了在用户界面中提供更直观的控制而设计的,而实际粗糙度 m 是用于计算的half m = PerceptualRoughnessToRoughness(perceptualRoughness);   // m 是实际的学术粗糙度// 计算粗糙度的平方,并确保其最小值为 1e-4f 以避免数值问题half sq = max(1e-4f, m * m);// 根据公式 (2.0 / sq) - 2.0 计算光泽度参数 n// 参考文献: https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdfhalf n = (2.0 / sq) - 2.0;// 确保光泽度参数 n 的最小值为 1e-4f,以防止 pow(0,0) 的情况发生// 当粗糙度为 1.0 且 NdotH 为零时,可能会出现这种情况n = max(n, 1e-4f);return n;
}
数学表达式

n = max ⁡ ( 2 max ⁡ ( 1 e − 4 , m 2 ) − 2 , 1 e − 4 ) n = \max\left(\frac{2}{\max(1e-4, m^2)} - 2, 1e-4\right) n=max(max(1e4,m2)22,1e4)

其中:
m = p e r c e p t u a l R o u g h n e s s 2 m = perceptualRoughness^2 m=perceptualRoughness2
这里 m m m 是实际粗糙度,而 perceptualRoughness 是用户界面中提供的感知粗糙度值。

10.FresnelTerm

inline half3 FresnelTerm (half3 F0, half cosA)
{half t = Pow5 (1 - cosA);   // 按照 Schlick 插值方法return F0 + (1-F0) * t;
}
数学表达式

根据Schlick的近似方法,菲涅耳项 F ( θ ) F(\theta) F(θ) 可以表示为:

F ( θ ) = F 0 + ( 1 − F 0 ) ( 1 − cos ⁡ θ ) 5 F(\theta) = F_0 + (1 - F_0)(1 - \cos\theta)^5 F(θ)=F0+(1F0)(1cosθ)5

其中:

  • F 0 F_0 F0 是法线入射时(即当入射角 (\theta) 为0度时)的反射率,
  • cos ⁡ θ \cos\theta cosθ 是视角方向和表面法线之间的点积(即 cosA 在代码中),
  • ( 1 − cos ⁡ θ ) 5 (1 - \cos\theta)^5 (1cosθ)5 是Schlick近似的核心部分,用来模拟随着视角变化的反射率变化。

下图来自Unity shader 入门精要

在这里插入图片描述

11.FresnelLerp

inline half3 FresnelLerp (half3 F0, half3 F90, half cosA)
{half t = Pow5 (1 - cosA);   // 按照 Schlick 插值方法return lerp (F0, F90, t);
}
数学表达式

根据Schlick的近似方法,菲涅耳项 F ( θ ) F(\theta) F(θ) 可以表示为:

F ( θ ) = F 0 + ( F 90 − F 0 ) ( 1 − cos ⁡ θ ) 5 F(\theta) = F_0 + (F_{90} - F_0)(1 - \cos\theta)^5 F(θ)=F0+(F90F0)(1cosθ)5

其中:

  • F 0 F_0 F0 是法线入射时(即当入射角 (\theta) 为0度时)的反射率,
  • F 90 F_{90} F90 是当入射角接近90度时的反射率,
  • cos ⁡ θ \cos\theta cosθ 是视角方向和表面法线之间的点积(即 cosA 在代码中),
  • ( 1 − cos ⁡ θ ) 5 (1 - \cos\theta)^5 (1cosθ)5 是Schlick近似的核心部分,用来模拟随着视角变化的反射率变化。

在代码实现中,使用了线性插值(lerp)来简化这一过程。线性插值的公式为:

lerp ( A , B , t ) = A + t ( B − A ) \text{lerp}(A, B, t) = A + t(B - A) lerp(A,B,t)=A+t(BA)

因此,结合Schlick近似与线性插值,可以得到:

F ( θ ) = lerp ( F 0 , F 90 , t ) = F 0 + t ( F 90 − F 0 ) F(\theta) = \text{lerp}(F_0, F_{90}, t) = F_0 + t(F_{90} - F_0) F(θ)=lerp(F0,F90,t)=F0+t(F90F0)

其中 :
t = ( 1 − cos ⁡ θ ) 5 t = (1 - \cos\theta)^5 t=(1cosθ)5

12.总结

BRDF1_Unity_PBS 的实现基于 Disney 工作,并以 Torrance-Sparrow 微面模型为基础,使用了多种不同的组件来计算最终的 BRDF(双向反射分布函数)。

1.核心公式

BRDF 主要由漫反射项和镜面反射项组成:

B R D F = k D π + k S ⋅ D ⋅ V ⋅ F 4 BRDF = \frac{kD}{\pi} + kS \cdot \frac{D \cdot V \cdot F}{4} BRDF=πkD+kS4DVF

其中:

  • k D kD kD 是漫反射颜色,
  • k S kS kS 是镜面反射颜色,
  • D D D 是法线分布函数(NDF),
  • V V V 是几何可见性函数,
  • F F F 是菲涅尔项。
2. 漫反射项

漫反射项通过 DisneyDiffuse 函数计算,考虑了视角方向、光源方向、半角向量与感知粗糙度之间的关系:

d i f f u s e T e r m = D i s n e y D i f f u s e ( N d o t V , N d o t L , L d o t H , p e r c e p t u a l R o u g h n e s s ) × N d o t L diffuseTerm = DisneyDiffuse(NdotV, NdotL, LdotH, perceptualRoughness) \times NdotL diffuseTerm=DisneyDiffuse(NdotV,NdotL,LdotH,perceptualRoughness)×NdotL

这里,DisneyDiffuse 的具体形式如下:

f d 90 = 0.5 + 2 ⋅ ( L d o t H ) 2 ⋅ p e r c e p t u a l R o u g h n e s s fd90 = 0.5 + 2 \cdot (LdotH)^2 \cdot perceptualRoughness fd90=0.5+2(LdotH)2perceptualRoughness
l i g h t S c a t t e r = ( 1 + ( f d 90 − 1 ) ⋅ ( 1 − N d o t L ) 5 ) lightScatter = (1 + (fd90 - 1) \cdot (1 - NdotL)^5) lightScatter=(1+(fd901)(1NdotL)5)
v i e w S c a t t e r = ( 1 + ( f d 90 − 1 ) ⋅ ( 1 − N d o t V ) 5 ) viewScatter = (1 + (fd90 - 1) \cdot (1 - NdotV)^5) viewScatter=(1+(fd901)(1NdotV)5)
d i f f u s e T e r m = l i g h t S c a t t e r ⋅ v i e w S c a t t e r × N d o t L diffuseTerm = lightScatter \cdot viewScatter \times NdotL diffuseTerm=lightScatterviewScatter×NdotL

3. 镜面反射项

镜面反射项依赖于三个主要组件:法线分布函数NDF( D D D),几何可见性函数( V V V ),和菲涅尔项( F F F)。

GGX 分支

当选择 GGX 作为 NDF 时:

r o u g h n e s s = m a x ( r o u g h n e s s , 0.002 ) roughness = max(roughness, 0.002) roughness=max(roughness,0.002)
V = S m i t h J o i n t G G X V i s i b i l i t y T e r m ( N d o t L , N d o t V , r o u g h n e s s ) V = SmithJointGGXVisibilityTerm(NdotL, NdotV, roughness) V=SmithJointGGXVisibilityTerm(NdotL,NdotV,roughness)
D = G G X T e r m ( N d o t H , r o u g h n e s s ) D = GGXTerm(NdotH, roughness) D=GGXTerm(NdotH,roughness)

s p e c u l a r T e r m = V ⋅ D ⋅ π specularTerm = V \cdot D \cdot \pi specularTerm=VDπ

其中,SmithJointGGXVisibilityTermGGXTerm 分别为:

S m i t h J o i n t G G X V i s i b i l i t y T e r m = 0.5 l a m b d a V + l a m b d a L + 1 e − 5 f SmithJointGGXVisibilityTerm = \frac{0.5}{lambdaV + lambdaL + 1e-5f} SmithJointGGXVisibilityTerm=lambdaV+lambdaL+1e5f0.5
G G X T e r m = r o u g h n e s s 2 π ⋅ ( ( N d o t H ⋅ r o u g h n e s s 2 − N d o t H ) ⋅ N d o t H + 1 ) 2 + 1 e − 7 f GGXTerm = \frac{roughness^2}{\pi \cdot ((NdotH \cdot roughness^2 - NdotH) \cdot NdotH + 1)^2 + 1e-7f} GGXTerm=π((NdotHroughness2NdotH)NdotH+1)2+1e7froughness2

Beckmann 分支

如果选择了 Beckmann 分布,则使用 SmithBeckmannVisibilityTermNDFBlinnPhongNormalizedTerm

k = r o u g h n e s s ⋅ 2 π k = roughness \cdot \sqrt{\frac{2}{\pi}} k=roughnessπ2
V = S m i t h V i s i b i l i t y T e r m ( N d o t L , N d o t V , k ) ⋅ 0.25 V = SmithVisibilityTerm(NdotL, NdotV, k) \cdot 0.25 V=SmithVisibilityTerm(NdotL,NdotV,k)0.25
D = n + 2 2 π ⋅ ( N d o t H ) n D = \frac{n + 2}{2\pi} \cdot (NdotH)^n D=2πn+2(NdotH)n

其中,SmithVisibilityTerm 如下:

g L = N d o t L ⋅ ( 1 − k ) + k gL = NdotL \cdot (1 - k) + k gL=NdotL(1k)+k
g V = N d o t V ⋅ ( 1 − k ) + k gV = NdotV \cdot (1 - k) + k gV=NdotV(1k)+k
S m i t h V i s i b i l i t y T e r m = 1 g L ⋅ g V + 1 e − 5 f SmithVisibilityTerm = \frac{1}{gL \cdot gV + 1e-5f} SmithVisibilityTerm=gLgV+1e5f1

4. 菲涅尔项

对于菲涅尔项,使用 Schlick 近似:

F = F 0 + ( 1 − F 0 ) ⋅ ( 1 − c o s A ) 5 F = F0 + (1 - F0) \cdot (1 - cosA)^5 F=F0+(1F0)(1cosA)5

对于间接镜面反射部分,使用 FresnelLerp

F = l e r p ( F 0 , F 90 , ( 1 − c o s A ) 5 ) F = lerp(F0, F90, (1 - cosA)^5) F=lerp(F0,F90,(1cosA)5)

5.综合颜色计算

最终的颜色由以下三部分构成:

  1. 漫反射部分: d i f f C o l o r × ( g i . d i f f u s e + l i g h t . c o l o r × d i f f u s e T e r m ) diffColor \times (gi.diffuse + light.color \times diffuseTerm) diffColor×(gi.diffuse+light.color×diffuseTerm)
  2. 镜面反射部分: s p e c u l a r T e r m × l i g h t . c o l o r × F r e s n e l T e r m ( s p e c C o l o r , l h ) specularTerm \times light.color \times FresnelTerm(specColor, lh) specularTerm×light.color×FresnelTerm(specColor,lh)
  3. 间接镜面反射部分: s u r f a c e R e d u c t i o n × g i . s p e c u l a r × F r e s n e l L e r p ( s p e c C o l o r , g r a z i n g T e r m , n v ) surfaceReduction \times gi.specular \times FresnelLerp(specColor, grazingTerm, nv) surfaceReduction×gi.specular×FresnelLerp(specColor,grazingTerm,nv)

通过这些步骤,BRDF1_Unity_PBS 实现了一个物理基础的渲染模型,支持不同的微表面分布和可见性模型,提供了灵活且真实的材质表现。

相关文章:

Unity 内置Standard Shader UNITY_BRDF_PBS函数分析 (二)

四、BRDF1_Unity_PBS // 主物理基BRDF实现 // 基于Disney工作并以Torrance-Sparrow微面模型为基础 // 公式&#xff1a; // BRDF kD / π kS * (D * V * F) / 4 // I BRDF * (N L) // // * NDF&#xff08;法线分布函数&#xff09;可根据 UNITY_BRDF_GGX 选择&#…...

GitHub万星项目维护者分享:开源协作的避坑指南

GitHub万星项目维护者分享&#xff1a;开源协作的避坑指南 ——开发者张三与237个文件改动PR的五年战争 序幕&#xff1a;深夜的炸弹 2019年夏天&#xff0c;张三维护的开源项目TerminalX刚突破8000星&#xff0c;一个标题猩红的PR突然弹出&#xff1a;“彻底重构&#xff0…...

Linux基础篇、第四章_01软件安装rpm_yum_源码安装_二进制安装

Linux基础篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; ————laowang 基础命令&#xff1a;rpm、yum、源码安装、二进制安装 一、rpm本地安装&#xff1a; (无需网络安装&#xff0c;无法解决软件依赖) rpm -ivh …...

焊接机排错

焊接机 一、前定位后焊接 两个机台&#xff0c;①极柱定位&#xff0c;相机定位所有极柱点和mark点&#xff1b;②焊接机&#xff0c;相机定位mark点原理&#xff1a;极柱定位在成功定位到所有极柱点和mark点后&#xff0c;可以建立mark点和极柱点的关系。焊接机定位到mark点…...

4.2 Prompt工程与任务建模:高效提示词设计与任务拆解方法

提示词工程&#xff08;Prompt Engineering&#xff09;和任务建模&#xff08;Task Modeling&#xff09;已成为构建高效智能代理&#xff08;Agent&#xff09;系统的核心技术。提示词工程通过精心设计的自然语言提示词&#xff08;Prompts&#xff09;&#xff0c;引导大型语…...

oracle 锁的添加方式和死锁的解决

DML锁添加方式 DML 锁可由一个用户进程以显式的方式加锁&#xff0c;也可通过某些 SQL 语句隐含方式实现。 DML 锁有三种加锁方式&#xff1a;共享锁方式、独占锁方式、共享更新。 共享锁&#xff0c;独占锁用于 TM 锁&#xff0c;共享锁用于 TX 锁。 1)共享方式的表级锁 共享方…...

Nginx 二进制部署与 Docker 部署深度对比

一、核心概念解析 1. 二进制部署 通过包管理器&#xff08;如 apt/yum&#xff09;或源码编译安装 Nginx&#xff0c;直接运行在宿主机上。其特点包括&#xff1a; 直接性&#xff1a;与操作系统深度绑定&#xff0c;直接使用系统库和内核功能 。定制化&#xff1a;支持通过…...

以太网的mac帧格式

一.以太网的mac帧 帧的要求 1.长度 2.物理层...

每日算法-250424

每日算法打卡 (24/04/25) - LeetCode 2971 & 1647 记录一下今天解决的两道 LeetCode 题目 2971. 找到最大周长的多边形 题目 思路 贪心 一个基本的多边形构成条件是&#xff1a;最长边必须小于其他所有边的长度之和。 为了找到周长最大的多边形&#xff0c;我们应该尽可能…...

在本地部署n8n:完整指南

n8n是一个强大的工作流自动化工具&#xff0c;可以帮助你连接不同的应用程序和服务&#xff0c;无需编写复杂的代码。本指南将带你完成在本地计算机上部署n8n的完整过程。 什么是n8n&#xff1f; n8n&#xff08;发音为"n-eight-n"&#xff09;是一个开源的工作流自…...

棋盘格角点检测顺序问题

文章目录 前言一、OpenCV函数测试二、原因分析三、libcbdetect修改总结 前言 棋盘格角点检测在相机拼接、机械臂手眼标定中等应用很广泛&#xff0c;通常也要求尽量各种角度摆放从而保证标定精度。然后就自然想到了这个问题&#xff1a;如果棋盘格任意角度摆放怎么能对应上角点…...

C++之类和对象:定义,实例化,this指针,封装

C语言是面向过程的&#xff0c;C是面向对象的&#xff0c;利用对象交互&#xff0c;接口完成事情。 类的定义&#xff1a; 我们在C语言中可以用struct创建自定义结构体&#xff0c;在C中可以在结构体中定义函数了&#xff0c;这种就被称为类。 #include<iostream> usi…...

Ubuntu系统下交叉编译iperf3

一、参考资料 Linux下iperf3移植到arm下测试100M网口-CSDN博客 Iperf3移植到ARM Linux及使用教程-CSDN博客 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编…...

游戏引擎学习第243天:异步纹理下载

仓库 https://gitee.com/mrxiao_com/2d_game_6 https://gitee.com/mrxiao_com/2d_game_5 回顾并为今天设定阶段 目前的开发工作主要回到了图形渲染相关的部分。我们之前写了自己的软件渲染器&#xff0c;这个渲染器性能意外地好&#xff0c;甚至可以以相对不错的帧率运行过场…...

27、Session有什么重⼤BUG?微软提出了什么⽅法加以解决?

Session的重大BUG 1、进程回收导致Session丢失 原理&#xff1a; IIS的进程回收机制会在系统繁忙、达到特定内存阈值等情况下&#xff0c;自动回收工作进程&#xff08;w3wp.exe&#xff09;。由于Session数据默认存储在进程内存中&#xff0c;进程回收时这些数据会被清除。 …...

机器学习在网络安全中的应用:守护数字世界的防线

一、引言 随着信息技术的飞速发展&#xff0c;网络安全问题日益凸显&#xff0c;成为全球关注的焦点。传统的网络安全防护手段&#xff0c;如防火墙、入侵检测系统&#xff08;IDS&#xff09;和防病毒软件&#xff0c;虽然在一定程度上能够抵御攻击&#xff0c;但在面对复杂多…...

从数据到智慧:解密机器学习的自主学习密码

在数字洪流奔涌的时代&#xff0c;每一次点击、每一行代码、每一条传感器数据都在生成海量信息。传统编程如同精心设计的齿轮组&#xff0c;需要工程师逐行编写规则&#xff1b;而机器学习则打破这一范式&#xff0c;赋予机器从数据中自主提炼规律、总结模式的超能力。这种能力…...

Trae或者VsCode无法识别相对路径(不自动切换工作目录)

在VsCode中或者Trae中&#xff0c;只要是在vscode的基础上修改得到的编辑器&#xff0c;都默认没有勾选自动选择当前文件路径为工作路径&#xff0c;因此需要手动修改工作路径或者设置&#xff0c;否则无法识别相对路径&#xff0c;PyCharm中就不会出现这种问题。 解决方法&…...

解决VSCode每次SSH连接服务器时,都需要下载vscode-server

如下图所示&#xff0c;本地下载或者在服务器终端上运行wget指令获得vscode服务器包 注意&#xff0c;解压完成后&#xff0c;需要修改文件名为你本地vscode的commit ID...

架构-系统工程与信息系统基础

一、系统工程核心知识 1. 系统工程定义 本质&#xff1a;一种组织管理技术&#xff0c;从整体出发分析系统要素&#xff08;组成、结构、信息流、控制机制&#xff09;&#xff0c;追求“整体最优”&#xff0c;借助计算机实现规划、设计、管理、控制的优化。目标&#xff1a…...

矩阵运算和线性代数操作开源库

用于矩阵运算和线性代数操作常用的开源库推荐&#xff0c;涵盖不同编程语言和硬件平台&#xff1a; C/C 库 Eigen 特点&#xff1a;高性能的模板库&#xff0c;支持矩阵/向量运算、线性求解、特征值计算等&#xff0c;无需依赖外部BLAS/LAPACK。 官网&#xff1a;https://eig…...

无标注文本的行业划分(行业分类)算法 —— 无监督或自监督学习

对于无标注文本的行业划分&#xff08;行业分类&#xff09;&#xff0c;属于典型的无监督或自监督学习任务。以下是几种常见的算法方法及实现思路&#xff0c;适用于缺乏标注数据的场景&#xff1a; 一、基于关键词匹配的规则方法 核心思想&#xff1a;通过预定义的行业关键…...

电子病历高质量语料库构建方法与架构项目(计划篇)

电子病历(EMR)作为医疗信息化的重要产物,包含了丰富的医疗信息和临床知识,是辅助临床决策、药物挖掘和医学研究的重要资源。然而,电子病历数据具有非结构化、噪声大、专业性强等特点,如何构建高质量电子病历语料库成为医疗自然语言处理领域的核心挑战。本全计划将从项目背景…...

什么混合检索?在基于大模型的应用开发中,混合检索主要解决什么问题?

混合检索的定义 混合检索(Hybrid Retrieval)是一种结合多种检索技术优势的信息检索方法,旨在通过整合不同检索策略提升检索系统的准确性、召回率和适应性。其核心思想是将基于关键词的检索(如BM25、TF-IDF)与基于语义的检索(如向量检索、深度学习模型)相结合,以应对单…...

优化uniappx页面性能,处理页面滑动卡顿问题

问题&#xff1a;在页面遇到滑动特别卡的情况就是在页面使用了动态样式或者动态类&#xff0c;做切换的时候页面重新渲染导致页面滑动卡顿 解决&#xff1a;把动态样式和动态类做的样式切换改为通过获取元素修改样式属性值 循环修改样式示例 bannerList.forEach((_, index)…...

Yocto meta-toradex-security layer 创建独立数据分区

By Toradex 胡珊逢 简介 Toradex 为其产品使用的软件系统如 Linux 提供了诸多的安全功能&#xff0c;例如 Secure Boot、分区加密、OP-TEE 等&#xff0c;帮助用户应对安全合规。这些功能可以通过在 Yocto Project 中添加由 Toradex 开发的 meta-toradex-securitylayer 被轻松…...

uniapp 安卓离线本地打包,Android Studio生成apk包

第一步&#xff1a;HbuilderX生成本地资源包 下载最新的SDK 下载完后压缩下来是这样的 将HbuilderX生成的复制到这里&#xff0c;替换 Android Studio引入下载的最新文件里的HBuilder-Integrate-AS目录 好&#xff0c;接下来开始修改配置 把你的证书签名&#xff…...

C语言教程(十四):C 语言指针详解

一、指针的基本概念 指针是一个变量&#xff0c;其值为另一个变量的内存地址。简单来说&#xff0c;指针指向了内存中的某个位置&#xff0c;通过指针可以间接访问该位置存储的数据。指针的使用可以让程序更加高效地处理数据&#xff0c;特别是在处理数组、动态内存分配等方面。…...

2025年04月24日Github流行趋势

项目名称&#xff1a;markitdown 项目地址url&#xff1a;https://github.com/microsoft/markitdown项目语言&#xff1a;Python历史star数&#xff1a;53,351今日star数&#xff1a;822项目维护者&#xff1a;afourney, gagb, sugatoray, PetrAPConsulting, l-lumin项目简介&a…...

切割PDF使用python,库PyPDF2

使用 Python 将大型 PDF 文件分割成多个小文件 理解任务 将一个 170M 的 PDF 文件分割成多个 10M 左右的小文件。这在处理大型 PDF 文件时非常有用&#xff0c;例如&#xff1a; 减少单个文件的大小&#xff0c;方便传输或存储分别处理不同的文件部分提高 PDF 处理的效率 选…...

网络IP冲突的成因与解决方案

网络IP冲突的成因与解决方案 一、IP冲突的常见现象与危害二、IP冲突的常见原因三、6种实用解决方案四、预防IP冲突的4个最佳实践五、总结 前言 肝文不易&#xff0c;点个免费的赞和关注&#xff0c;有错误的地方请指出&#xff0c;看个人主页有惊喜。 作者&#xff1a;神的孩子…...

python版本得数独游戏

python版本得数独游戏 游戏说明&#xff1a; 游戏使用9x9数独棋盘&#xff0c;.表示可填写的空格 输入格式为行,列,数值&#xff08;如3,5,7表示第3行第5列填7&#xff09; 系统会自动检查以下内容&#xff1a; 输入格式是否正确 数字是否在1-9范围内 是否修改固定数字 是…...

64位系统上编译32位openh264 x264

在64位系统上要使用i386(32位库)的时候&#xff0c;有些是找不到apt可以安装的版本&#xff0c;所以需要手动编译安装&#xff0c;下面是openh264和x264的编译过程。 默认编译openh264 git clone https://github.com/cisco/openh264make ARCHi386 OSlinux PREFIX/lib/i386-li…...

加深对vector理解OJ题

17. 电话号码的字母组合 - 力扣&#xff08;LeetCode&#xff09; OJ&#xff08;一&#xff09;电话号码的字母组合 思路&#xff1a;这里以引用leetcode里面的一个大佬里面的图 1.这道题中&#xff0c;我们用递归的方法来写。 为了简洁展示&#xff0c;我们举例子”456“&am…...

协作开发攻略:Git全面使用指南 — 第三部分 特殊应用场景

协作开发攻略&#xff1a;Git全面使用指南 — 第三部分 特殊应用场景 Git 是一种分布式版本控制系统&#xff0c;用于跟踪文件和目录的变更。它能帮助开发者有效管理代码版本&#xff0c;支持多人协作开发&#xff0c;方便代码合并与冲突解决&#xff0c;广泛应用于软件开发领域…...

机器学习(9)——随机森林

文章目录 1. 随机森林的基本原理想2. 算法流程2.1. 数据采样&#xff08;Bootstrap&#xff09;&#xff1a;2.2. 构建决策树&#xff1a;2.3. 聚合预测&#xff1a; 3. 随机森林的构建过程3.1. 数据集的随机抽样3.2. 决策树的训练3.3. 树的生长3.4. 多棵树的集成3.5. 输出预测…...

(第三篇)Springcloud之Ribbon负载均衡

一、简介 1、介绍 Spring Cloud Ribbon是Netflix发布的开源项目&#xff0c;是基于Netflix Ribbon实现的一套客户端负载均衡的工具。主要功能是提供客户端的软件负载均衡算法&#xff0c;将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时&…...

Linux并发与竞争:从生活例子到内核实战

Linux并发与竞争&#xff1a;从生活例子到内核实战 一、并发与竞争&#xff1a;多车道公路的交通问题 想象一条四车道的高速公路&#xff08;多核CPU&#xff09;&#xff0c;所有车辆&#xff08;线程/进程&#xff09;都想通过同一个收费站&#xff08;共享资源&#xff09…...

【金仓数据库征文】——金仓数据库:国产数据库的卓越之选

目录 一、金仓数据库的核心技术优势 &#xff08;一&#xff09;强大的事务处理能力 &#xff08;二&#xff09;高度安全 &#xff08;三&#xff09;全面兼容与深度适配 &#xff08;四&#xff09;强大的扩展性 &#xff08;五&#xff09;智能便捷的工具 二、电信行…...

人脸识别考勤系统实现教程:基于Face-Recognition、OpenCV与SQLite

引言 随着人工智能技术的飞速发展&#xff0c;人脸识别技术已广泛应用于安防、金融、教育等多个领域。本文将带领大家利用Python的face-recognition库、OpenCV和SQLite数据库&#xff0c;从零开始构建一个具备异常报警功能的人脸识别考勤系统。该系统能够实时检测视频流中的人…...

Golang 闭包学习

引言 在平常的 Go 语言开发中&#xff0c;常常需要将一段函数逻辑封装起来&#xff0c;异步执行、作为回调传递&#xff0c;甚至保持某些运行时状态。此时&#xff0c;闭包成为一种非常自然的编程手段。它允许我们在函数内部“记住”外部作用域中的变量&#xff0c;从而实现变…...

Trae+DeepSeek学习Python开发MVC框架程序笔记(四):使用sqlite验收用户名和密码

继续通过Trae向DeepSeek发问并修改程序&#xff0c;实现程序运行时生成数据库&#xff0c;用户在系统登录页面输入用户名和密码后&#xff0c;控制器通过模型查询用户数据库表来验证用户名和密码&#xff0c;验证通过后显示登录成功页面&#xff0c;验证失败则显示登录失败页面…...

【mdlib】0 全面介绍 mdlib - Rust 实现的 Markdown 工具集

mdlib 是由开发者 bahdotsh 创建的一个多功能 Markdown 工具集合&#xff0c;包含两个主要组件&#xff1a;一个轻量级 Markdown 解析库和一个功能完善的个人 Wiki 系统。该项目完全采用 Rust 实现&#xff0c;兼具高性能与跨平台特性。 核心组件 Markdown 解析库 特性&#…...

使用Django REST Framework快速开发API接口

以下是使用 Django 和 Django REST Framework (DRF) 开发 API 接口的核心步骤&#xff0c;涵盖模型、迁移、序列化、视图、路由等关键环节&#xff1a; 前言 什么是DRF&#xff1f; Django REST Framework&#xff08;DRF&#xff09; 是基于Django的一个强大且灵活的工具包&…...

Vue3项目中 npm 依赖安装 --save 与 --save-dev 的区别解析

这两个命令的区别如下&#xff1a; bash npm install --save types/crypto-js # 安装到 dependencies&#xff08;生产依赖&#xff09; npm install --save-dev types/crypto-js # 安装到 devDependencies&#xff08;开发依赖&#xff09; 核心区别 依赖分类不同…...

开源模型应用落地-语音合成-MegaTTS3-零样本克隆与多语言生成的突破

一、前言 在人工智能技术飞速发展的今天,文本转语音(TTS)技术正以前所未有的速度改变着人机交互的方式。近日,字节跳动与浙江大学联合推出了一款名为MegaTTS3 的开源TTS模型,再次刷新了行业对高质量语音合成的认知。作为一款轻量化设计的模型,MegaTTS3以仅0.45亿参数 的规…...

connection.cursor() 与 models.objects.filter

在 Django 中操作数据库时&#xff0c;connection.cursor() 和 models.objects.filter 是两种不同的方式&#xff0c;各有特点和适用场景&#xff1a; models.objects.filter (ORM 方式) 特点‌&#xff1a; 基于 Django 的 ORM&#xff08;对象关系映射&#xff09;框架&am…...

深入浅出JavaScript常见设计模式:从原理到实战(1)

深入浅出JavaScript常见设计模式&#xff1a;从原理到实战(1) 设计模式是一种在特定情境下解决软件设计中常见问题的通用方案或模板。在特定的开发场景中使用特定的设计模式&#xff0c;可以提升代码质量&#xff0c;增强代码可读性和可维护性&#xff0c;提高团队开发效率&…...

RCE学习

一、远程代码执行漏洞 1. 远程代码执行的定义 定义&#xff1a;远程代码执行漏洞&#xff08;Remote Code Execute&#xff0c;简称RCE&#xff09;是指程序预留了执行命令或代码的接口并被黑客利用的漏洞。广义上也包括远程命令执行&#xff08;Remote Command Execute&…...

Redis安装及入门应用

应用资料&#xff1a;https://download.csdn.net/download/ly1h1/90685065 1.获取文件&#xff0c;并在该文件下执行cmd 2.输入redis-server-lucifer.exe redis.windows.conf&#xff0c;即可运行redis 3.安装redis客户端软件 4.安装后运行客户端软件&#xff0c;输入链接地址…...