CDR6275

Michio SHIRAISHI Official Site


OpenGL 4.1とOpenGL ES SL 1.0で学ぶ3次元コンピュータグラフィックス

東邦大学理学部情報科学科 白石路雄
最終更新: 2014年2月20日

3. ポリゴンモデル

 2章では、1つの三角形を描いてきました。3次元の形状モデルは三角形の集まりとして表されていますので、複数の三角形を描く方法について述べます。

3.5 ポリゴンモデルのクラスを作る

 さて、それでは多数の三角形から構成されるモデルを抽象化したクラスを作ることによって、プログラムをシンプルにしましょう。作成するクラスは、SimplePolygonModelというクラスです。

#ifndef _SimplePolygonModel_Defined
#define _SimplePolygonModel_Defined

class SimplePolygonModel{
private:
  int nv; // 頂点の数
  int ne; // 稜線の数
  int nf; // 三角形の数
protected:
  float* vtxCoords;         // 頂点座標の配列
  float* vtxTextureCoords;  // テクスチャ座標の配列
  float* vtxNormals;        // 頂点における法線の配列
  unsigned int* edgeIdx;    // 稜線のインデックス
  unsigned int* faceVtxIdx; // 三角形を定義するインデックス
  float* faceVtxCoords;     // 面の頂点座標
  void setNumVertices(int numVertices);
  void setNumEdges(int numEdges);
  void setNumFaces(int numFaces);
public:
  SimplePolygonModel();
  ~SimplePolygonModel();
  int numVertices(){ return nv; }
  int numEdges(){ return ne; }
  int numFaces(){ return nf; }
  float* vertexCoordinates(){ return vtxCoords; }
  float* vertexTextureCoordinates(){ return vtxTextureCoords; }
  float* vertexNormals(){ return vtxNormals; }
  unsigned int* edgeIndices(){ return edgeIdx; }
  unsigned int* faceVertexIndices(){ return faceVtxIdx; }
  float* faceVertexCoordinates();
  void dump();
};

#endif
#include "SimplePolygonModel.h"

#include <iostream>

SimplePolygonModel::SimplePolygonModel(){
  vtxCoords = 0;
  vtxTextureCoords = 0;
  vtxNormals = 0;
  edgeIdx = 0;
  faceVtxIdx = 0;
  faceVtxCoords = 0;
}

SimplePolygonModel::~SimplePolygonModel(){
  delete(vtxCoords);
  delete(vtxTextureCoords);
  delete(vtxNormals);
  delete(edgeIdx);
  delete(faceVtxIdx);
  delete(faceVtxCoords);
}

void SimplePolygonModel::setNumVertices(int numVertices){
  nv = numVertices;
  vtxCoords = new float[3*numVertices];
  vtxTextureCoords = new float[2*numVertices];
  vtxNormals = new float[3*numVertices];
}

void SimplePolygonModel::setNumEdges(int numEdges){
  ne = numEdges;
  edgeIdx = new unsigned int[2*numEdges];
}

void SimplePolygonModel::setNumFaces(int numFaces){
  nf = numFaces;
  faceVtxIdx = new unsigned int[3*numFaces];
}

float* SimplePolygonModel::faceVertexCoordinates(){
  delete faceVtxCoords;
  faceVtxCoords = new float[3*3*nf];
  for(int i=0; i<nf; i++){
    for(int j=0; j<3; j++){
      for(int k=0; k<3; k++){
        faceVtxCoords[3*(3*i+j)+k] = vtxCoords[3*faceVtxIdx[3*i+j]+k];
      }
      
    }
  }
  return faceVtxCoords;
}

void SimplePolygonModel::dump(){
  int i;
  for(i=0; i<nv; i++){
    std::cerr << "Vertex #" << i << ": (" << vtxCoords[3*i] << ", " << vtxCoords[3*i+1] << ", " << vtxCoords[3*i+2] << ")" << std::endl;
  }
  for(i=0; i<nf; i++){
    std::cerr << "Face #" << i << ": (" << faceVtxIdx[3*i] << ", " << faceVtxIdx[3*i+1] << ", " << faceVtxIdx[3*i+2] << ")" << std::endl;
  }
  float* fvc = faceVertexCoordinates();
  for(i=0; i<nf; i++){
    std::cerr << "Face #" << i << ": " << std::endl;
    for(int j=0; j<3; j++){
      std::cerr << "  (" << fvc[3*(3*i+j)] << ", " << fvc[3*(3*i+j)+1] << ", " << fvc[3*(3*i+j)+2] << ")" << std::endl;
    }  
  }
}

 そして、このクラスを継承した五角形を表すクラスSimplePentagonModelを作成します。

#ifndef _SimplePentagonModel_Defined
#define _SimplePentagonModel_Defined

#include "SimplePolygonModel.h"

