第8.1章:自定义Surface Shader基础

自定义Surface Shader是Cocos Creator着色器开发的核心技能,它允许我们创建独特的视觉效果和材质表现。本章将深入探讨如何从零开始构建自定义Surface Shader,掌握核心概念和实践技巧。

🎯 学习目标

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

  • Surface Shader的核心架构和工作原理
  • 从零开始创建自定义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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
CCEffect %{
techniques:
- name: opaque
passes:
- vert: custom-surface-vs:vert
frag: custom-surface-fs:frag
properties: &props
# 材质属性定义
mainTexture: { value: white }
mainColor: { value: [1, 1, 1, 1], editor: { type: color } }
roughness: { value: 0.5, range: [0, 1] }
metallic: { value: 0.0, range: [0, 1] }
normalTexture: { value: grey }
normalScale: { value: 1.0, range: [0, 2] }
emissiveTexture:{ value: black }
emissiveColor: { value: [0, 0, 0], editor: { type: color } }
emissiveIntensity: { value: 1.0, range: [0, 10] }
aoTexture: { value: white }
aoStrength: { value: 1.0, range: [0, 1] }
}%

CCProgram custom-surface-vs %{
precision highp float;
#include <surface-vertex>

// 自定义顶点属性
in vec3 a_position;
in vec3 a_normal;
in vec2 a_texCoord;
in vec4 a_tangent;

// 自定义varying变量
out vec2 v_uv;
out vec3 v_worldPos;
out vec3 v_worldNormal;
out vec3 v_worldTangent;
out vec3 v_worldBinormal;

void vert() {
SurfaceIn surfaceIn;
VertexInput(surfaceIn);

// 自定义顶点处理
customVertexProcessing(surfaceIn);

SurfaceVertex(surfaceIn);
}

void customVertexProcessing(inout SurfaceIn surfaceIn) {
// 世界空间位置
surfaceIn.worldPos = (cc_matWorld * vec4(surfaceIn.position.xyz, 1.0)).xyz;

// 世界空间法向量
surfaceIn.worldNormal = normalize((cc_matWorldIT * vec4(surfaceIn.normal, 0.0)).xyz);

// 世界空间切线和副法线
vec3 worldTangent = normalize((cc_matWorld * vec4(surfaceIn.tangent.xyz, 0.0)).xyz);
surfaceIn.worldTangent = vec4(worldTangent, surfaceIn.tangent.w);
surfaceIn.worldBinormal = cross(surfaceIn.worldNormal, worldTangent) * surfaceIn.tangent.w;

// UV坐标
surfaceIn.uv = surfaceIn.texCoord;

// 视图空间位置
surfaceIn.viewPos = (cc_matView * vec4(surfaceIn.worldPos, 1.0)).xyz;

// 裁剪空间位置
surfaceIn.clipPos = cc_matProj * vec4(surfaceIn.viewPos, 1.0);
}
}%

CCProgram custom-surface-fs %{
precision highp float;
#include <surface-fragment>

// Uniform属性
uniform sampler2D mainTexture;
uniform vec4 mainColor;
uniform float roughness;
uniform float metallic;
uniform sampler2D normalTexture;
uniform float normalScale;
uniform sampler2D emissiveTexture;
uniform vec3 emissiveColor;
uniform float emissiveIntensity;
uniform sampler2D aoTexture;
uniform float aoStrength;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 基础颜色采样
vec4 albedo = texture(mainTexture, In.uv) * mainColor;

// 法线贴图处理
vec3 normal = sampleNormalMap(In, normalTexture, normalScale);

// 金属度和粗糙度
float metallicValue = metallic;
float roughnessValue = roughness;

// 自发光
vec3 emissive = texture(emissiveTexture, In.uv).rgb * emissiveColor * emissiveIntensity;

// 环境光遮蔽
float ao = mix(1.0, texture(aoTexture, In.uv).r, aoStrength);

// 输出到Surface结构
Out.albedo = albedo;
Out.normal = normal;
Out.metallic = metallicValue;
Out.roughness = roughnessValue;
Out.emissive = emissive;
Out.ao = ao;
}

