Как можно передавать типы данных glm напрямую в GPU через буферы OpenGL?
Я сам написал математические утилиты, которые делают все необходимое для простой графики, которую я программирую. Однако я не знаю, как сделать так, чтобы они могли напрямую переходить в OpenGL. Это можно сделать с помощью glm, например:
std::vector<glm::vec3> locations;
[...]
glGenBuffers(NUM_BUFFERS, _vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo[POSITION_VBO]);
// throw data in vbo[0]
glBufferData(GL_ARRAY_BUFFER, _num_vertices * sizeof(locations[0]), &locations[0], GL_STATIC_DRAW);
Я хотел бы иметь возможность делать это с моим собственным типом vec3, math::vec3f, так как я не хотел бы "тратить" свое время на написание этих утилит. Его реализацию можно увидеть здесь:
namespace math
{
template<typename _type> class vec2;
template<typename _type>
class vec3
{
private:
_type _gl_a[3];
public:
_type x, y, z;
vec3() {};
vec3(_type _x, _type _y, _type _z)
{
_gl_a[0] = x = _x;
_gl_a[1] = y = _y;
_gl_a[2] = z = _z;
}
vec3(vec2<_type> v, _type w)
{
_gl_a[0] = x = v.x;
_gl_a[1] = y = v.y;
_gl_a[2] = z = w;
}
inline vec3<_type> operator=(vec2<_type> &v)
{
_gl_a[0] = x = v.x;
_gl_a[1] = y = v.y;
_gl_a[2] = z = 0;
}
inline vec3<_type> operator+(_type other) { return vec3<_type>(x + other, y + other, z + other); }
inline vec3<_type> operator-(_type other) { return vec3<_type>(x - other, y - other, z - other); }
inline vec3<_type> operator*(_type other) { return vec3<_type>(x * other, y * other, z * other); }
inline vec3<_type> operator/(_type other) { return vec3<_type>(x / other, y / other, z / other); }
inline vec3<_type> operator+(vec3<_type> &other) { return vec3<_type>(x + other.x, y + other.y, z + other.z); }
inline vec3<_type> operator-(vec3<_type> &other) { return vec3<_type>(x - other.x, y - other.y, z - other.z); }
inline vec3<_type> operator*(vec3<_type> &other) { return vec3<_type>(x * other.x, y * other.y, z * other.z); }
inline vec3<_type> operator/(vec3<_type> &other) { return vec3<_type>(x / other.x, y / other.y, z / other.z); }
inline _type operator[](int i)
{
if(i < 0 || i >= 3)
return 0;
_gl_a[0] = x;
_gl_a[1] = y;
_gl_a[2] = z;
return _gl_a[i];
}
inline double magnitude()
{
return sqrt(x * x + y * y + z * z);
}
inline vec3<_type> normal()
{
double m = this->magnitude();
return vec3<_type>(x / m, y / m, z / m);
}
inline _type dot(vec3<_type> other)
{
return x * other.x + y * other.y + z * other.z;
}
inline vec3<_type> cross(vec3<_type> other)
{
return vec3<_type>(y * other.z - other.y * z,
z * other.x - other.z * x,
x * other.y - other.x * y);
}
};
typedef vec3<float> vec3f;
typedef vec3<double> vec3d;
typedef vec3<int> vec3i;
typedef vec3<unsigned int> vec3ui;
typedef vec3<short> vec3s;
typedef vec3<unsigned short> vec3us;
};
Это еще одна функция перегрузки оператора, которую я должен добавить, или что-то совсем другое?
2 ответа
glBufferData занимает void
указатель. Это именно то, что вы делаете, используя кусок кода с glm::vec3. Однако вы можете сделать это разными способами.
Поскольку GLM хранит свои данные в union
Вы можете получить доступ к элементам вектора несколькими способами: operator[]
или по координатам x
, y
, z
, Поскольку элементы являются значениями, а не указателями, вам нужно &
оператор разыменовывать их. Так &myvec[0]
имеет тот же эффект, что и &myvec.x
,
Ваш фрагмент кода получает указатель 1-го glm:: vec3 в std::vector. Адрес памяти каждого glm:: vec3 является адресом памяти его 1-го элемента. Таким образом, вы передаете указатель на массив с плавающей точкой (элементы вектора, если они плотно упакованы - в GLM они есть):
std::vector<glm::vec3> locations;
glBufferData(GL_ARRAY_BUFFER, _num_vertices * sizeof(locations[0]), &locations[0], GL_STATIC_DRAW);
// same as above
//glBufferData(GL_ARRAY_BUFFER, _num_vertices * sizeof(locations[0]), &locations[0][0], GL_STATIC_DRAW);
// same as above
//glBufferData(GL_ARRAY_BUFFER, _num_vertices * sizeof(locations[0]), &locations[0].x, GL_STATIC_DRAW);
Я рекомендую вам ознакомиться с концепцией указателей, ссылок и объединений в C++, так как следующая часть моего ответа полагается на них.
Ваш math::vec3
Реализация имеет несколько проблем.
1) Как уже говорили другие в комментариях, вам нужны оба
_type operator[](int i);
_type operator[](int i) const;
поддерживать const
-ness.
2) Вернуть ссылку (_type&
) вместо значения (_type
). В настоящее время вы возвращаете значение _type operator[](int i);
Таким образом, ваши элементы доступны только для чтения. Используя ссылки, вы можете как читать, так и писать.
_type& operator[](int i);
_type& operator[](int i) const;
3) Поскольку у вас не может быть отрицательных индексов, вы не должны использовать подписанный int
для индексации. Используйте тип без знака: unsigned int
делает работу, но size_t
еще лучше.
&_type operator[](size_t i);
&_type operator[](size_t i) const;
4) Тестирование против if (i < 0 || i >= 3)
не обязательно. Это просто делает ваш элемент доступ к LOT медленнее в критических циклах (когда вы обращаетесь к элементам много раз). С помощью size_t
вы не можете иметь значение меньше 0, и в правильном коде вы никогда не должны передавать индекс, превышающий фактический размер вектора.
5) Вы храните свои данные дважды: один раз в _gl_a[3]
и однажды в x
, y
, z
, Это огромная трата памяти. Вместо этого вы должны использовать union
получить доступ к одним и тем же данным несколькими способами.
union // anonymous union
{
_gl_a[3];
struct { x, y, z, }; // anonymous struct
};
Как только у вас есть правильная реализация вашего math::vec3
, вы сможете использовать его так же, как типы GLM.
glBufferData()
занимает void*
так что все равно, какой тип вы ему передадите. Эта функция просто видит сырую память.
Однако вы должны указать OpenGL другими способами, как интерпретировать эти данные (например, glVertexAttribPointer()
). Если вы скажете ему ожидать массива с плавающей запятой x3, то вам нужно будет передать ему массив с плавающей запятой x3, иначе вы получите неправильный вывод.
В то время как glm::vec3
содержит 3 числа с плавающей точкой, у вас - 6 (при условии, что _type - это число с плавающей точкой). Вы дублировали компоненты, казалось бы, без причины. Либо удалите дубликаты, либо скажите opengl ожидать вашего формата, предпочтительно первого.