class SimplePentagonModel : public SimplePolygonModel{
public:
  SimplePentagonModel();
  ~SimplePentagonModel();
};

#endif
#include "SimplePentagonModel.h"

#define _USE_MATH_DEFINES
#include <math.h>

#include <string.h>

SimplePentagonModel::SimplePentagonModel() : SimplePolygonModel(){
  setNumVertices(5);
  for(int i=0; i<5; i++){
    vtxCoords[3*i+0] = 0.8f * (float)cos(i*M_PI*2.0/5.0);
    vtxCoords[3*i+1] = 0.8f * (float)sin(i*M_PI*2.0/5.0);
    vtxCoords[3*i+2] = 0.0f;
  }

  setNumFaces(3);
  unsigned int faceVertexIndices[9] = {
    0, 1, 2,
    0, 2, 3,
    0, 3, 4,
  };
  memcpy(faceVtxIdx, faceVertexIndices, sizeof(unsigned int)*9);

  setNumEdges(7);
  unsigned int edgeIndices[14] = {
    0, 1,
    1, 2,
    0, 2,
    2, 3,
    0, 3,
    3, 4,
    0, 4,
  };
  memcpy(edgeIdx, edgeIndices, sizeof(unsigned int)*14);

}

SimplePentagonModel::~SimplePentagonModel(){

}

 このようなクラスを作ると、次のようにプログラムを書くことができます。

#include <iostream>

#define _USE_MATH_DEFINES
#include <math.h>

#include "PentagonRefactored.h"

#include "SimpleProgramObject.h"
#include "SimplePentagonModel.h"

#include "Matrix.h"

enum
{
  ATTRIBUTE_VERTEX_COORDINATE,
  NUM_ATTRIBUTES
};

enum
{
  UNIFORM_PROJECTION_MATRIX,
  NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];




bool PentagonRefactored::initialize(){
#if !TARGET_OS_IPHONE
  BaseApplication::initializeWindow(640, 640, "PentagonRefactored");
#endif
  pentagonModel = new SimplePentagonModel();
  
  SimpleProgramObject programObject;
  const char* vertexShaderFileName = "first.vs";
  const char* fragmentShaderFileName = "first.fs";
  
  program = programObject.createProgram(vertexShaderFileName, fragmentShaderFileName, shaderSearchPath);

  // シェーダコード内の変数にインデックスを設定する
  glBindAttribLocation(program, ATTRIBUTE_VERTEX_COORDINATE, "vertexCoordinate");
  
  programObject.linkProgram();
  
  // 頂点配列オブジェクトを作成して設定する
#if TARGET_OS_IPHONE
  glGenVertexArraysOES(1, &vertexArrayObject);
  glBindVertexArrayOES(vertexArrayObject);
#else
  glGenVertexArrays(1, &vertexArrayObject);
  glBindVertexArray(vertexArrayObject);
#endif
  
  // 頂点バッファオブジェクトを作成する
  glGenBuffers(1, &vertexBufferObject);
  glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
  glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*pentagonModel->numVertices(), pentagonModel->vertexCoordinates(), GL_STATIC_DRAW);
  
  // 頂点バッファオブジェクトにシェーダ内の変数vertexCoodrinateを結びつける
  glEnableVertexAttribArray(ATTRIBUTE_VERTEX_COORDINATE);
  glVertexAttribPointer(ATTRIBUTE_VERTEX_COORDINATE, 3, GL_FLOAT, GL_FALSE, sizeof(float)*3, 0);
  
  glGenBuffers(1, &elementArrayBufferObject);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementArrayBufferObject);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*3*pentagonModel->numFaces(), pentagonModel->faceVertexIndices(), GL_STATIC_DRAW);

  // 背景色の設定
  glClearColor(0.75f, 0.75f, 0.75f, 1.0f);
  
  return true;
}


void PentagonRefactored::update(){
  
}

void PentagonRefactored::draw(){
#if TARGET_OS_IPHONE
  int viewport[4];
  glGetIntegerv(GL_VIEWPORT, viewport);
  glViewport(0, (viewport[3]-viewport[2])/2, viewport[2], viewport[2]);
#endif
  // 背景のクリア
  glClear(GL_COLOR_BUFFER_BIT);
  // 使用するプログラムオブジェクトをする
  glUseProgram(program);
  // 三角形を描く
  glDrawElements(GL_TRIANGLES, 3*pentagonModel->numFaces(), GL_UNSIGNED_INT, 0);
  // 使用するプログラムオブジェクトを解除する
  glUseProgram(0);
#if _WIN32 || (TARGET_OS_MAC && !TARGET_OS_IPHONE)
  // バッファの入れ替え
  glfwSwapBuffers(window);
  // イベントの取得
  glfwPollEvents();
#endif
}