iron_render
這裡就是放任何跟渲染相關功能的地方。2D渲染器,剛開始相對簡單(我想像中了啦)。
我的設計會是,在初始化(CreateRenderer
)之後,接下來只要在Game Loop裡面呼叫DrawXXXX
就可以畫出相對應的想要的圖案畫貼圖,之後會像視窗功能那樣在Game Loop中有StartDraw
跟EndDraw
之類的記錄這一Frame的狀態,DrawXXXX
會在StartDraw
跟EndDraw
之間呼叫。
// This is a example
while(1) {
StartDraw();
DrawRectangle();
DrawTexture();
EndDraw();
}
會說簡單只要還有一點,主要是大部分得圖形,甚至是全部,都可以視為是矩形。最多就4個頂點與,然後會有使用貼圖與顏色的不同,所以我會把預設的Shader直接寫在iron_render.c
文件裡面,基本的一些繪製就可以應付了,除非之後有甚麼特殊效果,需要用不同的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當中設置的變數,前面的in
、out
、uniform
是甚麼意思?
首先來講uniform
,可以看到在Shader中標示uniform
的變數,在DrawXXXX
中用glUniform4f
設定了變數,uniform
其實就是Shader裡的常數,透過外部去設置這個變數,如同之前所寫的,就是把顏色設定到shader裡。
in
跟out
兩個關鍵字比較新,之前OpenGL2.0時的GLSL是用varying
,這是用在shader管線之間傳遞參數用的,不會提供給外部,in
跟out
是視作一對的,例如: 傳遞_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
的地方,加寫這一段,等於是把之前設置頂點數據的地方,換句話說,創建VertexBufferObject
、VertexArrayObject
、IndexBufferObject
的地方((如果不知道這些是甚麼的話,也請參考這篇),初始化一份存起來即可
...
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);
...
然後DrawFirstTriangle
與DrawFirstTexture
的地方只要留下呼叫要使用的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);
這樣就完成了!