第5.1章:Surface Shader详解

Surface Shader是Cocos Creator提供的高级着色器功能,它简化了复杂光照模型的实现,让开发者能够专注于表面材质的定义,而不需要处理复杂的光照计算。本章将详细介绍Surface Shader的原理、使用方法和高级技巧。

🎯 学习目标

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

  • Surface Shader的核心概念与架构
  • 如何编写和使用Surface Shader
  • 光照模型的自定义和扩展
  • Surface Shader的高级特性和优化
  • 实际项目中的应用案例
  • 调试和性能调优技巧

💡 Surface Shader架构

核心概念

Surface Shader将着色器分为两个主要部分:

  • Surface Function: 定义表面材质属性
  • Lighting Model: 光照计算
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
graph TD
A[Surface Shader] --> B[Surface Function]
A --> C[Lighting Model]

B --> D[Albedo]
B --> E[Normal]
B --> F[Metallic]
B --> G[Roughness]
B --> H[Emission]
B --> I[Alpha]

C --> J[Lambert]
C --> K[BlinnPhong]
C --> L[Standard PBR]
C --> M[Custom Model]

D --> N[Final Color]
E --> N
F --> N
G --> N
H --> N
I --> N
J --> N
K --> N
L --> N
M --> N

与传统着色器的对比

特性传统着色器Surface Shader
复杂度需要完整实现只需定义表面属性
光照计算手动实现自动处理
多光源支持复杂实现内置支持
阴影处理需要额外代码自动处理
维护性难以维护易于维护
性能控制完全控制优化良好

🔧 基础Surface Shader

简单的Surface Shader

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
80
81
82
83
84
CCEffect %{
techniques:
- name: opaque
passes:
- vert: surface-vs:vert
frag: surface-fs:frag
properties:
mainColor: { value: [1, 1, 1, 1], editor: { type: color } }
mainTexture: { value: white }
migration:
properties:
mainColor: { formerlySerializedAs: tintColor }
}%

CCProgram surface-vs %{
precision highp float;
#include <builtin/uniforms/cc-global>
#include <builtin/uniforms/cc-local>
#include <common/common-vs>
#include <builtin/uniforms/cc-shadow>

in vec3 a_position;
in vec3 a_normal;
in vec2 a_texCoord;

out vec3 v_position;
out vec3 v_normal;
out vec2 v_uv;

vec4 vert () {
StandardVertInput In;
In.position = a_position;
In.normal = a_normal;
In.texCoord = a_texCoord;

StandardVertOutput Out = CCVertInput(In);

v_position = Out.worldPos;
v_normal = Out.worldNormal;
v_uv = Out.texCoord;

return Out.clipPos;
}
}%

CCProgram surface-fs %{
precision mediump float;
#include <builtin/uniforms/cc-global>
#include <common/common-fs>
#include <common/lighting/functions>

in vec3 v_position;
in vec3 v_normal;
in vec2 v_uv;

uniform sampler2D mainTexture;
uniform Constants {
vec4 mainColor;
};

// Surface函数:定义表面属性
void surf (out StandardSurface s) {
// 采样贴图颜色
vec4 texColor = texture(mainTexture, v_uv);

// 设置表面属性
s.albedo = texColor.rgb * mainColor.rgb;
s.alpha = texColor.a * mainColor.a;
s.metallic = 0.0;
s.roughness = 0.5;
s.normal = v_normal;
s.emissive = vec3(0.0);
s.occlusion = 1.0;
}

vec4 frag () {
StandardSurface s;
surf(s);

// 应用标准光照模型
vec4 color = CCStandardSurfaceShading(s);
return CCFragOutput(color);
}
}%

Surface数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// StandardSurface结构体定义
struct StandardSurface {
vec3 albedo; // 漫反射颜色/基础颜色
vec3 normal; // 世界空间法线
vec3 emissive; // 自发光
float metallic; // 金属度 [0,1]
float roughness; // 粗糙度 [0,1]
float occlusion; // 环境遮蔽 [0,1]
float alpha; // 透明度 [0,1]
vec3 position; // 世界空间位置(自动计算)
};

// 高级Surface属性(可选)
struct AdvancedSurface {
vec3 specular; // 镜面反射颜色(非金属流工作流)
float clearCoat; // 清漆强度
float clearCoatRoughness; // 清漆粗糙度
vec3 subsurface; // 次表面散射颜色
float transmission; // 透射强度
float thickness; // 厚度(用于透射)
float anisotropy; // 各向异性强度
};

