问题
I am trying to implement single-pass wireframe, but I have got couple of issues in the process.
Question #1
For some reasons I get only wireframe without (like with glPolygoneMode
- lines) filled geometry after my geometry shader worked.
)
But if I disable the geometry shader I get my geometry:
What I really like to achieve is both geometry and its wireframe.
Question #2
Actually, my primitives are triangle strips. I use them to avoid using on quads to improve performance. How can I skip edges of wireframe while drawing? (I've already saw post how it can be done, but it is still not to clear for me.)
Here are my shaders:
Vertex shader:
#version 330
//
// (C) Copyright 2010 Patrick Cozzi and Deron Ohlarik
//
// Distributed under the MIT License.
// See License.txt or http://www.opensource.org/licenses/mit-license.php.
//
layout(location = 0) in vec3 in_Position;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform mat4 og_viewportTransformationMatrix;
out vec2 windowPosition;
vec4 og_ClipToWindowCoordinates(vec4 v, mat4 viewportTransformationMatrix);
void main()
{
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_Position, 1.0);
windowPosition = og_ClipToWindowCoordinates(gl_Position, og_viewportTransformationMatrix).xy;
}
vec4 og_ClipToWindowCoordinates(vec4 v, mat4 viewportTransformationMatrix)
{
v.xyz /= v.w; // normalized device coordinates
v.xyz = (viewportTransformationMatrix * vec4(v.xyz, 1.0)).xyz; // window coordinates
return v;
}
Geomerty shader:
#version 330
//
// (C) Copyright 2010 Patrick Cozzi and Deron Ohlarik
//
// Distributed under the MIT License.
// See License.txt or http://www.opensource.org/licenses/mit-license.php.
//
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
in vec2 windowPosition[];
noperspective out vec3 distanceToEdges;
float og_distanceToLine(vec2 f, vec2 p0, vec2 p1);
void main()
{
vec2 p0 = windowPosition[0];
vec2 p1 = windowPosition[1];
vec2 p2 = windowPosition[2];
gl_Position = gl_in[0].gl_Position;
distanceToEdges = vec3(og_distanceToLine(p0, p1, p2), 0.0, 0.0);
EmitVertex();
gl_Position = gl_in[1].gl_Position;
distanceToEdges = vec3(0.0, og_distanceToLine(p1, p2, p0), 0.0);
EmitVertex();
gl_Position = gl_in[2].gl_Position;
distanceToEdges = vec3(0.0, 0.0, og_distanceToLine(p2, p0, p1));
EmitVertex();
}
float og_distanceToLine(vec2 f, vec2 p0, vec2 p1)
{
vec2 l = f - p0;
vec2 d = p1 - p0;
//
// Closed point on line to f
//
vec2 p = p0 + (d * (dot(l, d) / dot(d, d)));
return distance(f, p);
}
Fragment shader:
#version 330
//
// (C) Copyright 2010 Patrick Cozzi and Deron Ohlarik
//
// Distributed under the MIT License.
// See License.txt or http://www.opensource.org/licenses/mit-license.php.
//
uniform float u_halfLineWidth;
uniform vec3 u_color;
noperspective in vec3 distanceToEdges;
out vec4 fragmentColor;
void main()
{
float d = min(distanceToEdges.x, min(distanceToEdges.y, distanceToEdges.z));
if (d > u_halfLineWidth + 1.0)
{
discard;
}
d = clamp(d - (u_halfLineWidth - 1.0), 0.0, 2.0);
fragmentColor = vec4(u_color, exp2(-2.0 * d * d));
}
Help me to figure out where I've got it wrong.
Update:
I have updated Fragment shader
and Geometry shader
as mention Andon M. Coleman in his answer, but problem described in Question 2 still not solved.
Update 2
I have just make some little edits in fragmet shader and that solves my problem.
Update fragment shader
#version 330
//
// (C) Copyright 2010 Patrick Cozzi and Deron Ohlarik
//
// Distributed under the MIT License.
// See License.txt or http://www.opensource.org/licenses/mit-license.php.
//
uniform float u_halfLineWidth;
uniform vec3 u_color;
noperspective in vec3 distanceToEdges;
out vec4 fragmentColor;
void main()
{
float d = min(distanceToEdges.y, max(distanceToEdges.x, distanceToEdges.z));
if (d > u_halfLineWidth + 1.0)
{
fragmentColor = vec4(u_color, 1);
return;
}
fragmentColor = vec4(vec3(0,0,0), 1);
}
回答1:
Your first problem is related to your use of discard
.
At present, you are discarding the fragments that do not lie within an appropriate distance from your wireframe in the fragment shader. That is incorrect behavior, this will only draw the wireframe.
What you need to do is actually replace the discard
with a different color.
if (d > u_halfLineWidth + 1.0)
{
//discard; // DO NOT DO THIS
//
// Instead, do this
//
fragmentColor = vec4 (0.0, 0.0, 0.0, 1.0);
return;
}
Granted this is going to produce a black filled mesh, but if you used the color u_color
, the filled part of the mesh would be indistinguishable from the wireframe. Ultimately you probably want to define a per-vertex color that is separate from the wireframe color, but it is up to you to figure out how to do that.
As for Question #2, that is solved by tweaking the edge distances:
The idea in the article you linked to was that for each triangle your geometry shader emits, it should write a distance to the opposite edge for each vertex. If you know that one of the edges in your triangle is an interior edge and thus should not be included in the wireframe, you should set the distance for the vertex opposite that edge far enough away that it never interpolates to 0 (or whatever width you chose for the wireframe).
The following modifications to your GS will remove the interior edges of the triangle strip:
void main()
{
vec2 p0 = windowPosition[0];
vec2 p1 = windowPosition[1];
vec2 p2 = windowPosition[2];
// Alternate between using the first and last vertex as the one opposite the
// interior edge based on primitive ID.
bool strip_flip = (bool (gl_PrimitiveIDIn & 1));
gl_Position = gl_in[0].gl_Position;
if (strip_flip)
distanceToEdges = vec3(og_distanceToLine(p0, p1, p2), 0.0, 0.0);
else
distanceToEdges = vec3(og_distanceToLine(p0, p1, p2), 0.0, 99999.0);
EmitVertex();
gl_Position = gl_in[1].gl_Position;
if (strip_flip)
distanceToEdges = vec3(99999.0, og_distanceToLine(p1, p2, p0), 0.0);
else
distanceToEdges = vec3(0.0, og_distanceToLine(p1, p2, p0), 99999.0);
EmitVertex();
gl_Position = gl_in[2].gl_Position;
if (strip_flip)
distanceToEdges = vec3(99999.0, 0.0, og_distanceToLine(p2, p0, p1));
else
distanceToEdges = vec3( 0.0, 0.0, og_distanceToLine(p2, p0, p1));
EmitVertex();
}
This works by alternating between first/last vertex serving as the vertex opposite the interior edge that the strip inserted. For each odd triangle, the interior edge is opposite the first vertex, for each even triangle it is opposite the last. This is fundamentally how triangle strips operate, the winding is reversed on each successive triangle and that makes it easy to identify which edge to remove.
To ensure that distanceToEdges
never interpolates to anything less than or equal to u_HalfLineWidth + 1.0
in the direction of this edge, said coordinates are pushed from their normal 0.0 value to 99999.0.
Thus, if you refer to the dashed edge in the following diagram, Q is 99999.0:
Imagine a triangle mirrored across that dashed edge and then you should have a pretty good idea of what the Geometry Shader is doing.
来源:https://stackoverflow.com/questions/23684867/single-pass-wireframe-issue