Reputation: 31
I have got all the vertices of the 3d mesh. And I need to scale it into a unit cube with the diagonal point being(-0.5,-0.5,-0.5),(0.5,0.5,0.5);
How could I get the new vertice of the 3d mesh?
And since I need to do this in the vertex shader, which matrix can I multiple the vertice?
Upvotes: 1
Views: 2047
Reputation: 20171
It's very usual to provide a model matrix for OPs purpose. There are plenty of sources in the Web where OpenGL transformations are explained. To name a few of them:
Transformations and transformation matrices are absolutely worth to be learned even although this might appear difficult at the first glance. It's the basic tool for a lot of things in 3d graphics, Visual Simulation, and Animation.
The transformation of the mesh could / should be done by the shader. For this, the resp. model matrix has to be passed as uniform (and used to transform the incoming vertices, of course).
To prepare this model matrix:
Determine the bounding box of your mesh. This is rather trivial: get min and max for x, y, z components of all vertices.
size = max - min provides the size in x, y, z direction.
center = (min + max) / 2 provides the center of your bounding box.
To scale the size to 1 you need a scale matrix with (1 / size.x, 1 / size.y, 1 / size.z). (Hopefully, no component of size will be 0. Otherwise, you have to exclude it.)
Multiply the scale matrix with a translate matrix for -center. (In the special case, that the center is already (0, 0, 0) this won't do any harm.)
Demo:
int main()
{
const float vertices[] = {
-10.0f, -3.0f, -2.0f,
3.0f, 10.0f, 0.0f,
2.0f, -3.0f, 1.0f,
};
const size_t nVtcs = std::size(vertices) / 3;
// determine bounding box
std::pair<Vec3f, Vec3f> bBox = boundingBox(vertices, nVtcs);
std::cout << "min: " << bBox.first << ", max: " << bBox.second << '\n';
// size of bounding box
const Vec3f sizeBBox = bBox.second - bBox.first;
std::cout << "size: " << sizeBBox << '\n';
// center of bound box
const Vec3f center = (bBox.first + bBox.second) * 0.5f;
std::cout << "center: " << center << '\n';
// make scale matrix
const Mat4x4f matS
= scale(Vec3f{
sizeBBox.x > 0.0f ? 1 / sizeBBox.x : 1.0f,
sizeBBox.y > 0.0f ? 1 / sizeBBox.y : 1.0f,
sizeBBox.z > 0.0f ? 1 / sizeBBox.z : 1.0f });
//std::cout << "matS:\n" << matS;
// make center matrix
const Mat4x4f matT = translate(-center);
//std::cout << "matT:\n" << matT;
// make model matrix
const Mat4x4f matM = matS * matT;
//std::cout << "matM:\n" << matM;
// check model matrix (would be done in shader):
std::cout << "Vertex transform:\n";
for (size_t i = 0; i < nVtcs; ++i) {
const Vec3f vtx(vertices[3 * i + 0], vertices[3 * i + 1], vertices[3 * i + 2]);
std::cout << " " << (matM * Vec4f(vtx, 1.0f)) << '\n';
}
}
Output:
min: -10, -3, -2, max: 3, 10, 1
size: 13, 13, 3
center: -3.5, 3.5, -0.5
Vertex transform:
-0.5, -0.5, -0.5, 1
0.5, 0.5, 0.166667, 1
0.423077, -0.5, 0.5, 1
The output looks promising: for each component, at least, one vector provides the target min. and max. values of -0.5 and 0.5.
To make it an MCVE, I included all needed lin. algebra. This is for what the OP might use glm instead, which is more comprehensive but similar to use.
Instead of the final transformation of vertices (done in output loop), OP would feed vertices and the model matrix into the shader which will be in charge to do it. (The vertices will become a shader attribute, the model matrix a uniform.)
#include <iostream>
template <typename V>
struct Vec3T {
V x = (V)0;
V y = (V)0;
V z = (V)0;
Vec3T() = default;
Vec3T(V x, V y, V z): x(x), y(y), z(z) { }
};
template <typename V>
Vec3T<V> operator-(const Vec3T<V>& vec)
{
return { -vec.x, -vec.y, -vec.z };
}
template <typename V>
Vec3T<V> operator+(const Vec3T<V>& vec1, const Vec3T<V>& vec2)
{
return { vec1.x + vec2.x, vec1.y + vec2.y, vec1.z + vec2.z };
}
template <typename V>
Vec3T<V> operator-(const Vec3T<V>& vec1, const Vec3T<V>& vec2)
{
return { vec1.x - vec2.x, vec1.y - vec2.y, vec1.z - vec2.z };
}
template <typename V, typename S>
Vec3T<V> operator*(const Vec3T<V>& vec, S s)
{
return { vec.x * s, vec.y * s, vec.z * s };
}
template <typename V>
std::ostream& operator<<(std::ostream& out, const Vec3T<V>& vec)
{
return out << vec.x << ", " << vec.y << ", " << vec.z;
}
template <typename V>
struct Vec4T {
V x = (V)0;
V y = (V)0;
V z = (V)0;
V w = (V)0;
Vec4T() = default;
Vec4T(V x, V y, V z, V w): x(x), y(y), z(z), w(w) { }
template<typename U>
Vec4T(const Vec3T<U>& vec, V w): x(vec.x), y(vec.y), z(vec.z), w(w) { }
};
template <typename V>
std::ostream& operator<<(std::ostream& out, const Vec4T<V>& vec)
{
return out << vec.x << ", " << vec.y << ", " << vec.z << ", " << vec.w;
}
template <typename V>
constexpr V dot(const Vec4T<V>& vec1, const Vec4T<V>& vec2)
{
return vec1.x * vec2.x + vec1.y * vec2.y
+ vec1.z * vec2.z + vec1.w * vec2.w;
}
template <typename V>
struct Mat4x4T {
Vec4T<V> cols[4] = { };
Mat4x4T() = default;
Mat4x4T(const Vec4T<V> (&cols)[4]): cols(cols) { }
Vec4T<V>& operator[](size_t i) { return cols[i]; }
const Vec4T<V>& operator[](size_t i) const { return cols[i]; }
};
template <typename V>
Mat4x4T<V> transpose(const Mat4x4T<V>& mat)
{
return Mat4x4T<V>({
{ mat[0].x, mat[1].x, mat[2].x, mat[3].x },
{ mat[0].y, mat[1].y, mat[2].y, mat[3].y },
{ mat[0].z, mat[1].z, mat[2].z, mat[3].z },
{ mat[0].w, mat[1].w, mat[2].w, mat[3].w }
});
}
template <typename V>
Mat4x4T<V> operator*(const Mat4x4T<V>& mat1, const Mat4x4T<V>& mat2)
{
const Mat4x4T<V> mat1T = transpose(mat1);
return Mat4x4T<V>({
{ dot(mat1T.cols[0], mat2.cols[0]),
dot(mat1T.cols[1], mat2.cols[0]),
dot(mat1T.cols[2], mat2.cols[0]),
dot(mat1T.cols[3], mat2.cols[0])
},
{ dot(mat1T.cols[0], mat2.cols[1]),
dot(mat1T.cols[1], mat2.cols[1]),
dot(mat1T.cols[2], mat2.cols[1]),
dot(mat1T.cols[3], mat2.cols[1])
},
{ dot(mat1T.cols[0], mat2.cols[2]),
dot(mat1T.cols[1], mat2.cols[2]),
dot(mat1T.cols[2], mat2.cols[2]),
dot(mat1T.cols[3], mat2.cols[2])
},
{ dot(mat1T.cols[0], mat2.cols[3]),
dot(mat1T.cols[1], mat2.cols[3]),
dot(mat1T.cols[2], mat2.cols[3]),
dot(mat1T.cols[3], mat2.cols[3])
}
});
}
template <typename V>
Vec4T<V> operator*(const Mat4x4T<V>& mat, const Vec4T<V>& vec)
{
const Mat4x4T<V> matT = transpose(mat);
return Vec4T<V>(
dot(matT.cols[0], vec),
dot(matT.cols[1], vec),
dot(matT.cols[2], vec),
dot(matT.cols[3], vec));
}
template <typename V>
Mat4x4T<V> scale(const Vec3T<V>& s)
{
return Mat4x4T<V>({
{ s.x, (V)0, (V)0, (V)0 },
{ (V)0, s.y, (V)0, (V)0 },
{ (V)0, (V)0, s.z, (V)0 },
{ (V)0, (V)0, (V)0, (V)1 }
});
}
template <typename V>
Mat4x4T<V> translate(const Vec3T<V>& t)
{
return Mat4x4T<V>({
{ (V)1, (V)0, (V)0, (V)0 },
{ (V)0, (V)1, (V)0, (V)0 },
{ (V)0, (V)0, (V)1, (V)0 },
{ t.x, t.y, t.z, (V)1 }
});
}
template <typename V>
std::ostream& operator<<(std::ostream& out, const Mat4x4T<V>& mat)
{
return out
<< mat[0].x << '\t' << mat[1].x << '\t' << mat[2].x << '\t' << mat[3].x << '\n'
<< mat[0].y << '\t' << mat[1].y << '\t' << mat[2].y << '\t' << mat[3].y << '\n'
<< mat[0].z << '\t' << mat[1].z << '\t' << mat[2].z << '\t' << mat[3].z << '\n'
<< mat[0].w << '\t' << mat[1].w << '\t' << mat[2].w << '\t' << mat[3].w << '\n';
}
template <typename V>
void updateMinMax(Vec3T<V> value, Vec3T<V>& min, Vec3T<V>& max)
{
if (min.x > value.x) min.x = value.x;
if (max.x < value.x) max.x = value.x;
if (min.y > value.y) min.y = value.y;
if (max.y < value.y) max.y = value.y;
if (min.z > value.z) min.z = value.z;
if (max.z < value.z) max.z = value.z;
}
using Vec3f = Vec3T<float>;
using Vec4f = Vec4T<float>;
using Mat4x4f = Mat4x4T<float>;
template <typename V>
std::pair<Vec3T<V>, Vec3T<V>> boundingBox(const V* vtcs, size_t nVtcs)
{
if (!nVtcs) return { { }, { } };
Vec3T<V> min(vtcs[0], vtcs[1], vtcs[2]);
Vec3T<V> max = min;
for (size_t i = 1; i < nVtcs; ++i) {
const Vec3T<V> vtx(vtcs[3 * i + 0], vtcs[3 * i + 1], vtcs[3 * i + 2]);
updateMinMax(vtx, min, max);
}
return { min, max };
}
int main()
{
const float vertices[] = {
-10.0f, -3.0f, -2.0f,
3.0f, 10.0f, 0.0f,
2.0f, -3.0f, 1.0f,
};
const size_t nVtcs = std::size(vertices) / 3;
// determine bounding box
std::pair<Vec3f, Vec3f> bBox = boundingBox(vertices, nVtcs);
std::cout << "min: " << bBox.first << ", max: " << bBox.second << '\n';
// size of bounding box
const Vec3f sizeBBox = bBox.second - bBox.first;
std::cout << "size: " << sizeBBox << '\n';
// center of bound box
const Vec3f center = (bBox.first + bBox.second) * 0.5f;
std::cout << "center: " << center << '\n';
// make scale matrix
const Mat4x4f matS
= scale(Vec3f{
sizeBBox.x > 0.0f ? 1 / sizeBBox.x : 1.0f,
sizeBBox.y > 0.0f ? 1 / sizeBBox.y : 1.0f,
sizeBBox.z > 0.0f ? 1 / sizeBBox.z : 1.0f });
//std::cout << "matS:\n" << matS;
// make center matrix
const Mat4x4f matT = translate(-center);
//std::cout << "matT:\n" << matT;
// make model matrix
const Mat4x4f matM = matS * matT;
//std::cout << "matM:\n" << matM;
// check model matrix (would be done in shader):
std::cout << "Vertex transform:\n";
for (size_t i = 0; i < nVtcs; ++i) {
const Vec3f vtx(vertices[3 * i + 0], vertices[3 * i + 1], vertices[3 * i + 2]);
std::cout << " " << (matM * Vec4f(vtx, 1.0f)) << '\n';
}
}
Upvotes: 1