🎨 自定义光照模型

卡通风格光照

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
// 自定义卡通光照模型
vec3 ToonLighting(StandardSurface s, vec3 lightDir, vec3 lightColor, vec3 viewDir) {
// 计算光照角度
float NdotL = dot(s.normal, lightDir);

// 卡通风格的光照分级
float toonFactor;
if (NdotL > 0.8) {
toonFactor = 1.0;
} else if (NdotL > 0.4) {
toonFactor = 0.6;
} else if (NdotL > 0.0) {
toonFactor = 0.3;
} else {
toonFactor = 0.1;
}

// 计算漫反射
vec3 diffuse = s.albedo * lightColor * toonFactor;

// 简化的镜面反射
vec3 halfVector = normalize(lightDir + viewDir);
float specular = pow(max(0.0, dot(s.normal, halfVector)), 32.0);
specular = step(0.5, specular) * 0.8; // 卡通风格高光

return diffuse + vec3(specular);
}

// 应用自定义光照
vec4 frag () {
StandardSurface s;
surf(s);

// 获取光源信息
vec3 lightDir = normalize(cc_mainLitDir.xyz);
vec3 lightColor = cc_mainLitColor.rgb * cc_mainLitColor.w;
vec3 viewDir = normalize(cc_cameraPos.xyz - v_position);

// 使用自定义光照模型
vec3 finalColor = ToonLighting(s, lightDir, lightColor, viewDir);

// 添加环境光
finalColor += s.albedo * cc_ambientSky.rgb * s.occlusion;

// 添加自发光
finalColor += s.emissive;

return vec4(finalColor, s.alpha);
}

多种光照模型切换

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
// 光照模型枚举
#define LIGHTING_LAMBERT 0
#define LIGHTING_BLINN_PHONG 1
#define LIGHTING_PBR 2
#define LIGHTING_TOON 3
#define LIGHTING_CUSTOM 4

// 在Properties中定义
uniform Constants {
vec4 mainColor;
int lightingModel;
float toonSteps;
float rimPower;
};

// 统一的光照计算函数
vec3 CalculateLighting(StandardSurface s, vec3 lightDir, vec3 lightColor, vec3 viewDir) {
#if LIGHTING_MODEL == LIGHTING_LAMBERT
return LambertLighting(s, lightDir, lightColor);
#elif LIGHTING_MODEL == LIGHTING_BLINN_PHONG
return BlinnPhongLighting(s, lightDir, lightColor, viewDir);
#elif LIGHTING_MODEL == LIGHTING_PBR
return PBRLighting(s, lightDir, lightColor, viewDir);
#elif LIGHTING_MODEL == LIGHTING_TOON
return ToonLighting(s, lightDir, lightColor, viewDir);
#elif LIGHTING_MODEL == LIGHTING_CUSTOM
return CustomLighting(s, lightDir, lightColor, viewDir);
#else
return LambertLighting(s, lightDir, lightColor);
#endif
}

🚀 高级特性实现

多层纹理混合

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
// 复杂的Surface Shader
uniform sampler2D albedoMap;
uniform sampler2D detailMap;
uniform sampler2D maskMap;
uniform sampler2D normalMap;

uniform Constants {
vec4 mainColor;
vec4 detailColor;
float detailScale;
float normalStrength;
float mixFactor;
};

void surf (out StandardSurface s) {
vec2 detailUV = v_uv * detailScale;

// 采样各种纹理
vec4 baseColor = texture(albedoMap, v_uv);
vec4 detailSample = texture(detailMap, detailUV);
vec4 mask = texture(maskMap, v_uv);

// 颜色混合
vec3 blendedAlbedo = mix(
baseColor.rgb * mainColor.rgb,
detailSample.rgb * detailColor.rgb,
mask.r * mixFactor
);

// 法线贴图处理
vec3 normalSample = texture(normalMap, v_uv).rgb * 2.0 - 1.0;
normalSample.xy *= normalStrength;

// TBN矩阵计算(简化版)
vec3 normal = normalize(v_normal);
vec3 tangent = normalize(cross(normal, vec3(0.0, 1.0, 0.0)));
vec3 bitangent = cross(normal, tangent);
mat3 TBN = mat3(tangent, bitangent, normal);

// 设置Surface属性
s.albedo = blendedAlbedo;
s.normal = normalize(TBN * normalSample);
s.metallic = mask.g; // 绿色通道存储金属度
s.roughness = mask.b; // 蓝色通道存储粗糙度
s.occlusion = mask.a; // Alpha通道存储AO
s.alpha = baseColor.a * mainColor.a;
s.emissive = vec3(0.0);
}

