第8.4章:水面Surface Shader

水面效果是游戏中最具挑战性的视觉效果之一,涉及波浪动画、反射、折射、焦散等多种技术。本章将深入探讨如何使用Surface Shader创建逼真的水面效果。

🎯 学习目标

  • 理解水面渲染的物理原理
  • 掌握波浪动画和法线扰动技术
  • 学会实现反射和折射效果
  • 理解焦散和泡沫的渲染技术

💡 水面渲染原理

物理基础

水面渲染基于以下物理现象:

  • 波浪运动:正弦波叠加形成复杂波形
  • 菲涅尔反射:视角影响反射和透射比例
  • 折射:光线在水中的弯曲
  • 焦散:光线聚集形成的光斑图案

数学模型

1
2
3
4
5
// 基础波浪函数
float wave(vec2 position, float amplitude, float frequency, float speed, vec2 direction) {
float phase = dot(direction, position) * frequency + cc_time.x * speed;
return amplitude * sin(phase);
}

🔧 基础水面效果

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
88
89
90
91
CCEffect %{
techniques:
- name: simple-water
passes:
- vert: water-vs:vert
frag: water-fs:frag
properties: &props
waterTexture: { value: white }
normalTexture: { value: grey }
waterColor: { value: [0, 0.3, 0.6, 0.8], editor: { type: color } }
waveAmplitude: { value: 0.1, range: [0, 1] }
waveFrequency: { value: 2.0, range: [0.1, 10] }
waveSpeed: { value: 1.0, range: [0, 5] }
normalStrength: { value: 1.0, range: [0, 3] }
transparency: { value: 0.7, range: [0, 1] }
}%

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

uniform float waveAmplitude;
uniform float waveFrequency;
uniform float waveSpeed;

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

// 顶点波浪动画
vec3 worldPos = (cc_matWorld * vec4(surfaceIn.position.xyz, 1.0)).xyz;

// 多方向波浪叠加
float wave1 = sin(worldPos.x * waveFrequency + cc_time.x * waveSpeed) * waveAmplitude;
float wave2 = sin(worldPos.z * waveFrequency * 0.7 + cc_time.x * waveSpeed * 1.3) * waveAmplitude * 0.6;
float wave3 = sin((worldPos.x + worldPos.z) * waveFrequency * 0.5 + cc_time.x * waveSpeed * 0.8) * waveAmplitude * 0.4;

worldPos.y += wave1 + wave2 + wave3;

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

SurfaceVertex(surfaceIn);
}
}%

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

uniform sampler2D waterTexture;
uniform sampler2D normalTexture;
uniform vec4 waterColor;
uniform float normalStrength;
uniform float transparency;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 动态UV
vec2 uv1 = In.uv + cc_time.x * 0.02;
vec2 uv2 = In.uv * 0.7 - cc_time.x * 0.015;

// 基础纹理采样
vec4 waterTex = texture(waterTexture, uv1);

// 法线贴图采样和混合
vec3 normal1 = texture(normalTexture, uv1).xyz * 2.0 - 1.0;
vec3 normal2 = texture(normalTexture, uv2).xyz * 2.0 - 1.0;
vec3 blendedNormal = normalize(normal1 + normal2) * normalStrength;

// 菲涅尔效果
vec3 viewDir = normalize(cc_cameraPos.xyz - In.worldPos);
float fresnel = pow(1.0 - max(dot(In.worldNormal, viewDir), 0.0), 2.0);

// 水面颜色
vec4 finalColor = mix(waterTex, waterColor, 0.5);
finalColor.a = mix(transparency, 1.0, fresnel);

Out.albedo = finalColor;
Out.normal = transformNormalToWorld(In, blendedNormal);
Out.metallic = 0.0;
Out.roughness = 0.1; // 水面很光滑
Out.emissive = vec3(0.0);
Out.ao = 1.0;
}

