Java 带内部边框的 NASA Worldwind 多边形

Java NASA Worldwind Polygon with Internal Border

所以我的目标是制作一个系统,您可以在其中绘制一个表面多边形,它基本上在多边形周围形成一个虚线边框。问题是我希望这个边框是固定大小,这意味着每个破折号都应该是多边形周围的固定大小。我也在努力弄清楚如何制作它,以便如果你增加表面多边形的边界大小,它不会向外扩展,只会向内扩展,这样尺寸就会倾斜。

长话短说,我基本上开始计算自己的虚线边框并手工绘制每个虚线。这个问题出现在它们相遇的角度,并在破折号延伸到多边形之外时切断额外的长度。所附图片显示了我目前所拥有的以及它的基本外观。我也有点想保持多边形上的正常边框与图片中的一样,但这不是强制性的。

我想我只是感到惊讶,这在 NASA Worldwind 中很难做到,而在 directX 中大约有 20 行。这让我觉得我的做法是错误的。

作为注释。我研究过使用模板缓冲区,但事实证明这几乎是在浪费时间来开始工作并正确地 运行 通过世界风。我不认为我有足够的把握来正确地做到这一点。

P.S。 None 我已经开始使用的东西是一成不变的。我只需要最终结果是正确的。感谢您的帮助。

TLDR:需要使用设置宽度的内部边框绘制多边形。

我建议先绘制(绿色)区域,然后再绘制虚线(白色)。
画线时,Geometry Shader can be used to convert the line segments to quads (triangle stripes with 4 points), which enclose the thin region, which would be filled by a solid line, in the given line thickness. The fragment shader finnaly has to dash the line (cut parts of the line). Finally the Fragment Shader 必须生成虚线(从线中切出部分)。

要画线,Primitive type GL_LINE_STRIP_ADJACENCY has to be used (see GLSL Tutorial - Primitive Assembly),因为每条线段都必须知道它的前导和后继,才能计算角的角平分线。如果一个多边形是由一系列线定义的,那可以很容易地完成。列表的最后一个点必须添加到点列表的开头,列表的第一个点必须添加到点列表的末尾。

例如如果多边形由点 ABCD,则 GL_LINE_STRIP_ADJACENCY 的点列表将是 DABCDA。这将给出以下 4 个线段:

  • AB,前任 D 和继任者 C
  • BC,前任 A 和继任者 D
  • CD,前任 B 和继任者 A
  • DA,前任 C 和继任者 B

Vertex Shader只需通过多边形的角点即可:

#version 400

layout (location = 0) in vec3 inPos;

out TVertexData
{
    out vec3 pos;
} outData;

void main()
{
    outData.pos = inPos;
    gl_Position = vec4( inPos, 1.0 );
}

Geometry Shader计算每条线段末端的角平分线,根据线的粗细生成四边形
由于片段着色器还需要生成虚线,因此计算出线的长度和线的中心并传递给片段着色器。

#version 400

layout( lines_adjacency ) in;
layout( triangle_strip, max_vertices = 4 ) out;

in TVertexData
{
    vec3 pos;
} inData[];

out TGeometryData
{
    vec3  linePos;
    vec3  lineMidPoint;
    float lineLen;
} outData;

uniform float u_thickness;

void main()
{
    if ( gl_InvocationID != 0 )
        return;

    vec3 pos0    = inData[1].pos;
    vec3 pos1    = inData[2].pos;
    vec3 dirPred = normalize( inData[0].pos - pos0 );
    vec3 dirSucc = normalize( inData[3].pos - pos1 );
    vec3 dirLine = normalize( pos1 - pos0 ); 
    vec3 dirNorm = normalize( dirPred - dirLine * dot(dirLine, dirPred) );
    dirSucc = faceforward( dirSucc, -dirNorm, dirSucc );

    vec3 dir0 = abs( dot(dirPred, dirLine) ) > 0.99 ? dirNorm : normalize( dirPred + dirLine );
    vec3 dir1 = abs( dot(dirSucc, dirLine) ) > 0.99 ? dirNorm : normalize( dirSucc - dirLine );

    vec3 pos01 = pos0 + dir0 * u_thickness / dot(dir0, dirNorm);
    vec3 pos11 = pos1 + dir1 * u_thickness / dot(dir1, dirNorm);

    vec3 lineMidPoint0 = (pos0 + pos1) * 0.5;
    vec3 lineMidPoint1 = lineMidPoint0 + dirNorm * u_thickness;

    outData.lineLen = length( pos1 - pos0 );

    outData.lineMidPoint = lineMidPoint0; 
    outData.linePos = pos0;
    gl_Position = vec4(pos0, 1.0); EmitVertex();
    outData.linePos = pos1;
    gl_Position = vec4(pos1, 1.0); EmitVertex();

    outData. lineMidPoint = lineMidPoint1; 
    outData.linePos = pos01;
    gl_Position = vec4(pos01, 1.0); EmitVertex();
    outData.linePos = pos11;
    gl_Position = vec4(pos11, 1.0); EmitVertex();

    EndPrimitive();
}

最后 Fragment Shader 通过丢弃间隙中的片段生成虚线。

#version 400

in TGeometryData
{
    vec3  linePos;
    vec3  lineMidPoint;
    float lineLen;
} inData;

out vec4 fragColor;

uniform float u_dashLen;
uniform float u_gapLen;
uniform vec4  u_color;

void main()
{
    float midDist  = length( inData.linePos - inData.lineMidPoint ); 
    float modDist  = mod( midDist + u_dashLen * 0.5, u_dashLen + u_gapLen );
    float onDash   = max( step( modDist, u_dashLen ), 
                          step( inData.lineLen * 0.5 - u_dashLen, midDist ) );
    if ( onDash < 0.5 )
        discard;
    fragColor      = u_color * onDash;
}

请注意,此 Fragment Shader 使用 discard 关键字,以丢弃虚线间隙中的片段。如果使用 Blending,则 (if ( onDash < 0.5 ) discard;) 可以省略,因为间隙中的片段将使用 0 的 alpha 通道绘制。

如果先绘制实心(绿色)多边形,然后再绘制虚线(白色),则结果可能如下所示: