1

光源による陰影をつけようとしていますが、形をすべて 1 つの色にしたいと考えています。

私の問題は、どんなに頑張っても、単一のカラーモデルでシェーディングが得られないように見えることです. この例をわかりやすくするために、モデルを 1 つの三角形に単純化しました。

#include <GL/glut.h>
#include <math.h>
#include <iostream>

#include<map>
#include<vector>

using namespace std;

/* Verticies for simplified demo */
float vertices[][3] = {
            {0.1, 0.1, 0.1},
            {0.2, 0.8, 0.3},
            {0.3, 0.5, 0.5},
            {0.8, 0.2, 0.1},
           };
const int VERTICES_SIZE = 4;
/* Polygons for simplified demo */
int polygon[][3] = {
                {0, 1, 3},
                {0, 2, 1},
                {0, 3, 2},
                {1, 2, 3},
            };
const int POLYGON_SIZE = 4;
/* Average point for looking at */
float av_point[3];

/*
 * Holds the normal for each vertex calculated by averaging the
 * planar normals that each vertex is connected to.
 * It holds {index_of_vertex_in_vertices : normal}
 */
map<int, float*> vertex_normals;

/*
 * Calculates average point in list of vertices
 * Stores in result
 */
void averagePoint(float vertices[][3], int length, float result[3]) {
  for(int i = 0; i < length; i++) {
    result[0] += vertices[i][0];
    result[1] += vertices[i][1];
    result[2] += vertices[i][2];
  }

  result[0] /= length;
  result[1] /= length;
  result[2] /= length;
}

/*
 * Performs inplace normalisation of vector v
 */
void normalise(float v[3]) {
  GLfloat length = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
  v[0] /= length;
  v[1] /= length;
  v[2] /= length;
}

/*
 * Performs cross product of vectors u and v and stores
 * result in result
 * Normalises result.
 */
void crossProduct(float u[], float v[], float result[]) {
  result[0] = u[1] * v[2] - u[2] * v[1];
  result[1] = u[2] * v[0] - u[0] * v[2];
  result[2] = u[0] * v[1] - u[1] * v[0];
}

/*
 * Calculates normal for plane
 */
void calculate_normal(int polygon[3], float vertices[][3], float normal[3]) {
  GLfloat u[3], v[3];
  for (int i = 0; i < 3; i++) {
    u[i] = vertices[polygon[0]][i] - vertices[polygon[1]][i];
    v[i] = vertices[polygon[2]][i] - vertices[polygon[1]][i];
  }

  crossProduct(u, v, normal);
  normalise(normal);
}

/*
 * Populates vertex_normal with it's averaged face normal
 */
void calculate_vertex_normals (map<int, float*> &vertex_normal){
  map<int, vector<int> > vertex_to_faces;
  map<int, float*> faces_to_normal;
  // Loop over faces
  for (int i = 0; i < POLYGON_SIZE; i++) {
    float* normal = new float[3];
    calculate_normal(polygon[i], vertices, normal);
    for (int j = 0; j < 3; j++) {
     vertex_to_faces[polygon[i][j]].push_back(i);
    }
    faces_to_normal[i] = normal;
  }


  vertex_normal.clear();
  // Loop over vertices
  for (int v = 0; v < VERTICES_SIZE; v++) {
    vector<int> faces = vertex_to_faces[v];
    int faces_count = 0;
    float* normal = new float[3];
    for (vector<int>::iterator it = faces.begin(); it != faces.end(); ++it){
      normal[0] += faces_to_normal[*it][0];
      normal[1] += faces_to_normal[*it][1];
      normal[2] += faces_to_normal[*it][2];
      faces_count++;
    }
    normal[0] /= faces_count;
    normal[1] /= faces_count;
    normal[2] /= faces_count;
    vertex_normal[v] = normal;
  }

  // Delete normal declared in first loop
  for (int i = 0; i < POLYGON_SIZE; i++) {
    delete faces_to_normal[i];
  }
}

/*
 * Draws polygons in polygon array.
 */
void draw_polygon() {
  for(int i = 0; i < POLYGON_SIZE; i++) {
    glBegin(GL_POLYGON);
    for(int j = 0; j < 3; j++) {
      glNormal3fv(vertex_normals[polygon[i][j]]);
      glVertex3fv(vertices[polygon[i][j]]);
    }
    glEnd();
  }
}


/*
 * Sets up lighting and material properties
 */
