优秀的编程知识分享平台

网站首页 > 技术文章 正文

「图片显示移植-1」 尝试用opengl/GLFW显示图片

nanyue 2025-02-09 13:36:53 技术文章 7 ℃

GLFW【https://www.glfw.org】调用了opengl来做图形的显示。

我最近需要用opengl来显示图像,不能使用opencv等库。

看了一个glfw的官网,里面有
github:https://github.com/glfw/glfw

就去把代码下载下来,并且很容易编译完成了,并运行了源码中提供的tests和examples,感觉学到很多。

下面是example中的例子:




另外还有tests中的例子:


这是一个查看函数对内存使用情况的例子,这里显示了glfwInit,glfwCreateWindow等函数运行,内存分配和内存收回的情况。

它的代码为:


#define GLAD_GL_IMPLEMENTATION
#include 
#define GLFW_INCLUDE_NONE
#include 

#include 
#include 
#include 

#define CALL(x) (function_name = #x, x)
static const char* function_name = NULL;

struct allocator_stats
{
    size_t total;
    size_t current;
    size_t maximum;
};

static void error_callback(int error, const char* description)
{
    fprintf(stderr, "Error: %s\n", description);
}

static void* allocate(size_t size, void* user)
{
    struct allocator_stats* stats = user;
    assert(size > 0);

    stats->total += size;
    stats->current += size;
    if (stats->current > stats->maximum)
        stats->maximum = stats->current;

    printf("%s: allocate %zu bytes (current %zu maximum %zu total %zu)\n",
           function_name, size, stats->current, stats->maximum, stats->total);

    size_t* real_block = malloc(size + sizeof(size_t));
    assert(real_block != NULL);
    *real_block = size;
    return real_block + 1;
}

static void deallocate(void* block, void* user)
{
    struct allocator_stats* stats = user;
    assert(block != NULL);

    size_t* real_block = (size_t*) block - 1;
    stats->current -= *real_block;

    printf("%s: deallocate %zu bytes (current %zu maximum %zu total %zu)\n",
           function_name, *real_block, stats->current, stats->maximum, stats->total);

    free(real_block);
}

static void* reallocate(void* block, size_t size, void* user)
{
    struct allocator_stats* stats = user;
    assert(block != NULL);
    assert(size > 0);

    size_t* real_block = (size_t*) block - 1;
    stats->total += size;
    stats->current += size - *real_block;
    if (stats->current > stats->maximum)
        stats->maximum = stats->current;

    printf("%s: reallocate %zu bytes to %zu bytes (current %zu maximum %zu total %zu)\n",
           function_name, *real_block, size, stats->current, stats->maximum, stats->total);

    real_block = realloc(real_block, size + sizeof(size_t));
    assert(real_block != NULL);
    *real_block = size;
    return real_block + 1;
}

