�?.3章:自定义光照函�?

在掌握了经典光照模型和PBR理论后,我们需要学习如何创建自定义光照函数来实现特殊的视觉效果。本章将深入探讨光照函数的数学基础,并实现各种高级光照效果�?

🎯 学习目标

通过本章学习,你将掌握:

  • 光照函数的数学理论基础
  • 自定义BRDF模型的实现方�?- 子表面散射(SSS)的原理与实�?- 各向异性反射的计算技�?- 特殊材质效果(丝绸、毛发、皮肤等)的实现

📐 光照函数数学基础

BRDF的基本形�?

双向反射分布函数的通用形式�?

1
2
3
4
5
6
// BRDF基本方程
// fr(wi, wo) = 反射�?入射光的比例
vec3 BRDF(vec3 lightDir, vec3 viewDir, vec3 normal, MaterialProperties material) {
// lightDir: 入射光方�? // viewDir: 视线方向
// normal: 表面法向�? // material: 材质属�? return diffuse + specular;
}

能量守恒原理

任何BRDF都必须满足能量守恒:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 能量守恒检�?float energyConservation(vec3 brdf, float NdotL) {
// 在半球上积分BRDF * cos(theta) * sin(theta) <= 1
return dot(brdf * NdotL, vec3(1.0)) <= 1.0;
}

// 实用的归一化方�?vec3 normalizeBRDF(vec3 diffuse, vec3 specular) {
vec3 total = diffuse + specular;
float maxComponent = max(max(total.r, total.g), total.b);
if (maxComponent > 1.0) {
return total / maxComponent;
}
return total;
}

互易性原�?

BRDF必须满足互易性:fr(wi, wo) = fr(wo, wi)