void init()
{
  // Calculate average point for looking at
  averagePoint(vertices, VERTICES_SIZE, av_point);

  // Calculate vertices average normals
  calculate_vertex_normals(vertex_normals);

  glClearColor (0.0, 0.0, 0.0, 0.0);
  cout << "init" << endl;

  // Intialise and set lighting parameters
  GLfloat light_pos[] = {1.0, 1.0, 1.0, 0.0};
  GLfloat light_ka[] = {0.2, 0.2, 0.2, 1.0};
  GLfloat light_kd[] = {1.0, 1.0, 1.0, 1.0};
  GLfloat light_ks[] = {1.0, 1.0, 1.0, 1.0};

  glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
  glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ka);
  glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_kd);
  glLightfv(GL_LIGHT0, GL_SPECULAR, light_ks);

  // Initialise and set material parameters
  GLfloat material_ka[] = {1.0, 1.0, 1.0, 1.0};
  GLfloat material_kd[] = {0.43, 0.47, 0.54, 1.0};
  GLfloat material_ks[] = {0.33, 0.33, 0.52, 1.0};
  GLfloat material_ke[] = {0.0, 0.0, 0.0, 0.0};
  GLfloat material_se[] = {10.0};

  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,  material_ka);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,  material_kd);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  material_ks);
  glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  material_ke);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, material_se);

  // Smooth shading
  glShadeModel(GL_SMOOTH);

  // Enable lighting
  glEnable (GL_LIGHTING);
  glEnable (GL_LIGHT0);

  // Enable Z-buffering
  glEnable(GL_DEPTH_TEST);
}

/*
 * Free's resources
 */
void destroy() {
  for (int i = 0; i < VERTICES_SIZE; i++) {
    delete vertex_normals[i];
  }
}

/*
 * Display simple polygon
 */
void display (){
  glClear  (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  draw_polygon();
  glutSwapBuffers();
}

/*
 * Sets up camera perspective and view point
 * Looks at average point in model.
 */
void reshape (int w, int h)
{
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(70, 1.0, 0.1, 1000);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(0, 0, 1, av_point[0], av_point[1], av_point[2], 0, 0.5, 0);
}

int main (int argc, char **argv)
{

  // Initialize graphics window
  glutInit(&argc, argv);
  glutInitWindowSize(256, 256);
  glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE);

  // Initialize OpenGL
  init();

  glutCreateWindow("Rendering");
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);

  glutMainLoop   ();

  destroy();

  return 1;
}

私は OpenGL に本当に慣れていないので、それが単純なものであることを願っています。法線を設定することを覚えていたので、他に何が問題なのかわかりません。

最終的な目的は、コースワークのためにグーロー シェーディング (およびテクスチャ) を使用して顔をレンダリングすることですが、OpenGL (1.4 - コース要件) を自分で理解する必要があり、シェーダーの使用は許可されていません。この写真に似たものを作成しようとしています(Googleから取得): ここに画像の説明を入力

私の三角形で。

4

2 に答える 2

5

光源による陰影がありますが、形はすべて一色にしたいです。

これらの2つの要件は相互に排他的ではありませんか?あなたの望む結果は正確には何ですか。あなたが想像している絵を描くことができますか?実装に関しては、シェーダーの使用は、膨大な数のOpenGLステートマシンスイッチを操作するよりもはるかに簡単です。

アップデート

とにかく、これが私の改訂版のOPコードで、グーロー照明の対象となる単一の三角形を描画します。このコードは、鏡面反射のヒントを使用して単一の三角形をコンパイルおよび描画します。

私がしたことを見てみましょう。まず、三角形の元の設定があります。ここでは特別なことは何もありませんし、何も変更されていません(いくつかのインクルードを除いて) (編集)2番目の外観で変更を加えました。std::mapの使用は完全に説明されていませんでした。頂点の数がわかっているので、法線のメモリを事前に割り当てることができます。

#include <GL/glut.h>
#include <math.h>

// for memcpy
#include <string.h>

#include <map>
#include <vector>
#include <iostream>

using namespace::std;

/* Verticies for simplified demo */
const int VERTICES_SIZE = 4;
float vertices[VERTICES_SIZE][3] = {
            {0.1, 0.1, 0.1},
            {0.2, 0.8, 0.3},
            {0.3, 0.5, 0.5},
            {0.8, 0.2, 0.1},
           };