vec3 sampleNormalMap(SurfaceIn In, sampler2D normalMap, float scale) {
// 采样法线贴图
vec3 normalTex = texture(normalMap, In.uv).xyz * 2.0 - 1.0;
normalTex.xy *= scale;

// 构建TBN矩阵
vec3 N = normalize(In.worldNormal);
vec3 T = normalize(In.worldTangent.xyz);
vec3 B = normalize(In.worldBinormal);
mat3 TBN = mat3(T, B, N);

// 转换到世界空间
return normalize(TBN * normalTex);
}
}%

🔧 基础Surface Shader实现

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
CCEffect %{
techniques:
- name: diffuse
passes:
- vert: diffuse-vs:vert
frag: diffuse-fs:frag
properties: &props
mainTexture: { value: white }
diffuseColor: { value: [1, 1, 1, 1], editor: { type: color } }
brightness: { value: 1.0, range: [0, 2] }
}%

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

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

uniform sampler2D mainTexture;
uniform vec4 diffuseColor;
uniform float brightness;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 简单的漫反射材质
vec4 albedo = texture(mainTexture, In.uv) * diffuseColor;
albedo.rgb *= brightness;

Out.albedo = albedo;
Out.normal = normalize(In.worldNormal);
Out.metallic = 0.0;
Out.roughness = 1.0; // 完全粗糙,纯漫反射
Out.emissive = vec3(0.0);
Out.ao = 1.0;
}
}%

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
CCEffect %{
techniques:
- name: metallic
passes:
- vert: metallic-vs:vert
frag: metallic-fs:frag
properties: &props
baseColor: { value: [0.7, 0.7, 0.7, 1], editor: { type: color } }
metallicFactor: { value: 1.0, range: [0, 1] }
roughnessFactor:{ value: 0.1, range: [0, 1] }
reflectance: { value: 0.04, range: [0, 1] }
}%

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

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

uniform vec4 baseColor;
uniform float metallicFactor;
uniform float roughnessFactor;
uniform float reflectance;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 金属材质处理
Out.albedo = baseColor;
Out.normal = normalize(In.worldNormal);
Out.metallic = metallicFactor;
Out.roughness = roughnessFactor;
Out.emissive = vec3(0.0);
Out.ao = 1.0;

// 金属材质的F0值计算
vec3 f0 = mix(vec3(reflectance), baseColor.rgb, metallicFactor);

// 存储在自定义数据中(如果需要)
// Out.customData = vec4(f0, 1.0);
}
}%

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
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
CCEffect %{
techniques:
- name: transparent
passes:
- vert: transparent-vs:vert
frag: transparent-fs:frag
rasterizerState:
cullMode: none
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
properties: &props
mainTexture: { value: white }
tintColor: { value: [1, 1, 1, 0.5], editor: { type: color } }
transparency: { value: 0.5, range: [0, 1] }
refractionIndex:{ value: 1.33, range: [1, 2] }
}%

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

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

uniform sampler2D mainTexture;
uniform vec4 tintColor;
uniform float transparency;
uniform float refractionIndex;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 透明材质
vec4 albedo = texture(mainTexture, In.uv) * tintColor;
albedo.a *= transparency;

// 计算菲涅尔效应
vec3 viewDir = normalize(cc_cameraPos.xyz - In.worldPos);
float fresnel = calculateFresnel(In.worldNormal, viewDir, refractionIndex);

Out.albedo = albedo;
Out.normal = normalize(In.worldNormal);
Out.metallic = 0.0;
Out.roughness = 0.0; // 玻璃般光滑
Out.emissive = vec3(0.0);
Out.ao = 1.0;

// 透明度调整
Out.albedo.a = mix(albedo.a, 1.0, fresnel * 0.5);
}

float calculateFresnel(vec3 normal, vec3 viewDir, float ior) {
float cosTheta = max(dot(normal, viewDir), 0.0);
float f0 = pow((1.0 - ior) / (1.0 + ior), 2.0);
return f0 + (1.0 - f0) * pow(1.0 - cosTheta, 5.0);
}
}%

🎨 高级Surface Shader技巧

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
80
81
82
83
84
85
86
87
CCEffect %{
techniques:
- name: multi-texture
passes:
- vert: multi-vs:vert
frag: multi-fs:frag
properties: &props
# 第一层纹理
layer1Albedo: { value: white }
layer1Normal: { value: grey }
layer1Scale: { value: 1.0, range: [0.1, 10] }
layer1Strength: { value: 1.0, range: [0, 1] }

# 第二层纹理
layer2Albedo: { value: white }
layer2Normal: { value: grey }
layer2Scale: { value: 1.0, range: [0.1, 10] }
layer2Strength: { value: 0.5, range: [0, 1] }

# 混合控制
blendTexture: { value: white }
blendContrast: { value: 1.0, range: [0.1, 5] }
}%

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

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

