three-base 2.0.134 → 2.0.137

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/pass/ssao_pass.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AddEquation, CustomBlending, LinearFilter, NoBlending, NormalBlending, OneFactor, RGBAFormat, RawShaderMaterial, SrcColorFactor, Vector2, WebGLRenderTarget, Matrix4, } from "three";
1
+ import { LinearFilter, NoBlending, RGBAFormat, RawShaderMaterial, Vector2, WebGLRenderTarget, Matrix4, MultiplyBlending, } from "three";
2
2
  import fsQuad from "./fsQuad";
3
3
  const ssaoTextureTarget = new WebGLRenderTarget(512, 512, {
4
4
  minFilter: LinearFilter,
@@ -13,17 +13,18 @@ const blurTextureTarget = new WebGLRenderTarget(512, 512, {
13
13
  const size = new Vector2();
14
14
  const projectionMatrix = new Matrix4();
15
15
  const inverseProjectionMatrix = new Matrix4();
16
+ // SSAO 着色器 — 随机采样 + 法线半球判断 + 深度连续性 + 视距自适应半径
16
17
  const ssaoShader = new RawShaderMaterial({
17
18
  uniforms: {
18
19
  depthTexture: { value: null },
19
20
  resolution: { value: new Vector2(1 / 512, 1 / 512) },
20
21
  projectionMatrix: { value: new Matrix4() },
21
22
  inverseProjectionMatrix: { value: new Matrix4() },
22
- ssaoRadius: { value: 0.5 },
23
- ssaoBias: { value: 0.025 },
24
- ssaoIntensity: { value: 1.5 },
23
+ ssaoRadius: { value: 2.0 },
24
+ ssaoFalloff: { value: 5.0 },
25
+ ssaoBias: { value: 0.3 },
26
+ ssaoIntensity: { value: 1.0 },
25
27
  sampleCount: { value: 16 },
26
- // 对数深度缓冲区的 far 参数,用于反算线性视距
27
28
  cameraFar: { value: 20000 },
28
29
  },
29
30
  vertexShader: `
@@ -38,88 +39,144 @@ const ssaoShader = new RawShaderMaterial({
38
39
  #ifdef GL_ES
39
40
  precision highp float;
40
41
  #endif
41
-
42
+
42
43
  uniform sampler2D depthTexture;
43
44
  uniform vec2 resolution;
44
45
  uniform mat4 projectionMatrix;
45
46
  uniform mat4 inverseProjectionMatrix;
46
47
  uniform float ssaoRadius;
48
+ uniform float ssaoFalloff;
47
49
  uniform float ssaoBias;
48
50
  uniform float ssaoIntensity;
49
51
  uniform int sampleCount;
50
52
  uniform float cameraFar;
51
-
53
+
52
54
  varying vec2 vUv;
53
-
54
- // 将对数深度值反算回线性视距(正数,单位与世界坐标一致)
55
- // Three.js 对数深度编码: logDepth = log2(viewZ + 1.0) / log2(cameraFar + 1.0)
55
+
56
56
  float linearizeLogDepth(float logDepth) {
57
57
  return pow(cameraFar + 1.0, logDepth) - 1.0;
58
58
  }
59
-
60
- // 从屏幕 UV 和视距重建视空间坐标
61
- // viewDistance: 相机到该像素的视距(正数)
62
- vec3 reconstructPosition(vec2 uv, float viewDistance) {
63
- // 用 inverseProjectionMatrix 求出该像素的视空间射线方向
59
+
60
+ vec3 reconstructPosition(vec2 uv, float viewZ) {
64
61
  vec4 ndc = vec4(uv * 2.0 - 1.0, 0.0, 1.0);
65
62
  vec4 viewDir4 = inverseProjectionMatrix * ndc;
66
63
  vec3 viewDir = viewDir4.xyz / viewDir4.w;
67
- // viewDir.z 为负数(相机看向 -Z),viewDistance 为正数
68
- return viewDir * (viewDistance / (-viewDir.z));
64
+ return viewDir * (viewZ / (-viewDir.z));
69
65
  }
70
-
66
+
67
+ // 从深度图重建视空间法线(有限差分法)
68
+ vec3 reconstructNormal(vec2 uv, vec2 res) {
69
+ vec2 step = res * 2.0;
70
+
71
+ float dC = texture2D(depthTexture, uv).r;
72
+ float dL = texture2D(depthTexture, uv - vec2(step.x, 0.0)).r;
73
+ float dR = texture2D(depthTexture, uv + vec2(step.x, 0.0)).r;
74
+ float dD = texture2D(depthTexture, uv - vec2(0.0, step.y)).r;
75
+ float dU = texture2D(depthTexture, uv + vec2(0.0, step.y)).r;
76
+
77
+ if (dL >= 1.0 || dR >= 1.0 || dD >= 1.0 || dU >= 1.0 || dC >= 1.0) {
78
+ return vec3(0.0, 0.0, 1.0);
79
+ }
80
+
81
+ // 深度不连续时回退为中心深度
82
+ float edgeThreshold = 0.003;
83
+ float dL2 = abs(dL - dC) < edgeThreshold ? dL : dC;
84
+ float dR2 = abs(dR - dC) < edgeThreshold ? dR : dC;
85
+ float dD2 = abs(dD - dC) < edgeThreshold ? dD : dC;
86
+ float dU2 = abs(dU - dC) < edgeThreshold ? dU : dC;
87
+
88
+ float vC = linearizeLogDepth(dC);
89
+ float vL = linearizeLogDepth(dL2);
90
+ float vR = linearizeLogDepth(dR2);
91
+ float vD = linearizeLogDepth(dD2);
92
+ float vU = linearizeLogDepth(dU2);
93
+
94
+ vec3 pC = reconstructPosition(uv, vC);
95
+ vec3 pL = reconstructPosition(uv - vec2(step.x, 0.0), vL);
96
+ vec3 pR = reconstructPosition(uv + vec2(step.x, 0.0), vR);
97
+ vec3 pD = reconstructPosition(uv - vec2(0.0, step.y), vD);
98
+ vec3 pU = reconstructPosition(uv + vec2(0.0, step.y), vU);
99
+
100
+ vec3 tangentX = pR - pL;
101
+ vec3 tangentY = pU - pD;
102
+
103
+ if (length(tangentX) < 0.001 || length(tangentY) < 0.001) {
104
+ return vec3(0.0, 0.0, 1.0);
105
+ }
106
+
107
+ vec3 normal = cross(normalize(tangentX), normalize(tangentY));
108
+ return normalize(normal);
109
+ }
110
+
71
111
  float random(vec2 seed) {
72
112
  return fract(sin(dot(seed, vec2(12.9898, 78.233))) * 43758.5453);
73
113
  }
74
-
114
+
75
115
  void main() {
76
116
  float logDepth = texture2D(depthTexture, vUv).r;
77
-
117
+
78
118
  // 背景 / 无穷远 → 无遮挡
79
119
  if (logDepth >= 1.0) {
80
120
  gl_FragColor = vec4(1.0);
81
121
  return;
82
122
  }
83
-
84
- // 对数深度 → 线性视距
85
- float viewDistance = linearizeLogDepth(logDepth);
86
- vec3 position = reconstructPosition(vUv, viewDistance);
87
-
123
+
124
+ float viewZ = linearizeLogDepth(logDepth);
125
+ vec3 position = reconstructPosition(vUv, viewZ);
126
+ vec3 normal = reconstructNormal(vUv, resolution);
127
+
128
+ // 视距自适应:ssaoRadius 是世界空间遮蔽半径,换算为像素半径
129
+ // 近处像素多 → 像素半径大;远处像素少 → 像素半径小
130
+ float projScale = 1.0 / (2.0 * abs(viewZ) * resolution.x);
131
+ float pixelRadius = max(ssaoRadius * projScale, 1.0);
132
+
88
133
  float occlusion = 0.0;
89
- float radius = ssaoRadius;
90
-
91
134
  float angle = random(vUv) * 6.2831853;
92
135
  float stepAngle = 6.2831853 / float(sampleCount);
93
-
136
+
94
137
  for (int i = 0; i < 64; i++) {
95
138
  if (i >= sampleCount) break;
96
-
139
+
97
140
  float theta = angle + float(i) * stepAngle;
98
- float r = sqrt(float(i + 1)) * (radius / sqrt(float(sampleCount)));
99
-
100
- vec2 offset = vec2(cos(theta), sin(theta)) * r;
101
- vec2 sampleUv = vUv + offset * resolution;
102
-
141
+ float r = sqrt(float(i + 1)) * (pixelRadius / sqrt(float(sampleCount)));
142
+
143
+ // 屏幕空间偏移(像素 → UV)
144
+ vec2 sampleUv = vUv + vec2(cos(theta), sin(theta)) * r * resolution;
145
+
103
146
  if (sampleUv.x < 0.0 || sampleUv.x > 1.0 || sampleUv.y < 0.0 || sampleUv.y > 1.0) {
104
147
  continue;
105
148
  }
106
-
149
+
107
150
  float sampleLogDepth = texture2D(depthTexture, sampleUv).r;
108
- float sampleDistance = linearizeLogDepth(sampleLogDepth);
109
- vec3 samplePosition = reconstructPosition(sampleUv, sampleDistance);
110
-
151
+ // 背景像素跳过
152
+ if (sampleLogDepth >= 1.0) continue;
153
+
154
+ // 深度连续性:不同表面跳过(真正夹角深度值接近,假夹角深度差大)
155
+ float depthDiff = abs(sampleLogDepth - logDepth);
156
+ if (depthDiff > 0.002) continue;
157
+
158
+ float sampleViewZ = linearizeLogDepth(sampleLogDepth);
159
+ vec3 samplePosition = reconstructPosition(sampleUv, sampleViewZ);
160
+
111
161
  vec3 diff = samplePosition - position;
112
162
  float dist = length(diff);
113
-
114
- if (dist < radius && diff.z > ssaoBias) {
115
- float rangeCheck = smoothstep(0.0, radius, dist);
116
- occlusion += (1.0 - rangeCheck) * (1.0 - diff.z / radius);
163
+
164
+ // 半球判断:采样方向必须在法线半球侧
165
+ // tangentDist = dot(diff, normal) 是到切面平面的有符号距离
166
+ // 平坦面 ≈ 0(不会超过 bias);夹角处显著 > 0
167
+ float tangentDist = dot(diff, normal);
168
+ if (tangentDist < ssaoBias) continue;
169
+
170
+ // 在衰减范围内
171
+ if (dist < ssaoFalloff) {
172
+ float rangeCheck = smoothstep(0.0, ssaoFalloff, dist);
173
+ occlusion += (1.0 - rangeCheck) * (1.0 - tangentDist / ssaoFalloff);
117
174
  }
118
175
  }
119
-
176
+
120
177
  occlusion = 1.0 - (occlusion * ssaoIntensity / float(sampleCount));
121
178
  occlusion = clamp(occlusion, 0.0, 1.0);
122
-
179
+
123
180
  gl_FragColor = vec4(vec3(occlusion), 1.0);
124
181
  }`,
125
182
  blending: NoBlending,
@@ -132,6 +189,7 @@ const blurHShader = new RawShaderMaterial({
132
189
  tDiffuse: { value: null },
133
190
  resolution: { value: new Vector2(1 / 512, 1 / 512) },
134
191
  blurRadius: { value: 2.0 },
192
+ depthTexture: { value: null },
135
193
  },
136
194
  vertexShader: `
137
195
  attribute vec3 position;
@@ -145,24 +203,29 @@ const blurHShader = new RawShaderMaterial({
145
203
  #ifdef GL_ES
146
204
  precision highp float;
147
205
  #endif
148
-
206
+
149
207
  uniform sampler2D tDiffuse;
150
208
  uniform vec2 resolution;
151
209
  uniform float blurRadius;
152
-
210
+ uniform sampler2D depthTexture;
211
+
153
212
  varying vec2 vUv;
154
-
213
+
155
214
  void main() {
215
+ float centerDepth = texture2D(depthTexture, vUv).r;
156
216
  vec4 color = vec4(0.0);
157
217
  float totalWeight = 0.0;
158
-
218
+
159
219
  for (int i = -4; i <= 4; i++) {
160
220
  float weight = exp(-float(i * i) / 8.0);
161
221
  vec2 offset = vec2(float(i) * blurRadius * resolution.x, 0.0);
162
- color += texture2D(tDiffuse, vUv + offset) * weight;
163
- totalWeight += weight;
222
+ vec2 sampleUv = vUv + offset;
223
+ float sampleDepth = texture2D(depthTexture, sampleUv).r;
224
+ float depthWeight = abs(sampleDepth - centerDepth) < 0.001 ? 1.0 : 0.1;
225
+ color += texture2D(tDiffuse, sampleUv) * weight * depthWeight;
226
+ totalWeight += weight * depthWeight;
164
227
  }
165
-
228
+
166
229
  gl_FragColor = color / totalWeight;
167
230
  }`,
168
231
  blending: NoBlending,
@@ -175,6 +238,7 @@ const blurVShader = new RawShaderMaterial({
175
238
  tDiffuse: { value: null },
176
239
  resolution: { value: new Vector2(1 / 512, 1 / 512) },
177
240
  blurRadius: { value: 2.0 },
241
+ depthTexture: { value: null },
178
242
  },
179
243
  vertexShader: `
180
244
  attribute vec3 position;
@@ -188,24 +252,29 @@ const blurVShader = new RawShaderMaterial({
188
252
  #ifdef GL_ES
189
253
  precision highp float;
190
254
  #endif
191
-
255
+
192
256
  uniform sampler2D tDiffuse;
193
257
  uniform vec2 resolution;
194
258
  uniform float blurRadius;
195
-
259
+ uniform sampler2D depthTexture;
260
+
196
261
  varying vec2 vUv;
197
-
262
+
198
263
  void main() {
264
+ float centerDepth = texture2D(depthTexture, vUv).r;
199
265
  vec4 color = vec4(0.0);
200
266
  float totalWeight = 0.0;
201
-
267
+
202
268
  for (int i = -4; i <= 4; i++) {
203
269
  float weight = exp(-float(i * i) / 8.0);
204
270
  vec2 offset = vec2(0.0, float(i) * blurRadius * resolution.y);
205
- color += texture2D(tDiffuse, vUv + offset) * weight;
206
- totalWeight += weight;
271
+ vec2 sampleUv = vUv + offset;
272
+ float sampleDepth = texture2D(depthTexture, sampleUv).r;
273
+ float depthWeight = abs(sampleDepth - centerDepth) < 0.001 ? 1.0 : 0.1;
274
+ color += texture2D(tDiffuse, sampleUv) * weight * depthWeight;
275
+ totalWeight += weight * depthWeight;
207
276
  }
208
-
277
+
209
278
  gl_FragColor = color / totalWeight;
210
279
  }`,
211
280
  blending: NoBlending,
@@ -213,10 +282,11 @@ const blurVShader = new RawShaderMaterial({
213
282
  depthWrite: false,
214
283
  transparent: true,
215
284
  });
216
- const compositeShader = new RawShaderMaterial({
285
+ const outputShader = new RawShaderMaterial({
217
286
  uniforms: {
218
287
  ssaoTexture: { value: null },
219
288
  ssaoStrength: { value: 1.0 },
289
+ debug: { value: 0.0 },
220
290
  },
221
291
  vertexShader: `
222
292
  attribute vec3 position;
@@ -230,65 +300,73 @@ const compositeShader = new RawShaderMaterial({
230
300
  #ifdef GL_ES
231
301
  precision highp float;
232
302
  #endif
233
-
303
+
234
304
  uniform sampler2D ssaoTexture;
235
305
  uniform float ssaoStrength;
236
-
306
+ uniform float debug;
307
+
237
308
  varying vec2 vUv;
238
-
309
+
239
310
  void main() {
240
311
  float ssao = texture2D(ssaoTexture, vUv).r;
241
312
  float finalOcclusion = mix(1.0, ssao, ssaoStrength);
242
-
243
- gl_FragColor = vec4(finalOcclusion, finalOcclusion, finalOcclusion, 1.0);
313
+
314
+ if (debug > 0.5) {
315
+ gl_FragColor = vec4(vec3(ssao), 1.0);
316
+ } else {
317
+ gl_FragColor = vec4(vec3(finalOcclusion), 1.0);
318
+ }
244
319
  }`,
245
- blending: CustomBlending,
246
- blendSrc: SrcColorFactor,
247
- blendDst: OneFactor,
248
- blendEquation: AddEquation,
320
+ blending: MultiplyBlending,
249
321
  depthTest: false,
250
322
  depthWrite: false,
251
- transparent: false,
323
+ transparent: true,
252
324
  });
253
325
  export default function ssao_pass(renderer, camera, depthTexture, devicePixelRatio, options) {
254
- if (!options.enable) {
326
+ if (!options.enable || !options.strength) {
255
327
  return;
256
328
  }
257
- const originRenderTarget = renderer.getRenderTarget();
258
329
  renderer.getSize(size);
259
330
  size.multiplyScalar(devicePixelRatio);
260
331
  ssaoTextureTarget.setSize(size.x, size.y);
261
332
  blurTextureTarget.setSize(size.x, size.y);
262
333
  projectionMatrix.copy(camera.projectionMatrix);
263
334
  inverseProjectionMatrix.copy(projectionMatrix).invert();
335
+ // === 1. SSAO 遮蔽计算 ===
264
336
  ssaoShader.uniforms.depthTexture.value = depthTexture;
265
337
  ssaoShader.uniforms.resolution.value.set(1 / size.x, 1 / size.y);
266
338
  ssaoShader.uniforms.projectionMatrix.value = projectionMatrix;
267
339
  ssaoShader.uniforms.inverseProjectionMatrix.value = inverseProjectionMatrix;
268
- ssaoShader.uniforms.ssaoRadius.value = options.radius || 0.5;
269
- ssaoShader.uniforms.ssaoBias.value = options.bias || 0.025;
270
- ssaoShader.uniforms.ssaoIntensity.value = options.intensity || 1.5;
340
+ ssaoShader.uniforms.ssaoRadius.value = options.radius || 2.0;
341
+ ssaoShader.uniforms.ssaoFalloff.value = options.falloff || 5.0;
342
+ ssaoShader.uniforms.ssaoBias.value = options.bias || 0.3;
343
+ ssaoShader.uniforms.ssaoIntensity.value = options.intensity || 1.0;
271
344
  ssaoShader.uniforms.sampleCount.value = options.sampleCount || 16;
272
345
  ssaoShader.uniforms.cameraFar.value = options.cameraFar || camera.far || 20000;
273
346
  renderer.setRenderTarget(ssaoTextureTarget);
274
347
  renderer.clear();
275
348
  fsQuad.render(renderer, ssaoShader);
349
+ // === 2. 双向模糊 ===
350
+ const blurRes = new Vector2(1 / size.x, 1 / size.y);
351
+ const blurR = options.blurRadius || 2.0;
276
352
  blurHShader.uniforms.tDiffuse.value = ssaoTextureTarget.texture;
277
- blurHShader.uniforms.resolution.value.set(1 / size.x, 1 / size.y);
278
- blurHShader.uniforms.blurRadius.value = options.blurRadius || 2.0;
353
+ blurHShader.uniforms.resolution.value.copy(blurRes);
354
+ blurHShader.uniforms.blurRadius.value = blurR;
355
+ blurHShader.uniforms.depthTexture.value = depthTexture;
279
356
  renderer.setRenderTarget(blurTextureTarget);
280
357
  renderer.clear();
281
358
  fsQuad.render(renderer, blurHShader);
282
359
  blurVShader.uniforms.tDiffuse.value = blurTextureTarget.texture;
283
- blurVShader.uniforms.resolution.value.set(1 / size.x, 1 / size.y);
284
- blurVShader.uniforms.blurRadius.value = options.blurRadius || 2.0;
360
+ blurVShader.uniforms.resolution.value.copy(blurRes);
361
+ blurVShader.uniforms.blurRadius.value = blurR;
362
+ blurVShader.uniforms.depthTexture.value = depthTexture;
285
363
  renderer.setRenderTarget(ssaoTextureTarget);
286
364
  renderer.clear();
287
365
  fsQuad.render(renderer, blurVShader);
288
- compositeShader.uniforms.ssaoTexture.value = ssaoTextureTarget.texture;
289
- compositeShader.uniforms.ssaoStrength.value = options.strength || 1.0;
290
- renderer.setRenderTarget(originRenderTarget);
291
- compositeShader.blending = NormalBlending;
292
- compositeShader.transparent = false;
293
- fsQuad.render(renderer, compositeShader);
366
+ // === 3. 乘法混合到屏幕 ===
367
+ renderer.setRenderTarget(null);
368
+ outputShader.uniforms.ssaoTexture.value = ssaoTextureTarget.texture;
369
+ outputShader.uniforms.ssaoStrength.value = options.strength || 1.0;
370
+ outputShader.uniforms.debug.value = options.debug ? 1.0 : 0.0;
371
+ fsQuad.render(renderer, outputShader);
294
372
  }