glLineStipple устарел в OpenGL 3.1 - PullRequest
23 голосов
/ 16 мая 2011

glLineStipple устарел в последних API OpenGL.Чем он заменен?Если не заменить, как я могу получить подобный эффект?(Конечно, я не хочу использовать профиль совместимости ...)

Ответы [ 3 ]

25 голосов
/ 16 мая 2011

Извините, он не был заменен ничем.Первой идеей для подражания будет идея геометрического шейдера.Вы задаете геометрический шейдер линией, вычисляете длину его экранного пространства и на основе этого генерируете переменное число подстрок между его начальной и конечной вершинами.


РЕДАКТИРОВАТЬ: Возможно, вы могли бы также использовать 1D-текстуру с альфа-каналом (или красным), кодирующим шаблон как 0.0 (без строки) или 1.0 (строка), а затем получить координату текстуры линий от 0 до 1, и в фрагменте chader вы сделаетепростой альфа-тест, отбрасывающий фрагменты с альфа-уровнем ниже некоторого порога.Вы можете упростить геометрический шейдер для генерации вашей строки texCoords, так как в противном случае вам понадобятся разные вершины для каждой линии.Таким образом, вы также можете сделать texCoord зависимым от длины строки экрана.

Все становится сложнее, если вы рисуете треугольники (используя режим многоугольника GL_LINE).Затем вы должны самостоятельно выполнить преобразование треугольных линий в геометрическом шейдере, вставив треугольники и выполнив линии (что также может послужить причиной для отказа от режима многоугольника в будущем, если это еще не сделано).


РЕДАКТИРОВАТЬ: Хотя я считаю, что этот вопрос оставлен, я сделал тройной простой шейдер для второго подхода.Это просто минимальное решение, не стесняйтесь добавлять собственные функции самостоятельно.Я не тестировал его, потому что мне не хватает необходимого оборудования, но вы должны понять:

uniform mat4 modelViewProj;

layout(location=0) in vec4 vertex;

void main()
{
    gl_Position = modelViewProj * vertex;
}

Вершинный шейдер - это простой проход.

layout(lines) in;
layout(line_strip, max_vertices=2) out;

uniform vec2 screenSize;
uniform float patternSize;

noperspective out float texCoord;

void main()
{
    vec2 winPos0 = screenSize.xy * gl_in[0].gl_Position.xy / gl_in[0].gl_Position.w;
    vec2 winPos1 = screenSize.xy * gl_in[1].gl_Position.xy / gl_in[1].gl_Position.w;
    gl_Position = gl_in[0].gl_Position;
    texCoord = 0.0;
    EmitVertex();
    gl_Position = gl_in[1].gl_Position;
    texCoord = 0.5 * length(winPos1-winPos0) / patternSize;
    EmitVertex();
}

В геометриишейдером берем строку и вычисляем длину ее экранного пространства в пикселях.Затем мы делим это на размер текстуры узора, который будет factor*16 при эмуляции вызова на glLineStipple(factor, pattern).Это принимается как 1D координата текстуры конечной точки второй линии.

Обратите внимание, что эту координату текстуры необходимо интерполировать линейно (noperspective спецификатор интерполяции).Обычная перпендикулярно правильная интерполяция привела бы к тому, что шаблон сглаживания "сжимается" в более отдаленных частях линии, тогда как мы явно работаем со значениями экранного пространства.

uniform sampler1D pattern;
uniform vec4 lineColor;

noperspective in float texCoord;

layout(location=0) out vec4 color;

void main()
{
    if(texture(pattern, texCoord).r < 0.5)
        discard;
    color = lineColor;
}

Фрагментный шейдер теперь просто выполняет простой альфа-тест, используя значение из текстуры шаблона, которое содержит 1 для строки и 0 для отсутствия строки.Таким образом, чтобы эмулировать фиксированную функцию, у вас будет 16-пиксельная 1-компонентная 1D-текстура вместо 16-битного шаблона.Не забудьте установить режим обтекания шаблона на GL_REPEAT, в режиме фильтрации я не уверен, но я думаю, что GL_NEAREST было бы хорошей идеей.

Но, как было сказано ранее, еслиВы хотите визуализировать треугольники, используя glPolygonMode, это не сработает.Вместо этого вы должны адаптировать геометрический шейдер так, чтобы он принимал треугольники и генерировал 3 линии для каждого треугольника.