uniform sampler2D layer1Albedo;
uniform sampler2D layer1Normal;
uniform float layer1Scale;
uniform float layer1Strength;

uniform sampler2D layer2Albedo;
uniform sampler2D layer2Normal;
uniform float layer2Scale;
uniform float layer2Strength;

uniform sampler2D blendTexture;
uniform float blendContrast;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// UV坐标计算
vec2 uv1 = In.uv * layer1Scale;
vec2 uv2 = In.uv * layer2Scale;

// 采样纹理
vec4 albedo1 = texture(layer1Albedo, uv1);
vec4 albedo2 = texture(layer2Albedo, uv2);

vec3 normal1 = sampleNormal(layer1Normal, uv1);
vec3 normal2 = sampleNormal(layer2Normal, uv2);

// 混合权重
float blendFactor = texture(blendTexture, In.uv).r;
blendFactor = pow(blendFactor, blendContrast);

// 纹理混合
vec4 finalAlbedo = mix(albedo1 * layer1Strength,
albedo2 * layer2Strength,
blendFactor);

vec3 finalNormal = normalize(mix(normal1, normal2, blendFactor));

Out.albedo = finalAlbedo;
Out.normal = transformNormalToWorld(In, finalNormal);
Out.metallic = 0.0;
Out.roughness = 0.5;
Out.emissive = vec3(0.0);
Out.ao = 1.0;
}

vec3 sampleNormal(sampler2D normalMap, vec2 uv) {
return texture(normalMap, uv).xyz * 2.0 - 1.0;
}

vec3 transformNormalToWorld(SurfaceIn In, vec3 tangentNormal) {
vec3 N = normalize(In.worldNormal);
vec3 T = normalize(In.worldTangent.xyz);
vec3 B = normalize(In.worldBinormal);
mat3 TBN = mat3(T, B, N);
return normalize(TBN * tangentNormal);
}
}%

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
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
CCEffect %{
techniques:
- name: procedural
passes:
- vert: procedural-vs:vert
frag: procedural-fs:frag
properties: &props
scale: { value: 10.0, range: [1, 100] }
lacunarity: { value: 2.0, range: [1, 5] }
persistence: { value: 0.5, range: [0.1, 1] }
octaves: { value: 4, range: [1, 8] }
color1: { value: [0.2, 0.1, 0.05, 1], editor: { type: color } }
color2: { value: [0.8, 0.6, 0.4, 1], editor: { type: color } }
noiseOffset: { value: [0, 0], editor: { type: vec2 } }
}%

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

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

uniform float scale;
uniform float lacunarity;
uniform float persistence;
uniform int octaves;
uniform vec4 color1;
uniform vec4 color2;
uniform vec2 noiseOffset;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 程序化纹理生成
vec2 noiseUV = In.uv * scale + noiseOffset;

// 分形噪声
float noise = fbmNoise(noiseUV, octaves, lacunarity, persistence);

// 颜色混合
vec4 albedo = mix(color1, color2, noise);

// 程序化法线
vec3 normal = calculateProceduralNormal(noiseUV);

// 基于噪声的粗糙度
float roughnessValue = mix(0.2, 0.8, noise);

Out.albedo = albedo;
Out.normal = transformNormalToWorld(In, normal);
Out.metallic = 0.0;
Out.roughness = roughnessValue;
Out.emissive = vec3(0.0);
Out.ao = 1.0;
}

float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}

float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);

float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(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;
}

float fbmNoise(vec2 st, int octaves, float lacunarity, float persistence) {
float value = 0.0;
float amplitude = 1.0;
float frequency = 1.0;

for (int i = 0; i < octaves; i++) {
value += amplitude * noise(st * frequency);
amplitude *= persistence;
frequency *= lacunarity;
}

return value;
}