动态效果Surface Shader

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
// 时间相关的动态效果
uniform Constants {
vec4 mainColor;
float waveSpeed;
float waveAmplitude;
float noiseScale;
float glowIntensity;
};

// Noise函数
float noise(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}

float smoothNoise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);

float a = noise(i);
float b = noise(i + vec2(1.0, 0.0));
float c = noise(i + vec2(0.0, 1.0));
float d = noise(i + vec2(1.0, 1.0));

vec2 u = f * f * (3.0 - 2.0 * f);

return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}

void surf (out StandardSurface s) {
vec2 animatedUV = v_uv;

// 基于时间的UV动画
float time = cc_time.x * waveSpeed;
animatedUV.x += sin(time + v_uv.y * 10.0) * waveAmplitude;
animatedUV.y += cos(time + v_uv.x * 8.0) * waveAmplitude * 0.5;

// 采样贴图纹理
vec4 texColor = texture(mainTexture, animatedUV);

// 噪声计算
float noiseValue = smoothNoise(v_uv * noiseScale + time);

// 基础颜色
s.albedo = texColor.rgb * mainColor.rgb;

// ��̬�����Ⱥʹֲڶ�
s.metallic = 0.5 + 0.3 * sin(time + noiseValue * 6.28);
s.roughness = 0.2 + 0.3 * noiseValue;

// 发光脉冲效果
float glowPulse = 0.5 + 0.5 * sin(time * 3.0);
s.emissive = texColor.rgb * glowIntensity * glowPulse * noiseValue;

s.normal = v_normal;
s.alpha = texColor.a * mainColor.a;
s.occlusion = 1.0;
}

🎮 实际应用案例

水面Surface Shader

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
// 水面着色器
uniform sampler2D waterNormal;
uniform sampler2D foamTexture;
uniform samplerCube skybox;

uniform WaterProperties {
vec4 waterColor;
vec4 deepWaterColor;
float waveSpeed;
float waveScale;
float normalStrength;
float foamThreshold;
float reflectionStrength;
float fresnelPower;
};

// 计算水面波浪
vec3 calculateWaterNormal(vec2 uv, float time) {
vec2 uv1 = uv * waveScale + vec2(time * waveSpeed, 0.0);
vec2 uv2 = uv * waveScale * 0.7 + vec2(0.0, time * waveSpeed * 0.8);

vec3 normal1 = texture(waterNormal, uv1).rgb * 2.0 - 1.0;
vec3 normal2 = texture(waterNormal, uv2).rgb * 2.0 - 1.0;

return normalize(normal1 + normal2);
}

void surf (out StandardSurface s) {
float time = cc_time.x;

// 计算水面法线
vec3 waterNorm = calculateWaterNormal(v_uv, time);
waterNorm.xy *= normalStrength;

// 计算视角方向
vec3 viewDir = normalize(cc_cameraPos.xyz - v_position);

// 菲涅尔效应
float fresnel = pow(1.0 - max(0.0, dot(viewDir, waterNorm)), fresnelPower);

// 计算反射
vec3 reflectDir = reflect(-viewDir, waterNorm);
vec3 reflection = textureLod(skybox, reflectDir, 0.0).rgb;

// 水深模拟(简化版噪声)
float depth = smoothNoise(v_uv * 5.0 + time * 0.1);

// 泡沫效果
float foam = smoothstep(foamThreshold, foamThreshold + 0.1, depth);
vec4 foamColor = texture(foamTexture, v_uv * 8.0 + time * 0.2);

// 基础颜色
vec3 baseColor = mix(deepWaterColor.rgb, waterColor.rgb, depth);
baseColor = mix(baseColor, foamColor.rgb, foam * foamColor.a);

// 添加反射
baseColor = mix(baseColor, reflection, fresnel * reflectionStrength);

// 设置Surface属性
s.albedo = baseColor;
s.normal = normalize(v_normal + waterNorm);
s.metallic = 0.0;
s.roughness = 0.1 + foam * 0.4;
s.alpha = 0.8 + fresnel * 0.2;
s.emissive = vec3(0.0);
s.occlusion = 1.0;
}

皮肤Surface Shader

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
// 皮肤着色器(次表面散射模拟)
uniform sampler2D skinAlbedo;
uniform sampler2D skinNormal;
uniform sampler2D skinSSS;

