OpenGL3.3+GLFW+GLEW+GLM小人歩行アニメーションを実現
10094 ワード
現在、ネット上で比較システムを探すチュートリアルは基本的に旧版OpenGLであり、glutベースであるが、このライブラリはすでに前世紀のものである......新版OpenGL 3.3以上のチュートリアルでは、以下の2つを強くお勧めします.
openGL tutorial http://www.opengl-tutorial.org/cn/
learn openGL https://learnopengl-cn.github.io
2つのチュートリアルは1つの理解に重点を置いて、1つのコードは実現して、挿入して一緒に見ることができます.
また、xuhongxuさんに感謝します.
本文はここで原発し,グラフィック学の授業の実験である.
1、環境 OpenGL 3.3 GLFWは、ウィンドウの作成およびユーザ入力の処理に用いる である.
OpenGL関数の具体的な実装を決定するための GLEW . GLM,画像関連数学計算ツールライブラリ OSX 10.10
2、ウィンドウとライブラリの初期化
GLFWウィンドウの初期化:
GLEWライブラリの初期化:
3、シェーダ
OpenGL3.3以上のバージョンでは、即時レンダリングモードは使用されず、コアモードが使用され、シェーダで使用される言語はGLSLです.コアモードのワークフローは、下図のようなグラフィックレンダリングラインで、処理中に3 Dのグラフィックを画面に表示される2 D画像に一歩一歩レンダリングします.実験では、自分の頂点シェーダ(vertex shader)とフラグメントシェーダ(fragment shader)を定義する必要があります.
頂点シェーダの定義は次のとおりです.
クリップシェーダの定義は次のとおりです.
コードに頂点シェーダとクリップシェーダをファイルパスでロードし、変数を使用してインデックスを格納します.使用するロード関数loadShadersの主な内容は、ファイルの読み取りや解析などの操作であり、煩雑で貼り付けられない.
イメージレンダリングの各ループで、現在のシェーダを描画する前に取得します.
4、VBO、VBO、EBOとcorbuffer
頂点配列オブジェクト(vertex array object)を作成し、すべての頂点プロパティ呼び出しを格納します.
頂点バッファオブジェクト(vertex buffer object)とインデックスバッファオブジェクト(element buffer object)を作成します.
共通の立方体を作成します.1つの立方体には6つの面があり、各面は2つの三角形で綴られ、合計12の三角形が必要です.静的に画像を作成するには、各三角形の3つの頂点の3 D座標を順番に書く必要があります.煩雑すぎて、重複するデータがたくさんあるので、インデックスを使用して動的に作成します.vertices配列は立方体の8つの頂点を格納し、indices配列は12の三角形に対応する頂点のインデックスを格納する.
オブジェクトをバッファにバインドし、データをバッファにロードします.
頂点のカラーアトリビュートを設定し、同じバッファオブジェクト管理を使用して、カラーをランダムに生成します.
位置データと色データは、画像の2つのプロパティです.前に定義した頂点シェーダに渡します.
最後にVAOのバインドを解除します.
5、MVPマトリックス
各物体はもともと自分の局所空間内にあり、座標範囲は-1から1の間にある.
モデルマトリクスは、平行移動、回転、スケールの演算により、グラフィックのローカル座標を世界座標にマッピングします.
観察行列はカメラが世界空間を観察する位置と角度を定義する.
投影(Projection)マトリクスは、世界空間の座標が最終的に画面の前に現れる方法を決定します.
このため、物体の最終的な位置は、元の位置データに上記3つのマトリクスを加えて変換され、MVPマトリクスの演算はC++コードで行われ、頂点シェーダで最終的な座標データが計算される.
観測行列と投影行列は作図全体にわたって統一されている.
以前定義した汎用立方体を小人の体の各部分の素子として用いて,それぞれモデル行列を設定した.全部で体、頭、左腕、右腕、左足、右足、6つの部品が含まれています.小人の運動は体を中心とし、他の5つの部品は体の位置を基礎として運動する.左腕を例にとると、小人を前に進ませたいので、画像をレンダリングするたびに体を前に少し平行に移動します.身体のモデル行列を得た後,左肩位置の座標を算出した.左腕のシェイプをスケールして変換し、左腕の現在のフレームでの回転幅を回転して変換し、最後に左腕を以前に計算した左肩の位置に移動します.左腕の回転動作は時間とともに変化する.
他の体の部位は上と似ている.また、小さな人が通った経路に床を描きます.
6、結果
成果図gifが大きすぎて貼れないので、ここに突っ込んで見ることができます.
openGL tutorial http://www.opengl-tutorial.org/cn/
learn openGL https://learnopengl-cn.github.io
2つのチュートリアルは1つの理解に重点を置いて、1つのコードは実現して、挿入して一緒に見ることができます.
また、xuhongxuさんに感謝します.
本文はここで原発し,グラフィック学の授業の実験である.
1、環境
OpenGL関数の具体的な実装を決定するための
2、ウィンドウとライブラリの初期化
GLFWウィンドウの初期化:
if(!glfwInit())
{
return -1;
}
GLFWwindow* window;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//OpenGL 3
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//
glfwWindowHint(GLFW_RESIZABLE , GL_FALSE);//
glfwWindowHint(GLFW_SAMPLES, 4);
window = glfwCreateWindow(WIDTH, HEIGHT, "Hello World", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);//
glfwSetKeyCallback(window, key_callback);
GLEWライブラリの初期化:
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
glfwTerminate();
return -1;
}
3、シェーダ
OpenGL3.3以上のバージョンでは、即時レンダリングモードは使用されず、コアモードが使用され、シェーダで使用される言語はGLSLです.コアモードのワークフローは、下図のようなグラフィックレンダリングラインで、処理中に3 Dのグラフィックを画面に表示される2 D画像に一歩一歩レンダリングします.実験では、自分の頂点シェーダ(vertex shader)とフラグメントシェーダ(fragment shader)を定義する必要があります.
頂点シェーダの定義は次のとおりです.
#version 330 core
// , 0, C++ ,
layout(location = 0) in vec3 vertexPosition_modelspace;
// , 1, C++ ,
layout(location = 1) in vec3 vertexColor;
// ,
out vec4 fragmentColor;
// ,MVP
uniform mat4 MVP;
void main(){
// ,
gl_Position = MVP * vec4(vertexPosition_modelspace,1);
// ,
fragmentColor.xyz = vertexColor;
fragmentColor.a=0.9;
}
クリップシェーダの定義は次のとおりです.
#version 330 core
// ,
in vec4 fragmentColor;
//
out vec4 color;
void main(){
color = fragmentColor;
}
コードに頂点シェーダとクリップシェーダをファイルパスでロードし、変数を使用してインデックスを格納します.使用するロード関数loadShadersの主な内容は、ファイルの読み取りや解析などの操作であり、煩雑で貼り付けられない.
const GLchar* vertexShaderFile = "ColorArrays.vertexshader";
const GLchar* fragmentShaderFile = "ColorArrays.fragmentshader";
GLuint programID = loadShaders(vertexShaderFile, fragmentShaderFile);
イメージレンダリングの各ループで、現在のシェーダを描画する前に取得します.
glUseProgram(programID);
4、VBO、VBO、EBOとcorbuffer
頂点配列オブジェクト(vertex array object)を作成し、すべての頂点プロパティ呼び出しを格納します.
GLuint VAO;
glGenVertexArrays(1, &VAO);
//
glBindVertexArray(VAO);
頂点バッファオブジェクト(vertex buffer object)とインデックスバッファオブジェクト(element buffer object)を作成します.
GLuint VBO;
GLuint EBO;
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
共通の立方体を作成します.1つの立方体には6つの面があり、各面は2つの三角形で綴られ、合計12の三角形が必要です.静的に画像を作成するには、各三角形の3つの頂点の3 D座標を順番に書く必要があります.煩雑すぎて、重複するデータがたくさんあるので、インデックスを使用して動的に作成します.vertices配列は立方体の8つの頂点を格納し、indices配列は12の三角形に対応する頂点のインデックスを格納する.
static const GLfloat vertices[] = {
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
};
static const GLuint indices[] = {
0, 1, 2,
1, 2, 3,
1, 0, 5,
0, 5, 4,
5, 6, 7,
4, 5, 6,
2, 3, 7,
6, 2, 7,
1, 5, 3,
3, 5, 7,
0, 2, 4,
2, 4, 6,
};
オブジェクトをバッファにバインドし、データをバッファにロードします.
// arraybuffer
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW);
頂点のカラーアトリビュートを設定し、同じバッファオブジェクト管理を使用して、カラーをランダムに生成します.
GLuint colorbuffer;
glGenBuffers(1, &colorbuffer);
//8 ,RGB
static GLfloat colors[8*3];
//srand(time(NULL));
for (int v = 0; v < 8 ; v++)
{
colors[3*v+0] = (float)rand()/RAND_MAX;
colors[3*v+1] = (float)rand()/RAND_MAX;
colors[3*v+2] = (float)rand()/RAND_MAX;
}
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
位置データと色データは、画像の2つのプロパティです.前に定義した頂点シェーダに渡します.
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,(void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,(void*)0);
glEnableVertexAttribArray(1);
最後にVAOのバインドを解除します.
glBindVertexArray(0);
5、MVPマトリックス
各物体はもともと自分の局所空間内にあり、座標範囲は-1から1の間にある.
モデルマトリクスは、平行移動、回転、スケールの演算により、グラフィックのローカル座標を世界座標にマッピングします.
観察行列はカメラが世界空間を観察する位置と角度を定義する.
投影(Projection)マトリクスは、世界空間の座標が最終的に画面の前に現れる方法を決定します.
このため、物体の最終的な位置は、元の位置データに上記3つのマトリクスを加えて変換され、MVPマトリクスの演算はC++コードで行われ、頂点シェーダで最終的な座標データが計算される.
MVP=projection*view*model;//C++
gl_Position = MVP * vec4(vertexPosition_modelspace,1);//
観測行列と投影行列は作図全体にわたって統一されている.
// uniform MVP
GLuint MatrixID = glGetUniformLocation(programID, "MVP");
//45° , 4:3, 0.1 100
mat4 projection=perspective(45.0f,4.0f/3.0f,0.1f,100.0f);
// , ,
mat4 view=lookAt(vec3(4,2,0),vec3(0,0,3),vec3(0,1,0));
以前定義した汎用立方体を小人の体の各部分の素子として用いて,それぞれモデル行列を設定した.全部で体、頭、左腕、右腕、左足、右足、6つの部品が含まれています.小人の運動は体を中心とし、他の5つの部品は体の位置を基礎として運動する.左腕を例にとると、小人を前に進ませたいので、画像をレンダリングするたびに体を前に少し平行に移動します.身体のモデル行列を得た後,左肩位置の座標を算出した.左腕のシェイプをスケールして変換し、左腕の現在のフレームでの回転幅を回転して変換し、最後に左腕を以前に計算した左肩の位置に移動します.左腕の回転動作は時間とともに変化する.
// Z 0.05
model_body=translate(model_body, vec3(0.0f,0.0f,0.05f));
// MVP
MVP=projection*view*model_body;
// MVP
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
//
glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
//
vec4 shd_left(1.0f,0.0f,0.0f,1.0f);
//
shd_left=model_body*shd_left;
model_hand_left=mat4(1.0f);
//
model_hand_left=translate(model_hand_left,vec3(shd_left.x+0.1,shd_left.y+0.5,shd_left.z));
//
model_hand_left=rotate(model_hand_left,(float)sin(glfwGetTime()*2)/3,vec3(1.0f,0.0f,0.5f));
// ,
model_hand_left=translate(model_hand_left,vec3(0.0f,-0.5f,0.0f));
//
model_hand_left=scale(model_hand_left, vec3(0.1f,0.5f,0.1f));
// MVP
MVP=projection*view*model_hand_left;
//
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
//
glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
他の体の部位は上と似ている.また、小さな人が通った経路に床を描きます.
//
mat4 model_stay=mat4(1.0f);
model_stay=translate(model_stay, vec3(0.0f,-1.1f,5.0f));
model_stay=scale(model_stay, vec3(1.0f,0.1f,5.0f));
//...
//
//
MVP=projection*view*model_stay;
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
//
vec4 neck(0.0f,1.0f,0.0f,1.0f);
neck=model_body*neck;
model_head=mat4(1.0f);
model_head=translate(model_head,vec3(neck.x,neck.y+0.3,neck.z));
model_head=rotate(model_head,(float)sin(glfwGetTime()*2)/5,vec3(0.0f,0.0f,1.0f));
model_head=scale(model_head, vec3(0.1f,0.1f,0.1f));
MVP=projection*view*model_head;
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
//
vec4 shd_left(1.0f,0.0f,0.0f,1.0f);
shd_left=model_body*shd_left;
model_hand_left=mat4(1.0f);
model_hand_left=translate(model_hand_left,vec3(shd_left.x+0.1,shd_left.y+0.5,shd_left.z));
model_hand_left=rotate(model_hand_left,(float)sin(glfwGetTime()*2)/3,vec3(1.0f,0.0f,0.5f));
model_hand_left=translate(model_hand_left,vec3(0.0f,-0.5f,0.0f));
model_hand_left=scale(model_hand_left, vec3(0.1f,0.5f,0.1f));
MVP=projection*view*model_hand_left;
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
//
vec4 shd_right(-1.0f,0.0f,0.0f,1.0f);
shd_right=model_body*shd_right;
model_hand_right=mat4(1.0f);
model_hand_right=translate(model_hand_right,vec3(shd_right.x-0.1,shd_right.y+0.5,shd_right.z));
model_hand_right=rotate(model_hand_right,(float)sin(glfwGetTime()*2)/3,vec3(-1.0f,0.0f,-0.5f));
model_hand_right=translate(model_hand_right,vec3(0.0f,-0.5f,0.0f));
model_hand_right=scale(model_hand_right, vec3(0.1f,0.5f,0.1f));
MVP=projection*view*model_hand_right;
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
//
vec4 pp_left(0.5f,-1.0f,0.0f,1.0f);
pp_left=model_body*pp_left;
model_leg_left=mat4(1.0f);
model_leg_left=translate(model_leg_left,vec3(pp_left.x+0.05,pp_left.y,pp_left.z));
model_leg_left=rotate(model_leg_left,(float)sin(glfwGetTime()*2)/3,vec3(-1.0f,0.0f,0.0f));
model_leg_left=translate(model_leg_left,vec3(0.0f,-0.5f,0.0f));
model_leg_left=scale(model_leg_left, vec3(0.1f,0.5f,0.1f));
MVP=projection*view*model_leg_left;
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
//
vec4 pp_right(-0.5f,-1.0f,0.0f,1.0f);
pp_right=model_body*pp_right;
model_leg_right=mat4(1.0f);
model_leg_right=translate(model_leg_right,vec3(pp_right.x-0.05,pp_right.y,pp_right.z));
model_leg_right=rotate(model_leg_right,(float)sin(glfwGetTime()*2)/3,vec3(1.0f,0.0f,0.0f));
model_leg_right=translate(model_leg_right,vec3(0.0f,-0.5f,0.0f));
model_leg_right=scale(model_leg_right, vec3(0.1f,0.5f,0.1f));
MVP=projection*view*model_leg_right;
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
6、結果
成果図gifが大きすぎて貼れないので、ここに突っ込んで見ることができます.