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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
/*
* Copyright 2021 elven cache. All rights reserved.
* License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
*/
#ifndef BOKEH_DOF_SH
#define BOKEH_DOF_SH
#define TWO_PI (6.28318531)
#define GOLDEN_ANGLE (2.39996323)
#define MAX_BLUR_SIZE (20.0)
#define RADIUS_SCALE (0.5)
float ShadertoyNoise (vec2 uv) {
return fract(sin(dot(uv.xy, vec2(12.9898,78.233))) * 43758.5453123);
}
float GetCircleOfConfusion (float depth, float focusPoint, float focusScale)
{
// if depth is less than focusPoint, result will be negative. want to keep this
// relationship so comparison of (signed) blur size is same as comparing depth.
return clamp((1.0/focusPoint - 1.0/depth) * focusScale, -1.0, 1.0);
}
float GetBlurSize (float depth, float focusPoint, float focusScale)
{
float circleOfConfusion = GetCircleOfConfusion(depth, focusPoint, focusScale);
return circleOfConfusion * u_maxBlurSize;
}
// this is the function at bottom of blog post...
//vec3 OriginalDepthOfField (vec2 texCoord, float focusPoint, float focusScale)
//{
// float depth = texture2D(s_depth, texCoord).x;
// float centerSize = GetBlurSize(depth, focusPoint, focusScale);
// vec3 color = texture2D(s_color, texCoord).xyz;
//
// float total = 1.0;
// float radius = RADIUS_SCALE;
// for (float theta = 0.0; radius < MAX_BLUR_SIZE; theta += GOLDEN_ANGLE)
// {
// vec2 spiralCoord = texCoord + vec2(cos(theta), sin(theta)) * u_viewTexel.xy * radius;
// vec3 sampleColor = texture2D(s_color, spiralCoord).xyz;
// float sampleDepth = texture2D(s_depth, spiralCoord).x;
//
// float sampleSize = GetBlurSize(sampleDepth, focusPoint, focusScale);
// if (sampleDepth > depth)
// {
// sampleSize = clamp(sampleSize, 0.0, centerSize*2.0);
// }
// float m = smoothstep(radius-0.5, radius+0.5, sampleSize);
// color += mix(color/total, sampleColor, m);
// total += 1.0;
// radius += RADIUS_SCALE/radius;
// }
// return color * (1.0/total);
//}
void GetColorAndBlurSize (
sampler2D samplerColor,
sampler2D samplerDepth,
vec2 texCoord,
float focusPoint,
float focusScale,
out vec3 outColor,
out float outBlurSize
) {
#if USE_PACKED_COLOR_AND_BLUR
vec4 colorAndBlurSize = texture2DLod(samplerColor, texCoord, 0);
vec3 color = colorAndBlurSize.xyz;
float blurSize = colorAndBlurSize.w;
outColor = color;
outBlurSize = blurSize;
#else
vec3 color = texture2DLod(samplerColor, texCoord, 0).xyz;
float depth = texture2DLod(samplerDepth, texCoord, 0).x;
float blurSize = GetBlurSize(depth, focusPoint, focusScale);
outColor = color;
outBlurSize = blurSize;
#endif
}
float BokehShapeFromAngle (float lobeCount, float radiusMin, float radiusDelta2x, float rotation, float angle)
{
// don't shape for 0, 1 blades...
if (lobeCount <= 1.0f)
{
return 1.0f;
}
// divide edge into some number of lobes
float invPeriod = lobeCount / (2.0 * 3.1415926);
float periodFraction = fract(angle * invPeriod + rotation);
// apply triangle shape to each lobe to approximate blades of a camera aperture
periodFraction = abs(periodFraction - 0.5);
return periodFraction*radiusDelta2x + radiusMin;
}
vec4 DepthOfField(
sampler2D samplerColor,
sampler2D samplerDepth,
vec2 texCoord,
float focusPoint,
float focusScale
) {
vec3 color;
float centerSize;
GetColorAndBlurSize(
samplerColor,
samplerDepth,
texCoord,
focusPoint,
focusScale,
/*out*/color,
/*out*/centerSize);
float absCenterSize = abs(centerSize);
// as sample count gets lower, visible banding. disrupt with noise.
// use a better random/noise/dither function than this..
vec2 pixelCoord = texCoord.xy * u_viewRect.zw;
float random = ShadertoyNoise(pixelCoord + vec2(314.0, 159.0)*u_frameIdx);
float theta = random * TWO_PI;
float thetaStep = GOLDEN_ANGLE;
float total = 1.0;
float totalSampleSize = 0.0;
float loopValue = u_radiusScale;
float loopEnd = u_maxBlurSize;
while (loopValue < loopEnd)
{
float radius = loopValue;
float shapeScale = BokehShapeFromAngle(
u_lobeCount,
u_lobeRadiusMin,
u_lobeRadiusDelta2x,
u_lobeRotation,
theta);
vec2 spiralCoord = texCoord + vec2(cos(theta), sin(theta)) * u_viewTexel.xy * (radius * shapeScale);
vec3 sampleColor;
float sampleSize;
GetColorAndBlurSize(
samplerColor,
samplerDepth,
spiralCoord,
focusPoint,
focusScale,
/*out*/sampleColor,
/*out*/sampleSize);
float absSampleSize = abs(sampleSize);
// using signed sample size as proxy for depth comparison
if (sampleSize > centerSize)
{
absSampleSize = clamp(absSampleSize, 0.0, absCenterSize*2.0);
}
float m = smoothstep(radius-0.5, radius+0.5, absSampleSize);
color += mix(color/total, sampleColor, m);
totalSampleSize += absSampleSize;
total += 1.0;
theta += thetaStep;
loopValue += (u_radiusScale/loopValue);
}
color *= 1.0/total;
float averageSampleSize = totalSampleSize / (total-1.0);
return vec4(color, averageSampleSize);
}
#endif
|