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
|
#version 440
layout(location=0) in vec3 v_nearPoint;
layout(location=1) in vec3 v_farPoint;
layout(location=2) in flat uint v_viewIndex;
layout(location=0) out vec4 fragColor;
layout(std140, binding=0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
mat4 viewProj[QSHADER_VIEW_COUNT];
mat4 invViewProj[QSHADER_VIEW_COUNT];
#else
mat4 viewProj;
mat4 invViewProj;
#endif
float near;
float far;
float scale;
float yFactor;
uint gridFlags;
} u_buf;
// gridFlag enum values:
const uint drawAxisFlag = 1;
vec4 grid(vec2 fragPos2D, float scale, bool drawAxis, float lineAlpha) {
vec2 coord = fragPos2D * scale; // use the scale variable to set the distance between the lines
vec2 derivative = fwidth(coord); // derivative is pixel spacing in grid coords (1/derivative is distance between grid lines in pixels)
vec2 grid = abs(fract(coord - 0.5) - 0.5) / derivative; // distance from grid in pixels
float line = min(grid.x, grid.y);
float alpha = smoothstep(0.0, 1, 1 - line) * lineAlpha;
vec4 color = vec4(0.5, 0.5, 0.5, alpha); // NOT premultipled
float minimumx = min(derivative.x, 1.0);
float minimumz = min(derivative.y, 1.0);
if (drawAxis) {
vec2 axisDistance = abs(coord/derivative);
if (axisDistance.y < 1) // distance to first axis
color = vec4(1.0, 0.0, 0.0, alpha);
if (axisDistance.x < 1) // distance to second axis
color = vec4(0.0, 0.0, 1.0, alpha);
}
// Fade the lines when they get too close together
const float fadeLimit = 0.1; // Start fading at grid spacing 1/fadeLimit
const float fadeGradient = 1.5; // Fade out completely at length(derivative) == fadeLimit + 1/fadeGradient
float d2 = length(derivative);
if (d2 > fadeLimit)
color.a *= max(0.0, 1 - fadeGradient * (d2 - fadeLimit));
return color;
}
float computeLinearDepth(float fragDepth, float near, float far) {
if (near <= 0.0)
return fragDepth;
float linearDepth = (2.0 * near * far) / (near + far - fragDepth * (far - near));
return linearDepth / far;
}
void main(void)
{
const float nearFloor = v_nearPoint.y;
const float farFloor = v_farPoint.y;
// Find where the sight line intersects the floor
const float t = -nearFloor / (farFloor - nearFloor);
// Fragment posision in 3D world space
const vec3 fragPos3D = v_nearPoint + t * (v_farPoint - v_nearPoint);
const vec2 fragPos2D = fragPos3D.xz;
// Getting the fragment position in clip space
#if QSHADER_VIEW_COUNT >= 2
const vec4 clipSpacePos = u_buf.viewProj[v_viewIndex] * vec4(fragPos3D, 1.0);
#else
const vec4 clipSpacePos = u_buf.viewProj * vec4(fragPos3D, 1.0);
#endif
// The out color is a combination of a larger grid and a smaller grid.
// The grid is only shown at y=0.
const bool drawAxis = (u_buf.gridFlags & drawAxisFlag) != 0;
const vec4 outColor = max(grid(fragPos2D, 10.0*u_buf.scale, drawAxis, 0.5), grid(fragPos2D, 1.0*u_buf.scale, drawAxis, 1.0));
// Fade at far clip: Calculating the depth of the fragment, the linear
// depth and using the linear depth to calculate the fading constant
// Do a hard cutoff at near clip, since that is less common, and we don't have an
// instinct to expect a near fade anyway
const float fragDepth = clipSpacePos.z / clipSpacePos.w;
const float linearDepth = computeLinearDepth(fragDepth, u_buf.near, u_buf.far);
const float fading = float(fragDepth > -1 && fragDepth < 1) * (1-max((linearDepth - 0.7)/0.3, 0.0));
fragColor = vec4(outColor.rgb, outColor.a * fading);
gl_FragDepth = 0.5 * fragDepth + 0.5;
}
|