int main(void)
{
    struct allocator_stats stats = {0};
    const GLFWallocator allocator =
    {
        .allocate = allocate,
        .deallocate = deallocate,
        .reallocate = reallocate,
        .user = &stats
    };

    glfwSetErrorCallback(error_callback);
    glfwInitAllocator(&allocator);

    if (!CALL(glfwInit)())
        exit(EXIT_FAILURE);

    GLFWwindow* window = CALL(glfwCreateWindow)(400, 400, "Custom allocator test", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    CALL(glfwMakeContextCurrent)(window);
    gladLoadGL(glfwGetProcAddress);
    CALL(glfwSwapInterval)(1);

    while (!CALL(glfwWindowShouldClose)(window))
    {
        glClear(GL_COLOR_BUFFER_BIT);
        CALL(glfwSwapBuffers)(window);
        CALL(glfwWaitEvents)();
    }

    CALL(glfwTerminate)();
    exit(EXIT_SUCCESS);
}

这个代码中,稍微有点迷惑人的就是一个宏CALL(x)了,在11,12行


使用时:CALL(glfwInit)() 和 CALL(gffwCreateWindow)(400,...),如下图

按照宏定义展开:CALL(glfwInit)()展开为:function_name = "glfwInit",glfwInit()

CALL(glfwCreateWindow(400,...)) 展开为:function_name = "glfwCreateWindow",glfwCreateWindow(400,...)

这中间的逗号是很少见。我查了下,c语言中可以这样用,顺序运行的意思:

不过想想如果不用逗号用分号的好,应该会报错,因为分号意味着本行结束。预定义

#define CALL(x) (function_name = "x"; x)将会在分号处结束。

其他代码都比较正常点了。

本代码主要是为了查看函数的内存使用情况,程序员可以使用此函数检查有无内存泄漏的情况发生。

再看另一个例子:window.c


这个例子呢,我们通过这个window来设置窗口的显示属性。比如调整Opacity就能使窗口变透明:


改变窗口的大小后,窗口山显示的文字也会改变:


总代码量:

#define GLAD_GL_IMPLEMENTATION
#include 
#define GLFW_INCLUDE_NONE
#include 

#include 

#define NK_IMPLEMENTATION
#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_DEFAULT_FONT
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_BUTTON_TRIGGER_ON_RELEASE
#include 

#define NK_GLFW_GL2_IMPLEMENTATION
#include 

#include 
#include 
#include 
#include 

int main(int argc, char** argv)
{
    int windowed_x, windowed_y, windowed_width, windowed_height;
    int last_xpos = INT_MIN, last_ypos = INT_MIN;
    int last_width = INT_MIN, last_height = INT_MIN;
    int limit_aspect_ratio = false, aspect_numer = 1, aspect_denom = 1;
    int limit_min_size = false, min_width = 400, min_height = 400;
    int limit_max_size = false, max_width = 400, max_height = 400;
    char width_buffer[12] = "", height_buffer[12] = "";
    char xpos_buffer[12] = "", ypos_buffer[12] = "";
    char numer_buffer[12] = "", denom_buffer[12] = "";
    char min_width_buffer[12] = "", min_height_buffer[12] = "";
    char max_width_buffer[12] = "", max_height_buffer[12] = "";
    int may_close = true;

    if (!glfwInit())
        exit(EXIT_FAILURE);

    glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
    glfwWindowHint(GLFW_WIN32_KEYBOARD_MENU, GLFW_TRUE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);

    GLFWwindow* window = glfwCreateWindow(600, 600, "Window Features", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwMakeContextCurrent(window);
    gladLoadGL(glfwGetProcAddress);
    glfwSwapInterval(0);

    bool position_supported = true;

    glfwGetError(NULL);
    glfwGetWindowPos(window, &last_xpos, &last_ypos);
    sprintf(xpos_buffer, "%i", last_xpos);
    sprintf(ypos_buffer, "%i", last_ypos);
    if (glfwGetError(NULL) == GLFW_FEATURE_UNAVAILABLE)
        position_supported = false;

    glfwGetWindowSize(window, &last_width, &last_height);
    sprintf(width_buffer, "%i", last_width);
    sprintf(height_buffer, "%i", last_height);

    sprintf(numer_buffer, "%i", aspect_numer);
    sprintf(denom_buffer, "%i", aspect_denom);

    sprintf(min_width_buffer, "%i", min_width);
    sprintf(min_height_buffer, "%i", min_height);
    sprintf(max_width_buffer, "%i", max_width);
    sprintf(max_height_buffer, "%i", max_height);

    struct nk_context* nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS);

    struct nk_font_atlas* atlas;
    nk_glfw3_font_stash_begin(&atlas);
    nk_glfw3_font_stash_end();

    while (!(may_close && glfwWindowShouldClose(window)))
    {
        int width, height;

        glfwGetWindowSize(window, &width, &height);

        struct nk_rect area = nk_rect(0.f, 0.f, (float) width, (float) height);
        nk_window_set_bounds(nk, "main", area);

        nk_glfw3_new_frame();
        if (nk_begin(nk, "main", area, 0))
        {
            nk_layout_row_dynamic(nk, 30, 4);

            if (nk_button_label(nk, "Toggle Fullscreen"))
            {
                if (glfwGetWindowMonitor(window))
                {
                    glfwSetWindowMonitor(window, NULL,
                                         windowed_x, windowed_y,
                                         windowed_width, windowed_height, 0);
                }
                else
                {
                    GLFWmonitor* monitor = glfwGetPrimaryMonitor();
                    const GLFWvidmode* mode = glfwGetVideoMode(monitor);
                    glfwGetWindowPos(window, &windowed_x, &windowed_y);
                    glfwGetWindowSize(window, &windowed_width, &windowed_height);
                    glfwSetWindowMonitor(window, monitor,
                                         0, 0, mode->width, mode->height,
                                         mode->refreshRate);
                }
            }

            if (nk_button_label(nk, "Maximize"))
                glfwMaximizeWindow(window);
            if (nk_button_label(nk, "Iconify"))
                glfwIconifyWindow(window);
            if (nk_button_label(nk, "Restore"))
                glfwRestoreWindow(window);

            nk_layout_row_dynamic(nk, 30, 1);

            if (glfwGetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH))
            {
                nk_label(nk, "Press H to disable mouse passthrough", NK_TEXT_CENTERED);

                if (glfwGetKey(window, GLFW_KEY_H))
                    glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, false);
            }

            nk_label(nk, "Press Enter in a text field to set value", NK_TEXT_CENTERED);

            nk_flags events;
            const nk_flags flags = NK_EDIT_FIELD |
                                   NK_EDIT_SIG_ENTER |
                                   NK_EDIT_GOTO_END_ON_ACTIVATE;

            if (position_supported)
            {
                int xpos, ypos;
                glfwGetWindowPos(window, &xpos, &ypos);

                nk_layout_row_dynamic(nk, 30, 3);
                nk_label(nk, "Position", NK_TEXT_LEFT);

                events = nk_edit_string_zero_terminated(nk, flags, xpos_buffer,
                                                        sizeof(xpos_buffer),
                                                        nk_filter_decimal);
                if (events & NK_EDIT_COMMITED)
                {
                    xpos = atoi(xpos_buffer);
                    glfwSetWindowPos(window, xpos, ypos);
                }
                else if (xpos != last_xpos || (events & NK_EDIT_DEACTIVATED))
                    sprintf(xpos_buffer, "%i", xpos);

                events = nk_edit_string_zero_terminated(nk, flags, ypos_buffer,
                                                        sizeof(ypos_buffer),
                                                        nk_filter_decimal);
                if (events & NK_EDIT_COMMITED)
                {
                    ypos = atoi(ypos_buffer);
                    glfwSetWindowPos(window, xpos, ypos);
                }
                else if (ypos != last_ypos || (events & NK_EDIT_DEACTIVATED))
                    sprintf(ypos_buffer, "%i", ypos);

                last_xpos = xpos;
                last_ypos = ypos;
            }
            else
                nk_label(nk, "Position not supported", NK_TEXT_LEFT);

            nk_layout_row_dynamic(nk, 30, 3);
            nk_label(nk, "Size", NK_TEXT_LEFT);

            events = nk_edit_string_zero_terminated(nk, flags, width_buffer,
                                                    sizeof(width_buffer),
                                                    nk_filter_decimal);
            if (events & NK_EDIT_COMMITED)
            {
                width = atoi(width_buffer);
                glfwSetWindowSize(window, width, height);
            }
            else if (width != last_width || (events & NK_EDIT_DEACTIVATED))
                sprintf(width_buffer, "%i", width);

            events = nk_edit_string_zero_terminated(nk, flags, height_buffer,
                                                    sizeof(height_buffer),
                                                    nk_filter_decimal);
            if (events & NK_EDIT_COMMITED)
            {
                height = atoi(height_buffer);
                glfwSetWindowSize(window, width, height);
            }
            else if (height != last_height || (events & NK_EDIT_DEACTIVATED))
                sprintf(height_buffer, "%i", height);

            last_width = width;
            last_height = height;

            bool update_ratio_limit = false;
            if (nk_checkbox_label(nk, "Aspect Ratio", &limit_aspect_ratio))
                update_ratio_limit = true;

            events = nk_edit_string_zero_terminated(nk, flags, numer_buffer,
                                                    sizeof(numer_buffer),
                                                    nk_filter_decimal);
            if (events & NK_EDIT_COMMITED)
            {
                aspect_numer = abs(atoi(numer_buffer));
                update_ratio_limit = true;
            }
            else if (events & NK_EDIT_DEACTIVATED)
                sprintf(numer_buffer, "%i", aspect_numer);

            events = nk_edit_string_zero_terminated(nk, flags, denom_buffer,
                                                    sizeof(denom_buffer),
                                                    nk_filter_decimal);
            if (events & NK_EDIT_COMMITED)
            {
                aspect_denom = abs(atoi(denom_buffer));
                update_ratio_limit = true;
            }
            else if (events & NK_EDIT_DEACTIVATED)
                sprintf(denom_buffer, "%i", aspect_denom);

            if (update_ratio_limit)
            {
                if (limit_aspect_ratio)
                    glfwSetWindowAspectRatio(window, aspect_numer, aspect_denom);
                else
                    glfwSetWindowAspectRatio(window, GLFW_DONT_CARE, GLFW_DONT_CARE);
            }

            bool update_size_limit = false;

            if (nk_checkbox_label(nk, "Minimum Size", &limit_min_size))
                update_size_limit = true;

            events = nk_edit_string_zero_terminated(nk, flags, min_width_buffer,
                                                    sizeof(min_width_buffer),
                                                    nk_filter_decimal);
            if (events & NK_EDIT_COMMITED)
            {
                min_width = abs(atoi(min_width_buffer));
                update_size_limit = true;
            }
            else if (events & NK_EDIT_DEACTIVATED)
                sprintf(min_width_buffer, "%i", min_width);

            events = nk_edit_string_zero_terminated(nk, flags, min_height_buffer,
                                                    sizeof(min_height_buffer),
                                                    nk_filter_decimal);
            if (events & NK_EDIT_COMMITED)
            {
                min_height = abs(atoi(min_height_buffer));
                update_size_limit = true;
            }
            else if (events & NK_EDIT_DEACTIVATED)
                sprintf(min_height_buffer, "%i", min_height);

            if (nk_checkbox_label(nk, "Maximum Size", &limit_max_size))
                update_size_limit = true;

            events = nk_edit_string_zero_terminated(nk, flags, max_width_buffer,
                                                    sizeof(max_width_buffer),
                                                    nk_filter_decimal);
            if (events & NK_EDIT_COMMITED)
            {
                max_width = abs(atoi(max_width_buffer));
                update_size_limit = true;
            }
            else if (events & NK_EDIT_DEACTIVATED)
                sprintf(max_width_buffer, "%i", max_width);

            events = nk_edit_string_zero_terminated(nk, flags, max_height_buffer,
                                                    sizeof(max_height_buffer),
                                                    nk_filter_decimal);
            if (events & NK_EDIT_COMMITED)
            {
                max_height = abs(atoi(max_height_buffer));
                update_size_limit = true;
            }
            else if (events & NK_EDIT_DEACTIVATED)
                sprintf(max_height_buffer, "%i", max_height);

            if (update_size_limit)
            {
                glfwSetWindowSizeLimits(window,
                                        limit_min_size ? min_width : GLFW_DONT_CARE,
                                        limit_min_size ? min_height : GLFW_DONT_CARE,
                                        limit_max_size ? max_width : GLFW_DONT_CARE,
                                        limit_max_size ? max_height : GLFW_DONT_CARE);
            }

            int fb_width, fb_height;
            glfwGetFramebufferSize(window, &fb_width, &fb_height);
            nk_label(nk, "Framebuffer Size", NK_TEXT_LEFT);
            nk_labelf(nk, NK_TEXT_LEFT, "%i", fb_width);
            nk_labelf(nk, NK_TEXT_LEFT, "%i", fb_height);

            float xscale, yscale;
            glfwGetWindowContentScale(window, &xscale, &yscale);
            nk_label(nk, "Content Scale", NK_TEXT_LEFT);
            nk_labelf(nk, NK_TEXT_LEFT, "%f", xscale);
            nk_labelf(nk, NK_TEXT_LEFT, "%f", yscale);

            nk_layout_row_begin(nk, NK_DYNAMIC, 30, 5);
            int frame_left, frame_top, frame_right, frame_bottom;
            glfwGetWindowFrameSize(window, &frame_left, &frame_top, &frame_right, &frame_bottom);
            nk_layout_row_push(nk, 1.f / 3.f);
            nk_label(nk, "Frame Size:", NK_TEXT_LEFT);
            nk_layout_row_push(nk, 1.f / 6.f);
            nk_labelf(nk, NK_TEXT_LEFT, "%i", frame_left);
            nk_layout_row_push(nk, 1.f / 6.f);
            nk_labelf(nk, NK_TEXT_LEFT, "%i", frame_top);
            nk_layout_row_push(nk, 1.f / 6.f);
            nk_labelf(nk, NK_TEXT_LEFT, "%i", frame_right);
            nk_layout_row_push(nk, 1.f / 6.f);
            nk_labelf(nk, NK_TEXT_LEFT, "%i", frame_bottom);
            nk_layout_row_end(nk);

            nk_layout_row_begin(nk, NK_DYNAMIC, 30, 2);
            float opacity = glfwGetWindowOpacity(window);
            nk_layout_row_push(nk, 1.f / 3.f);
            nk_labelf(nk, NK_TEXT_LEFT, "Opacity: %0.3f", opacity);
            nk_layout_row_push(nk, 2.f / 3.f);
            if (nk_slider_float(nk, 0.f, &opacity, 1.f, 0.001f))
                glfwSetWindowOpacity(window, opacity);
            nk_layout_row_end(nk);

            nk_layout_row_begin(nk, NK_DYNAMIC, 30, 2);
            int should_close = glfwWindowShouldClose(window);
            nk_layout_row_push(nk, 1.f / 3.f);
            if (nk_checkbox_label(nk, "Should Close", &should_close))
                glfwSetWindowShouldClose(window, should_close);
            nk_layout_row_push(nk, 2.f / 3.f);
            nk_checkbox_label(nk, "May Close", &may_close);
            nk_layout_row_end(nk);

            nk_layout_row_dynamic(nk, 30, 1);
            nk_label(nk, "Attributes", NK_TEXT_CENTERED);

            nk_layout_row_dynamic(nk, 30, width > 200 ? width / 200 : 1);

            int decorated = glfwGetWindowAttrib(window, GLFW_DECORATED);
            if (nk_checkbox_label(nk, "Decorated", &decorated))
                glfwSetWindowAttrib(window, GLFW_DECORATED, decorated);

            int resizable = glfwGetWindowAttrib(window, GLFW_RESIZABLE);
            if (nk_checkbox_label(nk, "Resizable", &resizable))
                glfwSetWindowAttrib(window, GLFW_RESIZABLE, resizable);

            int floating = glfwGetWindowAttrib(window, GLFW_FLOATING);
            if (nk_checkbox_label(nk, "Floating", &floating))
                glfwSetWindowAttrib(window, GLFW_FLOATING, floating);

            int passthrough = glfwGetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH);
            if (nk_checkbox_label(nk, "Mouse Passthrough", &passthrough))
                glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, passthrough);

            int auto_iconify = glfwGetWindowAttrib(window, GLFW_AUTO_ICONIFY);
            if (nk_checkbox_label(nk, "Auto Iconify", &auto_iconify))
                glfwSetWindowAttrib(window, GLFW_AUTO_ICONIFY, auto_iconify);

            nk_value_bool(nk, "Focused", glfwGetWindowAttrib(window, GLFW_FOCUSED));
            nk_value_bool(nk, "Hovered", glfwGetWindowAttrib(window, GLFW_HOVERED));
            nk_value_bool(nk, "Visible", glfwGetWindowAttrib(window, GLFW_VISIBLE));
            nk_value_bool(nk, "Iconified", glfwGetWindowAttrib(window, GLFW_ICONIFIED));
            nk_value_bool(nk, "Maximized", glfwGetWindowAttrib(window, GLFW_MAXIMIZED));
        }
        nk_end(nk);

        glClear(GL_COLOR_BUFFER_BIT);
        nk_glfw3_render(NK_ANTI_ALIASING_ON);
        glfwSwapBuffers(window);

        glfwWaitEvents();
    }

    nk_glfw3_shutdown();
    glfwTerminate();
    exit(EXIT_SUCCESS);
}