EDIT: Фактически прямая поддержка целочисленных операций в шейдерах OpenGL 3 позволяет намполностью отбросьте весь этот подход к 1D-текстуре и работайте прямо с реальным битовым шаблоном.Таким образом, геометрический шейдер немного изменен, чтобы вывести фактическую координату шаблона размера экрана без нормализации:

texCoord = 0.5 * length(winPos1-winPos0);

В фрагментном шейдере мы просто берем битовый шаблон как целое число без знака (хотя в 32-битномконтрастировать с 16-битным значением glLineStipple) и коэффициентом растяжения шаблона и просто взять координату текстуры (ну, на самом деле, текстуры больше нет, но это не важно) по модулю 32, чтобы получить свою позицию на шаблоне (эти явные * 1044)* s раздражают, но мой компилятор GLSL говорит, что неявные преобразования между int и uint являются злыми):

uniform uint pattern;
uniform float factor;

...
uint bit = uint(round(linePos/factor)) & 31U;
if((pattern & (1U<<bit)) == 0U)
    discard;
4 голосов
/ 10 марта 2019

Чтобы ответить на этот вопрос, мы сначала должны выяснить, что на самом деле делает glLineStipple.

См. Изображение, где четырехугольник слева нарисован 4 отдельными отрезками линии, используя примитивный тип GL_LINES.
Круг справа нарисован последовательной многоугольной линией, используя примитивный тип GL_LINE_STRIP.

При использовании линейных сегментов шаблон пунктировок перезапускается на каждом сегменте. Шаблон * пересчитывается на каждом примитиве.
При использовании линейной полосы шаблон рисунка применяется без шва ко всему многоугольнику. Шаблон, непрерывно непрерывный за координатами вершины.
Имейте в виду, что длина рисунка растянута по диагонали. Это, возможно, ключ к реализации.

Для отдельных отрезков это совсем не сложно, но для линейных полос все немного сложнее. Длина линии не может быть рассчитана в шейдерной программе, не зная всех примитивов линии. Даже если бы были известны все примитивы (например, SSBO), тогда вычисления должны были бы выполняться в цикле.
См. Также Пунктирные линии с профилем ядра OpenGL .

В любом случае, нет необходимости реализовывать геометрический шейдер. Хитрость заключается в том, чтобы узнать начало отрезка линии в фрагментном шейдере. Это легко с помощью flat спецификатора интерполяции.

Вершинный шейдер должен передать нормализованную координату устройства фрагментному шейдеру. Один раз с интерполяцией по умолчанию и один раз без (плоской) интерполяции. Это приводит к тому, что в оттенке фрагмента первый входной параметр содержит координату NDC фактического положения на линии и более позднюю координату NDC начала линии.

#version 330

layout (location = 0) in vec3 inPos;

flat out vec3 startPos;
out vec3 vertPos;

uniform mat4 u_mvp;

void main()
{
    vec4 pos    = u_mvp * vec4(inPos, 1.0);
    gl_Position = pos;
    vertPos     = pos.xyz / pos.w;
    startPos    = vertPos;
}

Кроме переменных входных данных, фрагментный шейдер имеет одинаковые переменные. u_resolution содержит ширину и высоту области просмотра. u_factor и u_pattern - это множитель и 16-битная комбинация в соответствии с параметрами glLineStipple.

Таким образом, можно рассчитать длину строки от начала до фактического фрагмента:

vec2  dir  = (vertPos.xy-startPos.xy) * u_resolution/2.0;
float dist = length(dir);

И фрагмент на промежутке может быть отброшен командой discard.

uint bit = uint(round(dist / u_factor)) & 15U;
if ((u_pattern & (1U<<bit)) == 0U)
    discard; 

Фрагмент шейдера:

#version 330

flat in vec3 startPos;
in vec3 vertPos;

out vec4 fragColor;

uniform vec2  u_resolution;
uniform uint  u_pattern;
uniform float u_factor;

void main()
{
    vec2  dir  = (vertPos.xy-startPos.xy) * u_resolution/2.0;
    float dist = length(dir);

    uint bit = uint(round(dist / u_factor)) & 15U;
    if ((u_pattern & (1U<<bit)) == 0U)
        discard; 
    fragColor = vec4(1.0);
}

Эта реализация намного проще и короче, чем при использовании геометрических шейдеров. Квалификатор интерполяции flat поддерживается начиная с GLSL 1.30 и GLSL ES 3.00 . В этой версии геометрические шейдеры не поддерживаются.
Смотрите рендеринг строки, который был сгенерирован с помощью вышеуказанного шейдера.