vec3 calculateProceduralNormal(vec2 uv) {
float eps = 0.01;
float heightL = fbmNoise(uv - vec2(eps, 0.0), octaves, lacunarity, persistence);
float heightR = fbmNoise(uv + vec2(eps, 0.0), octaves, lacunarity, persistence);
float heightD = fbmNoise(uv - vec2(0.0, eps), octaves, lacunarity, persistence);
float heightU = fbmNoise(uv + vec2(0.0, eps), octaves, lacunarity, persistence);

vec3 normal;
normal.x = (heightL - heightR) / (2.0 * eps);
normal.y = (heightD - heightU) / (2.0 * eps);
normal.z = 1.0;

return normalize(normal);
}

vec3 transformNormalToWorld(SurfaceIn In, vec3 tangentNormal) {
vec3 N = normalize(In.worldNormal);
vec3 T = normalize(In.worldTangent.xyz);
vec3 B = normalize(In.worldBinormal);
mat3 TBN = mat3(T, B, N);
return normalize(TBN * tangentNormal);
}
}%

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
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: vertex-animation
passes:
- vert: anim-vs:vert
frag: anim-fs:frag
properties: &props
mainTexture: { value: white }
waveAmplitude: { value: 0.1, range: [0, 1] }
waveFrequency: { value: 5.0, range: [1, 20] }
waveSpeed: { value: 2.0, range: [0, 10] }
windDirection: { value: [1, 0, 0], editor: { type: vec3 } }
windStrength: { value: 0.5, range: [0, 2] }
}%

CCProgram anim-vs %{
#include <surface-vertex>

uniform float waveAmplitude;
uniform float waveFrequency;
uniform float waveSpeed;
uniform vec3 windDirection;
uniform float windStrength;

void vert() {
SurfaceIn surfaceIn;
VertexInput(surfaceIn);

// 顶点动画
applyVertexAnimation(surfaceIn);

SurfaceVertex(surfaceIn);
}

void applyVertexAnimation(inout SurfaceIn surfaceIn) {
vec3 worldPos = (cc_matWorld * vec4(surfaceIn.position.xyz, 1.0)).xyz;

// 波浪动画
float wave = sin(worldPos.x * waveFrequency + cc_time.x * waveSpeed) * waveAmplitude;

// 风力动画
vec3 wind = windDirection * windStrength * sin(cc_time.x + worldPos.y);

// 应用动画
worldPos.y += wave;
worldPos += wind;

// 更新位置
surfaceIn.position = vec4((inverse(cc_matWorld) * vec4(worldPos, 1.0)).xyz, 1.0);

// 更新法向量(基于动画)
vec3 tangent = vec3(1, cos(worldPos.x * waveFrequency + cc_time.x * waveSpeed) * waveAmplitude * waveFrequency, 0);
vec3 bitangent = vec3(0, 0, 1);
vec3 animatedNormal = normalize(cross(bitangent, tangent));

surfaceIn.normal = animatedNormal;
}
}%

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

uniform sampler2D mainTexture;

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

// 动态材质属性
float motion = length(In.worldPos - cc_cameraPos.xyz) * 0.01;
float dynamicRoughness = mix(0.3, 0.7, sin(cc_time.x + motion));

Out.albedo = albedo;
Out.normal = normalize(In.worldNormal);
Out.metallic = 0.0;
Out.roughness = dynamicRoughness;
Out.emissive = vec3(0.0);
Out.ao = 1.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
// TypeScript材质控制器
import { Component, Material, Vec4, Vec3, Vec2 } from 'cc';

