第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
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
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
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
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
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
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
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
// 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
// 动态材质管理器
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
// 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基础后,建议继续学习�?
👉 �?.2章:边缘光Surface Shader

💡 实践练习

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

*参考资�?

系列导航