Шейдер дает правильные результирующие отрезки линии, но не подходит для линейных отрезков, поскольку шаблон пунктирной линии перезапускается по каждой координате вершины.
Эта проблема не может быть решена даже с помощью геометрического шейдера. Эта часть вопроса остается нерешенной.

Для следующей простой демонстрационной программы я использовал GLFW API для создания окна, GLEW для загрузки OpenGL и GLM -OpenGL Matmatics для математика Я не предоставляю код для функции CreateProgram, которая просто создает программный объект из исходного кода вершинного шейдера и фрагментного шейдера:

#include <vector>
#include <string>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <gl/gl_glew.h>
#include <GLFW/glfw3.h>

std::string vertShader = R"(
#version 330

layout (location = 0) in vec3 inPos;

flat out vec3 startPos;
out vec3 vertPos;

uniform mat4 u_mvp;

void main()
{
    vec4 pos    = u_mvp * vec4(inPos, 1.0);
    gl_Position = pos;
    vertPos     = pos.xyz / pos.w;
    startPos    = vertPos;
}
)";

std::string fragShader = R"(
#version 330

flat in vec3 startPos;
in vec3 vertPos;

out vec4 fragColor;

uniform vec2  u_resolution;
uniform uint  u_pattern;
uniform float u_factor;

void main()
{
    vec2  dir  = (vertPos.xy-startPos.xy) * u_resolution/2.0;
    float dist = length(dir);

    uint bit = uint(round(dist / u_factor)) & 15U;
    if ((u_pattern & (1U<<bit)) == 0U)
        discard; 
    fragColor = vec4(1.0);
}
)";

GLuint CreateVAO(std::vector<glm::vec3> &varray)
{
    GLuint bo[2], vao;
    glGenBuffers(2, bo);
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glEnableVertexAttribArray(0); 
    glBindBuffer(GL_ARRAY_BUFFER, bo[0] );
    glBufferData(GL_ARRAY_BUFFER, varray.size()*sizeof(*varray.data()), varray.data(), GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); 

    return vao;
}

int main(void)
{
    if ( glfwInit() == 0 )
        return 0;
    GLFWwindow *window = glfwCreateWindow( 800, 600, "GLFW OGL window", nullptr, nullptr );
    if ( window == nullptr )
        return 0;
    glfwMakeContextCurrent(window);

    glewExperimental = true;
    if ( glewInit() != GLEW_OK )
        return 0;

    GLuint program    = CreateProgram(vertShader, fragShader);
    GLint loc_mvp     = glGetUniformLocation(program, "u_mvp");
    GLint loc_res     = glGetUniformLocation(program, "u_resolution");
    GLint loc_pattern = glGetUniformLocation(program, "u_pattern");
    GLint loc_factor  = glGetUniformLocation(program, "u_factor");

    glUseProgram(program);

    GLushort pattern = 0x18ff;
    GLfloat  factor  = 2.0f;
    glUniform1ui(loc_pattern, pattern);
    glUniform1f(loc_factor, factor);
    //glLineStipple(2.0, pattern);
    //glEnable(GL_LINE_STIPPLE);

    glm::vec3 p0(-1.0f, -1.0f, 0.0f);
    glm::vec3 p1(1.0f, -1.0f, 0.0f);
    glm::vec3 p2(1.0f, 1.0f, 0.0f);
    glm::vec3 p3(-1.0f, 1.0f, 0.0f);
    std::vector<glm::vec3> varray1{ p0, p1, p1, p2, p2, p3, p3, p0 };
    GLuint vao1 = CreateVAO(varray1);

    std::vector<glm::vec3> varray2;
    for (size_t u=0; u <= 360; u += 8)
    {
        double a = u*M_PI/180.0;
        double c = cos(a), s = sin(a);
        varray2.emplace_back(glm::vec3((float)c, (float)s, 0.0f));
    }
    GLuint vao2 = CreateVAO(varray2);

    glm::mat4(project);
    int vpSize[2]{0, 0};
    while (!glfwWindowShouldClose(window))
    {
        int w, h;
        glfwGetFramebufferSize(window, &w, &h);
        if (w != vpSize[0] ||  h != vpSize[1])
        {
            vpSize[0] = w; vpSize[1] = h;
            glViewport(0, 0, vpSize[0], vpSize[1]);
            float aspect = (float)w/(float)h;
            project = glm::ortho(-aspect, aspect, -1.0f, 1.0f, -10.0f, 10.0f);
            glUniform2f(loc_res, (float)w, (float)h);
        }

        glClear(GL_COLOR_BUFFER_BIT);

        glm::mat4 modelview1( 1.0f );
        modelview1 = glm::translate(modelview1, glm::vec3(-0.6f, 0.0f, 0.0f) );
        modelview1 = glm::scale(modelview1, glm::vec3(0.5f, 0.5f, 1.0f) );
        glm::mat4 mvp1 = project * modelview1;

        glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp1));
        glBindVertexArray(vao1);
        glDrawArrays(GL_LINES, 0, (GLsizei)varray1.size());

        glm::mat4 modelview2( 1.0f );
        modelview2 = glm::translate(modelview2, glm::vec3(0.6f, 0.0f, 0.0f) );
        modelview2 = glm::scale(modelview2, glm::vec3(0.5f, 0.5f, 1.0f) );
        glm::mat4 mvp2 = project * modelview2;

        glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp2));
        glBindVertexArray(vao2);
        glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)varray2.size());

        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwTerminate();

    return 0;
}
0 голосов
/ 04 марта 2019

