GLSLの初めの一歩として三角形表示プログラムをここに載せます。
シェーダプログラムはGLSL1.3以上になってます。
シェーダプログラムはGLSL1.3以上になってます。
- OpenGL3.0(GLSL1.3)以上
Triangle描画
#ref error :ご指定のファイルが見つかりません。ファイル名を確認して、再度指定してください。 (triangle.png)
- 全ソースコード
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <GL/glew.h>
#include <GL/glut.h>
GLuint programObject;
GLuint vertexShader;
GLuint fragmentShader;
void display();
void reshape(int w, int h);
void timer(int t);
void init();
bool initGlsl();
GLuint LoadShader(GLenum type, const char *fileName);
class Triangle
{
private:
GLuint vid[1];
GLint vertexLocation;
public:
void setup(GLuint programObj)
{
GLfloat vertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};
vertexLocation = glGetAttribLocation(programObj, "vertex");
glGenBuffers(1, vid);
glBindBuffer(GL_ARRAY_BUFFER, vid[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices)*sizeof(GLfloat), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// 描画
void render()
{
glBindBuffer(GL_ARRAY_BUFFER, vid[0]);
glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vertexLocation);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
};
Triangle triangle;
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(programObject);
triangle.render(); //三角形描画
glUseProgram(0);
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
}
void timer(int t)
{
glutPostRedisplay();
glutTimerFunc(t, timer, 17);
}
void init()
{
glClearColor(0.5, 0.5, 0.5, 1.0);
glClearDepth(1.0f);
}
bool initGlsl()
{
programObject = glCreateProgram();
if (programObject == 0) return false;
vertexShader = LoadShader(GL_VERTEX_SHADER, "simple.vert");
glAttachShader(programObject, vertexShader);
fragmentShader = LoadShader(GL_FRAGMENT_SHADER, "simple.frag");
glAttachShader(programObject, fragmentShader);
GLint linked;
glLinkProgram(programObject);
glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
if (!linked) {
GLint infoLen = 0;
glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char *infoLog = new char[sizeof(char)*infoLen];
glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
std::cerr << "Error linking program:\n" << infoLog << "\n";
delete [] infoLog;
}
glDeleteProgram(programObject);
return false;
}
glBindFragDataLocation(programObject, 0, "fragColor");
triangle.setup(programObject);
return true;
}
GLuint LoadShader(GLenum type, const char *fileName)
{
GLuint shader;
GLint compiled;
std::fstream inputFile(fileName);
std::istreambuf_iterator<char> dataBegin(inputFile);
std::istreambuf_iterator<char> dataEnd;
std::string fileData(dataBegin, dataEnd);
const char *file = fileData.c_str();
shader = glCreateShader(type);
if (shader == 0) return 0;
glShaderSource(shader, 1, &file, NULL);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char* infoLog = new char[sizeof(char)*infoLen];
glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
std::cerr << "Error compiling shader: " << fileName << "\n" << infoLog << "\n";
delete [] infoLog;
}
glDeleteShader(shader);
return 0;
}
return shader;
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitWindowSize(600, 400);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow("test");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutTimerFunc(100, timer, 17);
GLenum err;
err = glewInit();
if (err != GLEW_OK) {
std::cerr << "GLEW error : " << glewGetErrorString(err) << "\n";
std::exit(1);
}
init();
initGlsl();
glutMainLoop();
return 0;
}
- simple.vert
#version 130
in vec3 vertex;
void main(void)
{
gl_Position = vec4(vertex.xyz, 1.0);
}
gl_Positionは必須です。
頂点データvertexを頂点ごとgl_Positionに渡します。
頂点は3つあるので、この処理は3回実行されるイメージになると思います。
実際の処理は並列なのかパイプライン処理になるのかよくわかりませんが。。。
頂点データvertexを頂点ごとgl_Positionに渡します。
頂点は3つあるので、この処理は3回実行されるイメージになると思います。
実際の処理は並列なのかパイプライン処理になるのかよくわかりませんが。。。
- simple.frag
#version 130
out vec4 fragColor;
void main(void)
{
fragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
ここでは、シンプルにポリゴン面に対して緑色(0,1,0)を設定しています。
説明
事前の準備
三角形の設定と描画をわかりやすくするためにTriangleクラスを作ってみました。
あくまでも説明ようなので、これがベストな形というわけではないです。
あくまでも説明ようなので、これがベストな形というわけではないです。
void setup(GLuint programObj) { GLfloat vertices[] = { 0.0f, 0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, }; vertexLocation = glGetAttribLocation(programObj, "vertex"); glGenBuffers(1, vid); glBindBuffer(GL_ARRAY_BUFFER, vid[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices)*sizeof(GLfloat), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); }
setup関数では、頂点情報をVBOを使ってGPU側に送っています。
頂点データのVBOのIDはvid[0]に格納されます。
VBOの手順は次のとおり。
頂点データのVBOのIDはvid[0]に格納されます。
VBOの手順は次のとおり。
- IDの取得(glGendBuffers)
- 配列バッファにバインドする(glBindBuffer)
- バインドした配列バッファに頂点データを渡す(glBufferData)
また、頂点シェーダの入力となるvertexにアクセスするために、glGetAttribLocationを実行してvertexLocationを取得します。
vertexLocation = glGetAttribLocation(programObj, "vertex");
上記により、simple.vert記述内の以下の変数に関連付けられます。
in vec3 vertex;
描画
void render() { glBindBuffer(GL_ARRAY_BUFFER, vid[0]); glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(vertexLocation); glDrawArrays(GL_TRIANGLES, 0, 3); }
setupで生成したVBO(vid[0])の配列データをglBindBufferでバインドします。
glVertexAttribPointerとglEnableVertexAttribArrayでアトリビュートを使うようにします。
glDrawArraysで実際に描画を指示します。
glVertexAttribPointerとglEnableVertexAttribArrayでアトリビュートを使うようにします。
glDrawArraysで実際に描画を指示します。
これらの処理は、glUseProgram(programObject) ~ glUseProgram(0) の間で呼びます。
glUseProgram(programObject); triangle.render(); //三角形描画 glUseProgram(0);
フラグメントシェーダの使用について
以下の記述について、
glBindFragDataLocation(programObject, 0, "fragColor");
simple.frag内、
out vec4 fragColor;
この記述は、GLSL1.3(OpenGL3.0)以上でなければ対応していません。
もし、GLSL1.2以下を使うのであれば、次のような記述になると思います。
#version 120 void main(void) { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); }
gl_FragColorは、build-in変数でフラグメントの出力になります。
ただしこれは、GLSL1.5あたりから推奨していないものなので極力使用は避けたいと思います。
ただしこれは、GLSL1.5あたりから推奨していないものなので極力使用は避けたいと思います。
glBindFragDataLocationでエラーが出るとき
代わりに次を使います。
- glBindFragDataLocationEXT
添付ファイル