Skip to content

Commit f83bf1c

Browse files
committed
Update contact shadow renderer quality
1 parent 5352d69 commit f83bf1c

File tree

5 files changed

+174
-23
lines changed

5 files changed

+174
-23
lines changed

ContactShadowRenderer.js

Lines changed: 154 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ class ContactShadowRenderer extends Renderer {
44
if (!this.target._renderer.hasWebGL2) {
55
this.target._renderer.GL.getExtension('OES_standard_derivatives')
66
}
7+
this.fbo2 = target.createFramebuffer(options)
8+
this.blurShader = target.createShader(this.blurVert(), this.blurFrag())
79
this.intensity = 0.5
8-
this.numSamples = 15
10+
this.numShadowSamples = 15
11+
this.numBlurSamples = 20
912
this.exponent = 250
10-
this.bias = 1.
13+
this.bias = 0.1
1114
this.searchRadius = 100
15+
this.blurRadius = 50
1216
}
1317

1418
prefix() {
@@ -27,11 +31,25 @@ class ContactShadowRenderer extends Renderer {
2731
return this.prefix() + ContactShadowRenderer.frag
2832
}
2933

34+
blurVert() {
35+
return this.vert()
36+
}
37+
38+
blurFrag() {
39+
return this.prefix() + ContactShadowRenderer.blurFrag
40+
}
41+
3042
setIntensity(intensity) {
3143
this.intensity = intensity
3244
}
33-
setSamples(numSamples) {
34-
this.numSamples = numSamples
45+
setShadowSamples(numSamples) {
46+
this.numShadowSamples = numSamples
47+
}
48+
setBlurSamples(numSamples) {
49+
this.numBlurSamples = numSamples
50+
}
51+
setBlurRadius(r) {
52+
this.blurRadius = r
3553
}
3654
setExponent(exponent) {
3755
this.exponent = exponent
@@ -43,7 +61,7 @@ class ContactShadowRenderer extends Renderer {
4361
this.searchRadius = radius
4462
}
4563

46-
getUniforms() {
64+
getShadowUniforms() {
4765
const projInfo = [
4866
-2 / (this.target.width * this.target._renderer.uPMatrix.mat4[0]),
4967
-2 / (this.target.height * this.target._renderer.uPMatrix.mat4[5]),
@@ -56,7 +74,7 @@ class ContactShadowRenderer extends Renderer {
5674
uDepth: this.fbo.depth,
5775
uSize: [this.target.width, this.target.height],
5876
uIntensity: this.intensity,
59-
uNumSamples: this.numSamples,
77+
uNumSamples: this.numShadowSamples,
6078
uNear: this.target._renderer._curCamera.cameraNear,
6179
uFar: this.target._renderer._curCamera.cameraFar,
6280
uProjInfo: projInfo,
@@ -65,6 +83,55 @@ class ContactShadowRenderer extends Renderer {
6583
uSearchRadius: this.searchRadius,
6684
}
6785
}
86+
87+
getBlurUniforms() {
88+
return {
89+
uImg: this.fbo.color,
90+
uDepth: this.fbo.depth,
91+
uShadow: this.fbo2.color,
92+
uSize: [this.target.width, this.target.height],
93+
uIntensity: this.intensity,
94+
uNear: this.target._renderer._curCamera.cameraNear,
95+
uFar: this.target._renderer._curCamera.cameraFar,
96+
uNumSamples: this.numBlurSamples,
97+
uBlurRadius: this.blurRadius,
98+
}
99+
}
100+
101+
draw(cb) {
102+
const shadowUniforms = this.getShadowUniforms()
103+
const blurUniforms = this.getBlurUniforms()
104+
105+
this.fbo.draw(() => {
106+
this.target.push()
107+
cb()
108+
this.target.pop()
109+
})
110+
111+
this.target.push()
112+
113+
this.fbo2.draw(() => {
114+
this.target.push()
115+
this.target.clear()
116+
this.target.noStroke()
117+
this.target.rectMode(CENTER)
118+
this.target.shader(this.shader)
119+
for (const key in shadowUniforms) {
120+
this.shader.setUniform(key, shadowUniforms[key])
121+
}
122+
this.target.rect(0, 0, this.target.width, -this.target.height)
123+
this.target.pop()
124+
})
125+
126+
this.target.noStroke()
127+
this.target.rectMode(CENTER)
128+
this.target.shader(this.blurShader)
129+
for (const key in blurUniforms) {
130+
this.blurShader.setUniform(key, blurUniforms[key])
131+
}
132+
this.target.rect(0, 0, this.target.width, -this.target.height)
133+
this.target.pop()
134+
}
68135
}
69136

70137
p5.prototype.createContactShadowRenderer = function() {
@@ -120,11 +187,14 @@ uniform float uIntensity;
120187
uniform float uExponent;
121188
uniform float uBias;
122189
123-
const int MAX_NUM_SAMPLES = 50;
190+
const int MAX_NUM_SAMPLES = 100;
124191
125-
float rand(vec2 co){
192+
float rand(vec2 co) {
126193
return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
127194
}
195+
float rand(vec4 co) {
196+
return fract(rand(co.xz) + rand(co.xy) + rand(co.yw) + rand(co.zw));
197+
}
128198
129199
vec3 worldFromScreen(vec2 offset) {
130200
#ifdef IS_WEBGL2
@@ -189,8 +259,8 @@ void main() {
189259
float t = (float(i + 1) / float(uNumSamples));
190260
191261
// Sample a sort of random ish coordinate in a half sphere pointing up
192-
float phi = t * 11. * ${2 * Math.PI} + 0.5*rand(gl_FragCoord.xy);
193-
float theta = t * ${Math.PI / 2} + 0.5*rand(gl_FragCoord.xy);
262+
float phi = ${2 * Math.PI} * rand(vec4(gl_FragCoord.xy,t*100.,0.));
263+
float theta = ${Math.PI / 2} * rand(vec4(gl_FragCoord.xy,t*100.,100.));
194264
float radius = 1.0 - t*t;
195265
vec3 localOff = vec3(
196266
radius * cos(phi) * sin(theta),
@@ -228,10 +298,82 @@ void main() {
228298
}
229299
occlusion = 1.0 - (occlusion / float(uNumSamples));
230300
occlusion = clamp(pow(occlusion, 1.0 + uExponent), 0.0, 1.0);
301+
vec4 finalColor = vec4(occlusion, occlusion, occlusion, 1.);
302+
#ifdef IS_WEBGL2
303+
outColor = finalColor;
304+
#else
305+
gl_FragColor = finalColor;
306+
#endif
307+
}
308+
`
309+
310+
ContactShadowRenderer.blurFrag = `
311+
precision highp float;
312+
#ifdef IS_WEBGL2
313+
in highp vec2 vVertTexCoord;
314+
out highp vec4 outColor;
315+
#else
316+
varying highp vec2 vVertTexCoord;
317+
#endif
318+
319+
uniform sampler2D uImg;
320+
uniform sampler2D uDepth;
321+
uniform sampler2D uShadow;
322+
uniform vec2 uSize;
323+
uniform float uNear;
324+
uniform float uFar;
325+
uniform float uIntensity;
326+
uniform int uNumSamples;
327+
uniform float uBlurRadius;
328+
329+
#ifdef IS_WEBGL2
330+
#define texFn texture
331+
#else
332+
#define texFn texture2D
333+
#endif
334+
335+
float depthToZ(float depth) {
336+
float depthNormalized = 2.0 * depth - 1.0;
337+
return 2.0 * uNear * uFar / (uFar + uNear - depthNormalized * (uFar - uNear));
338+
}
339+
340+
const int MAX_NUM_SAMPLES = 100;
341+
342+
float rand(vec2 co) {
343+
return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
344+
}
345+
float rand(vec4 co) {
346+
return fract(rand(co.xz) + rand(co.xy) + rand(co.yw) + rand(co.zw));
347+
}
348+
349+
void main() {
350+
vec4 color = texFn(uImg, vVertTexCoord);
351+
352+
float origZ = depthToZ(texFn(uDepth, vVertTexCoord).x);
353+
float occlusion = texFn(uShadow, vVertTexCoord).x;
354+
float total = 1.;
355+
356+
for (int i = 0; i < MAX_NUM_SAMPLES; i++) {
357+
if (i >= uNumSamples) break;
358+
float t = (float(i) / float(uNumSamples - 1));
359+
float angle = (t*12.0) * ${2 * Math.PI};
360+
float radius = 1.0 - t;
361+
angle += 5.*rand(gl_FragCoord.xy);
362+
363+
vec2 offset = (vec2(cos(angle),sin(angle)) * radius * uBlurRadius)/uSize;
364+
float z = depthToZ(texFn(uDepth, vVertTexCoord + offset).x);
365+
366+
float weight = float(z >= origZ);
367+
float shadowSample = texFn(uShadow, vVertTexCoord + offset).x;
368+
occlusion += weight * shadowSample;
369+
total += weight;
370+
}
371+
occlusion /= total;
372+
vec4 mixedColor = vec4(color.rgb * mix(1., occlusion, uIntensity), color.a);
231373
#ifdef IS_WEBGL2
232-
outColor = vec4(color.rgb * mix(1., occlusion, uIntensity), color.a);
374+
outColor = mixedColor;
233375
#else
234-
gl_FragColor = vec4(color.rgb * mix(1., occlusion, uIntensity), color.a);
376+
gl_FragColor = mixedColor;
235377
#endif
236378
}
237379
`

GaussianBlurRenderer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ class GaussianBlurRenderer extends BlurRenderer {
3535
this.target.shader(this.shader)
3636
for (const key in uniforms) {
3737
this.shader.setUniform(key, uniforms[key])
38-
this.shader.setUniform('uDirection', 0)
39-
this.shader.setUniform('uImg', this.fbo.color)
4038
}
39+
this.shader.setUniform('uDirection', 0)
40+
this.shader.setUniform('uImg', this.fbo.color)
4141
this.target.rect(0, 0, this.target.width, -this.target.height)
4242
this.target.pop()
4343
})

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ Add the library to your source code, *after* loading p5 but *before* loading you
1818

1919
### Via CDN
2020
```html
21-
<script src="https://cdn.jsdelivr.net/npm/@davepagurek/[email protected].8/p5.Framebuffer.min.js"></script>
21+
<script src="https://cdn.jsdelivr.net/npm/@davepagurek/[email protected].9/p5.Framebuffer.min.js"></script>
2222
```
2323

2424
On OpenProcessing, paste this link into a new library slot:
2525
```
26-
https://cdn.jsdelivr.net/npm/@davepagurek/[email protected].8/p5.Framebuffer.min.js
26+
https://cdn.jsdelivr.net/npm/@davepagurek/[email protected].9/p5.Framebuffer.min.js
2727
```
2828

2929
### Self-hosted
@@ -434,9 +434,15 @@ Methods on `ContactShadowRenderer`:
434434
- `ContactShadowRenderer.prototype.setIntensity(intensity: number)`
435435
- Control how dark shadows are: 0 is no shadows, and 1 is full darkness
436436
- Defaults to 0.5
437-
- `ContactShadowRenderer.prototype.setSamples(numSamples: number)`
438-
- Control how many random samples to use in the shadow shader. More samples will look smoother but is more computationally intensive.
437+
- `ContactShadowRenderer.prototype.setShadowSamples(numSamples: number)`
438+
- Control how many random samples to use in the shadow shader. More samples will be more accurate but is more computationally intensive.
439439
- Defaults to 15
440+
- `ContactShadowRenderer.prototype.setBlurSamples(numSamples: number)`
441+
- Control how many random samples to use in the blur shader. More samples will be smoother but is more computationally intensive.
442+
- Defaults to 20
443+
- `ContactShadowRenderer.prototype.setBlurRadius(radius: number)`
444+
- Sets how far the blur extends when blurring shadows, in pixels, ignoring the pixel density
445+
- Defaults to 50
440446
- `ContactShadowRenderer.prototype.setSearchRadius(radius: number)`
441447
- Control how close together objects need to be for them to cast shadows
442448
- This is defined in *world space,* meaning all transformations are applied when checking distances

examples/shadows/sketch.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
let contactShadowRenderer
22

33
function setup() {
4-
createCanvas(400, 400, WEBGL)
4+
createCanvas(600, 600, WEBGL)
55
contactShadowRenderer = createContactShadowRenderer()
6-
contactShadowRenderer.setIntensity(0.4)
7-
contactShadowRenderer.setSearchRadius(150)
8-
contactShadowRenderer.setSamples(20)
6+
contactShadowRenderer.setIntensity(0.9)
7+
contactShadowRenderer.setExponent(500)
8+
contactShadowRenderer.setSearchRadius(100)
9+
contactShadowRenderer.setShadowSamples(10)
10+
contactShadowRenderer.setBlurSamples(20)
11+
contactShadowRenderer.setBias(0.1)
912
}
1013

1114
function draw() {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@davepagurek/p5.framebuffer",
3-
"version": "0.0.8",
3+
"version": "0.0.9",
44
"main": "p5.Framebuffer.js",
55
"author": "Dave Pagurek <[email protected]>",
66
"license": "MIT",

0 commit comments

Comments
 (0)