Правильный способ репликации поведения фиксированного канала в GLSL версии 1.3 и выше?

#opengl #glsl

#opengl #glsl

Вопрос:

Я расстраиваюсь из-за отсутствия ясности в документации о GLSL и того, что разрешено, устарело и рекомендуется в разных версиях.

У меня есть куча фрагментных шейдеров, которые я пишу в GLSL версии 1.3. Они работают, но есть одна загадочная ошибка, которая, как я подозреваю, может появиться, потому что я не указываю какой-либо вершинный шейдер, вместо этого полагаясь на устаревшую gl_TexCoord[i] переменную для получения текстурных данных. В любом случае, я хочу делать все стандартным и рекомендуемым способом.

Итак, я спрашиваю следующее: каков правильный минимальный пример вершинного шейдера, который не делает ничего, кроме репликации функциональности фиксированного канала в отношении позиционирования вершин и интерполяции координат текстуры в GLSL 1.3, и как мне прочитать координату текстуры в моем фрагментном шейдере?

Моя попытка прямо сейчас выглядит так:

 // Vertex shader
#version 130
  out vec2 texCoord;

  void main()
  {
    gl_Position = vec4(gl_Vertex.xy, 0.0, 1.0);
    texCoord = 0.5 * gl_Position.xy   vec2(0.5, 0.5);
  }

//Fragment shader
#version 130
  in vec2 texCoord;
  uniform sampler2D tex;
  out vec4 outColor;

  void main() {
    outColor = texture2D(tex, texCoord.xy);
  }
  

Но это не дает такого же результата, как простое исключение вершинного шейдера и замена texCoord на gl_TexCoord[0] (фактически, я вообще не получаю выходных данных в своем тесте). В довершение всего, мои драйверы жалуются, что gl_Vertex это тоже устарело!

Поэтому, пожалуйста, может кто-нибудь предоставить совместимый со стандартом рекомендуемый пример того, как просто делать то, что фиксированный канал уже делает в GLSL> = 1.3? Если рекомендуемый способ отличается в версиях выше 1.3, я был бы признателен за примеры, демонстрирующие это, если это возможно.