@Component('CustomSurfaceMaterial')
export class CustomSurfaceMaterial extends Component {

private _material: Material | null = null;

// 基础属性
public mainColor: Vec4 = new Vec4(1, 1, 1, 1);
public roughness: number = 0.5;
public metallic: number = 0.0;

// 纹理属性
public normalScale: number = 1.0;
public emissiveIntensity: number = 1.0;
public aoStrength: number = 1.0;

// 动画属性
public waveAmplitude: number = 0.1;
public waveFrequency: number = 5.0;
public waveSpeed: number = 2.0;

onLoad() {
this.initializeMaterial();
}

initializeMaterial() {
const renderer = this.getComponent('cc.MeshRenderer');
if (renderer) {
this._material = renderer.material;
this.updateMaterialProperties();
}
}

updateMaterialProperties() {
if (!this._material) return;

// 更新基础属性
this._material.setProperty('mainColor', this.mainColor);
this._material.setProperty('roughness', this.roughness);
this._material.setProperty('metallic', this.metallic);

// 更新纹理属性
this._material.setProperty('normalScale', this.normalScale);
this._material.setProperty('emissiveIntensity', this.emissiveIntensity);
this._material.setProperty('aoStrength', this.aoStrength);

// 更新动画属性
this._material.setProperty('waveAmplitude', this.waveAmplitude);
this._material.setProperty('waveFrequency', this.waveFrequency);
this._material.setProperty('waveSpeed', this.waveSpeed);
}

// 动态材质切换
switchToMetallic() {
this.metallic = 1.0;
this.roughness = 0.1;
this.updateMaterialProperties();
}

switchToDielectric() {
this.metallic = 0.0;
this.roughness = 0.8;
this.updateMaterialProperties();
}

// 动画控制
startWaveAnimation() {
this.waveAmplitude = 0.2;
this.waveSpeed = 3.0;
this.updateMaterialProperties();
}

stopWaveAnimation() {
this.waveAmplitude = 0.0;
this.updateMaterialProperties();
}
}

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
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
85
86
// 动态材质管理器
export class DynamicMaterialManager extends Component {

private materialInstances: Map<string, Material> = new Map();

// 材质模板
private materialTemplates = {
'basic': 'materials/BasicSurface',
'metallic': 'materials/MetallicSurface',
'transparent': 'materials/TransparentSurface',
'procedural': 'materials/ProceduralSurface',
'animated': 'materials/AnimatedSurface'
};

createMaterialInstance(templateName: string, instanceName: string): Material | null {
const templatePath = this.materialTemplates[templateName];
if (!templatePath) return null;

// 加载材质模板
const template = resources.get<Material>(templatePath);
if (!template) return null;

// 创建实例
const instance = new Material();
instance.copy(template);

// 存储实例
this.materialInstances.set(instanceName, instance);

return instance;
}

getMaterialInstance(instanceName: string): Material | null {
return this.materialInstances.get(instanceName) || null;
}

updateMaterialProperty(instanceName: string, propertyName: string, value: any) {
const material = this.getMaterialInstance(instanceName);
if (material) {
material.setProperty(propertyName, value);
}
}

// 材质动画系统
animateMaterialProperty(instanceName: string, propertyName: string,
startValue: any, endValue: any, duration: number) {
const material = this.getMaterialInstance(instanceName);
if (!material) return;

let elapsed = 0;
const update = (dt: number) => {
elapsed += dt;
const t = Math.min(elapsed / duration, 1.0);

// 插值计算
const currentValue = this.lerpValue(startValue, endValue, t);
material.setProperty(propertyName, currentValue);

if (t < 1.0) {
this.scheduleOnce(update, 0);
}
};

this.scheduleOnce(update, 0);
}

private lerpValue(start: any, end: any, t: number): any {
if (typeof start === 'number') {
return start + (end - start) * t;
} else if (start instanceof Vec3) {
return new Vec3(
start.x + (end.x - start.x) * t,
start.y + (end.y - start.y) * t,
start.z + (end.z - start.z) * t
);
} else if (start instanceof Vec4) {
return new Vec4(
start.x + (end.x - start.x) * t,
start.y + (end.y - start.y) * t,
start.z + (end.z - start.z) * t,
start.w + (end.w - start.w) * t
);
}
return end; // 不支持的类型直接返回结束值
}
}

📋 性能优化与最佳实践

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
// 优化的Surface Shader
CCProgram optimized-surface-fs %{
#include <surface-fragment>

// 精度控制
#ifdef CC_PLATFORM_MOBILE
precision mediump float;
#else
precision highp float;
#endif

uniform sampler2D mainTexture;
uniform vec4 mainColor;
uniform float roughness;
uniform float metallic;

// 条件编译优化
#ifdef ENABLE_NORMAL_MAPPING
uniform sampler2D normalTexture;
uniform float normalScale;
#endif

#ifdef ENABLE_EMISSION
uniform sampler2D emissiveTexture;
uniform vec3 emissiveColor;
#endif

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 减少纹理采样
vec4 albedo = texture(mainTexture, In.uv);

// 早期返回优化
if (albedo.a < 0.01) {
discard;
}

albedo *= mainColor;

// 条件功能
#ifdef ENABLE_NORMAL_MAPPING
vec3 normal = sampleNormalMap(normalTexture, In.uv, normalScale, In);
#else
vec3 normal = normalize(In.worldNormal);
#endif

#ifdef ENABLE_EMISSION
vec3 emissive = texture(emissiveTexture, In.uv).rgb * emissiveColor;
#else
vec3 emissive = vec3(0.0);
#endif

// 输出
Out.albedo = albedo;
Out.normal = normal;
Out.metallic = metallic;
Out.roughness = roughness;
Out.emissive = emissive;
Out.ao = 1.0;
}

