Инициализация OpenGL вручную правильным способом

#c #winapi #opengl #rendering #pixelformat

Вопрос:

Я не могу понять, почему треугольник, отображаемый в приведенном ниже примере, имеет черный цвет (автоматическая интерполяция не работает)

 INT WINAPI
WinMain(
    HINSTANCE, HINSTANCE, LPSTR, int
)
{
    HINSTANCE hInstance = GetModuleHandle(0);
    GLOBAL_INFO("Application started");
    WNDCLASSA window_class{}; 
    window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    window_class.lpfnWndProc = WndProc;
    window_class.hInstance = hInstance;
    window_class.lpszClassName = "M9Main";
    
    if (!RegisterClassA(amp;window_class))
    {
        return 0;
    }
    
    HWND window_handle = CreateWindow(
        "M9Main",
        "m9",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        R_WIDTH, R_HEIGHT,
        NULL, NULL,
        hInstance,
        NULL
    );

    if (!window_handle)
    {
        return 0;
    }

    if (!win32_opengl_init(window_handle))
    {
        return 0;
    }

    ShowWindow(window_handle, SW_SHOWDEFAULT);
    
    global_running = M9_TRUE;
    const char* vs =
    #include "shaders/sample.vs.glsl"

    const char* fs =
    #include "shaders/sample.fs.glsl"

    u32 shader = opengl_load_shaders(vs, fs);

    r32 vertices[] {
         0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 
        -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 
        -0.5f,  0.5f, 0.0f, 0.0f, 1.0f,
    };
    
    u32 VBO{};
    u32 VAO{};

    glGenVertexArrays(1, amp;VAO);
    glGenBuffers(1, amp;VBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);

    glVertexAttribPointer(
        0, 2, 
        GL_FLOAT, GL_FALSE, 
        5 * sizeof(r32), (void*)0
    );
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(
        1, 3, 
        GL_FLOAT, GL_FALSE, 
        5 * sizeof(r32), (void*)(sizeof(r32) * 2)
    );
    glEnableVertexAttribArray(1);

    glUseProgram(shader);

    i32 uDisp = glGetUniformLocation(shader, "uDisp");

    r32 disp[2]{};
    r32 delta = 0.001f;
    const game_controller_stateamp; ctl = global_game_input.keyboard_state;    
    while (global_running)
    {
        process_messages(global_game_input);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        if (ctl.move_up.pressed)
        {
            disp[1]  = delta;
        }
        else if (ctl.move_down.pressed)
        {
            disp[1] -= delta;
        }
        else if (ctl.move_left.pressed)
        {
            disp[0] -= delta;
        }
        else if (ctl.move_right.pressed)
        {
            disp[0]  = delta;
        }

        glUniform2f(uDisp, disp[0], disp[1]);


        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);


        SwapBuffers(opengl_context.device_ctx);
    }

    return 0;

}
 

Мои шейдеры:

 #version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

uniform vec2 uDisp;
out vec3 Color;

void main()
{
    gl_Position = vec4(aPos.x   uDisp.x, aPos.y   uDisp.y, 0.0f, 1.0);
    Color = aColor;
};
 
 #version 330 core

out vec4 outcolor;
in vec3 inColor;

void main()
{
    outcolor = vec4(inColor, 1.0f);
};
 

Я загружаю OpenGL вручную (без GLAD или smth), также я не использую библиотеку, такую как GLFW, для управления окном. Поэтому я должен инициализировать контекст OpenGL и описать формат пикселей. Все это делается в win32_opengl_init функциональном режиме:

 static b32
win32_opengl_init(HWND hwnd)
{
    opengl_context.device_ctx = GetDC(hwnd);
    HDCamp; dc = opengl_context.device_ctx;

    PIXELFORMATDESCRIPTOR pfd{};
    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;
    pfd.cDepthBits = 24;
    pfd.cStencilBits = 8;
    pfd.iLayerType = PFD_MAIN_PLANE;

    // TODO: display error message using last error code
    if (!SetPixelFormat(dc, ChoosePixelFormat(dc, amp;pfd), amp;pfd))
    {
        GLOBAL_ERROR("Cannot set pixel format: %lu", GetLastError());
        return M9_FALSE;
    }
    

    HGLRC rendering_context = wglCreateContext(dc);

    if (!rendering_context)
    {
        GLOBAL_ERROR("Cannot create rendering context: %lu", GetLastError());
        return M9_FALSE;
    }

    opengl_context.rendering_ctx = rendering_context;

    if (!win32_opengl_make_ctx_current(opengl_context))
    {
        GLOBAL_ERROR("Cannot make rendering context current: %lu", GetLastError());
        return M9_FALSE;
    }

/* loading functions */

    GLOBAL_INFO("OpenGL initialized successfully");

    return M9_TRUE;
}
 

Проблема, вероятно, в моем описании формата пикселей, потому что я не вижу никаких других различий между тем, что я делаю, и тем, что происходит в соответствующем учебнике: https://learnopengl.com/Getting-started/Hello-Triangle

Эта проблема исчезнет, когда я начну использовать GLFW для создания контекста opengl … Но я хочу все делать своими руками, без каких-либо библиотек.

Ответ №1:

Я не могу понять, почему треугольник, отображаемый в приведенном ниже примере, имеет черный цвет (автоматическая интерполяция не работает)

Но это довольно легко увидеть:

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

 out vec3 Color;
 

Ваш Шейдер Фрагментов:

 in vec3 inColor;
 

Они не совпадают, поэтому inColor просто не определены в вашем шейдере фрагментов.

Эта проблема исчезает, когда я начинаю использовать GLFW для создания контекста opengl …

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

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

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