Если это имеет значение — и я надеюсь, что это не так — мой видовой экран настроен следующим образом (F #, но код должен быть понятным):

 GL.Viewport(base.ClientRectangle.X, base.ClientRectangle.Y, base.ClientRectangle.Width, base.ClientRectangle.Height)
let ratio = (float base.ClientRectangle.Height) / (float base.ClientRectangle.Width)
GL.MatrixMode(MatrixMode.Projection)
GL.Ortho(-0.5, 0.5, ratio/2.0, -ratio/2.0, -10., 10.)
GL.MatrixMode(MatrixMode.Modelview)
  

Ответ №1:

Итак, я спрашиваю следующее: каков правильный минимальный пример вершинного шейдера, который не делает ничего, кроме репликации функциональности фиксированного канала (…)?

Не существует статического источника шейдеров GLSL, который будет реплицировать конвейер с фиксированной функцией — это способ «один размер, ошибка, шейдер подходит всем». Многие настройки конвейера фиксированных функций, например, выполненные с помощью glTexEnvi, переводятся в другой код шейдера.

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


РЕДАКТИРОВАТЬ из-за комментария:

Полностью рабочий, минимальный пример GLSL для OpenGL-2, использует GLFW для создания контекста и окна, GLEW для загрузки расширения.

 #include <stdlib.h>
#include <stdio.h>
#include <GL/glew.h>
#include <GL/glfw.h>

static void pushModelview()
{
    GLenum prev_matrix_mode;
    glGetIntegerv(GL_MATRIX_MODE, amp;prev_matrix_mode);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glMatrixMode(prev_matrix_mode);
}

static void popModelview()
{
    GLenum prev_matrix_mode;
    glGetIntegerv(GL_MATRIX_MODE, amp;prev_matrix_mode);
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glMatrixMode(prev_matrix_mode);
}

static const GLchar *vertex_shader_source =
"#version 120n"
"void main()"
"{"
"   gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;"
"   gl_TexCoord[0] = gl_MultiTexCoord0;"
"   gl_FrontColor = gl_Color;"
"   gl_BackColor = gl_Color;"
"}";
GLuint shaderVertex = 0;

static const GLchar *fragment_shader_source = 
"#version 120n"
"uniform sampler2D texCMYK;"
"uniform sampler2D texRGB;"
"void main()"
"{"
"   gl_FragColor = -texture2D(texCMYK, gl_TexCoord[0].st)   texture2D(texRGB, gl_TexCoord[0].st);"
"}";
GLuint shaderFragment = 0;

GLuint shaderProgram = 0;

#define TEX_CMYK_WIDTH 2
#define TEX_CMYK_HEIGHT 2
GLubyte textureDataCMYK[TEX_CMYK_WIDTH * TEX_CMYK_HEIGHT][3] = {
    {0x00, 0xff, 0xff}, {0xff, 0x00, 0xff},
    {0xff, 0xff, 0x00}, {0x00, 0x00, 0x00}
};
GLuint texCMYK = 0;

#define TEX_RGB_WIDTH 2
#define TEX_RGB_HEIGHT 2
GLubyte textureDataRGB[TEX_RGB_WIDTH * TEX_RGB_HEIGHT][3] = {
    {0x00, 0x00, 0xff}, {0xff, 0xff, 0xff},
    {0xff, 0x00, 0x00}, {0x00, 0xff, 0x00}
};
GLuint texRGB = 0;

GLfloat cube_vertices[][8] =  {
    /*  X     Y     Z   Nx   Ny   Nz    S    T */
    {-1.0, -1.0,  1.0, 0.0, 0.0, 1.0, 0.0, 0.0}, // 0
    { 1.0, -1.0,  1.0, 0.0, 0.0, 1.0, 1.0, 0.0}, // 1
    { 1.0,  1.0,  1.0, 0.0, 0.0, 1.0, 1.0, 1.0}, // 2
    {-1.0,  1.0,  1.0, 0.0, 0.0, 1.0, 0.0, 1.0}, // 3

    { 1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0},
    {-1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 0.0},
    {-1.0,  1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 1.0},
    { 1.0,  1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 1.0},

    {-1.0, -1.0,  1.0, -1.0, 0.0, 0.0, 0.0, 0.0},
    {-1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 0.0},
    {-1.0,  1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0},
    {-1.0,  1.0,  1.0, -1.0, 0.0, 0.0, 0.0, 1.0},
    
    { 1.0, -1.0, -1.0,  1.0, 0.0, 0.0, 0.0, 0.0},
    { 1.0, -1.0,  1.0,  1.0, 0.0, 0.0, 1.0, 0.0},
    { 1.0,  1.0,  1.0,  1.0, 0.0, 0.0, 1.0, 1.0},
    { 1.0,  1.0, -1.0,  1.0, 0.0, 0.0, 0.0, 1.0},
    
    { 1.0, -1.0, -1.0,  0.0, -1.0, 0.0, 0.0, 0.0},
    {-1.0, -1.0, -1.0,  0.0, -1.0, 0.0, 1.0, 0.0},
    {-1.0, -1.0,  1.0,  0.0, -1.0, 0.0, 1.0, 1.0},
    { 1.0, -1.0,  1.0,  0.0, -1.0, 0.0, 0.0, 1.0},

    {-1.0, 1.0,  1.0,  0.0,  1.0, 0.0, 0.0, 0.0},
    { 1.0, 1.0,  1.0,  0.0,  1.0, 0.0, 1.0, 0.0},
    { 1.0, 1.0, -1.0,  0.0,  1.0, 0.0, 1.0, 1.0},
    {-1.0, 1.0, -1.0,  0.0,  1.0, 0.0, 0.0, 1.0},
};

static void draw_cube(void)
{
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glVertexPointer(3, GL_FLOAT, sizeof(GLfloat) * 8, amp;cube_vertices[0][0]);
    glNormalPointer(GL_FLOAT, sizeof(GLfloat) * 8, amp;cube_vertices[0][3]);
    glTexCoordPointer(2, GL_FLOAT, sizeof(GLfloat) * 8, amp;cube_vertices[0][6]);

    glDrawArrays(GL_QUADS, 0, 24);
}

static void bind_sampler_to_unit_with_texture(GLchar const * const sampler_name, GLuint texture_unit, GLuint texture)
{
        glActiveTexture(GL_TEXTURE0   texture_unit); 
        glBindTexture(GL_TEXTURE_2D, texture);
        GLuint loc_sampler = glGetUniformLocation(shaderProgram, sampler_name);
        glUniform1i(loc_sampler, texture_unit);
}

static void display(double T)
{
    int window_width, window_height;

    glfwGetWindowSize(amp;window_width, amp;window_height);
    if( !window_width || !window_height )
        return;
    
    const float window_aspect = (float)window_width / (float)window_height;

    glDisable(GL_SCISSOR_TEST);

    glClearColor(0.5, 0.5, 0.7, 1.0);
    glClearDepth(1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    glEnable(GL_DEPTH_TEST);
    glViewport(0, 0, window_width, window_height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-window_aspect, window_aspect, -1, 1, 1, 100);
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0, 0, -5);

    pushModelview();
    glRotatef(T * 0.1 * 180, 0., 1., 0.);
    glRotatef(T * 0.1 *  60, 1., 0., 0.);
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    glUseProgram(shaderProgram);
    bind_sampler_to_unit_with_texture("texCMYK", 0, texCMYK);
    bind_sampler_to_unit_with_texture("texRGB", 1, texRGB);

    draw_cube();
    popModelview();
    
    glfwSwapBuffers();
}

static int open_window(void)
{
#if 0
    glfwWindowHint(GLFW_OPENGL_VERSION_MAJOR, 2);
    glfwWindowHint(GLFW_OPENGL_VERSION_MINOR, 0);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
#endif

    if( glfwOpenWindow(0, 0,     /* default size */
                       8,  8, 8, /* 8 bits per channel */
                       8, 24, 8, /* 8 alpha, 24 depth, 8 stencil */
                       GLFW_WINDOW) != GL_TRUE ) {
        fputs("Could not open window.n", stderr);
        return 0;
    }

    if( glewInit() != GLEW_OK ) {
        fputs("Could not initialize extensions.n", stderr);
        return 0;
    }
    return 1;
}

static int check_extensions(void)
{
    if( !GLEW_ARB_vertex_shader ||
        !GLEW_ARB_fragment_shader ) {
        fputs("Required OpenGL functionality not supported by system.n", stderr);
        return 0;
    }

    return 1;
}

static int check_shader_compilation(GLuint shader)
{
    GLint n;
    glGetShaderiv(shader, GL_COMPILE_STATUS, amp;n);
        if( n == GL_FALSE ) {
        GLchar *info_log;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, amp;n);
        info_log = malloc(n);
        glGetShaderInfoLog(shader, n, amp;n, info_log);
        fprintf(stderr, "Shader compilation failed: %*sn", n, info_log);
        free(info_log);
        return 0;
    }
    return 1;
}