#ifdef ENABLE_NORMAL_MAPPING
vec3 sampleNormalMap(sampler2D normalMap, vec2 uv, float scale, SurfaceIn In) {
vec3 normalTex = texture(normalMap, uv).xyz * 2.0 - 1.0;
normalTex.xy *= scale;

// 快速TBN变换
vec3 N = normalize(In.worldNormal);
vec3 T = normalize(In.worldTangent.xyz);
vec3 B = cross(N, T) * In.worldTangent.w;

return normalize(T * normalTex.x + B * normalTex.y + N * normalTex.z);
}
#endif
}%

2. LOD系统集成

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// LOD级别的Surface Shader
CCEffect %{
techniques:
- name: lod-high
passes:
- vert: lod-vs:vert
frag: lod-high-fs:frag
properties: &props
mainTexture: { value: white }
normalTexture: { value: grey }
roughnessTexture: { value: white }
metallicTexture: { value: white }
aoTexture: { value: white }
detailTexture: { value: white }

- name: lod-medium
passes:
- vert: lod-vs:vert
frag: lod-medium-fs:frag
properties: *props

- name: lod-low
passes:
- vert: lod-vs:vert
frag: lod-low-fs:frag
properties: *props
}%

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

// 高质量LOD
CCProgram lod-high-fs %{
#include <surface-fragment>

uniform sampler2D mainTexture;
uniform sampler2D normalTexture;
uniform sampler2D roughnessTexture;
uniform sampler2D metallicTexture;
uniform sampler2D aoTexture;
uniform sampler2D detailTexture;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 完整的纹理采样
vec4 albedo = texture(mainTexture, In.uv);
vec3 normal = sampleNormal(normalTexture, In.uv, In);
float roughness = texture(roughnessTexture, In.uv).r;
float metallic = texture(metallicTexture, In.uv).r;
float ao = texture(aoTexture, In.uv).r;

// 细节纹理
vec4 detail = texture(detailTexture, In.uv * 4.0);
albedo.rgb = mix(albedo.rgb, detail.rgb, 0.3);

Out.albedo = albedo;
Out.normal = normal;
Out.metallic = metallic;
Out.roughness = roughness;
Out.emissive = vec3(0.0);
Out.ao = ao;
}
}%

// 中等质量LOD
CCProgram lod-medium-fs %{
#include <surface-fragment>

uniform sampler2D mainTexture;
uniform sampler2D normalTexture;
uniform sampler2D roughnessTexture;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 简化的纹理采样
vec4 albedo = texture(mainTexture, In.uv);
vec3 normal = sampleNormal(normalTexture, In.uv, In);
float roughness = texture(roughnessTexture, In.uv).r;

Out.albedo = albedo;
Out.normal = normal;
Out.metallic = 0.0;
Out.roughness = roughness;
Out.emissive = vec3(0.0);
Out.ao = 1.0;
}
}%

// 低质量LOD
CCProgram lod-low-fs %{
#include <surface-fragment>

uniform sampler2D mainTexture;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 最简单的采样
vec4 albedo = texture(mainTexture, In.uv);

Out.albedo = albedo;
Out.normal = normalize(In.worldNormal);
Out.metallic = 0.0;
Out.roughness = 0.5;
Out.emissive = vec3(0.0);
Out.ao = 1.0;
}
}%

📖 本章总结

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

  • Surface Shader架构:核心组成和工作原理
  • 基础材质实现:漫反射、金属、透明材质的完整实现
  • 高级技巧:多纹理混合、程序化生成、顶点动画
  • 系统集成:材质属性管理和动态材质系统
  • 性能优化:着色器优化技巧和LOD系统集成
  • 最佳实践:企业级开发的规范和模式

🚀 下一步学习

掌握了自定义Surface Shader基础后,建议继续学习:
👉 第8.2章:边缘光Surface Shader

💡 实践练习

  1. 基础练习:创建一个包含漫反射和镜面反射的基础材质
  2. 进阶练习:实现多纹理混合的地形材质系统
  3. 高级练习:开发带有动画效果的程序化材质

参考资料

系列导航