stb_image.h
第三天-驱动OpenGL这篇有稍稍提到这个玩意
Single-header file
并推荐了stb这个在github上的repo。这个repo放的是作者Sean T. Barrett的撰写C语言时的各种工具。这些工具最大的特点就是把实作跟宣告写在同一个文件,利用macro分割开来,这样可以解决C与C++中麻烦的引用问题。
专案中要使用stb_image.h
是一个「使用起来」简单,载入处理一些常见的图档的函式库。
老样子,我把这个文件放在external
资料夹里面,再额外包一层stb
的资料夹。然後就可以开始载入图片了。
图片我用的是Kenny上的Animal Pack Redux,这是一个很赞的网站,里面提供了许多免费的素材~
首先,定义一个结构(struct
)把要的资料记录下来
// iron_types.h
typedef enum GL_TextureFmt{
TEX_FMT_WHITE_BLACK = GL_RED,
TEX_FMT_GRAYSCALE = GL_RG,
TEX_FMT_RGB = GL_RGB,
TEX_FMT_RGBA = GL_RGBA,
} TextureFmt;
typedef struct Texture {
unsigned int id;
int w, h;
TextureFmt gl_fmt;
} Texture;
然後,我还定义了一个enum,表示Texture的对应在OpenGL里面的格式,其实可以点实作进去看,其实就是OpenGL,定义的一系列的数字,用於识别图片格式。
再来就是载入了...
// iron_asset.c - LoadTextureFile
int channels;
unsigned char* pixels = stbi_load(file_name, &texture->w, &texture->h, &channels, 0);
if (pixels == NULL) {
stbi_image_free(pixels);
printf("Failed to load image: %s\n", file_name);
return RES_ERROR_LOAD_IMAGE_FILE;
}
int src_img_fmt;
if (channels == 2) {
texture->gl_fmt = TEX_FMT_GRAYSCALE;
src_img_fmt = GL_RG8;
} else if (channels == 3) {
texture->gl_fmt = TEX_FMT_RGB;
src_img_fmt = GL_RGB8;
} else if (channels == 4) {
texture->gl_fmt = TEX_FMT_RGBA;
src_img_fmt = GL_RGBA8;
} else {
texture->gl_fmt = TEX_FMT_WHITE_BLACK;
src_img_fmt = GL_R8;
}
// continue...
有用到了就两个,stbi_load
跟stbi_free
,stbi
可以视为他的命名空间。
stbi_load
最後会回传图片所有的像素点,存成unsigned char
的阵列,可以看到的第三个参数我代入channels
,这个就是图片的颜色通道,例如PNG有RGBA四个通道,那channels就会拿到「4」,最後在对应到OpenGL的格式。除了OpenGL的格式,我还存了图片的「原」格式,下面会用到
再来就是设置Texture,把他储存在OpenGL的状态机里面:
// iron_asset.c - LoadTextureFile
// part 1
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &texture->id);
glBindTexture(GL_TEXTURE_2D, texture->id);
// part 2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// part 3
glTexImage2D(GL_TEXTURE_2D, 0, src_img_fmt, texture->w, texture->h, 0, texture->gl_fmt, GL_UNSIGNED_BYTE, pixels);
// part 4
glBindTexture(GL_TEXTURE_2D, 0);
这里分作4个Part讲解比较容易
Part1: 生成与绑定新的Texture,第一行的bind texture是为了防止之後,有正在使用的texture,被盖到才加上的
Part2: 设置Texture的环绕(?)与过滤(?)的方式,老实说我不知道中文怎麽翻译,建议可以看看这个可以更加清楚。
Part3: 设置资料到OpenGL里,第2个参数必填0 参考
Part4: 设置完成,解除绑定
这样就把图片设置在OpenGL里罗~之後可以通过Texture.id
来呼叫。
之前写在iron_render.c
内部的shader code,需要加上两个参数
1.Texture的座标
2.Texture本身
这是更新後的样子:
static const char* DEFAULT_2D_VERTEX_SHADER_CODE = "#version 330 core\n"
"in vec2 _Pos;\n"
"in vec2 _Texcoords;\n" // 1.
"out vec2 Texcoords;\n" // 2.
"void main() {\n"
" gl_Position = vec4(_Pos, 0.0, 1.0);\n"
" Texcoords = _Texcoords;\n" // 3.
"}\n\0";
static const char* DEFAULT_2D_FRAGMENT_SHADER_CODE = "#version 330 core\n"
"in vec2 Texcoords;\n" // 4.
"uniform vec4 _Color;\n"
"uniform sampler2D _Texture2D;" // 5.
"out vec4 _FragColor;\n"
"void main() {\n"
" _FragColor = _Color * texture(_Texture2D, Texcoords);\n" // 6.
"}\n\0";
然後更新iron_render
模组内,预设的shader,在iron_asset.LoadShaderCode
的地方加上这个
// texture coordinate
int texcoord_location = glGetAttribLocation(shader->id, "_Texcoords");
if (texcoord_location < 0) {
return RES_ERROR_GET_SHADER_ATTRIBUTE;
}
shader->attribs_locations[SHADER_ATTRIB_VEC2_TEXCOORD] = texcoord_location;
// texture
int texture_location = glGetUniformLocation(shader->id, "_Texture2D");
if (texture_location < 0) {
return RES_ERROR_GET_SHADER_ATTRIBUTE;
}
shader->attribs_locations[SHADER_ATTRIB_SAMPLER2D_TEXTURE] = texcoord_location;
P.S. 写的时候发现不应该没有找到这个属性就Return,不然之後有不同需求的Shader,里面没有包含这属性就GG了。
最後...利用之前DrawFirstRectangle
的code,改造一下,就可以画出我们第一个图片了
unsigned int vbo, vao, ibo;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES), VERTICES, GL_STATIC_DRAW);
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(INDICES), INDICES, GL_STATIC_DRAW);
// set position attribute
glVertexAttribPointer(RENDER_2D_CONTEXT.default_shader.attribs_locations[SHADER_ATTRIB_VEC2_POS], 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(RENDER_2D_CONTEXT.default_shader.attribs_locations[SHADER_ATTRIB_VEC2_POS]);
// set texture coordinate attribute
glVertexAttribPointer(RENDER_2D_CONTEXT.default_shader.attribs_locations[SHADER_ATTRIB_VEC2_TEXCOORD], 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glEnableVertexAttribArray(RENDER_2D_CONTEXT.default_shader.attribs_locations[SHADER_ATTRIB_VEC2_TEXCOORD]);
glUseProgram(RENDER_2D_CONTEXT.default_shader.id);
// set shader color
V4f v = ColorToVec4f(c);
glUniform4f(RENDER_2D_CONTEXT.default_shader.attribs_locations[SHADER_ATTRIB_VEC4_COLOR], v.r, v.g, v.b, v.a);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture->id);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindVertexArray(0);
有个注意一下,原本的VERTICES
,除了顶点位置,我也把Texture的座标放进去了,所以原本的一组顶点是2个float会变成4个float,中间还需要告诉glVertexAttribPointer
顶点与贴图座标的间距。
完成之後,我的输出是这样,发福的倒立兔子
原因有几个
stbi_set_flip_vertically_on_load
翻转图片GL_BLEND
打开,可以在CreateRenderer
的地方补上
// ...
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// ...
今天就先这样,明天预计会写输入操作与时间处理的部分
<<: [Day 10] Leetcode 978. Longest Turbulent Subarray (C++)
>>: Day_03 : 让 Vite 来开启你的Vue 微谈模组化与演进(下)
我们在之前的分享中得知,品质分数多少会影响自然排序的表现,但很多人会把它和绩效挂上等号,这并不是必然...
建立好自己完整的表格之後,就可以开始搜寻自己要的东西了 SELECT * (全部栏位) FROM 什...
while 回圈 Kotlin 的 while 回圈跟 Java 一样,每一次的循环之前会检查条件式...
Amazon Linux 2 上将 Django 与 Nginx 整合 -Day 08 先前我们都是...
喵喵喵喵喵喵喵喵!!!终於写完 30 天的文章了,这 30 天中,我们从云端的概念开始,进入了 Go...