/ / Oggetto generato dall'ombreggiatura uniforme Opengl - c ++, opengl, illuminazione, normali, ombreggiatura

Opengl smooth shading oggetto generato - c ++, opengl, illuminazione, normali, ombreggiatura

Ho generato una forma 3d usando un rumore perlin. Sto cercando di sfumarlo, per farlo ho calcolato la faccia normale per ciascuno dei miei triangoli e poi ho calcolato la normale di ogni vertice triangolare facendo la media delle normali delle facce a cui appartengono e normalizzando il risultato finale. Il risultato finale assomiglia molto all'ombreggiatura piatta (vedi schermate allegate)

inserisci la descrizione dell'immagine qui inserisci la descrizione dell'immagine qui

Le normali sembrano corrette per me. Non riesco a usare gli shader e devo usare il vecchio modo obsoleto di renderizzare.

Il generatore di forme:

void Island::generateTopTriangles() {
float xStep = 2 * _xmax / _tess;
float zStep = 2 * _zmax / _tess;

PointMap top;
for (int i = 0; i <= _tess; i++) {
float z = -_zmax + i * zStep;
std::vector<Vector3f> rowTop;
for (int j = 0; j <= _tess; j++) {
float x = -_xmax + j * xStep;
rowTop.emplace_back(x, islandPerlin(x, z), z);
}
top.emplace_back(rowTop);
}

for (int i = 0; i < top.size() - 1; i++) {
const std::vector<Vector3f> &pointRow = top[i];
const std::vector<Vector3f> &pointUpRow = top[i + 1];
std::vector<Triangle> newRow;
for (int j = 0; j < pointRow.size() - 1; j++) {
const Vector3f &p1 = pointRow.at(j);
const Vector3f &p2 = pointRow.at(j + 1);
const Vector3f &p3 = pointUpRow.at(j);
const Vector3f &p4 = pointUpRow.at(j + 1);

Vertex::Ptr v1, v2, v3, v4, v5;
if (j == 0) {
v1 = std::make_shared<Vertex>(Vertex(p1, p3, Vector3f()));
} else { //Retrieve existing Vertex
v1 = newRow[newRow.size() - 1].v2;
}
v2 = std::make_shared<Vertex>(Vertex(p3, p2, Vector3f()));
if (i == 0) {
v3 = std::make_shared<Vertex>(Vertex(p2, p1, Vector3f()));
} else { //Retrieve existing Vertex
v3 = _triangles[_triangles.size() - 1][j == 0 ? 1 : newRow.size() + 1].v3;
}
v4 = std::make_shared<Vertex>(Vertex(p2, p4, Vector3f()));
v5 = std::make_shared<Vertex>(Vertex(p4, p3, Vector3f()));

//Create triangles
newRow.emplace_back(v1, v2, v3, computeNormal(v1->p, v2->p, v3->p));
newRow.emplace_back(v2, v4, v5, computeNormal(v2->p, v4->p, v5->p).invert());
}
_triangles.emplace_back(newRow);
}
}

Calcolo le normali del viso con un semplice prodotto incrociato tra due vettori:

Vector3f Island::computeNormal(const Vector3f &p1, const Vector3f &p2, const Vector3f &p3) {
Vector3f u = {p2.x - p1.x,
p2.y - p1.y,
p2.z - p1.z};
Vector3f v = {p3.x - p1.x,
p3.y - p1.y,
p3.z - p1.z};
Vector3f n = {u.y * v.z - u.z * v.y,
u.z * v.x - u.x * v.z,
u.x * v.y - u.y * v.x};
return n.normalize();
}

Le normali per vertice (inizializzate su 0):

void Island::computePerVertexNormal() {
for (auto row : _triangles) {
for (auto t : row) {
t.v1->n.x += t.n.x;
t.v1->n.y += t.n.y;
t.v1->n.z += t.n.z;
t.v2->n.x += t.n.x;
t.v2->n.y += t.n.y;
t.v2->n.z += t.n.z;
t.v3->n.x += t.n.x;
t.v3->n.y += t.n.y;
t.v3->n.z += t.n.z;
}
}
for (auto row : _triangles) {
for (auto t : row) {
t.v1->n.normalize();
t.v2->n.normalize();
t.v3->n.normalize();
}
}
}

E infine la parte del disegno:

void Island::draw() const {
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_BLEND);
glEnable(GL_COLOR_MATERIAL);

GLfloat specular[] = {0.1f, 0.1f, 0.1f, 0.0f};
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
GLfloat diffuse[] = {0.5f, 0.5f, 0.5f, 1.0f};
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
GLfloat emission[] = {0.0f, 0.0f, 0.0f, 1.0f};
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission);
GLfloat shininess = 128.0f;
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);

glShadeModel(GL_SMOOTH);
glColor4f(1.0f, 0.5f, 0.0f, 1.0f);
glBegin(GL_TRIANGLES);
for (auto &row : _triangles) {
for (auto &t : row) {
glNormal3f(t.v1->n.x, t.v1->n.y, t.v1->n.z);
glVertex3f(t.v1->p.x, t.v1->p.y, t.v1->p.z);
glNormal3f(t.v2->n.x, t.v2->n.y, t.v2->n.z);
glVertex3f(t.v2->p.x, t.v2->p.y, t.v2->p.z);
glNormal3f(t.v3->n.x, t.v3->n.y, t.v3->n.z);
glVertex3f(t.v3->p.x, t.v3->p.y, t.v3->p.z);
}
}
glEnd();

glDisable(GL_COLOR_MATERIAL);
glDisable(GL_BLEND);
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);
}

risposte:

2 per risposta № 1

La soluzione è stata semplice Mi sono confuso e ho pensato che i miei bordi fossero i vertici. Una volta corretti, poiché i nuovi vertici condividono i punti corretti questa volta, le nuove normali vengono ora calcolate dalle 6 facce triangolari adiacenti e ora sono corrette. il codice di generazione ora è anche molto più semplice.

void Island::generateTopTriangles() {
float xStep = 2 * _xmax / _tess;
float zStep = 2 * _zmax / _tess;

float z;
for (int i = 0; i <= _tess; i++) {
z = -_zmax + i * zStep;
std::vector<Vertex::Ptr> row;
for (int j = 0; j <= _tess; j++) {
float x = -_xmax + j * xStep;
row.emplace_back(std::make_shared<Vertex>(Vector3f(x, islandPerlin(x, z), z)));
}
_vertices.emplace_back(row);
}

for (int i = 0; i < _vertices.size() - 1; i++) {
const std::vector<Vertex::Ptr> &pointRow = _vertices[i];
const std::vector<Vertex::Ptr> &pointUpRow = _vertices[i + 1];
std::vector<Triangle::Ptr> newRow;
for (int j = 0; j < pointRow.size() - 1; j++) {
const Vertex::Ptr p1 = pointRow.at(j);
const Vertex::Ptr p2 = pointRow.at(j + 1);
const Vertex::Ptr p3 = pointUpRow.at(j);
const Vertex::Ptr p4 = pointUpRow.at(j + 1);

newRow.emplace_back(std::make_shared<Triangle>(p1, p2, p3, computeNormal(p3->p, p2->p, p1->p)));
newRow.emplace_back(std::make_shared<Triangle>(p3, p2, p4, computeNormal(p4->p, p2->p, p3->p)));
}
_triangles.emplace_back(newRow);
}
}

Il resto del codice era effettivamente corretto. Ecco i risultati: inserisci la descrizione dell'immagine qui