CDR6275

Michio SHIRAISHI Official Site


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

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

5. シェーディング

5.1 拡散光

 コンピュータグラフィックスでは、3次元の物体を2次元の画像として表現するわけですが、人間の目には2次元の画像を見ても、それが3次元の物体であると認識することができます。この理由の一つが、3次元の物体に色によるグラデーションがついていることです。

 3次元の物体に色がつく理由は、光源からの光が物体にあたり、その光が物体の表面で反射されて、人間の目に見えるからです。物体の表面における光の反射は一般的に複雑ですが、光があらゆる方向に均一に反射するとき、その反射光のことを拡散反射光と呼びます。

 拡散反射光の強さを求めるには、物体の表面に入ってくる光の量を調べればいいことが分かります。物体表面におけるある点における法線を\(\vec{n}\)とし、その点から光源の位置へのベクトルを\(\vec{l}\)とすると、その点に入ってくる光の量は、\(\vec{n}\cdot\vec{l}\)と表すことができます。

 したがって、光源の光の強さと物体ごとに定義された拡散反射する反射率を掛け合わせると色が求まります。

 プログラムは以下のようになります。

#version 100
attribute vec3 vertexCoordinate;
attribute vec3 vertexNormal;

uniform vec3 lightDirection;
uniform vec3 lightDiffuseColor;
uniform vec3 objectDiffuseColor;

uniform mat4 modelMatrix;

varying lowp vec4 colorVarying;

void main(){
  gl_Position = modelMatrix * vec4(vertexCoordinate, 1.0);
  float ndotl = max(0.0, dot(vertexNormal, normalize(lightDirection)));
  vec3 color = ndotl * lightDiffuseColor * objectDiffuseColor;
  colorVarying = vec4(color, 1.0);
}
#include <iostream>
#include "pmd.h"
#include "Diffuse.h"
#include "SimpleProgramObject.h"
#include "Matrix.h"

enum
{
  ATTRIBUTE_VERTEX_COORDINATE,
  ATTRIBUTE_VERTEX_NORMAL,
  NUM_ATTRIBUTES
};

enum
{
  UNIFORM_PROJECTION_MATRIX,
  UNIFORM_MODEL_MATRIX,
  UNIFORM_LIGHT_DIRECTION,
  UNIFORM_LIGHT_DIFFUSE_COLOR,
  UNIFORM_OBJECT_DIFFUSE_COLOR,
  NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];

bool Diffuse::initialize(){
#if !TARGET_OS_IPHONE
  BaseApplication::initializeWindow(640, 640, "Diffuse");
#endif

  polygonModel.load(modelSearchPath, "miku.pmd");
  
  SimpleProgramObject programObject;
  const char* vertexShaderFileName = "diffuse.vs";
  const char* fragmentShaderFileName = "first-colored.fs";
  
  program = programObject.createProgram(vertexShaderFileName, fragmentShaderFileName, shaderSearchPath);

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

  // 頂点バッファオブジェクトを作成する
  glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjects[1]);
  glBufferData(GL_ARRAY_BUFFER, sizeof(float)*polygonModel.numVertices()*3, polygonModel.vertexNormals(), GL_STATIC_DRAW);
  
  // 頂点バッファオブジェクトにシェーダ内の変数vertexCoodrinateを結びつける
  glEnableVertexAttribArray(ATTRIBUTE_VERTEX_NORMAL);
  glVertexAttribPointer(ATTRIBUTE_VERTEX_NORMAL, 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*polygonModel.numFaces(), polygonModel.faceVertexIndices(), GL_STATIC_DRAW);

  // 背景色の設定
  glClearColor(0.75f, 0.75f, 0.75f, 1.0f);
  
  // 頂点シェーダ内のuniform変数の位置を保存する
  uniforms[UNIFORM_PROJECTION_MATRIX] = glGetUniformLocation(program, "projectionMatrix");
  uniforms[UNIFORM_MODEL_MATRIX] = glGetUniformLocation(program, "modelMatrix");
  uniforms[UNIFORM_LIGHT_DIRECTION] = glGetUniformLocation(program, "lightDirection");
  uniforms[UNIFORM_LIGHT_DIFFUSE_COLOR] = glGetUniformLocation(program, "lightDiffuseColor");
  uniforms[UNIFORM_OBJECT_DIFFUSE_COLOR] = glGetUniformLocation(program, "objectDiffuseColor");
  
  Matrix::setIdentityM(modelMatrix, 0);
  Matrix::scaleM(modelMatrix, 0, 0.07f, 0.07f, 0.07f);
  Matrix::translateM(modelMatrix, 0, 0.0f, -polygonModel.getMaxHeight()/2.0f, 0.0f);
  
  return true;
}

void Diffuse::update(){
  
}

void Diffuse::draw(){
#if TARGET_OS_IPHONE
  int viewport[4];
  glGetIntegerv(GL_VIEWPORT, viewport);
  glViewport(0, (viewport[3]-viewport[2])/2, viewport[2], viewport[2]);
#endif

  glEnable(GL_CULL_FACE);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_GREATER);
#if TARGET_OS_IPHONE
  glClearDepthf(0.0);
#else
  glClearDepth(0.0);
#endif
  // 背景のクリア
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  // 使用するプログラムオブジェクトをする
  glUseProgram(program);
  // 投影行列を設定する
  glUniformMatrix4fv(uniforms[UNIFORM_MODEL_MATRIX], 1, 0, modelMatrix);
  glUniform3f(uniforms[UNIFORM_LIGHT_DIRECTION], 1.0f, 0.0f, 1.0f);
  glUniform3f(uniforms[UNIFORM_LIGHT_DIFFUSE_COLOR], 1.0f, 1.0f, 1.0f);
  glUniform3f(uniforms[UNIFORM_OBJECT_DIFFUSE_COLOR], 1.0f, 1.0f, 1.0f);
  // 三角形を描く
  glDrawElements(GL_TRIANGLES, 3*polygonModel.numFaces(), GL_UNSIGNED_INT, 0);
  // 使用するプログラムオブジェクトを解除する
  glUseProgram(0);
#if _WIN32 || (TARGET_OS_MAC && !TARGET_OS_IPHONE)
  // バッファの入れ替え
  glfwSwapBuffers(window);
  // イベントの取得
  glfwPollEvents();
#endif
}