vec3 transformNormalToWorld(SurfaceIn In, vec3 tangentNormal) {
vec3 N = normalize(In.worldNormal);
vec3 T = normalize(In.worldTangent.xyz);
vec3 B = normalize(cross(N, T)) * In.worldTangent.w;
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
CCEffect %{
techniques:
- name: reflective-water
passes:
- vert: reflective-vs:vert
frag: reflective-fs:frag
rasterizerState:
cullMode: none
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
properties: &props
waterTexture: { value: white }
normalTexture: { value: grey }
reflectionTexture:{ value: white }
waterColor: { value: [0, 0.3, 0.6, 0.8], editor: { type: color } }
reflectionStrength: { value: 0.8, range: [0, 1] }
refractionStrength: { value: 0.1, range: [0, 0.5] }
waveAmplitude: { value: 0.05, range: [0, 0.3] }
waveFrequency: { value: 3.0, range: [0.1, 10] }
waveSpeed: { value: 1.5, range: [0, 5] }
normalStrength: { value: 1.5, range: [0, 3] }
fresnelPower: { value: 2.0, range: [0.1, 5] }
}%

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

uniform float waveAmplitude;
uniform float waveFrequency;
uniform float waveSpeed;

out vec4 v_screenPos;

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

// 波浪动画
vec3 worldPos = (cc_matWorld * vec4(surfaceIn.position.xyz, 1.0)).xyz;

// Gerstner波浪(更真实的波浪形状)
vec2 direction1 = normalize(vec2(1.0, 0.3));
vec2 direction2 = normalize(vec2(-0.7, 1.0));

float phase1 = dot(direction1, worldPos.xz) * waveFrequency + cc_time.x * waveSpeed;
float phase2 = dot(direction2, worldPos.xz) * waveFrequency * 0.8 + cc_time.x * waveSpeed * 1.2;

worldPos.y += waveAmplitude * (sin(phase1) + sin(phase2) * 0.6);

// 水平位移(Gerstner波浪特征)
worldPos.x += waveAmplitude * 0.3 * cos(phase1) * direction1.x;
worldPos.z += waveAmplitude * 0.3 * cos(phase1) * direction1.y;

surfaceIn.position = vec4((inverse(cc_matWorld) * vec4(worldPos, 1.0)).xyz, 1.0);

SurfaceVertex(surfaceIn);

// 屏幕空间坐标用于反射采样
v_screenPos = cc_matProj * cc_matView * vec4(worldPos, 1.0);
}
}%

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

uniform sampler2D waterTexture;
uniform sampler2D normalTexture;
uniform sampler2D reflectionTexture;
uniform vec4 waterColor;
uniform float reflectionStrength;
uniform float refractionStrength;
uniform float normalStrength;
uniform float fresnelPower;

in vec4 v_screenPos;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 动态UV采样
vec2 uv1 = In.uv + cc_time.x * vec2(0.02, 0.01);
vec2 uv2 = In.uv * 0.8 - cc_time.x * vec2(0.01, 0.02);

// 法线贴图
vec3 normal1 = texture(normalTexture, uv1).xyz * 2.0 - 1.0;
vec3 normal2 = texture(normalTexture, uv2).xyz * 2.0 - 1.0;
vec3 waterNormal = normalize(normal1 + normal2) * normalStrength;

// 屏幕空间坐标
vec2 screenUV = v_screenPos.xy / v_screenPos.w * 0.5 + 0.5;

// 反射采样(垂直翻转)
vec2 reflectionUV = vec2(screenUV.x, 1.0 - screenUV.y);
reflectionUV += waterNormal.xy * refractionStrength;

vec4 reflection = texture(reflectionTexture, reflectionUV);

// 菲涅尔计算
vec3 viewDir = normalize(cc_cameraPos.xyz - In.worldPos);
vec3 worldNormal = transformNormalToWorld(In, waterNormal);
float fresnel = pow(1.0 - max(dot(worldNormal, viewDir), 0.0), fresnelPower);

// 基础水色
vec4 waterTex = texture(waterTexture, uv1);
vec4 baseWater = mix(waterTex, waterColor, 0.6);

// 反射混合
vec4 finalColor = mix(baseWater, reflection, fresnel * reflectionStrength);
finalColor.a = mix(0.7, 1.0, fresnel);

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

