如何避免使用着色器在 OpenGL 中消失线?

Posted

技术标签:

【中文标题】如何避免使用着色器在 OpenGL 中消失线?【英文标题】:How I can avoid in OpenGL disappearing lines using a shader? 【发布时间】:2021-01-12 23:04:21 【问题描述】:

所以我只使用片段着色器来绘制一些线条。顶点只是一个空的四边形。

我遇到的问题是,当我缩小相机并且线条变小时,它们有时会出现又消失,我不明白为什么。

这是不缩放的样子

这就是相机远离他们时的样子

我离他们越远,出现的人工制品就越多。

这就是我的顶点着色器的样子

#version 330 core

layout(location = 0) in vec2 _position;

out vec2 position;

uniform mat4 uCameraView;

void main() 
  gl_Position = uCameraView * vec4(_position.x, _position.y, 0.0f, 1.0f);
  position = _position;

这是片段

#version 330 core

in vec2 position;

uniform vec4 uGridColor;
uniform float uTileSize;
uniform float uGridBorderSize;

out vec4 fragColor;

void main() 
  vec2 uv = mod(position, uTileSize);
  vec2 border = mod(uv + (uGridBorderSize / 2.0), uTileSize);
  border -= mod(uv - (uGridBorderSize / 2.0), uTileSize);

  if (length(border) > uTileSize - uGridBorderSize) 
    fragColor = uGridColor;
   else 
    fragColor = vec4(0.0);
  

为什么会这样?也许与抗锯齿有关?我的 OpenGL 设置只是默认设置。

【问题讨论】:

你想要的是某种抗锯齿。 我该怎么做? 我们可以看一下C++代码吗?乍一看有点难以分辨,但使用完整的代码库可能会更清楚。 我敢打赌,你现在需要有恒定的线宽(不管缩放)你在取消缩放时遇到问题,线将小于像素粗,这有时会由于锯齿效应导致不可见的线条(可见和不可见是位置的函数)。你的图像显示你的线条有 +/-1 不同的粗细,它是一样的......将它应用到你的支票上......这是有问题的,因为我们不知道什么变量是什么以及具有什么值,范围等。您还应该以像素为单位传递视图分辨率,因为这将需要... 线条消失的原因是它们变得小于一个像素。在这种情况下你希望发生什么?通常有两种方法来解决这个问题:1)不允许线条粗细小于 1 像素,2)如果线条比背景颜色细 1 像素 lerp。所以在你的情况下,当线条变得小于 1 像素时,会变成灰色 【参考方案1】:

您当前的代码正在做出“是行”/“否行”的二元决定。但是,超出某个点(线宽

您需要计算像素覆盖率,即像素内有多少行,而不是使用二进制“是”/“否”。为此,您通常会使用无符号距离函数 (UDF)。这是像素空间中 UDF 线的一些 GLSL 代码(您也可以在标准化空间中使用它们,但是您必须调整平滑步长参数)。试试这个https://shadertoy.com

float lsd(vec2 a, vec2 b, vec2 p, float w)
    w *= 0.5;
    vec2  n = normalize(b-a);
    float l = length(b-a);
    float t = dot((p-a),n);
    float d = length((a-p)+t*n);
    float e = min(length(p-a)+w, length(p-b)+w);
    return (t > w && t < l-w) ? d : e;


float line(vec2 a, vec2 b, float width, vec2 fragcoord)
    return max(0., 1.-smoothstep(0., 1., lsd(a, b, fragcoord, width)-0.5*width));


void mainImage( out vec4 fragColor, in vec2 fragCoord )

    float l =
          line(vec2(8.,8.), vec2(128.,33.), 1., fragCoord)
        + line(vec2(33.,220.), vec2(260.,20.), 4., fragCoord);
    fragColor = vec4(l,l,l,1.0);

【讨论】:

【参考方案2】:

仅根据您的着色器很难判断,因此这里有一个如何围绕线网格进行缩放和平移的示例。它使用投影矩阵进行缩放,看起来与您实现缩放的方式略有不同,但重要的是它在缩放/平移时没有任何线条变细的伪影。

这是一个演示,希望 GIF 显示它,但是当您放大和缩小时,网格线的粗细是恒定的:

#include <iostream>
#include <vector>

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/string_cast.hpp>

using std::vector;
using glm::mat4;
using glm::vec3;
using glm::vec4;

void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;

// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;

vec3 rayCast(double xpos, double ypos, mat4 projection, mat4 view) 
    // converts a position from the 2d xpos, ypos to a normalized 3d direction
    float x = (2.0f * xpos) / SCR_WIDTH - 1.0f;
    float y = 1.0f - (2.0f * ypos) / SCR_HEIGHT;
    float z = 1.0f;
    vec3 ray_nds = vec3(x, y, z);
    vec4 ray_clip = vec4(ray_nds.x, ray_nds.y, -1.0f, 1.0f);
    // eye space to clip we would multiply by projection so
    // clip space to eye space is the inverse projection
    vec4 ray_eye = inverse(projection) * ray_clip;
    // convert point to forwards
    ray_eye = vec4(ray_eye.x, ray_eye.y, -1.0f, 0.0f);
    // world space to eye space is usually multiply by view so
    // eye space to world space is inverse view
    vec4 inv_ray_wor = (inverse(view) * ray_eye);
    vec3 ray_wor = vec3(inv_ray_wor.x, inv_ray_wor.y, inv_ray_wor.z);
    ray_wor = normalize(ray_wor);
    return ray_wor;


class Line 
    int shaderProgram;
    unsigned int VBO, VAO;
    vector<float> vertices;
    vec3 startPoint;
    vec3 endPoint;
    mat4 MVP;
    vec3 lineColor;
public:
    Line(vec3 start, vec3 end) 

        startPoint = start;
        endPoint = end;
        lineColor = vec3(1,1,1);

        const char *vertexShaderSource = "#version 330 core\n"
            "layout (location = 0) in vec3 aPos;\n"
            "uniform mat4 MVP;\n"
            "void main()\n"
            "\n"
            "   gl_Position = MVP * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
            "\0";
        const char *fragmentShaderSource = "#version 330 core\n"
            "out vec4 FragColor;\n"
            "uniform vec3 color;\n"
            "void main()\n"
            "\n"
            "   FragColor = vec4(color, 1.0f);\n"
            "\n\0";

        // vertex shader
        int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        // check for shader compile errors
        int success;
        char infoLog[512];
        glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
        if (!success)
        
            glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
            std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
        
        // fragment shader
        int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);
        // check for shader compile errors
        glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
        if (!success)
        
            glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
            std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
        
        // link shaders
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
        // check for linking errors
        glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
        if (!success) 
            glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
            std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
        
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);

        vertices = 
             start.x, start.y, start.z,
             end.x, end.y, end.z,

        ;
        
        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);
        glBindVertexArray(VAO);

        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);

        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);

        glBindBuffer(GL_ARRAY_BUFFER, 0); 
        glBindVertexArray(0); 

    

    int setMVP(mat4 mvp) 
        MVP = mvp;
    

    int setColor(vec3 color) 
        lineColor = color;
    

    int draw() 
        glUseProgram(shaderProgram);
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "MVP"), 1, GL_FALSE, &MVP[0][0]);
        glUniform3fv(glGetUniformLocation(shaderProgram, "color"), 1, &lineColor[0]);

        glBindVertexArray(VAO);
        glDrawArrays(GL_LINES, 0, 2);
        return 0;
    

    ~Line() 
        // optional: de-allocate all resources once they've outlived their purpose:
        // ------------------------------------------------------------------------
        glDeleteVertexArrays(1, &VAO);
        glDeleteBuffers(1, &VBO);
        glDeleteProgram(shaderProgram);
    