不到400行。使用glfw来创建窗口时,窗口的各种属性的获取和设置应该都在这个例子中了。

直接再去看一个绘图的代码,绘制了一个如下的图片:


其对应代码为 window.c:

#define GLAD_GL_IMPLEMENTATION
#include 
#define GLFW_INCLUDE_NONE
#include 

#include 
#include 

int main(int argc, char** argv)
{
    int xpos, ypos, height;
    const char* description;
    GLFWwindow* windows[4];

    if (!glfwInit())
    {
        glfwGetError(&description);
        printf("Error: %s\n", description);
        exit(EXIT_FAILURE);
    }

    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
    glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);

    glfwGetMonitorWorkarea(glfwGetPrimaryMonitor(), &xpos, &ypos, NULL, &height);

    for (int i = 0;  i < 4;  i++)
    {
        const int size = height / 5;
        const struct
        {
            float r, g, b;
        } colors[] =
        {
            { 0.95f, 0.32f, 0.11f },
            { 0.50f, 0.80f, 0.16f },
            {   0.f, 0.68f, 0.94f },
            { 0.98f, 0.74f, 0.04f }
        };

        if (i > 0)
            glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_FALSE);

        windows[i] = glfwCreateWindow(size, size, "Multi-Window Example", NULL, NULL);
        if (!windows[i])
        {
            glfwGetError(&description);
            printf("Error: %s\n", description);
            glfwTerminate();
            exit(EXIT_FAILURE);
        }

        glfwSetWindowPos(windows[i],
                         xpos + size * (1 + (i & 1)),
                         ypos + size * (1 + (i >> 1)));
        glfwSetInputMode(windows[i], GLFW_STICKY_KEYS, GLFW_TRUE);

        glfwMakeContextCurrent(windows[i]);
        gladLoadGL(glfwGetProcAddress);
        glClearColor(colors[i].r, colors[i].g, colors[i].b, 1.f);
    }

    for (int i = 0;  i < 4;  i++)
        glfwShowWindow(windows[i]);

    for (;;)
    {
        for (int i = 0;  i < 4;  i++)
        {
            glfwMakeContextCurrent(windows[i]);
            glClear(GL_COLOR_BUFFER_BIT);
            glfwSwapBuffers(windows[i]);

            if (glfwWindowShouldClose(windows[i]) ||
                glfwGetKey(windows[i], GLFW_KEY_ESCAPE))
            {
                glfwTerminate();
                exit(EXIT_SUCCESS);
            }
        }

        glfwWaitEvents();
    }
}