uniform SkinProperties {
vec4 skinColor;
vec4 sssColor;
float sssStrength;
float sssRadius;
float normalStrength;
float roughness;
float specularStrength;
};

// 简化的次表面散射计算
vec3 calculateSSS(vec3 normal, vec3 lightDir, vec3 viewDir, vec3 sssMap) {
// 背光照明,模拟光线透射
float backLight = max(0.0, dot(-normal, lightDir));

// 基于视角的散射
float viewScatter = pow(max(0.0, dot(-viewDir, lightDir)), sssRadius);

// 厚度信息从SSS贴图获取
float thickness = 1.0 - sssMap.r;

// 组合次表面散射
vec3 sss = sssColor.rgb * (backLight + viewScatter) * thickness * sssStrength;

return sss;
}

void surf (out StandardSurface s) {
// 采样皮肤贴图
vec4 albedoSample = texture(skinAlbedo, v_uv);
vec4 sssSample = texture(skinSSS, v_uv);
vec3 normalSample = texture(skinNormal, v_uv).rgb * 2.0 - 1.0;

// 法线处理
normalSample.xy *= normalStrength;
vec3 normal = normalize(v_normal);
// 简化的法线变换
s.normal = normalize(normal + normalSample);

// 计算次表面散射
vec3 lightDir = normalize(cc_mainLitDir.xyz);
vec3 viewDir = normalize(cc_cameraPos.xyz - v_position);
vec3 sss = calculateSSS(s.normal, lightDir, viewDir, sssSample.rgb);

// 设置Surface属性
s.albedo = albedoSample.rgb * skinColor.rgb;
s.metallic = 0.0; // 皮肤是非金属
s.roughness = roughness;
s.alpha = albedoSample.a * skinColor.a;
s.emissive = sss; // 将SSS作为自发光
s.occlusion = sssSample.g; // 绿色通道作为AO
}

🛠️ Surface Shader管理系统

动态Surface Shader切换

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
@ccclass('SurfaceShaderManager')
export class SurfaceShaderManager extends Component {
@property([EffectAsset])
surfaceShaders: EffectAsset[] = [];

@property(MeshRenderer)
targetRenderer: MeshRenderer = null!;

private currentShaderIndex: number = 0;
private materialInstances: Map<string, MaterialInstance> = new Map();

start() {
this.preloadSurfaceShaders();
}

private preloadSurfaceShaders() {
this.surfaceShaders.forEach((shader, index) => {
const material = new Material();
material.initialize({
effectAsset: shader,
technique: 0
});

const instance = new MaterialInstance({
parent: material
});

this.materialInstances.set(shader.name, instance);
});
}

switchToShader(shaderName: string) {
const instance = this.materialInstances.get(shaderName);
if (instance) {
this.targetRenderer.setMaterialInstance(instance, 0);
console.log(`Switched to Surface Shader: ${shaderName}`);
}
}

// 运行时参数调整
adjustShaderParameters(params: Record<string, any>) {
const currentInstance = this.targetRenderer.getMaterialInstance(0);
if (currentInstance) {
Object.entries(params).forEach(([key, value]) => {
currentInstance.setProperty(key, value);
});
}
}

// 预设材质应用
applyPreset(presetName: string) {
const presets = {
metal: {
metallic: 1.0,
roughness: 0.1,
mainColor: new Color(200, 200, 200, 255)
},
wood: {
metallic: 0.0,
roughness: 0.8,
mainColor: new Color(139, 107, 66, 255)
},
glass: {
metallic: 0.0,
roughness: 0.0,
mainColor: new Color(255, 255, 255, 100)
}
};

const preset = presets[presetName];
if (preset) {
this.adjustShaderParameters(preset);
}
}
}

