Opengl 替换glLineWidth的GLSL几何体着色器

Opengl 替换glLineWidth的GLSL几何体着色器,opengl,glsl,line,geometry-shader,Opengl,Glsl,Line,Geometry Shader,我正在尝试编写一个几何体着色器来替换glLineWidth行为。我想用一个可定制的宽度来画线(现在用一个统一的颜色就足够了)。线的厚度应始终相同,而与相机投影或线所在位置的距离无关 基于大量的谷歌搜索,我提出了以下几何体着色器: #version 330 layout (lines) in; layout (triangle_strip, max_vertices = 4) out; uniform mat4 u_model_matrix; uniform mat4 u_view

我正在尝试编写一个几何体着色器来替换
glLineWidth
行为。我想用一个可定制的宽度来画线(现在用一个统一的颜色就足够了)。线的厚度应始终相同,而与相机投影或线所在位置的距离无关

基于大量的谷歌搜索,我提出了以下几何体着色器:

#version 330

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

uniform mat4    u_model_matrix;
uniform mat4    u_view_matrix;
uniform mat4    u_projection_matrix;
uniform float   u_thickness = 4; // just a test default

void main()
{
    float r = u_thickness / 2;

    mat4 mv = u_view_matrix * u_model_matrix;
    vec4 p1 = mv * gl_in[0].gl_Position;
    vec4 p2 = mv * gl_in[1].gl_Position;

    vec2 dir = normalize(p2.xy - p1.xy);
    vec2 normal = vec2(dir.y, -dir.x);

    vec4 offset1, offset2;
    offset1 = vec4(normal * r, 0, 0);
    offset2 = vec4(normal * r, 0, 0);

    vec4 coords[4];
    coords[0] = p1 + offset1;
    coords[1] = p1 - offset1;
    coords[2] = p2 + offset2;
    coords[3] = p2 - offset2;

    for (int i = 0; i < 4; ++i) {
        coords[i] = u_projection_matrix * coords[i];
        gl_Position = coords[i];
        EmitVertex();
    }
    EndPrimitive();
}
。。。和我的片段着色器:

#version 330

uniform vec4 u_color = vec4(1, 0, 1, 1);
out vec4 fragColor;

void main() {
    fragColor = u_color;
}
我不能让数学在所有情况下都适用。使用正交摄影机时,上述功能可以正常工作:

但对于透视相机,问题是线条不是固定大小。相对于物体的距离,它变得越来越大越来越小


我希望使用透视相机时线条的大小也一样。我做错了什么

我不是专家,但我以前做过这件事,我会提供我的见解

我假设您的
gl\u位置
s直接来自使用投影矩阵计算的顶点着色器。这意味着它们的
w
分量是该点的“剪辑空间位置”;这是管道用来赋予投影效果的东西(越远的东西越小)。因此需要考虑到这一点

幸运的是,你唯一需要做的就是用它乘以你的偏移量

coords[0] = p1 + offset1 * p1.w;
coords[1] = p1 - offset1 * p1.w;
coords[2] = p2 + offset2 * p2.w;
coords[3] = p2 - offset2 * p2.w;

这应该会给你想要的效果。

我考虑了视口大小,并用它缩放了我的r。我不知道这是否是解决这个问题最有效的方法(我绝对不是数学头脑),但它确实有效

在下面的代码中,我现在在屏幕空间而不是相机/视图空间中完成所有工作,并使用u_viewportInvSize vec2(即1/viewportSize)缩放所需的半径

#version 330

layout (lines) in;                              // now we can access 2 vertices
layout (triangle_strip, max_vertices = 4) out;  // always (for now) producing 2 triangles (so 4 vertices)

uniform vec2    u_viewportInvSize;
uniform mat4    u_modelviewprojection_matrix;
uniform float   u_thickness = 4;

void main()
{
    float r = u_thickness;

    vec4 p1 = u_modelviewprojection_matrix * gl_in[0].gl_Position;
    vec4 p2 = u_modelviewprojection_matrix * gl_in[1].gl_Position;

    vec2 dir = normalize(p2.xy - p1.xy);
    vec2 normal = vec2(dir.y, -dir.x);

    vec4 offset1, offset2;
    offset1 = vec4(normal * u_viewportInvSize * (r * p1.w), 0, 0);
    offset2 = vec4(normal * u_viewportInvSize * (r * p2.w), 0, 0); // changing this to p2 fixes some of the issues

    vec4 coords[4];
    coords[0] = p1 + offset1;
    coords[1] = p1 - offset1;
    coords[2] = p2 + offset2;
    coords[3] = p2 - offset2;

    for (int i = 0; i < 4; ++i) {
        gl_Position = coords[i];
        EmitVertex();
    }
    EndPrimitive();
}
#版本330
布局(行)在;//现在我们可以访问2个顶点
布局(三角形带,最大顶点=4)向外;//始终(目前)生成2个三角形(因此4个顶点)
统一的vec2 u_viewportInvSize尺寸;
均匀mat4 u_模型视图投影矩阵;
均匀浮子u_厚度=4;
void main()
{
浮动r=u_厚度;
vec4 p1=u_模型视图[0]中的投影矩阵*gl_]。gl_位置;
vec4 p2=u_模型视图[1]中的投影矩阵*gl_]。gl_位置;
vec2 dir=标准化(p2.xy-p1.xy);
vec2 normal=vec2(dir.y,-dir.x);
vec4抵销1、抵销2;
偏移量1=vec4(正常*u_viewportInvSize*(r*p1.w),0,0);
offset2=vec4(normal*u_viewportInvSize*(r*p2.w),0,0);//将其更改为p2可以解决一些问题
向量4坐标[4];
坐标[0]=p1+偏移量1;
坐标[1]=p1-偏移量1;
坐标[2]=p2+偏移量2;
coords[3]=p2-抵销2;
对于(int i=0;i<4;++i){
gl_位置=坐标[i];
发射顶点();
}
EndPrimitive();
}