84行,也就是说,按照这种方式,绘制为窗口,我就可以在窗口里,绘制我自己的图片了。

那就再看一个绘制图片的例子:


其对应代码为:

//! [code]

#define GLAD_GL_IMPLEMENTATION
#include 
#define GLFW_INCLUDE_NONE
#include 

#include "linmath.h"

#include 
#include 
#include 

typedef struct Vertex
{
    vec2 pos;
    vec3 col;
} Vertex;

static const Vertex vertices[3] =
{
    { { -0.6f, -0.4f }, { 1.f, 0.f, 0.f } },
    { {  0.6f, -0.4f }, { 0.f, 1.f, 0.f } },
    { {   0.f,  0.6f }, { 0.f, 0.f, 1.f } }
};

static const char* vertex_shader_text =
"#version 330\n"
"uniform mat4 MVP;\n"
"in vec3 vCol;\n"
"in vec2 vPos;\n"
"out vec3 color;\n"
"void main()\n"
"{\n"
"    gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
"    color = vCol;\n"
"}\n";

static const char* fragment_shader_text =
"#version 330\n"
"in vec3 color;\n"
"out vec4 fragment;\n"
"void main()\n"
"{\n"
"    fragment = vec4(color, 1.0);\n"
"}\n";

static void error_callback(int error, const char* description)
{
    fprintf(stderr, "Error: %s\n", description);
}

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GLFW_TRUE);
}