vec3 transformNormalToWorld(SurfaceIn In, vec3 tangentNormal) {
vec3 N = normalize(In.worldNormal);
vec3 T = normalize(In.worldTangent.xyz);
vec3 B = normalize(cross(N, T)) * In.worldTangent.w;
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
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
130
131
CCEffect %{
techniques:
- name: depth-water
passes:
- vert: depth-vs:vert
frag: depth-fs:frag
properties: &props
waterTexture: { value: white }
normalTexture: { value: grey }
foamTexture: { value: white }
deepWaterColor: { value: [0, 0.1, 0.3, 1], editor: { type: color } }
shallowWaterColor:{ value: [0, 0.5, 0.8, 1], editor: { type: color } }
foamColor: { value: [1, 1, 1, 1], editor: { type: color } }
waterDepth: { value: 5.0, range: [0.1, 20] }
foamDepth: { value: 0.5, range: [0.1, 2] }
foamIntensity: { value: 1.0, range: [0, 3] }
transparency: { value: 0.8, range: [0, 1] }
waveParams: { value: [0.1, 3.0, 1.5, 1.0] } # amplitude, frequency, speed, normalStrength
}%

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

out vec4 v_screenPos;
out vec3 v_worldPos;

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

// 波浪动画
vec3 worldPos = (cc_matWorld * vec4(surfaceIn.position.xyz, 1.0)).xyz;
v_worldPos = worldPos;

float amplitude = waveParams.x;
float frequency = waveParams.y;
float speed = waveParams.z;

// 多层波浪
float wave1 = sin(worldPos.x * frequency + cc_time.x * speed) * amplitude;
float wave2 = sin(worldPos.z * frequency * 1.3 + cc_time.x * speed * 0.8) * amplitude * 0.7;
float wave3 = sin((worldPos.x - worldPos.z) * frequency * 0.6 + cc_time.x * speed * 1.2) * amplitude * 0.5;

worldPos.y += wave1 + wave2 + wave3;

surfaceIn.position = vec4((inverse(cc_matWorld) * vec4(worldPos, 1.0)).xyz, 1.0);

SurfaceVertex(surfaceIn);

v_screenPos = cc_matProj * cc_matView * vec4(worldPos, 1.0);
}
}%

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

uniform sampler2D waterTexture;
uniform sampler2D normalTexture;
uniform sampler2D foamTexture;
uniform sampler2D cc_sceneDepth; // 深度纹理

uniform vec4 deepWaterColor;
uniform vec4 shallowWaterColor;
uniform vec4 foamColor;
uniform float waterDepth;
uniform float foamDepth;
uniform float foamIntensity;
uniform float transparency;
uniform vec4 waveParams;

in vec4 v_screenPos;
in vec3 v_worldPos;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 屏幕空间坐标
vec2 screenUV = v_screenPos.xy / v_screenPos.w * 0.5 + 0.5;

// 深度采样
float sceneDepth = texture(cc_sceneDepth, screenUV).r;
float waterSurfaceDepth = v_screenPos.z / v_screenPos.w;
float depthDifference = abs(sceneDepth - waterSurfaceDepth);

// 深度衰减
float depthFactor = 1.0 - exp(-depthDifference * waterDepth);

// 动态UV
vec2 uv1 = In.uv + cc_time.x * vec2(0.015, 0.01);
vec2 uv2 = In.uv * 0.7 - cc_time.x * vec2(0.01, 0.02);

// 法线采样
vec3 normal1 = texture(normalTexture, uv1).xyz * 2.0 - 1.0;
vec3 normal2 = texture(normalTexture, uv2).xyz * 2.0 - 1.0;
vec3 waterNormal = normalize(normal1 + normal2) * waveParams.w;

// 基础水色(基于深度)
vec4 waterColor = mix(shallowWaterColor, deepWaterColor, depthFactor);

// 泡沫效果
float foamFactor = 1.0 - smoothstep(0.0, foamDepth, depthDifference);
vec4 foam = texture(foamTexture, uv1 * 2.0) * foamColor;

// 泡沫动画
float foamAnimation = sin(cc_time.x * 3.0 + In.uv.x * 10.0) * 0.3 + 0.7;
foamFactor *= foamAnimation * foamIntensity;

// 最终颜色混合
vec4 finalColor = mix(waterColor, foam, foamFactor);

// 菲涅尔透明度
vec3 viewDir = normalize(cc_cameraPos.xyz - In.worldPos);
vec3 worldNormal = transformNormalToWorld(In, waterNormal);
float fresnel = pow(1.0 - max(dot(worldNormal, viewDir), 0.0), 2.0);

finalColor.a = mix(transparency * (1.0 - depthFactor * 0.5), 1.0, fresnel);

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

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

