Подтвердить что ты не робот

Простая структура для шейдеров OpenGL в C/С++

Я просто хотел попробовать некоторые шейдеры на плоском изображении. Оказывается, что для написания программы на C, которая просто берет изображение в виде текстуры и применяется, скажем, гауссовское размытие, в качестве фрагментарного шейдера на нем не так просто: вам нужно инициализировать OpenGL, которые похожи на 100 строк кода, затем понимание GLBuffers и т.д. Кроме того, чтобы связываться с системой окон, нужно использовать GLUT, которая является другой структурой.

Оказывается, что композитор Nvidia Fx приятно играть с шейдерами. Но мне все же хотелось бы иметь простую программу на C или С++, которая просто применяет данный шейдер фрагмента к изображению и отображает результат. Есть ли у кого-нибудь пример или есть рамки?

4b9b3361

Ответ 1

Прежде всего, я бы избегал использовать глют - он глючит, не обновлялся примерно через десятилетие, и его дизайн не очень хорошо подходит тем, что сегодня требуется большинству людей (например, хотя вы можете использовать его для анимации, он действительно предназначен в первую очередь для создания статического дисплея). Я указал на ряд альтернатив переутомлению в предыдущем ответе.

Это (в основном) оставляет код для компиляции, ссылки и использования шейдеров. Я написал небольшой класс, который мне подходит для этой цели:

class shader_prog {
    GLuint vertex_shader, fragment_shader, prog;

    template <int N>
    GLuint compile(GLuint type, char const *(&source)[N]) {
        GLuint shader = glCreateShader(type);
        glShaderSource(shader, N, source, NULL);
        glCompileShader(shader);
        GLint compiled;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
        if (!compiled) {
            GLint length;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
            std::string log(length, ' ');
            glGetShaderInfoLog(shader, length, &length, &log[0]);
            throw std::logic_error(log);
            return false;
        }
        return shader;
    }
public:
    template <int N, int M>
    shader_prog(GLchar const *(&v_source)[N], GLchar const *(&f_source)[M]) {
        vertex_shader = compile(GL_VERTEX_SHADER, v_source);
        fragment_shader = compile(GL_FRAGMENT_SHADER, f_source);
        prog = glCreateProgram();
        glAttachShader(prog, vertex_shader);
        glAttachShader(prog, fragment_shader);
        glLinkProgram(prog);
    }

    operator GLuint() { return prog; }
    void operator()() { glUseProgram(prog); }

    ~shader_prog() {
        glDeleteProgram(prog);
        glDeleteShader(vertex_shader);
        glDeleteShader(fragment_shader);
    }
};

Для простой демонстрации несколько "проходных" шейдеров (просто имитируйте конвейер с фиксированной функциональностью):

const GLchar *vertex_shader[] = {
    "void main(void) {\n",
    "    gl_Position = ftransform();\n",
    "    gl_FrontColor = gl_Color;\n",
    "}"
};

const GLchar *color_shader[] = {
    "void main() {\n",
    "    gl_FragColor = gl_Color;\n",
    "}"
};

Что вы будете использовать примерно так:

void draw() { 
    // compile and link the specified shaders:
    static shader_prog prog(vertex_shader, color_shader);

    // Use the compiled shaders:    
    prog(); 

    // Draw something:
    glBegin(GL_TRIANGLES);
        glColor3f(0.0f, 0.0f, 1.0f);
        glVertex3f(-1.0f, 0.0f, -1.0f);
        glColor3f(0.0f, 1.0f, 0.0f);
        glVertex3f(1.0f, 0.0f, -1.0f);
        glColor3f(1.0f, 0.0f, 0.0f);
        glVertex3d(0.0, -1.0, -1.0);
    glEnd();
}

Если вы собираетесь использовать, например, несколько различных шейдеров фрагментов в процессе рисования вашей сцены, вы просто определяете статический объект для каждого, а затем выполняете prog1();, prog2(); и т.д. просто перед тем, как рисовать объекты, которые вы хотите затенять с каждым шейдером. Например,

void draw() { 
    static shader_prog wall_shader("wall_vertex", "wall_frag");
    static shader_prog skin_shader("skin_vertex", "skin_frag");

    wall_shader();
    draw_walls();

    skin_shader();
    draw_skin();
}

Изменить: как правильно указывает @rotoglup, использование переменных static задерживает разрушение до тех пор, пока контекст OpenGL не будет уничтожен, поэтому, когда деструкторы попытаются использовать glDeleteProgram/glDeleteShader, результаты непредсказуемы ( в лучшем случае).

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

Чтобы избежать обеих проблем, вы, как правило, хотите создать свои шейдерные объекты как члены экземпляра класса, чье жизненное время, в свою очередь, привязано к времени жизни того, что будет в тени:

class some_character_type { 
    shader_prog skin_shader;
public:
    // ...
};

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

Конечно, в некоторых случаях это тоже не желательно. Например, рассмотрим 3D-версию древних игр "убить множество целей", таких как Galaga или Centipede. Для подобных игр вы создаете и уничтожаете множество практически идентичных целей относительно быстро. Учитывая большое количество практически идентичных целей, вы, вероятно, захотите использовать что-то вроде shared_ptr<shader_prog>, чтобы создать один экземпляр шейдера, который был разделен между всеми экземплярами определенного целевого типа. Учитывая, что вы повторно используете одни и те же типы целей много раз, вам может понадобиться немного больше, чем это, поэтому вы сохраняете одни и те же шейдеры во всей игре, а не только, когда отображается конкретный тип цели.

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

Ответ 2

Я был в аналогичной позиции примерно полтора года назад. Я быстро нашел простой учебник и исходный код для использования GLSL.. но мне приходилось работать с GLUT и GLEW, и я думаю, что в итоге я собрал хотя бы один из них. Поскольку я использовал Windows (а Windows - нестандартный частный случай, который редко обрабатывается открытыми проектами), это также связано с нелепым процессом, когда я должен был вручную копировать и вставлять файлы DLL и заголовки в определенные общие местах. Это всегда боль, и я потерял хороший кусок в моей жизни, делая что-то вроде этого, но я протащил процесс по указанию, и это получилось в конце, как это обычно бывает.

В любом случае, самый удобный пример шейдера с использованием GLSL, который я могу найти прямо сейчас, - это http://www.lighthouse3d.com/opengl/glsl/index.php?minimal

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

Мне жаль, что он использует GLUT и GLEW. Если вы получите лучший ответ на этот вопрос, я тоже стану мгновенным поклонником того, что сайт предлагает код. Удачи.

Ответ 3

Этот tutorial может быть полезен (обратите внимание, что он содержит материал GLSL помимо старого материала Cg).

Обратите внимание, что я бы подумал о том, чтобы писать шейдеры для реализации неграфических объектов типа GPGPU в качестве устаревшего подхода в наши дни. OpenCL или CUDA, очевидно, способ идти в будущем.