GLSL #4: シェーダを使った三角形の描画
目標
前回作成したプログラム(下記参照)の固定パイプラインによる描画部分をシェーダに置き換える.つまり,前回と同じ動作をするプログラムをシェーダで記述する.
実装環境
- GLEW 3.2.1
- 設定方法はこちらを参照
- GLEW 1.13.0
- 設定方法はこちらを参照
- GLM
- 行列などを扱うヘッダファイルのみのライブラリ
- Windows 10 64bit
- Visual Studio 2015
GLMのダウンロード
- ここから「zip」ボタンを押して「glm-0.9.8.4.zip」をダウンロード
- 「C:\Libraries\」に「glm-0.9.8.4」フォルダを解凍
- 以降,この「C:\Libraries\glm-0.9.8.4」を「Path」と呼ぶ
Visual Studioのプロジェクト作成
- Visual Studioを開いて「新しいプロジェクト」を作成
- 「インクルードディレクトリ」に以下を追加
- Path
サンプルプログラム
今回から,「main.cpp」以外のファイルを「OpenGLWrapper」フォルダに入れておくことにしました.
window.h
こちらは前回から変更なし.
// // window.h // // 前回から変更なし
window.cpp
こちらも前回から変更なし.
// // window.cpp // // 前回から変更なし
shaderUtil.h
シェーダを読み込んでコンパイルする関数を記述したヘッダファイル. 基本的には「initializeProgram」関数を使います.
// // shaderUtil.h // #pragma once #include <iostream> using namespace std; #include <fstream> #include <sstream> #include <vector> #include <algorithm> #include <GL/glew.h> namespace gl { void initializeProgram( GLuint &theProgram, const string &vertShaderFileName, const string &fragShaderFileName, const string &geomShaderFileName = "" ); void readFile( const string &fileName, stringstream &contents ); GLuint createShader( GLenum eShaderType, const string &strShaderFile ); GLuint createProgram( const vector<GLuint> &shaderList ); }
shaderUtil.cpp
シェーダを読み込んでコンパイルする関数を記述したソースファイル.
// // shaderUtil.cpp // #include "shaderUtil.h" namespace gl { void initializeProgram( GLuint &theProgram, const string &vertShaderFileName, const string &fragShaderFileName, const string &geomShaderFileName ) { try { cout << "[Shader Utility] Making a shader program..." << endl; vector<GLuint> shaderList; // ----- Load shaders stringstream ssVertexShader, ssFragmentShader, ssGeometryShader; readFile(vertShaderFileName, ssVertexShader); readFile(fragShaderFileName, ssFragmentShader); if (!geomShaderFileName.empty()) readFile(geomShaderFileName, ssGeometryShader); // ----- Compile shaders shaderList.push_back(createShader(GL_VERTEX_SHADER, ssVertexShader.str())); shaderList.push_back(createShader(GL_FRAGMENT_SHADER, ssFragmentShader.str())); if (!geomShaderFileName.empty()) shaderList.push_back(createShader(GL_GEOMETRY_SHADER, ssGeometryShader.str())); // ----- Link the shaders to the program theProgram = createProgram(shaderList); // ----- Delete std::for_each(shaderList.begin(), shaderList.end(), glDeleteShader); cout << "[Shader Utility] ... done!" << endl; } catch (string e) { cerr << "Error: " << e << endl; exit(EXIT_FAILURE); } } void readFile( const string &fileName, stringstream &contents ) { ifstream ifs(fileName); if (!ifs.is_open()) { throw "There is no such file: " + fileName; } else { cout << "Loaded \"" << fileName << "\"." << endl; } string buf; contents = stringstream(""); while (ifs && getline(ifs, buf)) { contents << buf << endl; } } GLuint createShader( GLenum eShaderType, const string &strShaderFile ) { // ----- Create a shader object // Shader type (vertex or fragment) is specified by "eShaderType" GLuint shader = glCreateShader(eShaderType); const char *strFileData = strShaderFile.c_str(); // Fed a shader in text format into the shader object glShaderSource(shader, 1, &strFileData, NULL); // ----- Compile shader glCompileShader(shader); // ----- Check compile status GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { GLint infoLogLength; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); GLchar *strInfoLog = new GLchar[infoLogLength + 1]; glGetShaderInfoLog(shader, infoLogLength, NULL, strInfoLog); string strShaderType; switch (eShaderType) { case GL_VERTEX_SHADER: strShaderType = "vertex"; break; case GL_GEOMETRY_SHADER: strShaderType = "geometry"; break; case GL_FRAGMENT_SHADER: strShaderType = "fragment"; break; } string errorMsg("Failed to compile \"" + strShaderType + "\" shader: \n" + strInfoLog); delete[] strInfoLog; throw errorMsg; } else { string strShaderType; switch (eShaderType) { case GL_VERTEX_SHADER: strShaderType = "vertex"; break; case GL_GEOMETRY_SHADER: strShaderType = "geometry"; break; case GL_FRAGMENT_SHADER: strShaderType = "fragment"; break; } cout << "Successfully compiled \"" << strShaderType << "\" shader." << endl; } return shader; } GLuint createProgram(const vector<GLuint> &shaderList) { GLuint program = glCreateProgram(); for (size_t iLoop = 0; iLoop < shaderList.size(); iLoop++) glAttachShader(program, shaderList[iLoop]); glLinkProgram(program); GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); if (status == GL_FALSE) { GLint infoLogLength; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); GLchar *strInfoLog = new GLchar[infoLogLength + 1]; glGetProgramInfoLog(program, infoLogLength, NULL, strInfoLog); string errorMsg("Linker failure: " + string(strInfoLog)); delete[] strInfoLog; throw errorMsg; } else { cout << "Linked shader program to the program successfully." << endl; } for (size_t iLoop = 0; iLoop < shaderList.size(); iLoop++) glDetachShader(program, shaderList[iLoop]); return program; } }
main.cpp
// // main.cpp // #include <glm/glm.hpp> #include <glm/ext.hpp> #include "OpenGLWrapper/window.h" #include "OpenGLWrapper/shaderUtil.h" class MyWindow : gl::window { private: GLuint prog; // Shader program GLuint vbo; // Vertex buffer object (VBO) glm::vec3 vertData[6] = { glm::vec3(-0.6f, -0.4f, 0.0f), // Position #0 glm::vec3(0.6f, -0.4f, 0.0f), // Position #1 glm::vec3(0.0f, 0.6f, 0.0f), // Position #2 glm::vec3(1.0f, 0.0f, 0.0f), // Color #0 glm::vec3(0.0f, 1.0f, 0.0f), // Color #1 glm::vec3(0.0f, 0.0f, 1.0f) // Color #2 }; public: MyWindow( int w, int h, const string &name, const string &vertShader, const string &fragShader, const string &geomShader = "" ): window(w, h, name) { // ----- Load and compile shaders gl::initializeProgram(prog, vertShader, fragShader, geomShader); // ----- Cereate vertex buffer object glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertData), &vertData, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } void render() { // Rendering loop glClearColor(0.3f, 0.5f, 0.8f, 0.0f); while (!glfwWindowShouldClose(wnd)) { // Clear color and depth buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Draw glUseProgram(prog); { static glm::mat4 rotMat; rotMat = glm::rotate(glm::mat4(), (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f)); GLuint uniRotMat = glGetUniformLocation(prog, "rotMat"); glUniformMatrix4fv(uniRotMat, 1, GL_FALSE, glm::value_ptr(rotMat)); glBindBuffer(GL_ARRAY_BUFFER, vbo); glEnableVertexAttribArray(0); // Vertices glEnableVertexAttribArray(1); // Colors glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(vertData) / 2)); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); } glUseProgram(0); // Swap front and back buffers glfwSwapBuffers(wnd); // Poll for and process events glfwPollEvents(); } } }; int main() { MyWindow wnd( 640, 480, "GLFW 3 Window", "../shader/simple.vert", "../shader/simple.frag" ); wnd.render(); return 0; }
simple.vert
今回からシェーダを使います. こちらは頂点に関する記述をするVertex Shaderです.
// // simple.vert // #version 330 layout(location = 0) in vec3 position; layout(location = 1) in vec3 color; uniform mat4 rotMat; out vec3 Color; void main() { gl_Position = rotMat * vec4(position, 1.0); Color = color; }
simple.frag
今回からシェーダを使います. こちらは画素に関する記述をするFragment Shaderです.
// // simple.frag // #version 330 in vec3 Color; void main() { gl_FragColor = vec4(Color, 1.0); }
結果
動いた!目標達成です.前回同様,カラフルな三角形がゆっくりと回転します.