// this is now a plain array
float vertex_normals[VERTICES_SIZE][3];

/* Polygons for simplified demo */
const int POLYGON_SIZE = 4;
int polygon[POLYGON_SIZE][3] = {
                {0, 1, 3},
                {0, 2, 1},
                {0, 3, 2},
                {1, 2, 3},
};

/* Average point for looking at */
float av_point[3];
    
/*
 * Calculates average point in list of vertices
 * Stores in result
 */
void averagePoint(float vertices[][3], int length, float result[3]) {
  for(int i = 0; i < length; i++) {
    result[0] += vertices[i][0];
    result[1] += vertices[i][1];
    result[2] += vertices[i][2];
  }

  result[0] /= length;
  result[1] /= length;
  result[2] /= length;
}

/*
 * Performs inplace normalisation of vector v
 */
void normalise(float v[3]) {
  GLfloat length = sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
  v[0] /= length;
  v[1] /= length;
  v[2] /= length;
}

/*
 * Performs cross product of vectors u and v and stores
 * result in result
 * Normalises result.
 */
void crossProduct(float u[], float v[], float result[]) {
  result[0] = u[1] * v[2] - u[2] * v[1];
  result[1] = u[2] * v[0] - u[0] * v[2];
  result[2] = u[0] * v[1] - u[1] * v[0];
}

/*
 * Calculates normal for plane
 */
void calculate_normal(int polygon[3], float vertices[][3], float normal[3]) {
  GLfloat u[3], v[3];
  for (int i = 0; i < 3; i++) {
    u[i] = vertices[polygon[0]][i] - vertices[polygon[1]][i];
    v[i] = vertices[polygon[2]][i] - vertices[polygon[1]][i];
  }

  crossProduct(u, v, normal);
  normalise(normal);
}

編集:私の次の変更はここにありました。コメントを見る

/*
 * Populates normals with it's averaged face normal
 *
 * Passing the normal output buffer as a parameter was a bit
 * pointless, as this procedure accesses global variables anyway.
 * Either pass everything as parameters or noting at all,
 * be consequent. And doing it mixed is pure evil.
 */
void calculate_vertex_normals()
{
  // We love RAII, no need for new and delete!
  vector< vector<int> > vertex_to_faces(POLYGON_SIZE);
  vector< vector<float> > faces_to_normal(POLYGON_SIZE);

  // Loop over faces
  for (int i = 0; i < POLYGON_SIZE; i++) {
    vector<float> normal(3);
    calculate_normal(polygon[i], vertices, &normal[0]);
    for (int j = 0; j < 3; j++) {
     vertex_to_faces[polygon[i][j]].push_back(i);
    }
    faces_to_normal[i] = normal;
  }

  // Loop over vertices
  for (int v = 0; v < VERTICES_SIZE; v++) {
    // avoid a copy here by using a reference
    vector<int> &faces = vertex_to_faces[v];
    int faces_count = 0;
    float normal[3];
    for (vector<int>::iterator it = faces.begin(); it != faces.end(); ++it){
      normal[0] += faces_to_normal[*it][0];
      normal[1] += faces_to_normal[*it][1];
      normal[2] += faces_to_normal[*it][2];
      faces_count++;
    }
    // dividing a vector obtained by a number of unit length vectors
    // summed by the number of unit vectors summed does not normalize
    // it. You need to normalize it properly!
    normalise(normal);

    // memcpy is really be best choice here
    memcpy(vertex_normals[v], normal, sizeof(normal));
  }
}

draw_polygonは、この関数のかなり不幸な名前です。三角形のメッシュを描画します。*編集:頂点配列(1994年からOpenGL-1.1で利用可能)を使用することで、より適切に記述できます。

/* 
 * Draws polygons in polygon array.
 */
void draw_polygon() {
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_NORMAL_ARRAY);

  glVertexPointer(3, GL_FLOAT, 0, &vertices[0][0]);
  glNormalPointer(GL_FLOAT, 0, &vertex_normals[0][0]);

  glDrawElements(GL_TRIANGLES, POLYGON_SIZE*3, GL_UNSIGNED_INT, polygon);
}

ここでそれは面白くなってきています。よくある誤解は、OpenGLが「初期化されている」と人々が考えるというものです。そうではありません。初期化するのはデータです。あなたの場合、あなたのジオメトリデータ

/*
 * Sets up lighting and material properties
 */
