Обнаружение нормальной поверхности треугольника

Так что в настоящее время у меня есть треугольная сетка (сделанная с кривыми Безье), которую можно динамически изменять. Проблема, с которой я сталкиваюсь, заключается в попытке выяснить, какие треугольники нужно визуализировать, исходя из того, где находится камера. Камера всегда смотрит в начало координат (0,0,0), поэтому я нахожу каждый треугольник нормальным и получаю его точечный продукт с вектором моей камеры. Затем, основываясь на результате, определяется, должен ли треугольник быть «видимым» или нет.

Ниже приведен код, который я использую для расчетов:

void bezier_plane()
{
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 20; j++) {
grid[i][j].x = 0;
grid[i][j].y = 0;
grid[i][j].z = 0;
}
}
//Creates the grid using bezier calculation
CalcBezier();
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
Vector p1, p2, p3, normal;
p1.x = grid[i+1][j+1].x - grid[i][j].x; p1.y = grid[i+1][j+1].y - grid[i][j].y; p1.z = grid[i+1][j+1].z - grid[i][j].z;
p2.x = grid[i+1][j].x - grid[i][j].x;   p1.y = grid[i+1][j].y - grid[i][j].y; p1.z = grid[i+1][j].z - grid[i][j].z;

normal = CalcNormal(p2, p1);
double first = dotproduct(normal, Camera);

p3.x = grid[i][j+1].x - grid[i][j].x; p3.y = grid[i][j+1].y - grid[i][j].y; p3.z = grid[i][j+1].z - grid[i][j].z;

normal = CalcNormal(p1, p3);
double second = dotproduct(normal, Camera);

if (first < 0 && second < 0) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glColor3f(0, 1, 0);
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(grid[i][j].x, grid[i][j].y, grid[i][j].z);
glVertex3f(grid[i][j+1].x, grid[i][j+1].y, grid[i][j+1].z);
glVertex3f(grid[i+1][j].x, grid[i+1][j].y, grid[i+1][j].z);
glVertex3f(grid[i+1][j+1].x, grid[i+1][j+1].y, grid[i+1][j+1].z);
glEnd();
} else if (first < 0 && second > 0) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glColor3f(0, 1, 0);
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(grid[i][j].x, grid[i][j].y, grid[i][j].z);
glVertex3f(grid[i+1][j].x, grid[i+1][j].y, grid[i+1][j].z);
glVertex3f(grid[i+1][j+1].x, grid[i+1][j+1].y, grid[i+1][j+1].z);
glEnd();
} else if (first > 0 && second < 0) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glColor3f(0, 1, 0);
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(grid[i][j].x, grid[i][j].y, grid[i][j].z);
glVertex3f(grid[i][j+1].x, grid[i][j+1].y, grid[i][j+1].z);
glVertex3f(grid[i+1][j+1].x, grid[i+1][j+1].y, grid[i+1][j+1].z);
glEnd();
}
}
}
}

Вот CalcNormal:

   Vector CalcNormal(Vector p1, Vector p2)
{
Vector normal;
normal.x = (p1.y * p2.z) - (p1.z * p2.y);
normal.y = (p1.z * p2.x) - (p1.x * p2.z);
normal.z = (p1.x * p2. y) - (p1.y * p2.x);
return normal;
}

double dotproduct(Vector normal, Vector Camera)
{
return (normal.x * Camera.x + normal.y * Camera.y + normal.z + Camera.z);
}

Прямо сейчас мой код дает этот результат. Часть, обведенная красным, НЕ должна отображаться (я полагаю, треугольники сзади).
результат

3

Решение

Ваш подход к тестированию нормалей будет по-прежнему иметь визуальные артефакты, потому что треугольники, обращенные к камере, могут также быть неясным. Представьте, что эта выпуклость была в углу, ближайшем к камере.

У вас также будут треугольники, которые частично видны и частично скрыты.

Решение, которое будет работать на уровне пикселей:

  • glEnable(GL_DEPTH_TEST)​
  • Нарисуйте поверхность первый с твердый треугольники вместо каркаса
  • Очистить буфер кадра, но не буфер глубины
  • Теперь нарисуйте всю свою сцену. Буфер глубины предотвратит отрисовку затененных пикселей
3

Другие решения

«Нормальное — это глобальная переменная» — может быть, это уже ваша проблема? Это похоже на худшее применение глобальных данных, о котором я только могу подумать! Вместо этого вызывая эту вещь crossproduct и возвращение вектора звучит как хорошая идея, не так ли? Так же dotproduct должен взять два вектора в качестве параметра.

Тем не менее, ваш подход является здравым. Если у вас всегда одинаковое направление для углов треугольников, перекрестное произведение двух сторон даст вам нормальное. Кроме того, если угол между нормалью и видом меньше 90 градусов, он отвлекается от вида и должен быть невидимым. Следовательно, проблема должна быть в вашей реализации, и первое, что вы должны исправить, — это использовать глобальное состояние, которое может быть сохранено в регистрах ЦП.

Изменить: Вы могли бы использовать перегрузку оператора в интересах читателя здесь:

class Vector
{
Vector(){}
Vector(scalar x0, scalar y0, scalar z0): x(x0), y(y0), z(z0){}
float x, y, z;
};

Vector operator-(Vector const& v1, Vector const& v2)
{
return Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
}

Затем запустите тело цикла следующим образом:

Vector const point1 = grid[i, j];
Vector const point2 = grid[i + 1, j];
Vector const point3 = grid[i, j + 1];
Vector const point4 = grid[i + 1, j + 1];

Они легко оптимизируются компилятором, облегчая отладку и улучшая читабельность. Также обратите внимание, что они постоянны, что заставляет компилятор убедиться, что вы не изменили их случайно. Затем вы вычисляете две нормали двух треугольников:

Vector const norm1 = crossproduct(point2 - point1, point3 - point1);
Vector const norm2 = crossproduct(point4 - point2, point4 - point3);

Затем вы можете проверить dotproduct на видимость:

bool const visible1 = dotproduct(norm1, Camera) > 0;
bool const visible2 = dotproduct(norm2, Camera) > 0;

Наконец, вы могли бы перегрузить glVertex3f() взять Vector, но я бы держался подальше от перегрузки функций других библиотек.

2