1
2
3
4
5
6
// 验证互易性的测试函数
bool testReciprocity(vec3 lightDir, vec3 viewDir, vec3 normal) {
vec3 brdf1 = BRDF(lightDir, viewDir, normal, material);
vec3 brdf2 = BRDF(viewDir, lightDir, normal, material);
vec3 diff = abs(brdf1 - brdf2);
return all(lessThan(diff, vec3(0.001))); // 允许小的数值误�?}

🎨 自定义BRDF模型

1. Oren-Nayar漫反射模�?

适用于粗糙表面的漫反射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Oren-Nayar漫反射模�?vec3 OrenNayarDiffuse(vec3 albedo, vec3 normal, vec3 lightDir, vec3 viewDir, float roughness) {
float sigma = roughness * roughness;
float sigma2 = sigma * sigma;

float NdotL = dot(normal, lightDir);
float NdotV = dot(normal, viewDir);

float s = dot(lightDir, viewDir) - NdotL * NdotV;
float t = mix(1.0, max(NdotL, NdotV), step(0.0, s));

float A = 1.0 - 0.5 * sigma2 / (sigma2 + 0.33);
float B = 0.45 * sigma2 / (sigma2 + 0.09);

return albedo / PI * (A + B * s / t) * max(NdotL, 0.0);
}

2. Ward各向异性模�?

适用于拉丝金属等各向异性材质:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Ward各向异性BRDF
vec3 WardAnisotropic(vec3 normal, vec3 tangent, vec3 lightDir, vec3 viewDir,
float alphaX, float alphaY, vec3 specularColor) {
vec3 H = normalize(lightDir + viewDir);
vec3 bitangent = cross(normal, tangent);

float NdotL = max(dot(normal, lightDir), 0.0);
float NdotV = max(dot(normal, viewDir), 0.0);
float NdotH = max(dot(normal, H), 0.0);
float VdotH = max(dot(viewDir, H), 0.0);

float HdotT = dot(H, tangent);
float HdotB = dot(H, bitangent);

float exponent = -2.0 * ((HdotT * HdotT) / (alphaX * alphaX) +
(HdotB * HdotB) / (alphaY * alphaY)) / (NdotH * NdotH);

float D = exp(exponent) / (4.0 * PI * alphaX * alphaY * NdotH * NdotH * NdotH * NdotH);

vec3 specular = specularColor * D / (4.0 * NdotL * NdotV);

return specular * NdotL;
}

3. Minnaert月球模型

模拟天鹅绒等特殊材质�?

1
2
3
4
5
6
7
// Minnaert月球模型
vec3 MinnaertDiffuse(vec3 albedo, vec3 normal, vec3 lightDir, vec3 viewDir, float k) {
float NdotL = max(dot(normal, lightDir), 0.0);
float NdotV = max(dot(normal, viewDir), 0.0);

return albedo * pow(NdotL * NdotV, k - 1.0) * NdotL;
}

🌟 子表面散射(SSS�?

SSS基础理论

子表面散射模拟光线在半透明材质内部的散射:

1
2
3
4
5
6
7
8
9
// 简化的子表面散射模�?vec3 SubsurfaceScattering(vec3 normal, vec3 lightDir, vec3 viewDir, 
vec3 thickness, vec3 scatterColor, float power) {
// 透射光计�? vec3 H = normalize(lightDir + normal * 0.6); // 稍微偏移法向�? float VdotH = pow(clamp(dot(viewDir, -H), 0.0, 1.0), power);

// 厚度衰减
float attenuation = exp(-length(thickness) * 2.0);

return scatterColor * VdotH * attenuation;
}

更高级的SSS实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 基于屏幕空间的近似SSS
vec3 ScreenSpaceSSS(sampler2D sceneTexture, vec2 uv, vec3 normal,
float thickness, vec3 sssColor, float sssStrength) {
vec3 blurredColor = vec3(0.0);
float totalWeight = 0.0;

// 高斯模糊采样
const int samples = 5;
const float offsets[5] = float[](0.0, 1.411, 3.294, 5.176, 7.059);
const float weights[5] = float[](0.1964, 0.2969, 0.0918, 0.0102, 0.0004);

for (int i = 0; i < samples; i++) {
vec2 offset = normal.xy * offsets[i] * thickness * 0.01;
blurredColor += texture(sceneTexture, uv + offset).rgb * weights[i];
blurredColor += texture(sceneTexture, uv - offset).rgb * weights[i];
totalWeight += weights[i] * 2.0;
}

blurredColor /= totalWeight;
return mix(texture(sceneTexture, uv).rgb, blurredColor * sssColor, sssStrength);
}

皮肤SSS效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 专门的皮肤SSS模型
vec3 SkinSSS(vec3 normal, vec3 lightDir, vec3 viewDir, vec3 albedo,
float thickness, vec3 sssProfile) {
float NdotL = dot(normal, lightDir);

// 正面光照
vec3 frontLighting = max(NdotL, 0.0) * albedo;

// 背面光照(透射�? float backLight = max(dot(-normal, lightDir), 0.0);
vec3 backLighting = backLight * sssProfile * thickness;

// 边缘散射
float rim = 1.0 - max(dot(normal, viewDir), 0.0);
vec3 rimLighting = pow(rim, 2.0) * sssProfile * 0.5;

return frontLighting + backLighting + rimLighting;
}

🧵 毛发和纤维着�?

Kajiya-Kay毛发模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Kajiya-Kay毛发着色模�?vec3 KajiyaKayHair(vec3 tangent, vec3 lightDir, vec3 viewDir, 
vec3 hairColor, float roughness) {
// 计算切线方向的反�? float TdotL = dot(tangent, lightDir);
float TdotV = dot(tangent, viewDir);

float sinTL = sqrt(1.0 - TdotL * TdotL);
float sinTV = sqrt(1.0 - TdotV * TdotV);

// 漫反射项
float diffuse = sinTL;

// 镜面反射�? float specular = pow(max(0.0, -TdotL * TdotV + sinTL * sinTV), 1.0 / roughness);

return hairColor * (diffuse + specular * 0.3);
}

Marschner毛发模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 更精确的Marschner毛发模型
struct HairProperties {
float longitudinalRoughness;
float azimuthalRoughness;
float refractiveIndex;
vec3 absorption;
};

vec3 MarschnerHair(vec3 tangent, vec3 normal, vec3 lightDir, vec3 viewDir,
vec3 hairColor, HairProperties props) {
// R:表面反�? // TT:透射-透射
// TRT:透射-反射-透射

vec3 finalColor = vec3(0.0);

// 简化的R项(表面反射�? vec3 H = normalize(lightDir + viewDir);
float HdotT = dot(H, tangent);
float fresnel = pow(1.0 - abs(HdotT), 5.0);
finalColor += hairColor * fresnel * 0.2;

// 简化的TT项(一次透射�? float transmission = pow(max(0.0, -dot(lightDir, tangent) * dot(viewDir, tangent)), 2.0);
finalColor += hairColor * transmission * 0.5;

// 简化的TRT项(内部反射�? float highlight = pow(max(0.0, dot(reflect(-lightDir, normal), viewDir)), 20.0);
finalColor += vec3(1.0, 0.8, 0.6) * highlight * 0.3;

return finalColor;
}

🌈 特殊材质效果

1. 丝绸材质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 丝绸着色器
vec3 SilkShading(vec3 normal, vec3 tangent, vec3 lightDir, vec3 viewDir,
vec3 silkColor, float anisotropy) {
// 各向异性反�? vec3 anisotropicReflection = WardAnisotropic(normal, tangent, lightDir, viewDir,
0.1, 0.3 * anisotropy, silkColor);

// 次表面散�? vec3 sss = SubsurfaceScattering(normal, lightDir, viewDir,
vec3(0.1), silkColor * 0.3, 2.0);

