147 lines
5.1 KiB
GLSL
147 lines
5.1 KiB
GLSL
|
|
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
|
|
|
#define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */
|
|
|
|
/**
|
|
* We want to know how much a pixel is covered by a line.
|
|
* We replace the square pixel with acircle of the same area and try to find the intersection area.
|
|
* The area we search is the circular segment. https://en.wikipedia.org/wiki/Circular_segment
|
|
* The formula for the area uses inverse trig function and is quite complexe. Instead,
|
|
* we approximate it by using the smoothstep function and a 1.05 factor to the disc radius.
|
|
*/
|
|
#define DISC_RADIUS (M_1_SQRTPI * 1.05)
|
|
#define LINE_SMOOTH_START (0.5 - DISC_RADIUS)
|
|
#define LINE_SMOOTH_END (0.5 + DISC_RADIUS)
|
|
|
|
/**
|
|
* Returns coverage of a line onto a sample that is distance_to_line (in pixels) far from the line.
|
|
* line_kernel_size is the inner size of the line with 100% coverage.
|
|
*/
|
|
float line_coverage(float distance_to_line, float line_kernel_size)
|
|
{
|
|
if (doSmoothLines) {
|
|
return smoothstep(
|
|
LINE_SMOOTH_END, LINE_SMOOTH_START, abs(distance_to_line) - line_kernel_size);
|
|
}
|
|
else {
|
|
return step(-0.5, line_kernel_size - abs(distance_to_line));
|
|
}
|
|
}
|
|
vec4 line_coverage(vec4 distance_to_line, float line_kernel_size)
|
|
{
|
|
if (doSmoothLines) {
|
|
return smoothstep(
|
|
LINE_SMOOTH_END, LINE_SMOOTH_START, abs(distance_to_line) - line_kernel_size);
|
|
}
|
|
else {
|
|
return step(-0.5, line_kernel_size - abs(distance_to_line));
|
|
}
|
|
}
|
|
|
|
vec2 decode_line_dir(vec2 dir)
|
|
{
|
|
return dir * 2.0 - 1.0;
|
|
}
|
|
|
|
float decode_line_dist(float dist)
|
|
{
|
|
return (dist - 0.1) * 4.0 - 2.0;
|
|
}
|
|
|
|
float neighbor_dist(vec3 line_dir_and_dist, vec2 ofs)
|
|
{
|
|
float dist = decode_line_dist(line_dir_and_dist.z);
|
|
vec2 dir = decode_line_dir(line_dir_and_dist.xy);
|
|
|
|
bool is_line = line_dir_and_dist.z != 0.0;
|
|
bool dir_horiz = abs(dir.x) > abs(dir.y);
|
|
bool ofs_horiz = (ofs.x != 0);
|
|
|
|
if (!is_line || (ofs_horiz != dir_horiz)) {
|
|
dist += 1e10; /* No line. */
|
|
}
|
|
else {
|
|
dist += dot(ofs, -dir);
|
|
}
|
|
return dist;
|
|
}
|
|
|
|
void neighbor_blend(
|
|
float line_coverage, float line_depth, vec4 line_color, inout float frag_depth, inout vec4 col)
|
|
{
|
|
line_color *= line_coverage;
|
|
if (line_coverage > 0.0 && line_depth < frag_depth) {
|
|
/* Alpha over. */
|
|
col = col * (1.0 - line_color.a) + line_color;
|
|
frag_depth = line_depth;
|
|
}
|
|
else {
|
|
/* Alpha under. */
|
|
col = col + line_color * (1.0 - col.a);
|
|
}
|
|
}
|
|
|
|
void main()
|
|
{
|
|
ivec2 center_texel = ivec2(gl_FragCoord.xy);
|
|
float line_kernel = sizePixel * 0.5 - 0.5;
|
|
|
|
fragColor = texelFetch(colorTex, center_texel, 0);
|
|
|
|
bool original_col_has_alpha = fragColor.a < 1.0;
|
|
|
|
float depth = texelFetch(depthTex, center_texel, 0).r;
|
|
|
|
float dist_raw = texelFetch(lineTex, center_texel, 0).b;
|
|
float dist = decode_line_dist(dist_raw);
|
|
|
|
/* TODO: Optimization: use textureGather. */
|
|
vec4 neightbor_col0 = texelFetchOffset(colorTex, center_texel, 0, ivec2(1, 0));
|
|
vec4 neightbor_col1 = texelFetchOffset(colorTex, center_texel, 0, ivec2(-1, 0));
|
|
vec4 neightbor_col2 = texelFetchOffset(colorTex, center_texel, 0, ivec2(0, 1));
|
|
vec4 neightbor_col3 = texelFetchOffset(colorTex, center_texel, 0, ivec2(0, -1));
|
|
|
|
vec3 neightbor_line0 = texelFetchOffset(lineTex, center_texel, 0, ivec2(1, 0)).rgb;
|
|
vec3 neightbor_line1 = texelFetchOffset(lineTex, center_texel, 0, ivec2(-1, 0)).rgb;
|
|
vec3 neightbor_line2 = texelFetchOffset(lineTex, center_texel, 0, ivec2(0, 1)).rgb;
|
|
vec3 neightbor_line3 = texelFetchOffset(lineTex, center_texel, 0, ivec2(0, -1)).rgb;
|
|
|
|
vec4 depths;
|
|
depths.x = texelFetchOffset(depthTex, center_texel, 0, ivec2(1, 0)).r;
|
|
depths.y = texelFetchOffset(depthTex, center_texel, 0, ivec2(-1, 0)).r;
|
|
depths.z = texelFetchOffset(depthTex, center_texel, 0, ivec2(0, 1)).r;
|
|
depths.w = texelFetchOffset(depthTex, center_texel, 0, ivec2(0, -1)).r;
|
|
|
|
vec4 line_dists;
|
|
line_dists.x = neighbor_dist(neightbor_line0, vec2(1, 0));
|
|
line_dists.y = neighbor_dist(neightbor_line1, vec2(-1, 0));
|
|
line_dists.z = neighbor_dist(neightbor_line2, vec2(0, 1));
|
|
line_dists.w = neighbor_dist(neightbor_line3, vec2(0, -1));
|
|
|
|
vec4 coverage = line_coverage(line_dists, line_kernel);
|
|
|
|
if (dist_raw > 0.0) {
|
|
fragColor *= line_coverage(dist, line_kernel);
|
|
}
|
|
|
|
/* We don't order fragments but use alpha over/alpha under based on current minimum frag depth.
|
|
*/
|
|
neighbor_blend(coverage.x, depths.x, neightbor_col0, depth, fragColor);
|
|
neighbor_blend(coverage.y, depths.y, neightbor_col1, depth, fragColor);
|
|
neighbor_blend(coverage.z, depths.z, neightbor_col2, depth, fragColor);
|
|
neighbor_blend(coverage.w, depths.w, neightbor_col3, depth, fragColor);
|
|
|
|
#if 1
|
|
/* Fix aliasing issue with really dense meshes and 1 pixel sized lines. */
|
|
if (!original_col_has_alpha && dist_raw > 0.0 && line_kernel < 0.45) {
|
|
vec4 lines = vec4(neightbor_line0.z, neightbor_line1.z, neightbor_line2.z, neightbor_line3.z);
|
|
/* Count number of line neighbors. */
|
|
float blend = dot(vec4(0.25), step(0.001, lines));
|
|
/* Only do blend if there are more than 2 neighbors. This avoids losing too much AA. */
|
|
blend = clamp(blend * 2.0 - 1.0, 0.0, 1.0);
|
|
fragColor = mix(fragColor, fragColor / fragColor.a, blend);
|
|
}
|
|
#endif
|
|
}
|