Created
May 28, 2018 06:16
-
-
Save sergey-shambir/5c02afde96bd728e04ce76a7a7866638 to your computer and use it in GitHub Desktop.
Correct Utah Teapot normals calculation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MeshDataP3N3 utils::tesselateTeapot(const Material &material, unsigned latitudeDivisions, unsigned longitudeDivisions) | |
{ | |
const size_t pointCount = std::size(kTeapotPatches) * (latitudeDivisions + 1) * (longitudeDivisions + 1); | |
const size_t vertexCount = 6 * std::size(kTeapotPatches) * latitudeDivisions * longitudeDivisions; | |
// TODO: (cg14.3) замените тип элемента в массиве на VertexP3N3. | |
std::vector<VertexP3N3> points(pointCount); | |
// Veticies | |
for (size_t p = 0; p < std::size(kTeapotPatches); p++) | |
{ | |
vec3 controlPonts[kTeapotPatchOrder + 1][kTeapotPatchOrder + 1]; | |
buildControlPoints(p, controlPonts); | |
for (size_t ru = 0; ru <= latitudeDivisions - 1; ru++) | |
{ | |
float u = 1.0f * ru / (latitudeDivisions - 1); | |
for (size_t rv = 0; rv <= longitudeDivisions - 1; rv++) | |
{ | |
float v = 1.0f * rv / (longitudeDivisions - 1); | |
size_t index = p * latitudeDivisions * longitudeDivisions + ru * longitudeDivisions + rv; | |
points[index].position = computePosition(controlPonts, u, v); | |
} | |
} | |
} | |
const auto getPoint = [&](unsigned patchIndex, unsigned ru, unsigned rv) -> VertexP3N3& { | |
size_t index = patchIndex * latitudeDivisions * longitudeDivisions + ru * longitudeDivisions + rv; | |
return points.at(index); | |
}; | |
// TODO: (cg14.3) добавьте цикл тройной вложенности, подобный циклу ниже, для расчёта нормалей | |
// Добавьте отдельную обработку точек в случаях: | |
// 1) "ru = latitudeDivisions - 1" | |
// 2) "rv = longitudeDivisions - 1" | |
// 3) "ru = latitudeDivisions - 1" и "rv = longitudeDivisions - 1" | |
for (unsigned p = 0; p < std::size(kTeapotPatches); p++) | |
{ | |
for (unsigned ru = 0; ru < latitudeDivisions - 1; ru++) | |
{ | |
for (unsigned rv = 0; rv < longitudeDivisions - 1; rv++) | |
{ | |
const VertexP3N3& pointA = getPoint(p, ru, rv); | |
const VertexP3N3& pointB = getPoint(p, ru, rv + 1); | |
const VertexP3N3& pointC = getPoint(p, ru + 1, rv); | |
VertexP3N3& target = getPoint(p, ru, rv); | |
target.normal = GetTriangleNormal(pointA.position, pointB.position, pointC.position); | |
} | |
} | |
{ | |
unsigned ru = latitudeDivisions - 2; | |
for (unsigned rv = 0; rv < longitudeDivisions - 1; rv++) | |
{ | |
const VertexP3N3& pointA = getPoint(p, ru - 1, rv); | |
const VertexP3N3& pointB = getPoint(p, ru - 1, rv + 1); | |
const VertexP3N3& pointC = getPoint(p, ru, rv); | |
VertexP3N3& target = getPoint(p, ru + 1, rv); | |
target.normal = GetTriangleNormal(pointA.position, pointB.position, pointC.position); | |
} | |
} | |
{ | |
unsigned rv = longitudeDivisions - 2; | |
for (unsigned ru = 0; ru < latitudeDivisions - 1; ru++) | |
{ | |
const VertexP3N3& pointA = getPoint(p, ru, rv); | |
const VertexP3N3& pointB = getPoint(p, ru, rv + 1); | |
const VertexP3N3& pointC = getPoint(p, ru + 1, rv); | |
VertexP3N3& target = getPoint(p, ru, rv + 1); | |
target.normal = GetTriangleNormal(pointA.position, pointB.position, pointC.position); | |
} | |
} | |
{ | |
unsigned ru = latitudeDivisions - 2; | |
unsigned rv = longitudeDivisions - 2; | |
const VertexP3N3& pointA = getPoint(p, ru, rv); | |
const VertexP3N3& pointB = getPoint(p, ru, rv + 1); | |
const VertexP3N3& pointC = getPoint(p, ru + 1, rv); | |
VertexP3N3& target = getPoint(p, ru + 1, rv + 1); | |
target.normal = GetTriangleNormal(pointA.position, pointB.position, pointC.position); | |
} | |
} | |
MeshDataP3N3 result; | |
result.primitive = gl::GL_TRIANGLES; | |
result.material = material; | |
// Каждая вершина будет уникальной из-за различных нормалей, поэтому | |
// индексы будут изменяться последовательно от 0 до vertexCount-1 | |
result.indicies.resize(vertexCount); | |
std::iota(result.indicies.begin(), result.indicies.end(), 0); | |
// На основе точек и вычисленных нормалей формируем вершины отдельных треугольников. | |
result.vertexes.reserve(vertexCount); | |
for (unsigned p = 0; p < std::size(kTeapotPatches); p++) | |
{ | |
for (unsigned ru = 0; ru < latitudeDivisions - 1; ru++) | |
{ | |
for (unsigned rv = 0; rv < longitudeDivisions - 1; rv++) | |
{ | |
// Один фрагмент ABCD делится на два треугольника ABC + CDA | |
// На каждой итерации мы добавляем два треугольника. | |
// Выделяем точки фрагмента из массива "points". | |
const VertexP3N3 pointA = getPoint(p, ru, rv); | |
const VertexP3N3 pointB = getPoint(p, ru, rv + 1); | |
const VertexP3N3 pointC = getPoint(p, ru + 1, rv + 1); | |
const VertexP3N3 pointD = getPoint(p, ru + 1, rv); | |
// Формируем треугольник ABC | |
result.vertexes.push_back(pointA); | |
result.vertexes.push_back(pointB); | |
result.vertexes.push_back(pointC); | |
// Формируем треугольник CDA | |
result.vertexes.push_back(pointC); | |
result.vertexes.push_back(pointD); | |
result.vertexes.push_back(pointA); | |
} | |
} | |
} | |
#if !defined(NDEBUG) | |
// Проверяем все вершины - значения координат должны действительными числами. | |
for (const VertexP3N3& v : result.vertexes) | |
{ | |
assert(std::isfinite(v.position.x)); | |
assert(std::isfinite(v.position.y)); | |
assert(std::isfinite(v.position.z)); | |
assert(std::isfinite(v.normal.x)); | |
assert(std::isfinite(v.normal.y)); | |
assert(std::isfinite(v.normal.z)); | |
} | |
#endif | |
return result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment