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),因为每条线段都必须知道它的前导和后继,才能计算角的角平分线。如果一个多边形是由一系列线定义的,那可以很容易地完成。列表的最后一个点必须添加到点列表的开头,列表的第一个点必须添加到点列表的末尾。
例如如果多边形由点 A、B、C 和 D,则 GL_LINE_STRIP_ADJACENCY
的点列表将是 D、A、B、C、D 和 A。这将给出以下 4 个线段:
- A 到 B,前任 D 和继任者 C
- B 到 C,前任 A 和继任者 D
- C 到 D,前任 B 和继任者 A
- D 到 A,前任 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 通道绘制。
如果先绘制实心(绿色)多边形,然后再绘制虚线(白色),则结果可能如下所示:
所以我的目标是制作一个系统,您可以在其中绘制一个表面多边形,它基本上在多边形周围形成一个虚线边框。问题是我希望这个边框是固定大小,这意味着每个破折号都应该是多边形周围的固定大小。我也在努力弄清楚如何制作它,以便如果你增加表面多边形的边界大小,它不会向外扩展,只会向内扩展,这样尺寸就会倾斜。
长话短说,我基本上开始计算自己的虚线边框并手工绘制每个虚线。这个问题出现在它们相遇的角度,并在破折号延伸到多边形之外时切断额外的长度。所附图片显示了我目前所拥有的以及它的基本外观。我也有点想保持多边形上的正常边框与图片中的一样,但这不是强制性的。
我想我只是感到惊讶,这在 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),因为每条线段都必须知道它的前导和后继,才能计算角的角平分线。如果一个多边形是由一系列线定义的,那可以很容易地完成。列表的最后一个点必须添加到点列表的开头,列表的第一个点必须添加到点列表的末尾。
例如如果多边形由点 A、B、C 和 D,则 GL_LINE_STRIP_ADJACENCY
的点列表将是 D、A、B、C、D 和 A。这将给出以下 4 个线段:
- A 到 B,前任 D 和继任者 C
- B 到 C,前任 A 和继任者 D
- C 到 D,前任 B 和继任者 A
- D 到 A,前任 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 通道绘制。
如果先绘制实心(绿色)多边形,然后再绘制虚线(白色),则结果可能如下所示: