[Day9] 预设贴图

今日目标

设计简单的2D渲染器

iron_render这里就是放任何跟渲染相关功能的地方。2D渲染器,刚开始相对简单(我想像中了啦)。

我的设计会是,在初始化(CreateRenderer)之後,接下来只要在Game Loop里面呼叫DrawXXXX就可以画出相对应的想要的图案画贴图,之後会像视窗功能那样在Game Loop中有StartDrawEndDraw之类的记录这一Frame的状态,DrawXXXX会在StartDrawEndDraw之间呼叫。

// This is a example

while(1) {
    StartDraw();

        DrawRectangle();

        DrawTexture();

    EndDraw();
}

会说简单只要还有一点,主要是大部分得图形,甚至是全部,都可以视为是矩形。最多就4个顶点与,然後会有使用贴图与颜色的不同,所以我会把预设的Shader直接写在iron_render.c文件里面,基本的一些绘制就可以应付了,除非之後有甚麽特殊效果,需要用不同的Shader来制作,就再说吧~;)

预设的Shader

预设的shader没甚麽变,还是长这样(如果不知道甚麽是shader的话,可以参考这篇)

// vertex
static const char* DEFAULT_2D_VERTEX_SHADER_CODE = "#version 330 core\n"
"in vec2 _Pos;\n"
"in vec2 _Texcoords;\n"
"out vec2 Texcoords;\n"
"void main() {\n"
"   gl_Position = vec4(_Pos, 0.0, 1.0);\n"
"   Texcoords = _Texcoords;\n"
"}\n\0";

// fragment
static const char* DEFAULT_2D_FRAGMENT_SHADER_CODE = "#version 330 core\n"
"in vec2 Texcoords;\n"
"uniform vec4 _Color;\n"
"uniform sampler2D _Texture2D;"
"out vec4 _FragColor;\n"
"void main() {\n"
"   _FragColor = _Color * texture(_Texture2D, Texcoords);\n"
"}\n\0";

之前没有说,shader当中设置的变数,前面的inoutuniform是甚麽意思?

首先来讲uniform,可以看到在Shader中标示uniform的变数,在DrawXXXX中用glUniform4f设定了变数,uniform其实就是Shader里的常数,透过外部去设置这个变数,如同之前所写的,就是把颜色设定到shader里。

inout两个关键字比较新,之前OpenGL2.0时的GLSL是用varying,这是用在shader管线之间传递参数用的,不会提供给外部,inout是视作一对的,例如: 传递_Pos

// In vertex shader
out vec2 _Pos;

然後经过管线传到fragment shader,那就要是:

// In fragment shader
in vec2 _Pos

如上,连参数名称也要一样。

但看看vertex shader的_Pos_Texcoords,开头就是in那这是哪来的呢?这些在vertex shader的就是attribute了,在OpenGL2.0的GLSL也是用这个关键字,这个要搭配

glGetAttribLocation(...)
glVertexAttribPointer(...)
glEnableVertexAttribArray(...)

一起使用,还有如果有点进上面参考资料的,里面的shader在attribute前面有加上layout(location = 0)其实就是直接标示了attrubute的位置,不用使用glGetAttribLocation

注: 这边也证实我之前说错了,可编程管线,也就是Shader,其语言不是3.0才有的,2.0就有了

第一个矩形第一章贴图

CreateRenderer的地方,加写这一段,等於是把之前设置顶点数据的地方,换句话说,创建VertexBufferObjectVertexArrayObjectIndexBufferObject的地方((如果不知道这些是甚麽的话,也请参考这篇),初始化一份存起来即可

...
    glGenVertexArrays(1, &RENDER_2D_CONTEXT.vao);
	glBindVertexArray(RENDER_2D_CONTEXT.vao);

	glGenBuffers(1, &RENDER_2D_CONTEXT.vbo);
	glBindBuffer(GL_ARRAY_BUFFER, RENDER_2D_CONTEXT.vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES), VERTICES, GL_STATIC_DRAW);

	glGenBuffers(1, &RENDER_2D_CONTEXT.ibo);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, RENDER_2D_CONTEXT.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]);

	glBindVertexArray(0);
...

然後DrawFirstTriangleDrawFirstTexture的地方只要留下呼叫要使用的shader与VAO绑上要的属性绘制图形这三个功能就好了。

// Draw Rect
    glBindVertexArray(RENDER_2D_CONTEXT.vao);

	glUseProgram(RENDER_2D_CONTEXT.default_shader.id);

	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, RENDER_2D_CONTEXT.default_texture.id);

	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

	glBindTexture(GL_TEXTURE_2D, 0);
	glBindVertexArray(0);
// DrawTexture
    glBindVertexArray(RENDER_2D_CONTEXT.vao);

	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);

完成後,如果呼叫DrawFirstRectangle的话会看到乌漆麻黑一片,这是因为预设的shader有这段:

_FragColor = _Color * texture(_Texture2D, Texcoords);

这表示强制需要使用一张贴图。

那就在CreateRenderer的地方,产生一张***1x1的白色像素***当作预设的贴图,方便在之後如果Shader或贴图载入错误了,就会直接使用预设的资源...

// create default texture, a pixel 1x1 white squad
	unsigned char white_pixel[4] = { 255, 255, 255, 255 };

	glBindTexture(GL_TEXTURE_2D, 0);

	glGenTextures(1, &RENDER_2D_CONTEXT.default_texture.id);
	glBindTexture(GL_TEXTURE_2D, RENDER_2D_CONTEXT.default_texture.id);

	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);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, white_pixel);

	glBindTexture(GL_TEXTURE_2D, 0);

这样就完成了!

参考

最後这是今天的成果,可以到我们github


<<:  [Day 05] 产出回应内文&初探AES CBC加密 - [C#]丰收款API必备前置作业(四)

>>:  【Day 4】物理时间、happens-before 关系、causality

夜间模式真的对眼睛比较好吗? 详细整理(下)

5.亮度 常见使用夜间模式的一个原因,也不是因为健康与否,只是因为觉得白底有点太亮了 确实亮度也一直...

[Day09] 什麽是链结串列

#328 - Odd Even Linked List 连结: https://leetcode....

浅谈 DBA 资料库管理师的职责

DBABootcamp 资料库管理师(Database Administrator, 简称 DBA)...

javascript物件教学1

基本物件设计: ...

Day 23 Object oriented programming

物件导向程序设计是程序设计中极为重要的一环,其基本概念为物件及类别。 类别定义事物的特点,物件为事件...