🎨 高级水面特效

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
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
CCEffect %{
techniques:
- name: caustic-water
passes:
- vert: caustic-vs:vert
frag: caustic-fs:frag
properties: &props
waterTexture: { value: white }
normalTexture: { value: grey }
causticTexture: { value: white }
waterColor: { value: [0, 0.2, 0.5, 0.8], editor: { type: color } }
causticColor: { value: [0.8, 1, 1, 1], editor: { type: color } }
causticIntensity: { value: 2.0, range: [0, 5] }
causticScale: { value: 4.0, range: [0.1, 10] }
causticSpeed: { value: 1.0, range: [0, 3] }
waveParams: { value: [0.08, 4.0, 2.0, 1.2] }
refractionStrength: { value: 0.15, range: [0, 0.5] }
}%

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

out vec3 v_lightDir;

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

// 波浪动画
vec3 worldPos = (cc_matWorld * vec4(surfaceIn.position.xyz, 1.0)).xyz;

float amplitude = waveParams.x;
float frequency = waveParams.y;
float speed = waveParams.z;

// 复合波浪
float wave1 = sin(worldPos.x * frequency + cc_time.x * speed) * amplitude;
float wave2 = sin(worldPos.z * frequency * 1.1 + cc_time.x * speed * 0.9) * amplitude * 0.8;
float wave3 = sin((worldPos.x + worldPos.z * 0.7) * frequency * 0.7 + cc_time.x * speed * 1.3) * amplitude * 0.6;

worldPos.y += wave1 + wave2 + wave3;

surfaceIn.position = vec4((inverse(cc_matWorld) * vec4(worldPos, 1.0)).xyz, 1.0);

// 计算光线方向
v_lightDir = normalize(cc_mainLitDir.xyz);

SurfaceVertex(surfaceIn);
}
}%

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

uniform sampler2D waterTexture;
uniform sampler2D normalTexture;
uniform sampler2D causticTexture;
uniform vec4 waterColor;
uniform vec4 causticColor;
uniform float causticIntensity;
uniform float causticScale;
uniform float causticSpeed;
uniform vec4 waveParams;
uniform float refractionStrength;

in vec3 v_lightDir;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 动态UV
vec2 uv1 = In.uv + cc_time.x * vec2(0.01, 0.015);
vec2 uv2 = In.uv * 0.8 - cc_time.x * vec2(0.012, 0.008);

// 法线贴图
vec3 normal1 = texture(normalTexture, uv1).xyz * 2.0 - 1.0;
vec3 normal2 = texture(normalTexture, uv2).xyz * 2.0 - 1.0;
vec3 waterNormal = normalize(normal1 + normal2) * waveParams.w;

// 焦散UV计算
vec2 causticUV1 = In.uv * causticScale + cc_time.x * causticSpeed * vec2(0.1, 0.05);
vec2 causticUV2 = In.uv * causticScale * 0.7 - cc_time.x * causticSpeed * vec2(0.08, 0.12);

// 法线扰动焦散UV
causticUV1 += waterNormal.xy * refractionStrength;
causticUV2 += waterNormal.xy * refractionStrength * 0.8;

// 焦散纹理采样
float caustic1 = texture(causticTexture, causticUV1).r;
float caustic2 = texture(causticTexture, causticUV2).g;

// 焦散图案混合
float causticPattern = min(caustic1, caustic2);
causticPattern = pow(causticPattern, 2.0); // 增强对比度
// 光照计算
vec3 worldNormal = transformNormalToWorld(In, waterNormal);
float lightDot = max(dot(worldNormal, -v_lightDir), 0.0);

// 焦散强度受光照影响
float causticValue = causticPattern * lightDot * causticIntensity;

// 基础水色
vec4 baseWater = texture(waterTexture, uv1) * waterColor;

// 焦散发光
vec3 causticGlow = causticValue * causticColor.rgb;

// 菲涅尔效果
vec3 viewDir = normalize(cc_cameraPos.xyz - In.worldPos);
float fresnel = pow(1.0 - max(dot(worldNormal, viewDir), 0.0), 2.0);

Out.albedo = baseWater;
Out.normal = worldNormal;
Out.metallic = 0.0;
Out.roughness = 0.06;
Out.emissive = causticGlow;
Out.ao = 1.0;
}

