「图片显示移植-1」 尝试用opengl/GLFW显示图片
zhezhongyun 2025-07-09 00:20 2 浏览
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 <glad/gl.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#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 <glad/gl.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <stdarg.h>
#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 <nuklear.h>
#define NK_GLFW_GL2_IMPLEMENTATION
#include <nuklear_glfw_gl2.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
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 <glad/gl.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <stdlib.h>
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 <glad/gl.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include "linmath.h"
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
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的图片,如下:
文章太长了,需要新开一篇写。
相关推荐
- 一篇文章带你了解SVG 渐变知识(svg动画效果)
-
渐变是一种从一种颜色到另一种颜色的平滑过渡。另外,可以把多个颜色的过渡应用到同一个元素上。SVG渐变主要有两种类型:(Linear,Radial)。一、SVG线性渐变<linearGradie...
- Vue3 实战指南:15 个高效组件开发技巧解析
-
Vue.js作为一款流行的JavaScript框架,在前端开发领域占据着重要地位。Vue3的发布,更是带来了诸多令人兴奋的新特性和改进,让开发者能够更高效地构建应用程序。今天,我们就来深入探讨...
- CSS渲染性能优化(低阻抗喷油器阻值一般为多少欧)
-
在当今快节奏的互联网环境中,网页加载速度直接影响用户体验和业务转化率。页面加载时间每增加100毫秒,就会导致显著的流量和收入损失。作为前端开发的重要组成部分,CSS的渲染性能优化不容忽视。为什么CSS...
- 前端面试题-Vue 项目中,你做过哪些性能优化?
-
在Vue项目中,以下是我在生产环境中实践过且用户反馈较好的性能优化方案,整理为分类要点:一、代码层面优化1.代码分割与懒加载路由懒加载:使用`()=>import()`动态导入组件,结...
- 如何通过JavaScript判断Web页面按钮是否置灰?
-
在JavaScript语言中判断Web页面按钮是否置灰(禁用状态),可以通过以下几种方式实现,其具体情形取决于按钮的禁用方式(原生disabled属性或CSS样式控制):一、检查原生dis...
- 「图片显示移植-1」 尝试用opengl/GLFW显示图片
-
GLFW【https://www.glfw.org】调用了opengl来做图形的显示。我最近需要用opengl来显示图像,不能使用opencv等库。看了一个glfw的官网,里面有github:http...
- 大模型实战:Flask+H5三件套实现大模型基础聊天界面
-
本文使用Flask和H5三件套(HTML+JS+CSS)实现大模型聊天应用的基本方式话不多说,先贴上实现效果:流式输出:思考输出:聊天界面模型设置:模型设置会话切换:前言大模型的聊天应用从功能...
- ae基础知识(二)(ae必学知识)
-
hi,大家好,我今天要给大家继续分享的还是ae的基础知识,今天主要分享的就是关于ae的路径文字制作步骤(时间关系没有截图)、动态文字的制作知识、以及ae特效的扭曲的一些基本操作。最后再次复习一下ae的...
- YSLOW性能测试前端调优23大规则(二十一)---避免过滤器
-
AlphalmageLoader过滤器是IE浏览器专有的一个关于图片的属性,主要是为了解决半透明真彩色的PNG显示问题。AlphalmageLoader的语法如下:filter:progid:DX...
- Chrome浏览器的渲染流程详解(chrome预览)
-
我们来详细介绍一下浏览器的**渲染流程**。渲染流程是浏览器将从网络获取到的HTML、CSS和JavaScript文件,最终转化为用户屏幕上可见的、可交互的像素画面的过程。它是一个复杂但高度优...
- 在 WordPress 中如何设置背景色透明度?
-
最近开始写一些WordPress专业的知识,阅读数奇低,然后我发一些微信昵称技巧,又说我天天发这些小学生爱玩的玩意,写点文章真不容易。那我两天发点专业的东西,两天发点小学生的东西,剩下三天我看着办...
- manim 数学动画之旅--图形样式(数学图形绘制)
-
manim绘制图形时,除了上一节提到的那些必需的参数,还有一些可选的参数,这些参数可以控制图形显示的样式。绘制各类基本图形(点,线,圆,多边形等)时,每个图形都有自己的默认的样式,比如上一节的图形,...
- Web页面如此耗电!到了某种程度,会是大损失
-
现在用户上网大多使用移动设备或者笔记本电脑。对这两者来说,电池寿命都很重要。在这篇文章里,我们将讨论影响电池寿命的因素,以及作为一个web开发者,我们如何让网页耗电更少,以便用户有更多时间来关注我们的...
- 11.mxGraph的mxCell和Styles样式(graph style)
-
3.1.3mxCell[翻译]mxCell是顶点和边的单元对象。mxCell复制了模型中可用的许多功能。使用上的关键区别是,使用模型方法会创建适当的事件通知和撤销,而使用单元进行更改时没有更改记...
- 按钮重复点击:这“简单”问题,为何难住大半面试者与开发者?
-
在前端开发中,按钮重复点击是一个看似不起眼,实则非常普遍且容易引发线上事故的问题。想象一下:提交表单时,因为网络卡顿或手抖,重复点击导致后端创建了多条冗余数据…这些场景不仅影响用户体验,更可能造成实...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 教程 (33)
- HTML 简介 (35)
- HTML 实例/测验 (32)
- HTML 测验 (32)
- JavaScript 和 HTML DOM 参考手册 (32)
- HTML 拓展阅读 (30)
- HTML文本框样式 (31)
- HTML滚动条样式 (34)
- HTML5 浏览器支持 (33)
- HTML5 新元素 (33)
- HTML5 WebSocket (30)
- HTML5 代码规范 (32)
- HTML5 标签 (717)
- HTML5 标签 (已废弃) (75)
- HTML5电子书 (32)
- HTML5开发工具 (34)
- HTML5小游戏源码 (34)
- HTML5模板下载 (30)
- HTTP 状态消息 (33)
- HTTP 方法:GET 对比 POST (33)
- 键盘快捷键 (35)
- 标签 (226)
- HTML button formtarget 属性 (30)
- CSS 水平对齐 (Horizontal Align) (30)
- opacity 属性 (32)