第6.4章:材质数据结构

深入理解材质数据在GPU内存中的组织方式对于优化着色器性能至关重要。本章将详细讲解Surface材质输入参数、GPU内存布局优化以及材质数据的最佳实践。

🎯 学习目标

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

  • Surface材质输入参数的完整结�?- GPU内存布局和对齐规�?- UBO(Uniform Buffer Object)的优化技�?- 材质数据的批处理和实例化
  • 性能优化的最佳实�?

📊 Surface材质输入结构

SurfaceIn标准输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Surface Shader标准输入结构
struct SurfaceIn {
// 顶点位置信息
vec3 worldPos; // 世界空间位置
vec3 viewPos; // 视图空间位置
vec4 clipPos; // 裁剪空间位置
vec2 screenPos; // 屏幕空间位置

// 纹理坐标
vec2 uv; // 主纹理坐�? vec2 uv1; // 第二套UV坐标
vec2 uv2; // 第三套UV坐标

// 法向量信�? vec3 worldNormal; // 世界空间法向�? vec3 viewNormal; // 视图空间法向�?
// 切线空间(用于法线贴图)
vec4 worldTangent; // 世界空间切线(w分量为handedness�? vec3 worldBinormal; // 世界空间副切�?
// 顶点颜色
vec4 color; // 顶点颜色

// 雾效相关
float fogFactor; // 雾效因子
};

SurfaceOut标准输出

1
2
3
4
5
6
7
8
9
10
11
12
// Surface Shader标准输出结构
struct SurfaceOut {
// 基础材质属�? vec4 albedo; // 基础颜色(RGB�? 透明度(A�? vec3 normal; // 世界空间法向�? vec3 emissive; // 自发光颜�?
// PBR材质属�? float metallic; // 金属�?[0,1]
float roughness; // 粗糙�?[0,1]
float ao; // 环境遮蔽 [0,1]

// 高级属�? float specularIntensity; // 镜面反射强度
vec3 anisotropy; // 各向异性参�? float clearCoat; // 清漆层强�? float clearCoatRoughness; // 清漆层粗糙度

// 子表面散�? vec3 subsurface; // 子表面散射颜�? float thickness; // 厚度信息
};

🔧 GPU内存布局基础

标量类型内存布局

1
2
3
4
5
6
// 基本标量类型的内存布局
layout(std140) uniform ScalarTypes {
float f; // 4字节�?字节对齐
int i; // 4字节�?字节对齐
bool b; // 4字节�?字节对齐(作为int存储�? uint u; // 4字节�?字节对齐
}; // 总共16字节

向量类型内存布局

1
2
3
4
5
6
7
8
9
// 向量类型的内存布局
layout(std140) uniform VectorTypes {
vec2 v2; // 8字节�?字节对齐
vec3 v3; // 12字节�?6字节对齐
vec4 v4; // 16字节�?6字节对齐
ivec2 iv2; // 8字节�?字节对齐
ivec3 iv3; // 12字节�?6字节对齐
ivec4 iv4; // 16字节�?6字节对齐
}; // 注意:vec3实际占用16字节空间

矩阵类型内存布局

