单遍线框问题 [英] Single-pass wireframe issue

查看:128
本文介绍了单遍线框问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现单遍线框,但是在此过程中遇到了一些问题.

问题1

由于某些原因,在我的几何着色器工作后,我只能得到未填充(如glPolygoneMode-线)几何的线框.

   )

但是,如果我禁用了几何着色器,则会得到我的几何:

  

我真正想要实现的是几何图形及其线框.

问题#2

实际上,我的图元是三角带.我使用它们来避免在四边形上使用以提高性能.绘制时如何跳过线框的边缘? (我已经看过发布怎么可以可以完成,但对我来说还不是很清楚.)

这是我的着色器:

顶点着色器:

#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着色器:

#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);
}

片段着色器:

#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));
}

帮我弄清楚哪里出了错.

更新:

我已经更新了Fragment shaderGeometry shader,并提到了安东·科尔曼(Andon M. Coleman)的答案,但是问题2 中描述的问题仍然没有解决.

更新2

我刚刚在fragmet着色器中做了一些小修改,就解决了我的问题.

更新片段着色器

#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);
}

解决方案

您的第一个问题与您使用discard有关.

目前,您正在 丢弃 不在片段着色器中距线框适当距离内的片段.那是不正确的行为,这只会 绘制线框.

实际上,您要做的是用另一种颜色替换discard.

 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;
}
 

确保这将产生黑色填充的网格,但是如果使用颜色u_color,则网格的填充部分将与线框无法区分.最终,您可能想定义一个与线框颜色分开的每个顶点颜色,但是您要自己决定如何做到这一点.


对于问题2,可以通过调整边距来解决:

与您链接的文章中的想法是,对于几何着色器发出的每个三角形,它应该为每个顶点的相对边缘写一个距离.如果您知道三角形中的一条边是 内部 边,因此不应包含在线框中,则应设置顶点的距离 与该边缘相反 足够远,以至于它永远不会插值到 0 (或您为线框选择的任何宽度).

对GS的以下修改将删除三角带的内部边缘:

 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();
}
 

这是通过在第一个/最后一个顶点之间交替来进行的,该第一个/最后一个顶点是与条插入的内部边缘相对的顶点.对于每个奇数三角形,内部边缘与第一个顶点相对,对于每个偶数三角形,内部边缘与最后一个顶点相对.从根本上讲,这是三角形带的工作方式,每个连续三角形上的绕组都反转,从而可以轻松识别要去除的边.

为确保distanceToEdges绝不会在此边缘方向上内插到小于或等于u_HalfLineWidth + 1.0的任何值,请将该坐标从其常规 0.0 值推入 99999.0 .

因此,如果您参考下图中的虚线边缘,则Q为99999.0:

       

想象一下一个三角形,该三角形镜像到虚线边缘,然后您应该对几何着色器"的功能有所了解.

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 shaderand 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);
}

解决方案

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.

这篇关于单遍线框问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