Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Media Art II 2013 第7回 : openFrameworks 3Dグラフィクス、OpenGL

34,992 views

Published on

Published in: Technology, Art & Photos
  • Be the first to comment

Media Art II 2013 第7回 : openFrameworks 3Dグラフィクス、OpenGL

  1. 1. Media Art II 2013 第7回 : openFrameworks 3Dグラフィクス、OpenGL 多摩美術大学情報デザイン学科メディア芸術コース 2013年10月28日 田所 淳
  2. 2. 今日の内容 ‣ openFrameworksの、3D機能、OpenGLの機能を中心に 取り上げます
  3. 3. 今日の内容 ‣ 3DやOpenGLに関する機能を概観します クラス名 機能 ofBox, ofSphere 3Dプリミティブオブジェクトの描画 ofEasyCam 簡単に利用可能なカメラ(視点)機能 ofLight ライティング効果 ofMesh 3D物体の全ての頂点情報を持ったデータ ofVBO 3Dシーンの物体の情報(位置・色)の保存 ofShader プログラマブルシェーダ
  4. 4. 3Dプリミティブ図形の描画 ofBox、ofSphere
  5. 5. 3Dプリミティブ図形の描画 ofBox、ofSphere ‣ openFrameworks v080から、3Dの基本図形の描き方が 変化 ! ‣ 球体: ofSphere → ofSpherePrimtive ‣ 立方体: ofBox → ofBoxPrimitive ! ‣ ofRectやofCircleのように、testApp::draw() 関数内に記 述して座標とサイズを指定するだけで簡単に利用可能
  6. 6. 3Dプリミティブ図形の描画 ofBox、ofSphere ‣ testApp.h #pragma once ! #include "ofMain.h" ! class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void void void void void void void void void keyPressed(int key); keyReleased(int key); mouseMoved(int x, int y); mouseDragged(int x, int y, int button); mousePressed(int x, int y, int button); mouseReleased(int x, int y, int button); windowResized(int w, int h); dragEvent(ofDragInfo dragInfo); gotMessage(ofMessage msg); ofBoxPrimitive box; // 立方体プリミティブ ofSpherePrimitive sphere; // 球プリミティブ };
  7. 7. 3Dプリミティブ図形の描画 ofBox、ofSphere ‣ testApp.cpp #include "testApp.h" ! void testApp::setup(){ ofBackground(0); } ! void testApp::update(){ } ! void testApp::draw(){ ofTranslate(ofGetWidth()/2, ofGetHeight()/2); ofSetColor(255); // 立方体 box.set(200); // サイズ設定 box.setPosition(-150, 0, 0); // 位置 box.drawWireframe(); // ワイヤーフレームを描画 // 球 sphere.set(100, 16); // 半径と面の細かさ sphere.setPosition(150, 0, 0); // 位置 sphere.drawWireframe(); // ワイヤーフレームを描画 }
  8. 8. 3Dプリミティブ図形の描画 ofBox、ofSphere ‣ 完成!!
  9. 9. 視点をインタラクティブに移動 ofEasyCam
  10. 10. 視点をインタラクティブに移動 - ofEasyCam ‣ 次に、この図形を様々な角度から眺められるようにする ‣ 物体を動かすのではなく、視点(カメラ)を移動して、物体 を注視する角度を変更してみる ‣ 視点を指定するofEasyCamというクラスがあり、とても 簡単に視点の移動が可能 ‣ より詳細な視点や画角などの指定には、ofCameraという クラスもある
  11. 11. 視点をインタラクティブに移動 - ofEasyCam ‣ testApp.h #pragma once ! #include "ofMain.h" ! class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void void void void void void void void void keyPressed(int key); keyReleased(int key); mouseMoved(int x, int y); mouseDragged(int x, int y, int button); mousePressed(int x, int y, int button); mouseReleased(int x, int y, int button); windowResized(int w, int h); dragEvent(ofDragInfo dragInfo); gotMessage(ofMessage msg); ofBoxPrimitive box; // 立方体プリミティブ ofSpherePrimitive sphere; // 球プリミティブ ofEasyCam cam; // カメラ };
  12. 12. 視点をインタラクティブに移動 - ofEasyCam ‣ testApp.cpp … (前略) … ! void testApp::draw(){ cam.begin(); // カメラ開始 ofSetColor(255); // 立方体 box.set(200); // サイズ設定 box.setPosition(-150, 0, 0); // 位置 box.drawWireframe(); // ワイヤーフレームを描画 // 球 sphere.set(100, 16); // 半径と面の細かさ sphere.setPosition(150, 0, 0); // 位置 sphere.drawWireframe(); // ワイヤーフレームを描画 cam.end(); // カメラ終了 }
  13. 13. 視点をインタラクティブに移動 - ofEasyCam ‣ testApp.cpp … (前略) … ! void testApp::draw(){ cam.begin(); // カメラ開始 ofSetColor(255); // 立方体 camera.begin( )と box.set(200); // サイズ設定 camera.end( )で囲まれた範囲 box.setPosition(-150, 0, 0); // 位置 が、マウスのドラッグで視点変 更可能となる box.drawWireframe(); // ワイヤーフレームを描画 // 球 sphere.set(100, 16); // 半径と面の細かさ sphere.setPosition(150, 0, 0); // 位置 sphere.drawWireframe(); // ワイヤーフレームを描画 cam.end(); // カメラ終了 }
  14. 14. 視点をインタラクティブに移動 - ofEasyCam ‣ マウスをドラッグすると物体を回転できる
  15. 15. ライティング ofLight
  16. 16. ライティング - ofLight ‣ プリミティブを描画する命令 ‣ ‣ ‣ プリミティブ.drawWireframe() → ワイヤーフレーム プリミティブ.draw() → 塗り潰し 実際にやってみる
  17. 17. ライティング - ofLight ‣ testApp.cpp void testApp::draw(){ cam.begin(); // カメラ開始 ofSetColor(255); // 立方体 box.set(200); // サイズ設定 box.setPosition(-150, 0, 0); // 位置 box.draw(); // 塗りつぶしで描画 ←ココ // 球 sphere.set(100, 16); // 半径と面の細かさ sphere.setPosition(150, 0, 0); // 位置 sphere.draw(); // 塗りつぶしで描画 cam.end(); // カメラ終了 } ←ココ
  18. 18. ライティング - ofLight ‣ 陰影も何もない、のっぺりとした図形になってしまう
  19. 19. ライティング - ofLight ‣ なぜ、陰影のないのっぺりとした図形になってしまうのか? ‣ ライティングがされていないから ‣ 適切な照明を物体に照射することで、リアルな陰影が表現 される ‣ ofLightクラスを使用する
  20. 20. ライティング - ofLight ‣ ofLightでは「Phongシェーディング」というアルゴリズ ムで、物体にリアルな陰影や反射を付加している ‣ 3Dグラフィックにおいて、モデリングされた面上の点に 影をつけるための照明と陰影モデル ‣ ユタ大学のBui Tuong Phongによって開発 (1973)
  21. 21. ライティング - ofLight ‣ Phongシェーディング ‣ 3つの光の反射の強度と色を指定する ‣ 鏡面反射 (Specular) ‣ 拡散反射 (Diffuse) ‣ 環境反射 (Ambient)
  22. 22. ライティング - ofLight ‣ testApp.h #pragma once ! #include "ofMain.h" ! class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void void void void void void void void void keyPressed(int key); keyReleased(int key); mouseMoved(int x, int y); mouseDragged(int x, int y, int button); mousePressed(int x, int y, int button); mouseReleased(int x, int y, int button); windowResized(int w, int h); dragEvent(ofDragInfo dragInfo); gotMessage(ofMessage msg); ofBoxPrimitive box; // 立方体プリミティブ ofSpherePrimitive sphere; // 球プリミティブ ofEasyCam cam; // カメラ ofLight light; // ライト };
  23. 23. ライティング - ofLight ‣ testApp.cpp … (前略) … ! void testApp::setup(){ ofBackground(0); // ライティングを有効に light.enable(); // スポットライトを配置 light.setSpotlight(); // 照明の位置 light.setPosition(-100, 100, 100); // 環境反射光の色 light.setAmbientColor(ofFloatColor(0.5, 0.2, 0.2, 1.0)); // 拡散反射光の色 light.setDiffuseColor(ofFloatColor(0.5, 0.5, 1.0)); // 鏡面反射光の色 light.setSpecularColor(ofFloatColor(1.0, 1.0, 1.0)); } ! … (後略) …
  24. 24. ライティング - ofLight ‣ 立体のの前後の重なりが、おかしい!?
  25. 25. ライティング - ofLight ‣ openFrameworksでは、そのままの設定では後から実行 した命令がどんどん上のレイヤーに描かれる ‣ 3Dの立体の場合、有り得ない重なりになることも ‣ DEPTH TEST (深度テスト) を有効にする必要があり ‣ ofEnableDepthTest() で有効になる
  26. 26. ライティング - ofLight ‣ testApp.cpp を修正 … (前略) … ! void testApp::setup(){ ofBackground(0); ofEnableDepthTest(); // 深度テストを有効に ofEnableSmoothing(); // 表示をスムースに ←ココ // ライティングを有効に light.enable(); // スポットライトを配置 light.setSpotlight(); // 照明の位置 light.setPosition(-100, 100, 100); // 環境反射光の色 light.setAmbientColor(ofFloatColor(0.5, 0.2, 0.2, 1.0)); // 拡散反射光の色 light.setDiffuseColor(ofFloatColor(0.5, 0.5, 1.0)); // 鏡面反射光の色 light.setSpecularColor(ofFloatColor(1.0, 1.0, 1.0)); } ! … (後略) …
  27. 27. ライティング - ofLight ‣ 正しい重なりに
  28. 28. メッシュ (頂点情報の集合) ofMesh
  29. 29. メッシュ (頂点情報の集合) - ofMesh ‣ 立方体や球体などの単純な図形ではなく、より複雑な曲面 や多面体などの3Dの形状を描くにはどうすれば良いのか? ‣ ポリゴンメッシュ (Polygon Mesh) を使用する ‣ openFrameworksでは、ofMeshクラスで生成できる
  30. 30. メッシュ (頂点情報の集合) - ofMesh ‣ メッシュによって構成された複雑な形状の例
  31. 31. メッシュ (頂点情報の集合) - ofMesh ‣ メッシュ (ポリゴンメッシュ Polygon Mesh) ‣ 3Dグラフィックのモデリングの多面体オブジェクトの形 状を定義する頂点、辺、面の集合のこと ‣ 通常は三角形や四角形のポリゴン(面)によって構成される
  32. 32. メッシュ (頂点情報の集合) - ofMesh ‣ sin関数を利用して波を生成し、その3Dの形態をメッシュ で表現してみる ‣ まず、x, y軸方向に均等に並ぶグリッドを作成 ‣ グリッドの頂点をそれぞれ、x軸、y軸で異なる周波数のsin 関数で上下に振動させる ‣ 頂点の座標を描画する
  33. 33. メッシュ (頂点情報の集合) - ofMesh ‣ testApp.h #pragma once ! #include "ofMain.h" ! class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void void void void void void void void void keyPressed(int key); keyReleased(int key); mouseMoved(int x, int y); mouseDragged(int x, int y, int button); mousePressed(int x, int y, int button); mouseReleased(int x, int y, int button); windowResized(int w, int h); dragEvent(ofDragInfo dragInfo); gotMessage(ofMessage msg); ofEasyCam cam; // カメラ ofMesh mesh; // 3Dメッシュ int w, h; // メッシュの幅と高さ };
  34. 34. メッシュ (頂点情報の集合) - ofMesh ‣ testApp.cpp #include "testApp.h" ! void testApp::setup(){ // 画面の設定 ofBackground(0); ofEnableDepthTest(); cam.setDistance(100); // メッシュの幅と高さ w = 200; h = 200; // 頂点の色を初期化 for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { mesh.addColor(ofFloatColor(0.5, 0.8, 1.0)); } } }
  35. 35. メッシュ (頂点情報の集合) - ofMesh ‣ testApp.cpp void testApp::update(){ // まず全ての頂点情報を削除 mesh.clearVertices(); // 全ての頂点の位置を更新して頂点情報として追加 for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { float x = sin(i * 0.1 + ofGetElapsedTimef())*10.0; float y = sin(j*0.15 + ofGetElapsedTimef()) * 10.0; float z = x + y; mesh.addVertex(ofVec3f(i - w/2, j - h/2, z)); } } }
  36. 36. メッシュ (頂点情報の集合) - ofMesh ‣ testApp.cpp void testApp::draw(){ // メッシュの描画 ofSetHexColor(0xffffff); cam.begin(); // カメラ開始 // 頂点の位置をドットで表示 glPointSize(2.0); glEnable(GL_POINT_SMOOTH); mesh.drawVertices(); cam.end(); // カメラ終了 // ログの表示 string info; info = "Vertex num = " + ofToString(w * h, 0) + "n"; info += "FPS = " + ofToString(ofGetFrameRate(), 2); ofDrawBitmapString(info, 30, 30); }
  37. 37. メッシュ (頂点情報の集合) - ofMesh ‣ 完成!!
  38. 38. VBO (頂点バッファオブジェクト) ofVBO
  39. 39. VBO (頂点バッファオブジェクト) - ofVBO ‣ ポリゴンメッシュなどの複雑な3D形状 ‣ 大量の頂点の情報を高速に演算する必要がある ‣ CPUに高負荷な演算 ! ‣ VBO (頂点バッファオブジェクト) ‣ あらかじめ頂点データをOpenGL側に作成しておき、グラフィ クスカード側で演算を行う手法 ‣ CPUの負荷を減らし、高速な描画を実現する ! ‣ 先程ofMeshで作成した3Dの波を、ofVBOによる計算に変更し てみる
  40. 40. VBO (頂点バッファオブジェクト) - ofVBO ‣ VBOのイメージ GPU 大量の頂点 座標情報 高速演算 大量の頂点 色情報 CPU
  41. 41. VBO (頂点バッファオブジェクト) - ofVBO ‣ testApp.h #pragma once ! #include "ofMain.h" ! class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); … (中略) … // クラス定数 static const int WIDTH = 200; static const int HEIGHT = 200; static const int NUM_PARTICLES = WIDTH * HEIGHT; ofEasyCam cam; // カメラ ofVbo myVbo; // VBO ofVec3f myVerts[NUM_PARTICLES]; // 頂点の座標 ofFloatColor myColor[NUM_PARTICLES]; // 頂点の色情報 };
  42. 42. VBO (頂点バッファオブジェクト) - ofVBO ‣ testApp.cpp #include "testApp.h" ! const int testApp::WIDTH; const int testApp::HEIGHT; const int testApp::NUM_PARTICLES; ! void testApp::setup(){ // 画面の設定 ofBackground(0); ofEnableDepthTest(); ofEnableBlendMode(OF_BLENDMODE_ADD); cam.setDistance(100); // 頂点情報を初期化 for (int i = 0; i < for (int j = 0; myVerts[j * myColor[j * } } WIDTH; i++) { j < HEIGHT; j++) { WIDTH + i].set(i - WIDTH/2, j - HEIGHT/2, 0); WIDTH + i].set(0.5, 0.8, 1.0, 1.0); // 頂点バッファに位置と色の情報を設定 myVbo.setVertexData(myVerts, NUM_PARTICLES, GL_DYNAMIC_DRAW); myVbo.setColorData(myColor, NUM_PARTICLES, GL_DYNAMIC_DRAW); }
  43. 43. VBO (頂点バッファオブジェクト) - ofVBO ‣ testApp.cpp void testApp::update(){ // 頂点の座標を更新 for (int i = 0; i < WIDTH; i++) { for (int j = 0; j < HEIGHT; j++) { float x = sin(i * 0.1 + ofGetElapsedTimef()) * 10.0; float y = sin(j * 0.15 + ofGetElapsedTimef()) * 10.0; float z = x + y; myVerts[j * WIDTH + i] = ofVec3f(i - WIDTH/2, j - HEIGHT/2, z); } } // 頂点バッファの頂点の座標情報を更新 myVbo.updateVertexData(myVerts, NUM_PARTICLES); } ! void testApp::draw(){ // VBOの頂点を描画 cam.begin(); glPointSize(2); myVbo.draw(GL_POINTS, 0, NUM_PARTICLES); cam.end(); // ログの表示 string info; info = "Vertex num = " + ofToString(NUM_PARTICLES, 0) + "n"; info += "FPS = " + ofToString(ofGetFrameRate(), 2); ofDrawBitmapString(info, 30, 30); }
  44. 44. VBO (頂点バッファオブジェクト) - ofVBO ‣ 完成!!
  45. 45. VBO 応用 カメラの明度でメッシュを生成
  46. 46. カメラの明度でメッシュを生成 ‣ VBO + Mesh、応用 ‣ カメラから映像を入力 ‣ 映像のそれぞれのピクセルの明さを頂点の高さに ‣ ビデオからの色を、頂点の色に
  47. 47. VBO (頂点バッファオブジェクト) - ofVBO ‣ testApp.h #pragma once ! #include "ofMain.h" ! class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); … (中略) … // クラス定数 static const int WIDTH = 640; static const int HEIGHT = 480; static const int NUM_PARTICLES = WIDTH * HEIGHT; ofEasyCam cam; // カメラ ofLight light; // ライト ofVbo myVbo; // VBO ofVec3f myVerts[NUM_PARTICLES]; // 頂点の座標 ofFloatColor myColor[NUM_PARTICLES]; // 頂点の色情報 ofVideoGrabber myVideo; }; // ビデオキャプチャ
  48. 48. VBO (頂点バッファオブジェクト) - ofVBO ‣ testApp.cpp #include "testApp.h" ! const int testApp::WIDTH; const int testApp::HEIGHT; const int testApp::NUM_PARTICLES; ! void testApp::setup(){ // 画面の設定 ofBackground(0); ofEnableDepthTest(); ofEnableBlendMode(OF_BLENDMODE_ADD); cam.setDistance(400); myVideo.initGrabber(640, 480); // カメラ映像をキャプチャ ! // 頂点情報を初期化 for (int i = 0; i < for (int j = 0; myVerts[j * myColor[j * } } WIDTH; i++) { j < HEIGHT; j++) { WIDTH + i].set(i - WIDTH/2, j - HEIGHT/2, 0); WIDTH + i].set(1.0, 1.0, 1.0, 1.0); // 頂点バッファに位置と色の情報を設定 myVbo.setVertexData(myVerts, NUM_PARTICLES, GL_DYNAMIC_DRAW); myVbo.setColorData(myColor, NUM_PARTICLES, GL_DYNAMIC_DRAW); }
  49. 49. VBO (頂点バッファオブジェクト) - ofVBO ‣ testApp.cpp void testApp::update(){ // カメラからの映像を更新 myVideo.update(); // もしカメラのフレームが更新されていたら if (myVideo.isFrameNew()) { // カメラの映像のピクセル情報を抽出 unsigned char * pixels = myVideo.getPixels(); // ピクセルごとに処理 for (int i = 0; i < WIDTH; i++) { for (int j = 0; j < HEIGHT; j++) { // ピクセルノRGB値を取得 float r = (float)pixels[j * myVideo.width * 3 + i * 3] / 256.0; float g = (float)pixels[j * myVideo.width * 3 + i * 3 + 1] / 256.0; float b = (float)pixels[j * myVideo.width * 3 + i * 3 + 2] / 256.0; // RGBから明度を算出 float brightness = (r + g + b) / 3.0f; // 明度から頂点の位置を設定 myVerts[j * WIDTH + i] = ofVec3f(i - WIDTH/2, j - HEIGHT/2, brightness * 256.0); // 頂点の色はカメラのピクセルの値をそのまま使用 } myColor[j * WIDTH + i] = ofFloatColor(r, g, b, 0.8); } // VBOの座標と色の情報を更新 } } myVbo.updateVertexData(myVerts, NUM_PARTICLES); myVbo.updateColorData(myColor, NUM_PARTICLES);
  50. 50. VBO (頂点バッファオブジェクト) - ofVBO ‣ testApp.cpp void testApp::draw(){ // VBOを描画 cam.begin(); ofScale(1, -1, 1); glPointSize(3); myVbo.draw(GL_POINTS, 0, NUM_PARTICLES); cam.end(); // ログの表示 string info; info = "Vertex num = " + ofToString(NUM_PARTICLES, 0) + "n"; info += "FPS = " + ofToString(ofGetFrameRate(), 2); ofDrawBitmapString(info, 30, 30); }
  51. 51. カメラの明度でメッシュを生成 ‣ 完成!!
  52. 52. VBO (頂点バッファオブジェクト) - ofVBO ‣ よりVBOのパワーを実感できるサンプル ‣ 大量のパーティクルをnoise関数で飛散させる ‣ それぞれのパーティクルにはテクスチャ画像を貼る ‣ 高速にアニメーションしてみる
  53. 53. VBO (頂点バッファオブジェクト) - ofVBO ‣ 大量のパーティクルを高速にアニメーション

×