0

我正在为旋转操纵器编写一个 glsl 着色器。我的目标是 Autodesk Maya 风格,xy 和 z 旋转各有 3 个轴,沿相机前轴旋转的一个轴在它们上等于 4;

用户可以通过直接在视口中选择特定轴来将其操作结果限制在特定轴上。

为了启用对象选择,我使用带有 2 个附加纹理的 custrom 帧缓冲区作为渲染目标 - 一个用于颜色,一个用于选择 ID。

问题是让这 4 个轴由 GL_LINES 模式绘制会给用户带来问题。如果线条画得又细又漂亮,用户应该非常精确地点击它们来激活特定的轴,因为他必须精确地选择具有正确 object_id 轴的像素。我想通过制作两张单独的图纸来缓解他的生活。用于颜色输出的细线和用于 objectid 输出纹理的 3 倍宽的线。

我是openGL的新手。现在我的天真逻辑是传递一个布尔统一,告诉片段着色器绘制颜色或对象ID缓冲区。

这是带有传递的 render_target 选择器的绘图命令

    GLfloat lw;
    glGetFloatv(GL_LINE_WIDTH,&lw);
    glUniform1ui(7,0);//render to RGB
    glDrawArrays(GL_POINTS,0,1);
    glUniform1ui(7,1);//render to OBJECTID
    glLineWidth(10);
    glDrawArrays(GL_POINTS,0,1);
    glLineWidth(lw);

这是片段着色器:

#version 450
//inputs
layout(location=6) uniform uint manipulator_selected_axis;
layout(location=7) uniform uint render_to_selection_buffer;

//outputs
layout(location=0) out vec4  out_color;
layout(location=1) out float out_objectid;

void main(void)     
{
    if (render_to_selection_buffer==1)
    {
        switch (gl_PrimitiveID)
        {
            case 0: //CAMERA FORWARD
                out_objectid=float(253)/256;
                break;
            case 1: //X 
                out_objectid=float(250)/256;
                break;
            case 2: //Y
                out_objectid=float(251)/256;
                break;
            case 3: //Z
                out_objectid=float(252)/256;
                break;
        }
        out_color=vec4(0,0,0,0);
    }
    else
    {
        vec4 active_axis_color=vec4(1.0,1.0,0.0,1.0);
        switch(gl_PrimitiveID)
        {
            case 0: //CAMERA FORWARD
                if(manipulator_selected_axis==0)
                    out_color=active_axis_color;
                else
                    out_color =vec4(1.0,0.5,1.0,1.0);
                break;
            case 1: //X
                if(manipulator_selected_axis==1)
                    out_color=active_axis_color;    
                else
                    out_color =vec4(1.0,0.0,0.0,1.0);
                break;
            case 2: //Y
                if(manipulator_selected_axis==2)
                    out_color=active_axis_color;    
                else
                    out_color =vec4(0.0,1.0,0.0,1.0);
                break;
            case 3: //Z
                if(manipulator_selected_axis==3)
                    out_color=active_axis_color;    
                else
                    out_color =vec4(0.0,0.0,1.0,1.0);
                break;
            case 4:
                out_color =vec4(0.6,0.6,0.6,1.0);
                break;
        }
        out_objectid=0;

    }
}

我真的不喜欢我为此使用的逻辑,因为它看起来丑陋且未经优化。首先,因为着色器必须运行两次,并且它有一个相当复杂的几何着色器,我在其中生成圆。其次,因为我的片段着色器中有很多 if else 的东西,我被告知 gpu 不喜欢分支。

那么问题 1 编写这样的着色器的替代正确方法是什么?问题2更具技术性。

在此处输入图像描述

在这里,我提供了通过 theese 2 glDrawArrays 调用得到的屏幕截图。如您所见,我的颜色缓冲区中有黑色轮廓。问题是当 framgent 着色器执行 out_objectid 目标的渲染代码时,它仍然会向 out_color 写入一些脏的随机信息,如下图所示,所以我不得不用 out_color=vec4(0,0,0,0 )。至少它给出了干净的黑色。