static int init_resources(void)
{
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
    glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);

    glGenTextures(1, amp;texCMYK);
    glBindTexture(GL_TEXTURE_2D, texCMYK);
    glTexImage2D(GL_TEXTURE_2D, 0,  GL_RGB8, TEX_CMYK_WIDTH, TEX_CMYK_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, textureDataCMYK);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glGenTextures(1, amp;texRGB);
    glBindTexture(GL_TEXTURE_2D, texRGB);
    glTexImage2D(GL_TEXTURE_2D, 0,  GL_RGB8, TEX_RGB_WIDTH, TEX_RGB_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, textureDataRGB);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    shaderVertex = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(shaderVertex, 1, (const GLchar**)amp;vertex_shader_source, NULL);
    glCompileShader(shaderVertex);
    if( !check_shader_compilation(shaderVertex) )
        return 0;

    shaderFragment = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(shaderFragment, 1, (const GLchar**)amp;fragment_shader_source, NULL);
    glCompileShader(shaderFragment);
    if( !check_shader_compilation(shaderFragment) )
        return 0;

    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, shaderVertex);
    glAttachShader(shaderProgram, shaderFragment);
    glLinkProgram(shaderProgram);

    return 1;
}

static void main_loop(void)
{
    glfwSetTime(0);
    while( glfwGetWindowParam(GLFW_OPENED) == GL_TRUE ) {
        display(glfwGetTime());
    }
}

int main(int argc, char *argv[])
{
    if( glfwInit() != GL_TRUE ) {
        fputs("Could not initialize framework.n", stderr);
        return -1;
    }

    if( !open_window() )
        return -1;

    if( !check_extensions() )
        return -1;

    if( !init_resources() )
        return -1;

    main_loop();
    
    glfwTerminate();
    return 0;
}
  

Комментарии:

1. Меня не интересуют такие вещи, как параметры glTexEnvi. Меня интересуют только координаты вершин и текстур, чтобы я мог использовать свой фрагментный шейдер (исключенный из вопроса для краткости) на текстурированных примитивах. Поскольку мой код шейдера неверен, не могли бы вы привести функциональный пример того, как это сделать?