Поскольку я изо всех сил старался (без каламбура) сделать это правильно, я подумал, что это может быть полезно другим, если я поделюсь своей реализацией набора шейперов, основанных на версии Кристиана Рау. Для управления плотностью паттернов фрагментному шейдеру требуется количество паттернов nPatterns на единицу длины области просмотра - вместо установки коэффициента. Также включена дополнительная функция плоскости отсечения. Остальное в основном комментирует и убирает.

Бесплатно для использования в любых целях.

Вершинный шейдер:

#version 330

in vec4 vertex;

void main(void)
{
    // just a pass-through
    gl_Position = vertex;
}

Геометрический шейдер:

#version 330

layout(lines) in;
layout(line_strip, max_vertices = 2) out;

uniform mat4 pvmMatrix;
uniform mat4 mMatrix;
uniform mat4 vMatrix;


out vec3 vPosition;  // passed to the fragment shader for plane clipping
out float texCoord;  // passed to the fragment shader for stipple pattern

void main(void)
{
    // to achieve uniform pattern density whatever the line orientation
    // the upper texture coordinate is made proportional to the line's length
    vec3 pos0 = gl_in[0].gl_Position.xyz;
    vec3 pos1 = gl_in[1].gl_Position.xyz;
    float max_u_texture = length(pos1 - pos0);

    // Line Start
    gl_Position = pvmMatrix * (gl_in[0].gl_Position);
    texCoord = 0.0;
    // depth position for clip plane
    vec4 vsPos0 = vMatrix * mMatrix * gl_Position;
    vPosition = vsPos0.xyz / vsPos0.w;
    EmitVertex();  // one down, one to go

    // Line End
    gl_Position = pvmMatrix * (gl_in[1].gl_Position);
    texCoord = max_u_texture;
    // depth position for clip plane
    vec4 vsPos1 = vMatrix * mMatrix * gl_Position;
    vPosition = vsPos0.xyz / vsPos0.w;
    EmitVertex();

    // done
    EndPrimitive();
}

Фрагмент шейдера:

#version 330

uniform int pattern;   // an integer between 0 and 0xFFFF representing the bitwise pattern
uniform int nPatterns; // the number of patterns/unit length of the viewport, typically 200-300 for good pattern density
uniform vec4 color;
uniform vec4 clipPlane0; // defined in view-space

in float texCoord;

in vec3 vPosition;

layout(location=0) out vec4 fragColor;

void main(void)
{
    // test vertex postion vs. clip plane position (optional)
    if (vPosition.z > clipPlane0.w) {
        discard;
        return;
    }

    // use 4 bytes for the masking pattern
    // map the texture coordinate to the interval [0,2*8[
    uint bitpos = uint(round(texCoord * nPatterns)) % 16U;
    // move a unit bit 1U to position bitpos so that
    // bit is an integer between 1 and 1000 0000 0000 0000 = 0x8000
    uint bit = (1U << bitpos);

    // test the bit against the masking pattern
    //  Line::SOLID:       pattern = 0xFFFF;  // = 1111 1111 1111 1111 = solid pattern
    //  Line::DASH:        pattern = 0x3F3F;  // = 0011 1111 0011 1111
    //  Line::DOT:         pattern = 0x6666;  // = 0110 0110 0110 0110
    //  Line::DASHDOT:     pattern = 0xFF18;  // = 1111 1111 0001 1000
    //  Line::DASHDOTDOT:  pattern = 0x7E66;  // = 0111 1110 0110 0110
    uint up = uint(pattern);

    // discard the bit if it doesn't match the masking pattern
    if ((up & bit) == 0U) discard;

    fragColor = color;
}
...