int main(void)
{
    glfwSetErrorCallback(error_callback);

    if (!glfwInit())
        exit(EXIT_FAILURE);

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

    GLFWwindow* window = glfwCreateWindow(640, 480, "OpenGL Triangle", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwSetKeyCallback(window, key_callback);

    glfwMakeContextCurrent(window);
    gladLoadGL(glfwGetProcAddress);
    glfwSwapInterval(1);

    // NOTE: OpenGL error checks have been omitted for brevity

    GLuint vertex_buffer;
    glGenBuffers(1, &vertex_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    const GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
    glCompileShader(vertex_shader);

    const GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
    glCompileShader(fragment_shader);

    const GLuint program = glCreateProgram();
    glAttachShader(program, vertex_shader);
    glAttachShader(program, fragment_shader);
    glLinkProgram(program);

    const GLint mvp_location = glGetUniformLocation(program, "MVP");
    const GLint vpos_location = glGetAttribLocation(program, "vPos");
    const GLint vcol_location = glGetAttribLocation(program, "vCol");

    GLuint vertex_array;
    glGenVertexArrays(1, &vertex_array);
    glBindVertexArray(vertex_array);
    glEnableVertexAttribArray(vpos_location);
    glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
                          sizeof(Vertex), (void*) offsetof(Vertex, pos));
    glEnableVertexAttribArray(vcol_location);
    glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE,
                          sizeof(Vertex), (void*) offsetof(Vertex, col));

    while (!glfwWindowShouldClose(window))
    {
        int width, height;
        glfwGetFramebufferSize(window, &width, &height);
        const float ratio = width / (float) height;

        glViewport(0, 0, width, height);
        glClear(GL_COLOR_BUFFER_BIT);

        mat4x4 m, p, mvp;
        mat4x4_identity(m);
        mat4x4_rotate_Z(m, m, (float) glfwGetTime());
        mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 1.f, -1.f);
        mat4x4_mul(mvp, p, m);

        glUseProgram(program);
        glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) &mvp);
        glBindVertexArray(vertex_array);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwDestroyWindow(window);

    glfwTerminate();
    exit(EXIT_SUCCESS);
}