;

vec3 cameraPos = glm::vec3(0.0f, 0.0f, 15.0f);
vec3 cameraFront = glm::vec3(0,0,-1);
mat4 model = mat4(1.0);
glm::mat4 view;
glm::mat4 projection;
float scrollSpeed = 2.0f;
float fov = 45.0f;

int main()


    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "grid", NULL, NULL);
    if (window == NULL)
    
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    
    glfwMakeContextCurrent(window);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    

    Line x(vec3(0,0,0), vec3(1,0,0));
    x.setColor(vec3(1,0,0));
    Line y(vec3(0,0,0), vec3(0,1,0));
    y.setColor(vec3(0,1,0));

    std::vector<Line*> grid = ;
    for (int i = -5; i < 6; i++) 
        grid.push_back(new Line(vec3(-5, i, 0), vec3(5,i, 0)));
    
    for (int j = -5; j < 6; j++) 
        grid.push_back(new Line(vec3(j, -5, 0), vec3(j,5, 0)));
    ;

    while (!glfwWindowShouldClose(window))
    

        if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
            glfwSetWindowShouldClose(window, true);

        float currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);

        view = glm::lookAt(cameraPos,  cameraPos + cameraFront, vec3(0,1,0));

        projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);

        for (int i = 0; i < grid.size(); i++) 
            grid[i]->setMVP(projection * view * model);
            grid[i]->draw();
        

        glfwSwapBuffers(window);
        glfwPollEvents();
    
    for (int i = 0; i < grid.size(); i++) 
        delete grid.at(i);
    
    glfwTerminate();
    return 0;


void processInput(GLFWwindow *window)

    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);



void mouse_callback(GLFWwindow* window, double xpos, double ypos)


    if (firstMouse)
    
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; 

    lastX = xpos;
    lastY = ypos;


    int state = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE);
    if (state == GLFW_PRESS)
    
        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
        cameraPos -= scrollSpeed * glm::vec3(xoffset/(float)SCR_WIDTH, yoffset/(float)SCR_WIDTH, 0);

     else 
        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
        firstMouse = true;
    


void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)

    cameraPos += (float)yoffset * rayCast(lastX, lastY, projection, view);

【讨论】:

以上是关于如何避免使用着色器在 OpenGL 中消失线?的主要内容,如果未能解决你的问题,请参考以下文章

着色器在 Opengl Shader Builder 上工作,但不在我的 OpenGL 应用程序中

着色器在 2d OpenGL 中不起作用

在片段着色器中绘制别名像素完美线?

在opengl矩形上使用着色器会导致它消失[关闭]

OpenGL 对象不显示,着色器相关

OpenGL ES着色器语言----------------储存修饰符