几何体着色器并不以快速著称。使用几何体着色器是一个性能杀手,因此只有在所有其他选项都不可用时才建议使用。 答案中提供了一种不使用几何体着色器的可能解决方案


无论如何,如果要使用几何体着色器,请通过顶点着色器中的模型视图投影矩阵变换顶点坐标:

#版本330
在vec4 a_位置;
均匀mat4 u_模型视图投影矩阵;
void main()
{
gl_位置=u_模型视图投影矩阵*a_位置;
}
通过以下方式计算几何体着色器中的规格化设备坐标:

vec3 ndc_1=gl_in[0]。gl_Position.xyz/gl_in[0]。gl_Position.w;
vec3 ndc_2=gl_in[1]。gl_Position.xyz/gl_in[1]。gl_Position.w;
规范化的设备空间是一个立方体,其左、下、近(-1,-1,-1)和右、上、后(1,1,1)

计算直线各点之间的向量。按视口大小缩放,以考虑视口的纵横比。 最后,让学生们排队:

vec2 dir=normalize((ndc_2.xy-ndc_1.xy)*u_视口大小);
vec2 normal=vec2(-dir.y,dir.x);
计算垂直于直线的hal厚度偏移向量,并将其转换为标准化设备空间。这是通过按逆纵横比缩放并乘以2:

vec3 offset=vec3(正常*u_厚度*0.5/u_视口大小*2.0,0.0);
将偏移向量添加到标准化设备坐标并“撤消”透视分割:

gl\u Position=vec4((ndc\u 1+偏移)*gl\u在[0]。gl\u Position.w,gl\u在[0]。gl\u Position.w);
发射顶点();
gl_Position=vec4((ndc_1-偏移)*gl_在[0].gl_Position.w,gl_在[0].gl_Position.w);
发射顶点();
gl_Position=vec4((ndc_2+偏移)*gl_在[1]。gl_Position.w,gl_在[0]。gl_Position.w);
发射顶点();
gl_Position=vec4((ndc_2-偏移)*gl_在[1]。gl_Position.w,gl_在[0]。gl_Position.w);
发射顶点();
这可以进一步优化,并导致以下几何体着色器

#版本330
布局(行)在;//现在我们可以访问2个顶点
布局(三角形带,最大顶点=4)向外;//始终(目前)生成2个三角形(因此4个顶点)
统一的vec2 u_视口大小;
均匀浮子u_厚度=4;
void main()
{
vec4 p1=gl_位于[0]。gl_位置;
vec4 p2=gl_在[1]。gl_位置;
vec2 dir=规格化((p2.xy-p1.xy)*u_视口大小);
vec2 offset=vec2(-dir.y,dir.x)*u_厚度/u_视口大小;
gl_位置=p1+vec4(偏移量.xy*p1.w,0.0,0.0);
发射顶点();
gl_位置=p1-vec4(偏移量.xy*p1.w,0.0,0.0);
发射顶点();
gl_位置=p2+vec4(偏移量为0.xy*p2.w,0.0,0.0);
发射顶点();
gl_位置=p2-vec4(偏移量.xy*p2.w,0.0,0.0);
发射顶点();
EndPrimitive();
}
片段着色器

#版本330
out vec4 fragColor;
均匀vec4 u_颜色=vec4(1,0,1,1);
void main()
{
fragColor=u_颜色;
}

我已经试过了,没什么区别。请注意,我
#version 330

layout (lines) in;                              // now we can access 2 vertices
layout (triangle_strip, max_vertices = 4) out;  // always (for now) producing 2 triangles (so 4 vertices)

uniform vec2    u_viewportInvSize;
uniform mat4    u_modelviewprojection_matrix;
uniform float   u_thickness = 4;

void main()
{
    float r = u_thickness;

    vec4 p1 = u_modelviewprojection_matrix * gl_in[0].gl_Position;
    vec4 p2 = u_modelviewprojection_matrix * gl_in[1].gl_Position;

    vec2 dir = normalize(p2.xy - p1.xy);
    vec2 normal = vec2(dir.y, -dir.x);

    vec4 offset1, offset2;
    offset1 = vec4(normal * u_viewportInvSize * (r * p1.w), 0, 0);
    offset2 = vec4(normal * u_viewportInvSize * (r * p2.w), 0, 0); // changing this to p2 fixes some of the issues

    vec4 coords[4];
    coords[0] = p1 + offset1;
    coords[1] = p1 - offset1;
    coords[2] = p2 + offset2;
    coords[3] = p2 - offset2;

    for (int i = 0; i < 4; ++i) {
        gl_Position = coords[i];
        EmitVertex();
    }
    EndPrimitive();
}