vec3 transformNormalToWorld(SurfaceIn In, vec3 tangentNormal) {
vec3 N = normalize(In.worldNormal);
vec3 T = normalize(In.worldTangent.xyz);
vec3 B = normalize(cross(N, T)) * In.worldTangent.w;
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
CCEffect %{
techniques:
- name: ocean-water
passes:
- vert: ocean-vs:vert
frag: ocean-fs:frag
properties: &props
waterTexture: { value: white }
normalTexture: { value: grey }
foamTexture: { value: white }
skyboxTexture: { value: white }
deepOceanColor: { value: [0, 0.05, 0.2, 1], editor: { type: color } }
shallowOceanColor:{ value: [0, 0.3, 0.6, 1], editor: { type: color } }
foamColor: { value: [1, 1, 1, 1], editor: { type: color } }
waveHeight: { value: 0.2, range: [0, 1] }
waveScale: { value: 2.0, range: [0.1, 10] }
waveSpeed: { value: 1.0, range: [0, 5] }
windDirection: { value: [1, 0], editor: { type: vec2 } }
foamThreshold: { value: 0.7, range: [0, 1] }
reflectionStrength: { value: 0.8, range: [0, 1] }
}%

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

uniform float waveHeight;
uniform float waveScale;
uniform float waveSpeed;
uniform vec2 windDirection;

out vec3 v_waveDisplacement;

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

vec3 worldPos = (cc_matWorld * vec4(surfaceIn.position.xyz, 1.0)).xyz;

// Gerstner海浪模拟
vec2 windDir = normalize(windDirection);
float time = cc_time.x * waveSpeed;

// 多个波浪叠加
vec3 displacement = vec3(0.0);

// 大波浪
float phase1 = dot(windDir, worldPos.xz) * waveScale + time;
displacement.y += waveHeight * sin(phase1);
displacement.xz += waveHeight * 0.3 * cos(phase1) * windDir;

// 中等波浪
vec2 dir2 = normalize(windDir + vec2(0.3, -0.2));
float phase2 = dot(dir2, worldPos.xz) * waveScale * 1.3 + time * 0.9;
displacement.y += waveHeight * 0.7 * sin(phase2);
displacement.xz += waveHeight * 0.2 * cos(phase2) * dir2;

// 小波浪
vec2 dir3 = normalize(windDir + vec2(-0.4, 0.3));
float phase3 = dot(dir3, worldPos.xz) * waveScale * 2.1 + time * 1.2;
displacement.y += waveHeight * 0.4 * sin(phase3);
displacement.xz += waveHeight * 0.1 * cos(phase3) * dir3;

worldPos += displacement;
v_waveDisplacement = displacement;

surfaceIn.position = vec4((inverse(cc_matWorld) * vec4(worldPos, 1.0)).xyz, 1.0);

SurfaceVertex(surfaceIn);
}
}%

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

uniform sampler2D waterTexture;
uniform sampler2D normalTexture;
uniform sampler2D foamTexture;
uniform samplerCube skyboxTexture;
uniform vec4 deepOceanColor;
uniform vec4 shallowOceanColor;
uniform vec4 foamColor;
uniform float foamThreshold;
uniform float reflectionStrength;

in vec3 v_waveDisplacement;