void init_geometry()
{
  // Calculate average point for looking at
  averagePoint(vertices, VERTICES_SIZE, av_point);

  // Calculate vertices average normals
  calculate_vertex_normals(vertex_normals);
}

ここで注意が必要な部分があります。OpenGL固定機能イルミネーションは他のすべてと同じ状態です。glLightfvを呼び出すと、呼び出されたときの状態に基づいて内部パラメーターが設定されます。これを呼び出すと、モデルビューによって位置が変換されます。ただし、適切なモデルビューが設定されていないと、イルミネーションを設定できません。そのため、描画関数でmodelviewを設定した直後に呼び出す独自の関数に入れました。

void setup_illumination()
{
  // Intialise and set lighting parameters
  GLfloat light_pos[] = {1.0, 1.0, 1.0, 0.0};
  GLfloat light_ka[] = {0.2, 0.2, 0.2, 1.0};
  GLfloat light_kd[] = {1.0, 1.0, 1.0, 1.0};
  GLfloat light_ks[] = {1.0, 1.0, 1.0, 1.0};

  glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
  glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ka);
  glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_kd);
  glLightfv(GL_LIGHT0, GL_SPECULAR, light_ks);

  // Initialise and set material parameters
  GLfloat material_ka[] = {1.0, 1.0, 1.0, 1.0};
  GLfloat material_kd[] = {0.43, 0.47, 0.54, 1.0};
  GLfloat material_ks[] = {0.33, 0.33, 0.52, 1.0};
  GLfloat material_ke[] = {0.0, 0.0, 0.0, 0.0};
  GLfloat material_se[] = {10.0};

  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,  material_ka);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,  material_kd);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  material_ks);
  glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  material_ke);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, material_se);

  // Smooth shading
  glShadeModel(GL_SMOOTH);

  // Enable lighting
  glEnable (GL_LIGHTING);
  glEnable (GL_LIGHT0);
}

描画機能については、いくつか変更がありました。コード内のコメントを参照してください

/*
 * Display simple polygon
 */
void display (void)
{
  // float window sizes are usefull for view volume calculations
  //
  // requesting the window dimensions for each drawing iteration
  // is just two function calls. Compare this to the number of function
  // calls a typical application will do for the actual rendering
  // Trying to optimize away those two calls is a fruitless microoptimization
  float const window_width  = glutGet(GLUT_WINDOW_WIDTH);
  float const window_height = glutGet(GLUT_WINDOW_HEIGHT);
  float const window_aspect = window_width / window_height;

  // glViewport operates independent of the projection --
  // another reason to put it into the drawing code
  glViewport(0, 0, window_width, window_height);

  glClearDepth(1.);
  glClearColor (0.0, 0.0, 0.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // It's a often made mistake to setup projection in the window resize
  // handler. Projection is a drawing state, hence should be set in
  // the drawing code. Also in most programs you will have multiple
  // projections mixed throughout rendering a single frame so there you
  // actually **must** set projection in drawing code, otherwise it
  // wouldn't work.
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(70, window_aspect, 1, 100);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(0, 0, -3, av_point[0], av_point[1], av_point[2], 0, 1, 0);
 
  // Fixed function pipeline light position setup calls operate on the current
  // modelview matrix, so we must setup the illumination parameters with the
  // modelview matrix at least after the view transformation (look-at) applied.
  setup_illumination();
 
  // Enable depth testing (z buffering would be enabled/disabled with glDepthMask)
  glEnable(GL_DEPTH_TEST);

  draw_polygon();

  glutSwapBuffers();
}
   
int main (int argc, char **argv)
{    
  // Initialize graphics window
  glutInit(&argc, argv);
  glutInitWindowSize(256, 256);
  glutInitDisplayMode    (GLUT_DEPTH | GLUT_DOUBLE);

  // we actually have to create a window
  glutCreateWindow("illuination");

  // Initialize geometry
  init_geometry();

  glutDisplayFunc(display);

  glutMainLoop();

  return 0;
}
于 2013-02-10T12:12:29.847 に答える
0

いくつかの場所に(vertices正しいスペルである)という配列と、(最も明白な例です)という別の配列があるようです。これは間違いですか?最初の配列から1つの座標を取得し、別の無関係な配列から2番目の座標を取得する場合、通常の計算が混乱する可能性があります。verticiescalculate_normal

于 2013-02-10T11:36:27.103 に答える