1
2
3
// 矩阵类型的内存布局
layout(std140) uniform MatrixTypes {
mat2 m2; // 32字节�?个vec4,列主序�? mat3 m3; // 48字节�?个vec4,列主序�? mat4 m4; // 64字节�?个vec4,列主序�?}; // 矩阵总是按列主序存储

数组类型内存布局

1
2
3
// 数组类型的内存布局
layout(std140) uniform ArrayTypes {
float floatArray[4]; // 64字节(每个元�?6字节对齐�? vec2 vec2Array[4]; // 64字节(每个元�?6字节对齐�? vec3 vec3Array[4]; // 64字节(每个元�?6字节对齐�? vec4 vec4Array[4]; // 64字节(每个元�?6字节对齐�?}; // 数组元素总是16字节对齐

📋 标准材质数据结构

基础材质属�?

1
2
3
4
5
6
7
// 基础材质属性结构(64字节对齐�?layout(std140) uniform BaseMaterial {
vec4 albedoFactor; // 16字节:基础颜色因子
vec4 emissiveFactor; // 16字节:自发光因子
vec4 specularFactor; // 16字节:镜面反射因�? float metallicFactor; // 4字节:金属度因子
float roughnessFactor; // 4字节:粗糙度因子
float normalScale; // 4字节:法线强�? float aoStrength; // 4字节:AO强度
}; // 总共�?4字节

PBR材质扩展

1
2
3
4
5
6
7
8
9
10
11
12
// PBR材质扩展属性(96字节对齐�?layout(std140) uniform PBRMaterial {
// 基础属性(64字节�? vec4 albedoFactor;
vec4 emissiveFactor;
vec4 specularFactor;
float metallicFactor;
float roughnessFactor;
float normalScale;
float aoStrength;

// 扩展属性(32字节�? float clearCoatFactor; // 4字节:清漆层强度
float clearCoatRoughness; // 4字节:清漆层粗糙�? vec2 anisotropyFactor; // 8字节:各向异性因�? vec3 subsurfaceColor; // 12字节:子表面散射颜色
float thicknessFactor; // 4字节:厚度因�?}; // 总共�?6字节

纹理索引管理

1
2
3
4
5
6
7
// 纹理索引结构�?2字节对齐�?layout(std140) uniform TextureIndices {
int albedoTexture; // 4字节:基础颜色纹理索引
int normalTexture; // 4字节:法线纹理索�? int metallicTexture; // 4字节:金属度纹理索引
int roughnessTexture; // 4字节:粗糙度纹理索引
int aoTexture; // 4字节:AO纹理索引
int emissiveTexture; // 4字节:自发光纹理索引
int heightTexture; // 4字节:高度纹理索�? int maskTexture; // 4字节:遮罩纹理索�?}; // 总共�?2字节

🚀 材质数据优化技�?

1. 内存对齐优化

1
2
3
4
5
6
7
8
9
// �?低效的内存布局
layout(std140) uniform IneffectiveLayout {
float param1; // 4字节
vec3 color1; // 16字节(浪�?字节�? float param2; // 4字节
vec3 color2; // 16字节(浪�?字节�?}; // 总共�?0字节,浪�?字节

// �?高效的内存布局
layout(std140) uniform EffectiveLayout {
vec4 color1; // 16字节(param1作为w分量�? vec4 color2; // 16字节(param2作为w分量�?}; // 总共�?2字节,节�?字节

2. 数据打包技�?

1
2
3
4
5
6
7
8
9
10
11
12
// 将多个标量打包成向量
struct PackedMaterialData {
vec4 factors; // x:metallic, y:roughness, z:ao, w:normalScale
vec4 coefficients; // x:clearCoat, y:clearCoatRough, z:thickness, w:unused
vec4 flags; // x:hasNormal, y:hasAO, z:hasEmissive, w:hasHeight
};

// 访问函数
float getMetallic(PackedMaterialData data) { return data.factors.x; }
float getRoughness(PackedMaterialData data) { return data.factors.y; }
float getAO(PackedMaterialData data) { return data.factors.z; }
float getNormalScale(PackedMaterialData data) { return data.factors.w; }

3. 条件编译优化

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
// 使用宏定义减少内存使�?#define ENABLE_CLEAR_COAT 1
#define ENABLE_SUBSURFACE 0
#define ENABLE_ANISOTROPY 0

layout(std140) uniform OptimizedMaterial {
vec4 albedoFactor;
vec4 emissiveFactor;
float metallicFactor;
float roughnessFactor;
float normalScale;
float aoStrength;

#if ENABLE_CLEAR_COAT
float clearCoatFactor;
float clearCoatRoughness;
#endif

#if ENABLE_SUBSURFACE
vec3 subsurfaceColor;
float thicknessFactor;
#endif

#if ENABLE_ANISOTROPY
vec2 anisotropyFactor;
#endif
};

🎯 实例化和批处�?

材质实例化数�?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 实例化材质数据(支持多个对象共享材质�?layout(std140) uniform InstancedMaterials {
// 每个实例的材质参�? vec4 instanceColors[MAX_INSTANCES]; // 每个实例的颜色调�? vec4 instanceFactors[MAX_INSTANCES]; // 每个实例的因子(metallic, roughness, ao, scale�? vec4 instanceUVTransform[MAX_INSTANCES]; // 每个实例的UV变换(offset.xy, scale.xy�?};

// 在着色器中访问实例数�?void surf(in SurfaceIn In, inout SurfaceOut Out) {
int instanceID = gl_InstanceID;

vec4 baseColor = texture(albedoTexture, In.uv) * instanceColors[instanceID];
vec4 factors = instanceFactors[instanceID];

Out.albedo = baseColor;
Out.metallic = factors.x;
Out.roughness = factors.y;
Out.ao = factors.z;
}

材质批处理系�?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 批处理材质数据结�?struct BatchMaterialData {
vec4 albedoFactor;
vec4 emissiveFactor;
float metallicFactor;
float roughnessFactor;
float normalScale;
float aoStrength;
};

layout(std140) uniform MaterialBatch {
BatchMaterialData materials[BATCH_SIZE]; // 批量材质数据
int materialIndices[MAX_OBJECTS]; // 对象到材质的索引映射
};

// 批处理访问函�?BatchMaterialData getMaterial(int objectID) {
int materialIndex = materialIndices[objectID];
return materials[materialIndex];
}

📱 移动端优化策�?

1. 精度优化

1
2
3
4
5
6
7
8
// 移动端精度优�?precision mediump float;  // 全局设置中等精度

// 选择性使用高精度
layout(std140) uniform MobileMaterial {
lowp vec4 albedoFactor; // 颜色可以用低精度
mediump float metallicFactor; // 材质参数用中等精�? mediump float roughnessFactor;
highp vec3 worldPosition; // 位置需要高精度
};

2. 纹理通道复用

1
2
3
4
5
6
7
8
9
10
11
// 将多个材质属性打包到一张纹理中
// R: Metallic, G: Roughness, B: AO, A: Height
uniform sampler2D materialPropertiesTexture;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
vec4 properties = texture(materialPropertiesTexture, In.uv);

Out.metallic = properties.r;
Out.roughness = properties.g;
Out.ao = properties.b;
float height = properties.a; // 可用于视差映�?}

3. LOD材质系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 基于距离的材质LOD
uniform float cameraDistance;
uniform float lodDistances[3]; // LOD距离阈�?
int calculateMaterialLOD() {
if (cameraDistance < lodDistances[0]) return 0; // 高质�? else if (cameraDistance < lodDistances[1]) return 1; // 中等质量
else if (cameraDistance < lodDistances[2]) return 2; // 低质�? else return 3; // 最低质�?}

void surf(in SurfaceIn In, inout SurfaceOut Out) {
int lod = calculateMaterialLOD();

switch(lod) {
case 0: // 高质量:完整PBR
// 完整的材质计�? break;
case 1: // 中等质量:简化PBR
// 省略一些次要效�? break;
case 2: // 低质量:基础光照
// 只保留基本漫反射
break;
case 3: // 最低质量:无光�? // 只使用基础颜色
break;
}
}

🔍 调试和性能分析

材质数据可视�?

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
// 材质属性可视化调试
#define DEBUG_MODE_ALBEDO 0
#define DEBUG_MODE_METALLIC 1
#define DEBUG_MODE_ROUGHNESS 2
#define DEBUG_MODE_NORMAL 3
#define DEBUG_MODE_AO 4
#define DEBUG_MODE_EMISSIVE 5

uniform int debugMode;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 正常材质计算...

// 调试输出
switch(debugMode) {
case DEBUG_MODE_ALBEDO:
Out.albedo = vec4(Out.albedo.rgb, 1.0);
break;
case DEBUG_MODE_METALLIC:
Out.albedo = vec4(vec3(Out.metallic), 1.0);
break;
case DEBUG_MODE_ROUGHNESS:
Out.albedo = vec4(vec3(Out.roughness), 1.0);
break;
case DEBUG_MODE_NORMAL:
Out.albedo = vec4(Out.normal * 0.5 + 0.5, 1.0);
break;
case DEBUG_MODE_AO:
Out.albedo = vec4(vec3(Out.ao), 1.0);
break;
case DEBUG_MODE_EMISSIVE:
Out.albedo = vec4(Out.emissive, 1.0);
break;
}
}

性能监控

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
// TypeScript中的材质性能监控
class MaterialPerformanceMonitor {
private materialCount: number = 0;
private textureCount: number = 0;
private uniformBufferSize: number = 0;

analyzeScene(scene: Node) {
const renderers = scene.getComponentsInChildren(MeshRenderer);

renderers.forEach(renderer => {
const material = renderer.material;
this.analyzeMaterial(material);
});

this.reportStatistics();
}

private analyzeMaterial(material: Material) {
this.materialCount++;

// 分析纹理使用
const passes = material.passes;
passes.forEach(pass => {
const textures = pass.getBinding('mainTexture');
if (textures) this.textureCount++;
});

// 分析Uniform Buffer大小
const properties = material.passes[0].properties;
this.uniformBufferSize += this.calculateUBOSize(properties);
}

private calculateUBOSize(properties: any): number {
let size = 0;
for (const prop in properties) {
const value = properties[prop];
if (typeof value === 'number') size += 4;
else if (value instanceof Vec2) size += 8;
else if (value instanceof Vec3) size += 16; // 对齐�?6字节
else if (value instanceof Vec4) size += 16;
else if (value instanceof Mat4) size += 64;
}
return size;
}

private reportStatistics() {
console.log(`Material Performance Report:
- Total Materials: ${this.materialCount}
- Total Textures: ${this.textureCount}
- Total UBO Size: ${this.uniformBufferSize} bytes
- Average UBO Size: ${this.uniformBufferSize / this.materialCount} bytes/material`);
}
}

🎨 高级材质数据技�?

1. 动态材质参�?

1
2
3
4
5
6
7
8
9
10
11
// 支持动画的材质参�?layout(std140) uniform AnimatedMaterial {
vec4 baseParams; // 基础参数
vec4 animationAmplitude; // 动画幅度
vec4 animationFrequency; // 动画频率
vec4 animationPhase; // 动画相位
float time; // 全局时间
};

vec4 getAnimatedParameter(vec4 base, vec4 amplitude, vec4 frequency, vec4 phase) {
return base + amplitude * sin(frequency * time + phase);
}

2. 材质变体系统

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
// 材质变体管理
#define VARIANT_STANDARD 0
#define VARIANT_TRANSPARENT 1
#define VARIANT_CUTOUT 2
#define VARIANT_DOUBLE_SIDED 3

uniform int materialVariant;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 基础材质计算
calculateBaseMaterial(In, Out);

// 根据变体调整
switch(materialVariant) {
case VARIANT_TRANSPARENT:
Out.albedo.a *= transparencyFactor;
break;
case VARIANT_CUTOUT:
if (Out.albedo.a < cutoffThreshold) discard;
Out.albedo.a = 1.0;
break;
case VARIANT_DOUBLE_SIDED:
// 双面渲染特殊处理
if (!gl_FrontFacing) Out.normal = -Out.normal;
break;
}
}

📖 本章总结

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

  • �?材质数据结构:SurfaceIn/SurfaceOut的完整定�?- �?GPU内存布局:标量、向量、矩阵、数组的对齐规则
  • �?**内存优化技�?*:数据打包、对齐优化、条件编�?- �?**实例化技�?*:批处理和实例化材质数据管理
  • �?**移动端优�?*:精度控制、纹理复用、LOD系统
  • �?调试工具:可视化调试和性能监控

🚀 下一步学�?

完成了第6章光照模型与材质的学习后,建议继续学习:

👉 �?章:宏定义与函数重映射

💡 实践练习

  1. 基础练习:设计一个内存对齐优化的材质结构
  2. 进阶练习:实现材质实例化渲染系统
  3. 高级练习:创建完整的材质LOD管理系统

*参考资�?

系列导航