在此处输入图像描述 所以问题2。我的错误在哪里?如何防止 objectid 块写入 out_color?谢谢更新:按照我的建议,我达到了这种效果。我无法描述 openGL 内部发生了什么。看起来它仍然写入 GL_DEPTH_COMPONENT 并进行了一些未定义的深度测试,从而导致了这种结果。我不希望在此绘制调用期间进行任何深度写入,因为操纵器始终位于顶部。也许你可以给我额外的建议。

在此处输入图像描述 绘图代码

glBindBuffer(GL_ARRAY_BUFFER,0);
glUseProgram(program_id_ctl_manip_color);

glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
glUniform4f(0,0,0,0,1);
glUniform1f(5,0.67);
glUniform1ui(6,manipulator_axis_highlight);
glUniformMatrix4fv(10,1,GL_TRUE,cam.matrix_view.m);
glUniformMatrix4fv(14,1,GL_TRUE,cam.matrix_projection_ortho.m);
glUniformMatrix4fv(18,1,GL_TRUE,cam.matrix_camera.m);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glDrawArrays(GL_POINTS,0,1);


GLfloat lw;
glGetFloatv(GL_LINE_WIDTH,&lw);
glLineWidth(6);
glUseProgram(program_id_ctl_manip_objectid);
glUniform4f(0,0,0,0,1);
glUniform1f(5,0.67);
glUniform1ui(6,manipulator_axis_highlight);
glUniformMatrix4fv(10,1,GL_TRUE,cam.matrix_view.m);
glUniformMatrix4fv(14,1,GL_TRUE,cam.matrix_projection_ortho.m);
glUniformMatrix4fv(18,1,GL_TRUE,cam.matrix_camera.m);
glDrawBuffer(GL_COLOR_ATTACHMENT1);
glDrawArrays(GL_POINTS,0,1);
GLenum buffers[2]={GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1};
glDrawBuffers(2,buffers);

glLineWidth(lw);
4

1 回答 1

2

我们先来回答问题2:

我的错误在哪里?如何防止 objectid 块写入 out_color

GL 根本不是那样工作的。光栅化器生成片段,并且您的片段着色器必须确定您附加的所有渲染目标的输出值- 或者它可以丢弃整个片段,因此不会将任何输出写入帧缓冲区。如果您没有明确写入输出变量,则它的内容将是未定义的 - 您很可能会从最后一次计算中获得特定寄存器中的一些内容,从而解释最后一张图像中的随机噪声。将其写为黑色当然会导致第一张图像。

您可以做一些事情来防止黑色边框,例如:

  • 启用 Alpha 混合并将完全透明的像素写入您的out_color.
  • 只需将out_color输出的绘制缓冲区设置为GL_NONE

可能有很多其他策略可能会获得相同的结果,但它们都不是真正有效的,这让我们想到问题 1:编写这样一个具有相同效果的着色器的替代正确方法是什么?

编写这样的着色器的替代正确方法是什么?

当然有不同的解决方案。直接的解决方案是简单地使用两个不同的着色器。无论如何,您必须在绘图调用之间切换统一值,因此您不能在单个绘图调用中使用两种模式。您也可以简单地切换着色器。

另一个问题是内部开关。不要那样做。您可以输入线的不同颜色和对象 ID 作为顶点属性。在片段着色器中进行这样的切换是非常不寻常的,而且效率不高。从好的方面来说,你仍然有完全统一的控制流,所以你不会看到最坏的情况下的性能,但我仍然认为你的构造至少是不好的风格。

您不需要多个渲染目标。MRT 仅在您希望所有渲染目标都使用相同的片段时才有意义,您只想输出比每个片段的 vec4 更多的数据,并且一次完成。但是在您的情况下,您实际上想要不同的几何形状,并且您已经在进行多遍操作,因此您一无所获。在您的情况下,MRT 只会让事情变得更加复杂。

只需使用两个着色器,并在它们之间切换着色器和绘制缓冲区(并且只需要一个绘制缓冲区)将是一个简单的解决方案。

您的代码还有另一个问题:glLineWidth()除 1.0 之外的值已弃用。您的代码不适用于现代 GL 核心配置文件。如果您想要宽线,您实际上应该绘制基于 traingle 的图元。

于 2014-11-23T16:22:56.210 回答