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的图片,如下:
文章太长了,需要新开一篇写。