Surface Shader性能分析

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
@ccclass('SurfaceShaderProfiler')
export class SurfaceShaderProfiler extends Component {
@property(Label)
performanceLabel: Label = null!;

@property([Material])
testMaterials: Material[] = [];

private frameCount: number = 0;
private totalTime: number = 0;
private currentMaterialIndex: number = 0;

start() {
this.startProfiling();
}

private startProfiling() {
this.schedule(this.switchMaterial, 2.0); // 每2秒切换材质
this.schedule(this.updatePerformanceInfo, 0.1); // 每100ms更新性能信息
}

private switchMaterial() {
if (this.testMaterials.length === 0) return;

const material = this.testMaterials[this.currentMaterialIndex];
const renderer = this.getComponent(MeshRenderer);

if (renderer && material) {
const startTime = performance.now();
renderer.setMaterial(material, 0);
const endTime = performance.now();

console.log(`Material switch time: ${endTime - startTime}ms`);

this.currentMaterialIndex = (this.currentMaterialIndex + 1) % this.testMaterials.length;
}
}

private updatePerformanceInfo() {
this.frameCount++;
this.totalTime += 0.1;

if (this.totalTime >= 1.0) {
const fps = this.frameCount / this.totalTime;
const currentMaterial = this.testMaterials[this.currentMaterialIndex];

this.performanceLabel.string =
`FPS: ${fps.toFixed(1)}\n` +
`Material: ${currentMaterial?.name || 'Unknown'}\n` +
`Shader Complexity: ${this.getShaderComplexity(currentMaterial)}`;

this.frameCount = 0;
this.totalTime = 0;
}
}

private getShaderComplexity(material: Material): string {
if (!material) return 'Unknown';

const defines = material.passes[0]?.defines || {};
const defineCount = Object.keys(defines).length;

if (defineCount < 5) return 'Low';
if (defineCount < 10) return 'Medium';
return 'High';
}
}

🐛 调试和优化

Surface Shader调试功能

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
// 调试模式定义
#define DEBUG_NONE 0
#define DEBUG_ALBEDO 1
#define DEBUG_NORMAL 2
#define DEBUG_METALLIC 3
#define DEBUG_ROUGHNESS 4
#define DEBUG_AO 5
#define DEBUG_EMISSION 6

uniform Constants {
vec4 mainColor;
int debugMode;
};

void surf (out StandardSurface s) {
// 正常的Surface设置
// ...

// 调试模式
#if DEBUG_MODE == DEBUG_ALBEDO
s.albedo = s.albedo;
s.emissive = vec3(0.0);
#elif DEBUG_MODE == DEBUG_NORMAL
s.albedo = s.normal * 0.5 + 0.5;
s.emissive = vec3(0.0);
#elif DEBUG_MODE == DEBUG_METALLIC
s.albedo = vec3(s.metallic);
s.emissive = vec3(0.0);
#elif DEBUG_MODE == DEBUG_ROUGHNESS
s.albedo = vec3(s.roughness);
s.emissive = vec3(0.0);
#elif DEBUG_MODE == DEBUG_AO
s.albedo = vec3(s.occlusion);
s.emissive = vec3(0.0);
#elif DEBUG_MODE == DEBUG_EMISSION
s.albedo = vec3(0.0);
s.emissive = s.emissive;
#endif
}

性能优化技巧

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
// 优化技巧1:预计算常用值
uniform Constants {
vec4 mainColor;
float roughness;
float metallic;
// 预计算数值
float roughnessSquared; // roughness * roughness
float oneMinusMetallic; // 1.0 - metallic
};

// 优化技巧2:条件编译减少不必要计算
#if USE_NORMAL_MAP
vec3 normalSample = texture(normalMap, v_uv).rgb * 2.0 - 1.0;
s.normal = normalize(TBN * normalSample);
#else
s.normal = v_normal;
#endif

// 优化技巧3:LOD系统
#if QUALITY_LEVEL >= 2
// 高质量渲染
s.normal = calculateDetailNormal(v_uv);
s.emissive = calculateComplexEmission(v_uv);
#elif QUALITY_LEVEL >= 1
// 中等质量渲染
s.normal = calculateSimpleNormal(v_uv);
s.emissive = vec3(0.0);
#else
// 低质量渲染
s.normal = v_normal;
s.emissive = vec3(0.0);
#endif

🚀 下一步学习

掌握Surface Shader学习后,建议继续学习:

  1. 第6.1章:光照模型详解 - 高级光照模型
  2. 第11.3章:着色器优化技术详解 - 着色器优化技巧
  3. 第3.3章:着色器调试与开发工具 - 着色器调试工具

📖 总结

通过本章学习,我们深入应用掌握了:

  • ✅ Surface Shader的核心概念与架构
  • ✅ 如何编写和使用Surface Shader
  • ✅ 自定义光照模型的实现方法
  • ✅ 高级特性和动态效果的实现
  • ✅ 实际项目中的应用案例
  • ✅ 调试和性能优化技巧

Surface Shader是一个强大的工具,让着色器开发变得更加简单和直观。通过合理使用Surface Shader,我们可以实现各种各样的材质效果,同时保持代码的简洁性和可维护性。


重要提醒:Surface Shader虽然降低了开发难度,但仍需要注意性能优化。在移动端开发时要根据设备性能合理选择复杂度和渲染质量。