glm 数据类型如何通过 OpenGL 缓冲区直接传递给 GPU?
How can glm data types be passed directly to the GPU via OpenGL buffers?
我自己编写了数学实用程序,它可以完成我正在编写的简单图形所需的一切。但是,我不知道如何让它们有资格直接传递给 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 来执行此操作,因为我不想 "wasted" 自己花时间编写这些实用程序。它的实现可以在这里看到:
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;
};
这是我必须添加的另一个运算符重载函数,还是完全不同的东西?
glBufferData()
需要一个 void*
,所以它不关心你传递给它的是什么类型。该函数只看到原始内存。
但是,您需要以其他方式告诉 OpenGL 如何解释该数据(例如 glVertexAttribPointer()
)。如果你告诉它期望一个 float x3 数组,那么你需要传递一个 float x3 数组,否则你会得到错误的输出。
glm::vec3
包含 3 个浮点数,而你的包含 6 个(假设 _type 是浮点数)。您似乎无缘无故地复制了组件。要么删除重复项,要么告诉 opengl 期待您的格式,最好是前者。
glBufferData 接受一个 void
指针。这正是您使用带有 glm::vec3 的代码片段所做的。但是,您可以通过不同的方式进行此操作。
由于 GLM 将其数据存储在 union
中,您可以通过多种方式访问向量的元素:通过 operator[]
或通过坐标 x
、y
, z
。由于元素是值,而不是指针,因此您需要 &
运算符来取消引用它们。所以 &myvec[0]
与 &myvec.x
具有相同的效果。
您的代码片段采用 std::vector 中第一个 glm::vec3 的指针。每个 glm::vec3 的内存地址是其第一个元素的内存地址。通过这种方式,您可以将指针传递给一个浮点数组(向量的元素,如果它们紧密排列 - 在 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) Return 引用 (_type&
) 而不是值 (_type
)。目前,您 return 的值是 _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)
进行测试。它只会使您的元素在关键循环中的访问速度变慢(当您多次访问元素时)。使用 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 类型一样使用它。
我自己编写了数学实用程序,它可以完成我正在编写的简单图形所需的一切。但是,我不知道如何让它们有资格直接传递给 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 来执行此操作,因为我不想 "wasted" 自己花时间编写这些实用程序。它的实现可以在这里看到:
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;
};
这是我必须添加的另一个运算符重载函数,还是完全不同的东西?
glBufferData()
需要一个 void*
,所以它不关心你传递给它的是什么类型。该函数只看到原始内存。
但是,您需要以其他方式告诉 OpenGL 如何解释该数据(例如 glVertexAttribPointer()
)。如果你告诉它期望一个 float x3 数组,那么你需要传递一个 float x3 数组,否则你会得到错误的输出。
glm::vec3
包含 3 个浮点数,而你的包含 6 个(假设 _type 是浮点数)。您似乎无缘无故地复制了组件。要么删除重复项,要么告诉 opengl 期待您的格式,最好是前者。
glBufferData 接受一个 void
指针。这正是您使用带有 glm::vec3 的代码片段所做的。但是,您可以通过不同的方式进行此操作。
由于 GLM 将其数据存储在 union
中,您可以通过多种方式访问向量的元素:通过 operator[]
或通过坐标 x
、y
, z
。由于元素是值,而不是指针,因此您需要 &
运算符来取消引用它们。所以 &myvec[0]
与 &myvec.x
具有相同的效果。
您的代码片段采用 std::vector 中第一个 glm::vec3 的指针。每个 glm::vec3 的内存地址是其第一个元素的内存地址。通过这种方式,您可以将指针传递给一个浮点数组(向量的元素,如果它们紧密排列 - 在 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) Return 引用 (_type&
) 而不是值 (_type
)。目前,您 return 的值是 _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)
进行测试。它只会使您的元素在关键循环中的访问速度变慢(当您多次访问元素时)。使用 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 类型一样使用它。