//! [code]

这个代码中,有一句是控制三角形旋转的:


mat4x4_rotate_Z,将这一句注释掉,三角形就不旋转了, 就可以专心该其他地方,来绘制我们的图片了。

或者改成mat4x4_ratate_X,让三角形绕X轴旋转,如下图:



现在把程序改成绘制为4边形。

但是由于如图:


所以,我们需要给到4边形的4个顶点,然后顺序绘制三角形,使用GL_TRAINAGLE_STRIP(这好像有点让计算机重复劳动,但是我们少写一些代码的意思)

将顶点坐标由:

改为:


glDrawArrays函数也要做一些改变,如上图,显示效果如下:



成功显示了一个像素。我们现在尝试显示4个正方形,一个正方形代表一个像素。

分析一下坐标:

分析坐标可以发现,我们可以用扇形GL_TRIANGLE_FAN来绘制.

那么顶点改为:


绘制函数draw也需要改为:

相当于绘制了4个像素,但是,这4个像素的颜色有点不正常,是渐变的,我们需要每个像素单独显示一个颜色。

我们的每个像素需要用两个三角形来组成,并且这两个三角形顶点的要色要统一。

这也就意味着,我们需要单独绘制三角形。不能用GL_TRIANGLE_FAN,和GL_TRIANGLE_STRIP了,只能老老实实的用GL_TRIANGLES来绘制三角形了。这样才能使三角形的三个顶点颜色一致,从而使三角形内部颜色不发生渐变。

下面是我改好代码,把三角形的顶点全部拆开写,每个像素需要两个三角形:



显示效果如下:

这里就成功了显示了一个2x2的图片。

后续显示一个28x28的图片,如下:


文章太长了,需要新开一篇写。

Tags:

最近发表
标签列表