void surf(in SurfaceIn In, inout SurfaceOut Out) {
// 动态UV
vec2 uv1 = In.uv * 2.0 + cc_time.x * vec2(0.02, 0.01);
vec2 uv2 = In.uv * 1.5 - cc_time.x * vec2(0.015, 0.025);

// 法线混合
vec3 normal1 = texture(normalTexture, uv1).xyz * 2.0 - 1.0;
vec3 normal2 = texture(normalTexture, uv2).xyz * 2.0 - 1.0;
vec3 oceanNormal = normalize(normal1 + normal2 * 0.8);

// 泡沫检测(基于波浪高度)
float waveIntensity = length(v_waveDisplacement);
float foamFactor = smoothstep(foamThreshold, 1.0, waveIntensity);

// 泡沫纹理
vec4 foam = texture(foamTexture, uv1 * 3.0) * foamColor;
foam *= sin(cc_time.x * 2.0 + In.uv.x * 15.0) * 0.3 + 0.7; // 泡沫闪烁

// 深度相关的颜色
vec3 viewDir = normalize(cc_cameraPos.xyz - In.worldPos);
vec3 worldNormal = transformNormalToWorld(In, oceanNormal);

// 天空盒反射
vec3 reflectDir = reflect(-viewDir, worldNormal);
vec4 skyReflection = textureCube(skyboxTexture, reflectDir);

// 菲涅尔计算
float fresnel = pow(1.0 - max(dot(worldNormal, viewDir), 0.0), 2.0);

// 基础海水颜色
vec4 oceanColor = mix(shallowOceanColor, deepOceanColor, fresnel);

// 反射混合
vec4 finalColor = mix(oceanColor, skyReflection, fresnel * reflectionStrength);

// 泡沫叠加
finalColor = mix(finalColor, foam, foamFactor);

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

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

🔧 TypeScript水面控制系统

水面效果控制器

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
import { Component, Material, Vec2, Vec4, _decorator } from 'cc';

const { ccclass, property, menu } = _decorator;

@ccclass('WaterController')
@menu('Custom/WaterController')
export class WaterController extends Component {

@property({ type: Vec4, displayName: '水面颜色' })
public waterColor: Vec4 = new Vec4(0, 0.3, 0.6, 0.8);

@property({ range: [0, 1], displayName: '波浪振幅' })
public waveAmplitude: number = 0.1;

@property({ range: [0.1, 10], displayName: '波浪频率' })
public waveFrequency: number = 2.0;

@property({ range: [0, 5], displayName: '波浪速度' })
public waveSpeed: number = 1.0;

@property({ type: Vec2, displayName: '风向' })
public windDirection: Vec2 = new Vec2(1, 0);

@property({ range: [0, 3], displayName: '法线强度' })
public normalStrength: number = 1.0;

@property({ range: [0, 1], displayName: '反射强度' })
public reflectionStrength: number = 0.8;

@property({ range: [0, 1], displayName: '透明度' })
public transparency: number = 0.7;

@property({ displayName: '启用焦散' })
public enableCaustics: boolean = false;

@property({ range: [0, 5], displayName: '焦散强度' })
public causticIntensity: number = 2.0;

private _material: Material | null = null;
private _time: number = 0;

onLoad() {
const renderer = this.getComponent('cc.MeshRenderer');
if (renderer && renderer.material) {
this._material = renderer.material;
this.updateMaterial();
}
}

update(deltaTime: number) {
this._time += deltaTime;
this.updateMaterial();
}

private updateMaterial() {
if (!this._material) return;

// 基础属性
this._material.setProperty('waterColor', this.waterColor);
this._material.setProperty('waveAmplitude', this.waveAmplitude);
this._material.setProperty('waveFrequency', this.waveFrequency);
this._material.setProperty('waveSpeed', this.waveSpeed);
this._material.setProperty('normalStrength', this.normalStrength);
this._material.setProperty('reflectionStrength', this.reflectionStrength);
this._material.setProperty('transparency', this.transparency);

// 风向
this._material.setProperty('windDirection', this.windDirection);

// 焦散效果
if (this.enableCaustics) {
this._material.setProperty('causticIntensity', this.causticIntensity);
}
}

// 天气效果
public setCalmWater() {
this.waveAmplitude = 0.05;
this.waveSpeed = 0.5;
this.updateMaterial();
}

public setRoughSea() {
this.waveAmplitude = 0.3;
this.waveSpeed = 3.0;
this.updateMaterial();
}

public setStormyWater() {
this.waveAmplitude = 0.5;
this.waveSpeed = 5.0;
this.windDirection = new Vec2(Math.random() - 0.5, Math.random() - 0.5);
this.updateMaterial();
}

// 时间控制
public setDayTime() {
this.waterColor = new Vec4(0, 0.4, 0.7, 0.8);
this.reflectionStrength = 0.6;
this.updateMaterial();
}

public setNightTime() {
this.waterColor = new Vec4(0, 0.1, 0.3, 0.9);
this.reflectionStrength = 0.9;
this.updateMaterial();
}

// 深度效果
public setShallowWater() {
this.waterColor = new Vec4(0.2, 0.6, 0.8, 0.6);
this.transparency = 0.5;
this.updateMaterial();
}

public setDeepWater() {
this.waterColor = new Vec4(0, 0.1, 0.4, 0.9);
this.transparency = 0.8;
this.updateMaterial();
}
}

📖 本章总结

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

  • ✅水面渲染的物理原理和数学模型
  • ✅波浪动画和Gerstner波浪技术
  • ✅反射、折射和菲涅尔效果
  • ✅焦散和泡沫的高级渲染技术

🚀 下一步学习

完成了第8章的自定义Surface Shader学习后,建议继续学习:
👉 第9章:2D着色器特效

💡 实践练习

  1. 创建一个池塘水面效果
  2. 实现海浪冲击岸边的泡沫效果
  3. 开发水下看向水面的折射效果

系列导航