// 边缘�? float rim = pow(1.0 - max(dot(normal, viewDir), 0.0), 3.0);
vec3 rimLighting = rim * silkColor * 0.5;

return anisotropicReflection + sss + rimLighting;
}

2. 天鹅绒材�?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 天鹅绒着色器
vec3 VelvetShading(vec3 normal, vec3 lightDir, vec3 viewDir,
vec3 velvetColor, float fuzziness) {
float NdotL = max(dot(normal, lightDir), 0.0);
float NdotV = max(dot(normal, viewDir), 0.0);

// 逆向背光效果
float backScatter = pow(max(0.0, -dot(lightDir, viewDir)), 2.0);

// 边缘模糊效果
float fuzz = pow(1.0 - NdotV, fuzziness);

// Minnaert漫反�? vec3 diffuse = MinnaertDiffuse(velvetColor, normal, lightDir, viewDir, 0.5);

return diffuse + velvetColor * (backScatter + fuzz) * 0.5;
}

3. 车漆材质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 汽车油漆着色器
vec3 CarPaintShading(vec3 normal, vec3 lightDir, vec3 viewDir,
vec3 baseColor, vec3 flakeColor, float flakeDensity) {
// 基础PBR计算
vec3 basePBR = calculatePBR(baseColor, 0.05, 0.1, normal, viewDir, lightDir, vec3(1.0));

// 金属片效�? vec3 H = normalize(lightDir + viewDir);
float HdotN = max(dot(H, normal), 0.0);

// 模拟金属片的随机分布
float noise = fract(sin(dot(normal.xy, vec2(12.9898, 78.233))) * 43758.5453);
float flakes = step(1.0 - flakeDensity, noise) * pow(HdotN, 50.0);

// 清漆�? float clearCoat = pow(max(dot(reflect(-viewDir, normal), lightDir), 0.0), 100.0);

return basePBR + flakeColor * flakes * 0.3 + clearCoat * 0.1;
}

🔧 完整的自定义光照系统

可配置的光照管理�?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
CCEffect %{
techniques:
- name: custom-lighting
passes:
- vert: custom-vs:vert
frag: custom-fs:frag
properties: &props
mainTexture: { value: white }
lightingModel: { value: 0, range: [0, 10] }
customParam1: { value: 1.0, range: [0, 2] }
customParam2: { value: 1.0, range: [0, 2] }
customParam3: { value: 1.0, range: [0, 2] }
customColor: { value: [1, 1, 1, 1], editor: { type: color } }
}%

CCProgram custom-vs %{
#include <surface-vertex>
}%

