比德勒科技

微信小程序移植

日期:2024-08-05 01:43 / 作者:www.biddlecn.com

「科技」3款社交APP遭差评:王思聪吐槽“都是垃圾”,专家表示“还有机会”

1月15日,三家互联网公司在同一天发布了它们各自的社交软件,剑指已经突破10亿月活跃用户的微信。而微信迅速用行动给出了“回应”。马桶MT在1月14日即被微信封杀,“多闪”邀请链接也在下午发布的同时被微信屏蔽,子弹短信的分享二维码也已被封杀。

|来源:《小康》?中国小康网

|作者:齐鲁

1月15日,三家互联网公司在同一天发布了它们各自的社交软件,剑指已经突破10亿月活跃用户的微信。 就连发布会时间也岔开了。当天上午,由原快播创始人王欣带队的匿名社交软件“马桶MT”发布。下午,由抖音衍生而来的视频社交软件“多闪”发布。晚上,锤子科技创始人罗永浩带队的、由原“子弹短信”升级而来的“聊天宝”发布。

三款社交APP同一天发布,剑指微信

而被三大新品“围攻”的微信,迅速用行动给出了“回应”。马桶MT在1月14日即被微信封杀,“多闪”邀请链接也在下午发布的同时被微信屏蔽,子弹短信的分享二维码也已被封杀。

“不知道你怕什么?”1月14日,马桶MT上线前夕,快播创始人王欣在社交媒体上晒出了“已停止访问该网页”的微信页面截图,故作不解。

很简单,想通过微信导入用户,现在还不行。

对于这三款社交APP,“耿直boy”王思聪也出来泼了一盆冷水:“3个产品都是垃圾,没有机会。随后又评论称:“还不如做个ins图片+打赏的社交APP,匿名社交都是炒冷饭了。”

王思聪微博截图

用户体验如何?

三款社交APP发布后,网友讨论激烈。

“听名字就不行,难道以后我要说‘我们马桶上聊’‘把你的马桶给我’?”有网友吐槽说。

还有网友这样评论:“马桶被封的漂亮,号称人脉暗网,主打熟人匿名社交,这其实就是在放大人性的恶,不封等着过年么?”

15日,马桶MT在微信上被“封杀”,在一些第三方应用平台上被下架。

“多闪挺不错的,下载体验了,跟一个网友聊的蛮舒服的,最后下线我们互相留了微信号。”

“聊天宝,这个名字感觉像回到了80年代。”

网友对三款APP的微博讨论截图

会撼动微信吗?

众所周知,腾讯靠社交起家,社交对腾讯来说是绝对的腹地。此次三家APP进军社交领域,既有陌生人社交也有熟人社交,来势汹汹。

尤其是多闪,其产品负责人徐璐冉介绍多闪时完全拿微信对标,“翻开微信,最近的状态都是工作”、“在微信上记录生活束手束脚,因为有着复杂且庞大的社交网络”、“多是点赞之交”、“最亲密的人的状态被稀释”等等。

目前微信用户数已经突破10亿;朋友圈日活跃用户约7.5亿人,每天有近100亿人次的访问量。从全球来看,用户数这个级别的互联网企业都不多。

这么密集的攻击,微信能扛得住吗?这三款APP会对微信或者腾讯产生影响吗?

DCCI互联网研究院院长刘兴亮接受采访时称,看上去很难,因为同类产品不太可能打败微信,微信未来肯定是被完全不同于微信的东西打败,比如像移动支付干掉实体钱包这样。

“不过也不是完全没有机会,因为目前网络社交并没有被完全满足,用户的社交需求也千奇百怪,尤其是陌生人社交,还是有一些小机会的。”刘兴亮说。

拓展:匿名社交背后的心理学

摄影 / meisu

从心理学角度来看,匿名社交软件有其必要。人有天然的归属的需要,这是人类最基本和广泛的社会动机。据统计,44%的人每天花费在社交网络的时间超过了1小时,对许多人而言,社交网络已经成为了他们整个网络体验的中心。我们对自我的概念很大程度上是同他人的互动塑造的,渴求点赞和转发,在一定程度上就是对“自我”的确认。

与他人进行有意义的交往是社会生活的前提,在社交中,人们会尽量寻求并维持“酬赏大于付出”的人际关系,如果个人被群体踢出局,我们只得依靠自身的资源维持生活,这种情况实在是又寂寞又恐慌。相较于陌生人之间的松散联系,熟人间的社交来的更亲密、更能实现酬赏的最大化。在熟人社交之中,这种自我证实能减少焦虑感。

曾有社会学家将人际关系区分为首属群体和次属群体,首属群体不正式、关系紧密且持久,自我完全融入其中,家庭和朋友圈是典型的首属群体;次属群体则相反,它更正式和工具性,目的是达到目标。部门、公司、生意合作伙伴属于次属群体,在这个群体中,人更多地是地位和机器上的部件,人们不会把过多的自我投入到这种公共关系里。

人们倾向于喜欢在价值观、兴趣、背景方面与自己相似的人,偏好与强化自我观念的群体来往,寻找相似性就成了自我确认的重要因素。自我展露无疑是最常用的方法,我们将自身的信息告知他人,与他人共享内心的感受和信息,如果移植在社交网络上,就是诸多匿名社交行为的心理根源。

《小康》原创文章,转载请注明出处。

·END·

扫一扫关注中国小康网

微信公众号

(长按保存图片/识别图中二维码)

↓↓↓点击“阅读原文”,进入中国小康网wap版,更多独家专稿、县域专题、时事热点,第一时间精彩呈现。

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

GLFW【】调用了opengl来做图形的显示。

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

看了一个glfw的官网,里面有github:

就去把代码下载下来,并且很容易编译完成了,并运行了源码中提供的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来绘制三角形了。这样才能使三角形的三个顶点颜色一致,从而使三角形内部颜色不发生渐变。

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

绘制方法改为GL_TRIANGLES

显示效果如下:

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

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

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