summaryrefslogtreecommitdiff
path: root/servers/rendering/rasterizer_rd/shaders/ssao_blur.glsl
blob: 3e63e3cb5979e43e6d8b7f490f5f4d81bd7f1cd7 (plain)
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
#[compute]

#version 450

VERSION_DEFINES

layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;

layout(set = 0, binding = 0) uniform sampler2D source_ssao;
layout(set = 1, binding = 0) uniform sampler2D source_depth;
#ifdef MODE_UPSCALE
layout(set = 2, binding = 0) uniform sampler2D source_depth_mipmaps;
#endif

layout(r8, set = 3, binding = 0) uniform restrict writeonly image2D dest_image;

//////////////////////////////////////////////////////////////////////////////////////////////
// Tunable Parameters:

layout(push_constant, binding = 1, std430) uniform Params {
	float edge_sharpness; /** Increase to make depth edges crisper. Decrease to reduce flicker. */
	int filter_scale;
	float z_far;
	float z_near;
	bool orthogonal;
	uint pad0;
	uint pad1;
	uint pad2;
	ivec2 axis; /** (1, 0) or (0, 1) */
	ivec2 screen_size;
}
params;

/** Filter radius in pixels. This will be multiplied by SCALE. */
#define R (4)

//////////////////////////////////////////////////////////////////////////////////////////////

// Gaussian coefficients
const float gaussian[R + 1] =
		//float[](0.356642, 0.239400, 0.072410, 0.009869);
		//float[](0.398943, 0.241971, 0.053991, 0.004432, 0.000134);  // stddev = 1.0
		float[](0.153170, 0.144893, 0.122649, 0.092902, 0.062970); // stddev = 2.0
//float[](0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108); // stddev = 3.0

void main() {
	// Pixel being shaded
	ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
	if (any(greaterThanEqual(ssC, params.screen_size))) { //too large, do nothing
		return;
	}

#ifdef MODE_UPSCALE

	//closest one should be the same pixel, but check nearby just in case
	float depth = texelFetch(source_depth, ssC, 0).r;

	depth = depth * 2.0 - 1.0;
	if (params.orthogonal) {
		depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
	} else {
		depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
	}

	vec2 pixel_size = 1.0 / vec2(params.screen_size);
	vec2 closest_uv = vec2(ssC) * pixel_size + pixel_size * 0.5;
	vec2 from_uv = closest_uv;
	vec2 ps2 = pixel_size; // * 2.0;

	float closest_depth = abs(textureLod(source_depth_mipmaps, closest_uv, 0.0).r - depth);

	vec2 offsets[4] = vec2[](vec2(ps2.x, 0), vec2(-ps2.x, 0), vec2(0, ps2.y), vec2(0, -ps2.y));
	for (int i = 0; i < 4; i++) {
		vec2 neighbour = from_uv + offsets[i];
		float neighbour_depth = abs(textureLod(source_depth_mipmaps, neighbour, 0.0).r - depth);
		if (neighbour_depth < closest_depth) {
			closest_uv = neighbour;
			closest_depth = neighbour_depth;
		}
	}

	float visibility = textureLod(source_ssao, closest_uv, 0.0).r;
	imageStore(dest_image, ssC, vec4(visibility));
#else

	float depth = texelFetch(source_depth, ssC, 0).r;

#ifdef MODE_FULL_SIZE
	depth = depth * 2.0 - 1.0;

	if (params.orthogonal) {
		depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
	} else {
		depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
	}

#endif
	float depth_divide = 1.0 / params.z_far;

	//depth *= depth_divide;

	/*
	if (depth > params.z_far * 0.999) {
		discard; //skybox
	}
	*/

	float sum = texelFetch(source_ssao, ssC, 0).r;

	// Base weight for depth falloff.  Increase this for more blurriness,
	// decrease it for better edge discrimination
	float BASE = gaussian[0];
	float totalWeight = BASE;
	sum *= totalWeight;

	ivec2 clamp_limit = params.screen_size - ivec2(1);

	for (int r = -R; r <= R; ++r) {
		// We already handled the zero case above.  This loop should be unrolled and the static branch optimized out,
		// so the IF statement has no runtime cost
		if (r != 0) {
			ivec2 ppos = ssC + params.axis * (r * params.filter_scale);
			float value = texelFetch(source_ssao, clamp(ppos, ivec2(0), clamp_limit), 0).r;
			ivec2 rpos = clamp(ppos, ivec2(0), clamp_limit);

			float temp_depth = texelFetch(source_depth, rpos, 0).r;
#ifdef MODE_FULL_SIZE
			temp_depth = temp_depth * 2.0 - 1.0;
			if (params.orthogonal) {
				temp_depth = ((temp_depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
			} else {
				temp_depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - temp_depth * (params.z_far - params.z_near));
			}
			//temp_depth *= depth_divide;
#endif
			// spatial domain: offset gaussian tap
			float weight = 0.3 + gaussian[abs(r)];
			//weight *= max(0.0, dot(temp_normal, normal));

			// range domain (the "bilateral" weight). As depth difference increases, decrease weight.
			weight *= max(0.0, 1.0 - params.edge_sharpness * abs(temp_depth - depth));

			sum += value * weight;
			totalWeight += weight;
		}
	}

	const float epsilon = 0.0001;
	float visibility = sum / (totalWeight + epsilon);

	imageStore(dest_image, ssC, vec4(visibility));
#endif
}