CCProgram custom-fs %{
#include <surface-fragment>

uniform CustomProperties {
int lightingModel;
float customParam1;
float customParam2;
float customParam3;
vec4 customColor;
};
uniform sampler2D mainTexture;

// 包含所有自定义光照函数...

vec3 calculateCustomLighting(int model, SurfaceIn In, vec3 albedo, vec3 normal) {
vec3 viewDir = normalize(cc_cameraPos.xyz - In.worldPos);
vec3 lightDir = normalize(-cc_mainLitDir.xyz);
vec3 lightColor = cc_mainLitColor.rgb * cc_mainLitColor.a;

switch(model) {
case 0: // Standard PBR
return calculatePBR(albedo, customParam1, customParam2, normal, viewDir, lightDir, lightColor);

case 1: // Oren-Nayar
return OrenNayarDiffuse(albedo, normal, lightDir, viewDir, customParam1);

case 2: // Ward Anisotropic
return WardAnisotropic(normal, In.worldTangent.xyz, lightDir, viewDir,
customParam1, customParam2, customColor.rgb);

case 3: // Subsurface Scattering
return SkinSSS(normal, lightDir, viewDir, albedo, customParam1, customColor.rgb);

case 4: // Hair Kajiya-Kay
return KajiyaKayHair(In.worldTangent.xyz, lightDir, viewDir, albedo, customParam1);

case 5: // Silk
return SilkShading(normal, In.worldTangent.xyz, lightDir, viewDir, albedo, customParam1);

case 6: // Velvet
return VelvetShading(normal, lightDir, viewDir, albedo, customParam1);

case 7: // Car Paint
return CarPaintShading(normal, lightDir, viewDir, albedo, customColor.rgb, customParam1);

default:
return calculatePBR(albedo, 0.0, 0.5, normal, viewDir, lightDir, lightColor);
}
}

void surf (in SurfaceIn In, inout SurfaceOut Out) {
vec4 albedo = texture(mainTexture, In.uv);
vec3 normal = normalize(In.worldNormal);

vec3 finalColor = calculateCustomLighting(lightingModel, In, albedo.rgb, normal);

Out.albedo = vec4(finalColor, albedo.a);
Out.normal = normal;
}
}%

🎯 性能优化技�?

1. 预计算查找表

1
2
3
4
5
6
// 预计算复杂函数的LUT
uniform sampler2D brdfLUT; // 预计算的BRDF查找�?
vec3 sampleBRDFLUT(float NdotV, float roughness) {
vec2 uv = vec2(NdotV, roughness);
return texture(brdfLUT, uv).rgb;
}

2. 近似算法

1
2
3
4
5
6
7
8
9
10
11
// 快速近似函�?float fastPow(float x, float power) {
// 使用exp2和log2的快速近�? return exp2(power * log2(x));
}

float fastAtan2(float y, float x) {
// 快速atan2近似
float absY = abs(y);
float angle = (x >= 0.0) ? (PI/4.0 * y/x - PI/4.0 * y/x * (absY - x) / (absY + x)) :
(3.0*PI/4.0 - PI/4.0 * y/x * (absY + x) / (absY - x));
return angle;
}

3. LOD系统

1
2
3
4
5
6
7
8
9
10
// 基于复杂度的LOD
float calculateComplexityLOD(float distance, int lightingModel) {
float baseLOD = distance / 100.0;

// 不同光照模型的复杂度权重
float complexityWeight = 1.0;
if (lightingModel >= 3) complexityWeight = 2.0; // SSS等复杂模�? if (lightingModel >= 7) complexityWeight = 3.0; // 最复杂模型

return baseLOD * complexityWeight;
}

📖 本章总结

通过本章学习,我们深入掌握了�?

  • �?BRDF数学基础:能量守恒、互易性等基本原理
  • �?**自定义光照模�?*:Oren-Nayar、Ward、Minnaert等特殊模�?- �?**子表面散�?*:SSS理论与实现技�?- �?特殊材质:毛发、丝绸、天鹅绒等材质的着色方�?- �?系统集成:可配置的自定义光照系统
  • �?性能优化:LUT、近似算法、LOD等优化技�?

🚀 下一步学�?

掌握了自定义光照函数后,建议继续学习�?
👉 �?.4章:材质数据结构

💡 实践练习

  1. 基础练习:实现一个Oren-Nayar漫反射材�?2. 进阶练习:创建简单的子表面散射效�?3. 高级练习:实现完整的毛发着色系�?

*参考资�?

系列导航