Related
So I am trying to create an arbitrary curved shape using OpenGL and currently my code is only able to produce one curve between the specified control points, below is my OpenGL code:
#include <GL/glut.h>
#include <stdlib.h>
GLfloat controlPoints[18][3] =
{
{0.0, 8.0, 0.0},
{ -1.5, 3.0, 0.0}, //2
{-5.5, 4.0, 0.0},
{-5.5, 4.0, 0.0},
{-2.5, 0.0, 0.0}, //4
{-6.0, -4.0, 0.0},
{-6.0, -4.0, 0.0},
{-1.5, -3.0, 0.0}, //6
{0.0, -8.0, 0.0},
{0.0, -8.0, 0.0},
{1.0, -3.0, 0.0}, //8
{6.0, -5.0, 0.0},
{6.0, -5.0, 0.0},
{3.0, 0.0, 0.0}, //10
{6.5, 4.5, 0.0},
{6.5, 4.5, 0.0},
{1.5, 3.0, 0.0}, //12
{0.0, 8.0, 0.0}
};
void init(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_FLAT);
for (int i = 0; (i + 3) < 3; i += 3)
{
glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &controlPoints[i][0]);
}
//glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &controlPoints2[0][0]);
glEnable(GL_MAP1_VERTEX_3);
// The evaluator with a stride of 3 and an order of 4
}
void display(void)
{
int i;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0);
//draw(controlPoints);
//draw(controlPoints2);
glBegin(GL_LINE_STRIP);
{
for (int i = 0; i <= 18; i++)
{
glEvalCoord1f((GLfloat)i / 18.0);
}
}
glEnd();
glBegin(GL_LINE_STRIP);
{
for (i = 0; i < 18; i++)
{
glVertex3fv(&controlPoints[i][0]);
}
}
glEnd();
glPointSize(6.0);
glColor3f(0.0, 0.0, 1.0);
glBegin(GL_POINTS);
{
for (i = 0; i < 18; i++)
{
glVertex3fv(&controlPoints[i][0]);
}
}
glEnd();
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
{
glOrtho(-10.0, 10.0, -10.0 * (GLfloat)h / (GLfloat)w, 10.0 * (GLfloat)h / (GLfloat)w, -10.0, 10.0);
}
else
{
glOrtho(-10.0 * (GLfloat)h / (GLfloat)w, 10.0 * (GLfloat)h / (GLfloat)w, -10.0, 10.0, -10.0, 10.0);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y)
{
switch (key)
{
case 27:
exit(0);
break;
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow(argv[0]);
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}
How do I modify my init portion of the code such that able to produce 6 curves between the three control points totaling up to 18? and if not possible is there a way I can do it using GL_LINE_STRIP?
Below is what my current output looks like:
My advice - avoid openGL evaluators completely!
Aside from some SGI machines back in the 90's, no GPU vendor has ever added hardware support for them, so it falls back to a fairly inefficient software implementation.
Anyhow, there are a few problems in your code...
glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3,
4, ///< this says you want 4 control points per curve
&controlPoints[i][0]);
However, there is something wrong here in the control points:
GLfloat controlPoints[18][3] =
{
{0.0, 8.0, 0.0},
{ -1.5, 3.0, 0.0}, //2
{-5.5, 4.0, 0.0}, ///< I'm assuming this is the last control point you want?
{-5.5, 4.0, 0.0}, ///< however this is duplicated here?
It looks as though you want a quadratic curve? (i.e. 3 control points per curve?)
// enable evaluators
glEnable(GL_MAP1_VERTEX_3);
// step through each triplet of CV's
for(int cv = 0; cv < 18; cv += 3) {
// specify the control point array
glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0,
3, ///< each vertex has 3 floats.
3, ///< I assume you want 3? (as in 3x CV per curve)
&controlPoints[cv][0]);
// render this curve segment
glBegin(GL_LINE_STRIP);
{
// choose how many divisions you want
int NUM_DIVISIONS = 32;
for (int i = 0; i <= NUM_DIVISIONS; i++)
{
glEvalCoord1f((GLfloat)i / (GLfloat) NUM_DIVISIONS);
}
}
glEnd();
}
glDisable(GL_MAP1_VERTEX_3);
However, as I said above, GL evaluators are terrible.
It's actually just a lot easier to simply write the code yourself.
One option would be to simply tessellate each curve, and then render (This would work with your current control point layout)
void render_quadratic_curves(
GLfloat controlPoints[][3],
int num_curves,
int num_divisions) {
int out_size_of_each_curve = (num_divisions + 1) * 3;
// allocate enough memory to store a curves
GLfloat* temp = new GLfloat[out_size_of_each_curve];
// re-render from the same vertex array.
glVertexPointer(3, GL_FLOAT, sizeof(float) * 3, temp);
glEnableClientState(GL_VERTEX_ARRAY);
for(int curve = 0; curve < num_curves; ++curve) {
// pointers to the control points for this curve
const GLfloat* P0 = controlPoints[3 * curve + 0];
const GLfloat* P1 = controlPoints[3 * curve + 1];
const GLfloat* P2 = controlPoints[3 * curve + 2];
for(int division = 0; division <= num_divisions; ++division) {
GLfloat t = (GLfloat) division / (GLfloat) NUM_DIVISIONS;
GLfloat inv_t = (1.0f - t);
// compute bezier coefficients for quadratic curve
GLfloat B0 = inv_t * inv_t;
GLfloat B1 = 2.0f * inv_t * t;
GLfloat B2 = t * t;
// compute XYZ coordinates
GLfloat x = P0[0] * B0 +
P1[0] * B1 +
P2[0] * B2;
GLfloat y = P0[1] * B0 +
P1[1] * B1 +
P2[1] * B2;
GLfloat z = P0[2] * B0 +
P1[2] * B1 +
P2[2] * B2;
// insert into the buffer for rendering
temp[3 * division + 0] = x;
temp[3 * division + 1] = y;
temp[3 * division + 2] = z;
}
// render this curve in one go as a strip
glDrawArrays(GL_LINE_STRIP, 0, num_divisions + 1);
}
// cleanup
glDisableClientState(GL_VERTEX_ARRAY);
delete [] temp;
}
However, in your case above you effectively have a loop, so this can be done in one go instead with GL_LINE_LOOP instead (This approach would nicely fit into a VBO)
void render_quadratic_curves_as_loop(
GLfloat controlPoints[][3],
int num_curves,
int num_divisions) {
// curves are 1 vertex smaller in size than previously,
// since the start vertex of one curve, is shared with the
// last vertex of the previous curve
int out_size_of_each_curve = num_divisions * 3;
// allocate enough memory to store all of the curves
GLfloat* temp = new GLfloat[out_size_of_each_curve * num_curves];
for(int curve = 0; curve < num_curves; ++curve) {
GLfloat* this_curve = temp + curve * out_size_of_each_curve;
// pointers to the control points for this curve
const GLfloat* P0 = controlPoints[3 * curve + 0];
const GLfloat* P1 = controlPoints[3 * curve + 1];
const GLfloat* P2 = controlPoints[3 * curve + 2];
// note! I am using less than here!
// the last vertex of each curve is simply the first
// vertex of the next one...
for(int division = 0; division < num_divisions; ++division) {
GLfloat t = (GLfloat) division / (GLfloat) NUM_DIVISIONS;
GLfloat inv_t = (1.0f - t);
// compute bezier coefficients for quadratic curve
GLfloat B0 = inv_t * inv_t;
GLfloat B1 = 2.0f * inv_t * t;
GLfloat B2 = t * t;
// compute XYZ coordinates
GLfloat x = P0[0] * B0 +
P1[0] * B1 +
P2[0] * B2;
GLfloat y = P0[1] * B0 +
P1[1] * B1 +
P2[1] * B2;
GLfloat z = P0[2] * B0 +
P1[2] * B1 +
P2[2] * B2;
// insert into the buffer for rendering
this_curve[3 * division + 0] = x;
this_curve[3 * division + 1] = y;
this_curve[3 * division + 2] = z;
}
}
// re-render from the same vertex array.
// This *could* be replaced with a VBO.
glVertexPointer(3, GL_FLOAT, sizeof(float) * 3, temp);
glEnableClientState(GL_VERTEX_ARRAY);
// render all of the curves in one go.
glDrawArrays(GL_LINE_LOOP, 0, out_size_of_each_curve * num_curves);
// cleanup
glDisableClientState(GL_VERTEX_ARRAY);
delete [] temp;
}
// You'll now need to remove the duplicate CV's from your array
GLfloat controlPoints[12][3] =
{
{0.0, 8.0, 0.0},
{ -1.5, 3.0, 0.0}, //2
{-5.5, 4.0, 0.0},
{-2.5, 0.0, 0.0}, //4
{-6.0, -4.0, 0.0},
{-1.5, -3.0, 0.0}, //6
{0.0, -8.0, 0.0},
{1.0, -3.0, 0.0}, //8
{6.0, -5.0, 0.0},
{3.0, 0.0, 0.0}, //10
{6.5, 4.5, 0.0},
{1.5, 3.0, 0.0}, //12
};
render_quadratic_curves_as_loop(controlPoints, 6, 32);
If you actually want 4 CV's per curve, then you can easily extend this into a cubic bezier.
// obviously each curve will now need an additional CV
void render_cubic_curves_as_loop(
GLfloat controlPoints[][3],
int num_curves,
int num_divisions) {
// curves are 1 vertex smaller in size than previously,
// since the start vertex of one curve, is shared with the
// last vertex of the previous curve
int out_size_of_each_curve = num_divisions * 3;
// allocate enough memory to store all of the curves
GLfloat* temp = new GLfloat[out_size_of_each_curve * num_curves];
for(int curve = 0; curve < num_curves; ++curve) {
GLfloat* this_curve = temp + curve * out_size_of_each_curve;
// pointers to the control points for this curve
const GLfloat* P0 = controlPoints[4 * curve + 0];
const GLfloat* P1 = controlPoints[4 * curve + 1];
const GLfloat* P2 = controlPoints[4 * curve + 2];
const GLfloat* P3 = controlPoints[4 * curve + 2];
// note! I am using less than here!
// the last vertex of each curve is simply the first
// vertex of the next one...
for(int division = 0; division < num_divisions; ++division) {
GLfloat t = (GLfloat) division / (GLfloat) NUM_DIVISIONS;
GLfloat inv_t = (1.0f - t);
// compute bezier coefficients for cubic curve
GLfloat B0 = inv_t * inv_t * inv_t;
GLfloat B1 = 3.0f * inv_t * inv_t * t;
GLfloat B2 = 3.0f * inv_t * t * t;
GLfloat B2 = t * t;
// compute XYZ coordinates
GLfloat x = P0[0] * B0 +
P1[0] * B1 +
P2[0] * B2 +
P3[0] * B3;
GLfloat y = P0[1] * B0 +
P1[1] * B1 +
P2[1] * B2 +
P3[1] * B3;
GLfloat z = P0[2] * B0 +
P1[2] * B1 +
P2[2] * B2 +
P3[2] * B3;
// insert into the buffer for rendering
this_curve[3 * division + 0] = x;
this_curve[3 * division + 1] = y;
this_curve[3 * division + 2] = z;
}
}
// re-render from the same vertex array.
// This *could* be replaced with a VBO.
glVertexPointer(3, GL_FLOAT, sizeof(float) * 3, temp);
glEnableClientState(GL_VERTEX_ARRAY);
// render all of the curves in one go.
glDrawArrays(GL_LINE_LOOP, 0, out_size_of_each_curve * num_curves);
// cleanup
glDisableClientState(GL_VERTEX_ARRAY);
delete [] temp;
}
NOTE: on modern hardware, if you have tessellation shaders available, that's usually the best option. Failing that, if you have hardware instancing, you can specify the basis coefficients as a shared vertex buffer, and the control points can be specified per instance.
generate a VBO to store the blending coefficients, and set the VBO to have a vertex divisor of 0.
void populate_shared_vertex_data_for_VBO(float* out, int NUM_DIVISIONS) {
for(int i = 0; i <= NUM_DIVISIONS; ++i) {
GLfloat t = (GLfloat) division / (GLfloat) (NUM_DIVISIONS + 1);
GLfloat inv_t = (1.0f - t);
// compute bezier coefficients for cubic curve
GLfloat B0 = inv_t * inv_t * inv_t;
GLfloat B1 = 3.0f * inv_t * inv_t * t;
GLfloat B2 = 3.0f * inv_t * t * t;
GLfloat B2 = t * t;
out[0] = B0;
out[1] = B1;
out[2] = B2;
out[3] = B3;
out += 4;
}
}
Load the control points for all curves into a single BIG VBO, set up the 4 per-instance attributes (i.e. specify 4 varying shader inputs, one for each CV, set each stride to sizeof(Cubic_Curve_CVS), and set the divisor to 1).
struct Cubic_Curve_CVS {
float P0[3];
float P1[3];
float P2[3];
float P3[3];
};
Cubic_Curve_CVS VBO_DATA[NUM_CURVES]; ///< load this
The vertex shader ends up being pretty simple to implement:
#version 450
uniform mat4 vs_mvp;
// share this buffer between all indices,
// i.e. glVertexAttribDivisor(0, 0);
layout(location = 0) in vec4 vs_coeffs;
// make these per-instance attributes
// i.e. :
// glVertexAttribDivisor(1, 1);
// glVertexAttribDivisor(2, 1);
// glVertexAttribDivisor(3, 1);
// glVertexAttribDivisor(4, 1);
layout(location = 1) in vec4 vs_CV0;
layout(location = 2) in vec4 vs_CV1;
layout(location = 3) in vec4 vs_CV2;
layout(location = 4) in vec4 vs_CV3;
void main()
{
float B0 = vs_coeffs.x;
float B1 = vs_coeffs.y;
float B2 = vs_coeffs.z;
float B3 = vs_coeffs.w;
vec4 V = vs_CV0 * B0 +
vs_CV1 * B1 +
vs_CV2 * B2 +
vs_CV3 * B3;
gl_Position = vs_mvp * V;
}
and then just render the whole lot in one go with glDrawArraysInstanced.
I have the following code for my own look-at matrix(multiplication of matrices and cross product of vectors work perfectly, I checked it):
template<typename Type>
void setLookAt(Matrix4x4<Type>& matrix, const Vector3<Type> eye, const Vector3<Type> center, const Vector3<Type> up) noexcept
{
Math::Vector3f right = Math::cross(center, up).normalize();
Matrix4x4f lookAt({
right.getX(), right.getY(), right.getZ(), 0.0,
up.getX(), up.getY(), up.getZ(), 0.0,
center.getX(), center.getY(), center.getZ(), 0.0,
0.0, 0.0, 0.0, 1.0
});
Matrix4x4f additionalMatrix({
0.0, 0.0, 0.0, -(eye.getX()),
0.0, 0.0, 0.0, -(eye.getY()),
0.0, 0.0, 0.0, -(eye.getZ()),
0.0, 0.0, 0.0, 1.0
});
lookAt.mul(additionalMatrix);
matrix = lookAt;
}
template<typename Type>
void setPerspectiveMatrix(Matrix4x4<Type>& matrix, Type fov, Type aspect, Type znear, Type zfar) noexcept
{
const Type yScale = static_cast<Type>(1.0 / tan(RADIANS_PER_DEGREE * fov / 2));
const Type xScale = yScale / aspect;
const Type difference = znear - zfar;
matrix = {
xScale, 0, 0, 0,
0, yScale, 0, 0,
0, 0, (zfar + znear) / difference, 2 * zfar * znear / difference,
0, 0, -1, 0
};
}
Matrix multiplication implementation:
// static const std::uint8_t ROW_SIZE = 4;
// static const std::uint8_t MATRIX_SIZE = ROW_SIZE * ROW_SIZE;
// static const std::uint8_t FIRST_ROW = 0;
// static const std::uint8_t SECOND_ROW = ROW_SIZE;
// static const std::uint8_t THIRD_ROW = ROW_SIZE + ROW_SIZE;
// static const std::uint8_t FOURTH_ROW = ROW_SIZE + ROW_SIZE + ROW_SIZE;
template<class Type>
void Matrix4x4<Type>::mul(const Matrix4x4& anotherMatrix) noexcept
{
Type currentElements[MATRIX_SIZE];
std::copy(std::begin(mElements), std::end(mElements), currentElements);
const Type* otherElements = anotherMatrix.mElements;
for (std::uint8_t i = 0; i < MATRIX_SIZE; i += ROW_SIZE)
{
mElements[i] = currentElements[i] * otherElements[FIRST_ROW] +
currentElements[i + 1] * otherElements[SECOND_ROW] +
currentElements[i + 2] * otherElements[THIRD_ROW] +
currentElements[i + 3] * otherElements[FOURTH_ROW];
mElements[i + 1] = currentElements[i] * otherElements[FIRST_ROW + 1] +
currentElements[i + 1] * otherElements[SECOND_ROW + 1] +
currentElements[i + 2] * otherElements[THIRD_ROW + 1] +
currentElements[i + 3] * otherElements[FOURTH_ROW + 1];
mElements[i + 2] = currentElements[i] * otherElements[FIRST_ROW + 2] +
currentElements[i + 1] * otherElements[SECOND_ROW + 2] +
currentElements[i + 2] * otherElements[THIRD_ROW + 2] +
currentElements[i + 3] * otherElements[FOURTH_ROW + 2];
mElements[i + 3] = currentElements[i] * otherElements[FIRST_ROW + 3] +
currentElements[i + 1] * otherElements[SECOND_ROW + 3] +
currentElements[i + 2] * otherElements[THIRD_ROW + 3] +
currentElements[i + 3] * otherElements[FOURTH_ROW + 3];
}
}
Cross product implementation:
template<typename Type>
Math::Vector3<Type> cross(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
const Type x = vector.getY()*anotherVector.getZ() - vector.getZ()*anotherVector.getY();
const Type y = -(vector.getX()*anotherVector.getZ() - vector.getZ()*anotherVector.getX());
const Type z = vector.getX()*anotherVector.getY() - vector.getY()*anotherVector.getX();
return { x, y, z };
}
Using it:
// OpenGL
glUseProgram(mProgramID);
Matrix4x4f lookAt;
setLookAt(lookAt, { 0.0f, 0.0f, 3.0f }, { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f });
glUniformMatrix4fv(glGetAttribLocation(mProgramID, "viewMatrix"), 1, GL_TRUE, lookAt);
Matrix4x4f projection;
setPerspectiveMatrix(projection, 45.0f, width / height, -0.1, 100.0f);
glUniformMatrix4fv(glGetAttribLocation(mProgramID, "projectionMatrix "), 1, GL_TRUE, projection);
// GLSL
layout (location = 0) in vec3 position;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
void main()
{
gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0f);
}
After using this code, I get a blank screen, although I would have to draw a cube. The problem is in the matrix itself, so other matrices work fine(offset, rotation, ...), but I can understand exactly where. Can you tell me what could be the problem?
"projectionMatrix" and "viewMatrix" are uniform variables. The uniform location can be get by glGetUniformLocation rather than glGetAttribLocation, which would return the attribute index of an active attribute:
GLint projLoc = glGetUniformLocation( mProgramID, "projectionMatrix" );
GLint viewLoc = glGetUniformLocation( mProgramID, "viewMatrix" );
At Perspective Projection the projection matrix describes the mapping from 3D points in the world as they are seen from of a pinhole camera, to 2D points of the viewport.
The eye space coordinates in the camera frustum (a truncated pyramid) are mapped to a cube (the normalized device coordinates).
At perspective projection the view space (volume) is defined by a frustum (a truncated pyramid), where the top of the pyramid is the viewer's position.
The direction of view (line of sight) and the near and the far distance define the planes which truncated the pyramid to a frustum (the direction of view is the normal vector of this planes).
This means both values, the distance to the near plane and the distance to the far plane have to be positive values:
Matrix4x4f lookAt;
setLookAt(lookAt, { 0.0f, 0.0f, 3.0f }, { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f });
glUniformMatrix4fv(viewLoc, 1, GL_TRUE, lookAt);
Matrix4x4f projection;
setPerspectiveMatrix(projection, 45.0f, width / height, 0.1f, 100.0f); // 0.1f instead of -0.1f
glUniformMatrix4fv(projLoc, 1, GL_TRUE, projection);
The view space is the local system which is defined by the point of view onto the scene.
The position of the view, the line of sight and the upwards direction of the view, define a coordinate system relative to the world coordinate system.
The view matrix has to transform from world space to view space, so the view matrix is the inverse matrix of the view coordinate system.
If the coordinate system of the view space is a Right-handed system, where the X-axis points to the left and the Y-axis points up, then the Z-axis points out of the view (Note in a right hand system the Z-Axis is the cross product of the X-Axis and the Y-Axis).
The z-axis line of sight is the vector from the point of view eye to the traget center:
template<typename Type>
void setLookAt(Matrix4x4<Type>& matrix, const Vector3<Type> eye, const Vector3<Type> center, const Vector3<Type> up) noexcept
{
Vector3f mz( { eye.getX()-center.getX(), eye.getY()-center.getY(), eye.getZ()-center.getZ() } );
mz = mz.normalize();
Vector3f my = up.normalize();
Vector3f mx = cross(my, mz).normalize();
Type tx = dot( mx, eye );
Type ty = dot( my, eye );
Type tz = -dot( mz, eye );
matrix = {
mx.getX(), mx.getY(), mx.getZ(), tx,
my.getX(), my.getY(), my.getZ(), ty,
mz.getX(), mz.getY(), mz.getZ(), tz,
0.0, 0.0, 0.0, 1.0
};
}
template<typename Type>
Vector3<Type> cross(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
const Type x = vector.getY()*anotherVector.getZ() - vector.getZ()*anotherVector.getY();
const Type y = -(vector.getX()*anotherVector.getZ() - vector.getZ()*anotherVector.getX());
const Type z = vector.getX()*anotherVector.getY() - vector.getY()*anotherVector.getX();
return { x, y, z };
}
template<typename Type>
Vector3<Type> Vector3<Type>::normalize(void) const
{
Type len = std::sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
return { mV[0] / len, mV[1] / len, mV[2] / len };
}
template<typename Type>
Type dot(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
Type ax = vector.getX(), ay = vector.getY(), az = vector.getZ();
Type bx = anotherVector.getX(), by = anotherVector.getY(), bz = anotherVector.getZ();
return ax*bx + ay*by + az*bz;
}
A perspective projection matrix can be defined by a frustum.
The distances left, right, bottom and top, are the distances from the center of the view to the side faces of the frustum, on the near plane. near and far specify the distances to the near and far plane on the frustum.
r = right, l = left, b = bottom, t = top, n = near, f = far
x y z t
2*n/(r-l) 0 (r+l)/(r-l) 0
0 2*n/(t-b) (t+b)/(t-b) 0
0 0 -(f+n)/(f-n) -2*f*n/(f-n)
0 0 -1 0
If the projection is symmetric, where the line of sight is axis of symmetry of the view frustum, then the matrix can be simplified:
x y z t
1/(ta*a) 0 0 0
0 1/ta 0 0
0 0 -(f+n)/(f-n) -2*f*n/(f-n)
0 0 -1 0
where:
a = w / h
ta = tan( fov_y / 2 );
2 * n / (r-l) = 1 / (ta * a)
2 * n / (t-b) = 1 / ta
Further the projection matrix switches from an right-handed system to an left-handed system, because the z axis is turned.
template<typename Type>
void setPerspectiveMatrix(Matrix4x4<Type>& matrix, Type fov, Type aspect, Type znear, Type zfar) noexcept
{
const Type yScale = static_cast<Type>(1.0 / tan(RADIANS_PER_DEGREE * fov / 2));
const Type xScale = yScale / aspect;
const Type difference = zfar - znear;
matrix = {
xScale, 0, 0, 0,
0, yScale, 0, 0,
0, 0, -(zfar + znear) / difference, -2 * zfar * znear / difference,
0, 0, -1, 0
};
}
So I watched a little intro course on youtube to learn the basics of OpenGL and learnt things like making a triangle and a simple camera class, etc. I've wanted to try and work towards making a voxel engine so obviously the first thing that I thought to make was a simple cube that I could eventually replicate. My problem is though that when I go to render the vertices and triangles they seem to be in a mess that doesn't resemble what I hard coded in the cube class. I know that 0,0 is the centre of the screen; 1 in the x axis is the right; -1 is the left; 1 in the y axis is the top and -1 is the bottom. Yet when I send through my vertices and triangles to the vertex buffer it seems to be doing something completely different. It's most likely a really stupid mistake on my part.
Cube::Cube()
{
m_vertices[0] = Vertex(glm::vec3(-0.5, -0.5, 0));
m_vertices[1] = Vertex(glm::vec3(-0.5, 0.5, 0));
m_vertices[2] = Vertex(glm::vec3(0.5, 0.5, 0));
m_vertices[3] = Vertex(glm::vec3(0.5, -0.5, 0));
m_vertices[4] = Vertex(glm::vec3(-0.5, -0.5, 1));
m_vertices[5] = Vertex(glm::vec3(-0.5, 0.5, 1));
m_vertices[6] = Vertex(glm::vec3(0.5, 0.5, 1));
m_vertices[7] = Vertex(glm::vec3(0.5, -0.5, 1));
m_triangles[0] = Triangle(0, 1, 2); //Front
//m_triangles[1] = Triangle(0, 2, 3); //Front
//m_triangles[2] = Triangle(1, 5, 6); //Top
//m_triangles[3] = Triangle(1, 6, 2); //Top
//m_triangles[4] = Triangle(3, 5, 4); //Left
//m_triangles[5] = Triangle(3, 5, 4); //Left
//m_triangles[6] = Triangle(3, 2, 7); //Right
//m_triangles[7] = Triangle(3, 3, 7); //Right
//m_triangles[8] = Triangle(7, 6, 4); //Back
//m_triangles[9] = Triangle(5, 6, 7); //Back
//m_triangles[10] = Triangle(0, 4, 7); //Bottom
//m_triangles[11] = Triangle(0, 3, 7); //Bottom
}
void Cube::Render()
{
Draw(m_vertices, sizeof(m_vertices) / sizeof(m_vertices[0]), m_triangles, (sizeof(m_triangles) / sizeof(m_triangles[0])));
}
The draw function inherited from my mesh class
void Mesh::Draw(Vertex* vertices, unsigned int numVertices, Triangle* triangles, unsigned int numTriangles)
{
//Array of indices
std::vector<unsigned int> indices;
for (int i = 0; i < numTriangles; i++)
{
indices.push_back(triangles[i].GetTriangle()[0]);
indices.push_back(triangles[i].GetTriangle()[1]);
indices.push_back(triangles[i].GetTriangle()[2]);
}
//How many vertices to draw
m_drawCount = indices.size();
//Generate and bind vertex array
glGenVertexArrays(1, &m_vertexArrayObject);
glBindVertexArray(m_vertexArrayObject);
//Generate and bind buffers
glGenBuffers(NUM_BUFFERS, m_vertexArrayBuffers);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[POSITION_VB]);
//Write vertex data to the buffer
glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(vertices[0]), &vertices[0], GL_STATIC_DRAW);
//Only one attribute for the vertex data
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vertexArrayBuffers[INDEX_VB]);
//Write vertex data to the buffer
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), &indices[0], GL_STATIC_DRAW);
//Unbind vertex array
glBindVertexArray(0);
glBindVertexArray(m_vertexArrayObject);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawElements(GL_TRIANGLES, m_drawCount, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
Vertex and Triangle structs in mesh.h
struct Vertex
{
public:
//Constructor
Vertex()
{
}
//Constructor
Vertex(const glm::vec3& pos)
{
//Set vertex position
this->m_pos = pos;
}
protected:
private:
//Vertex position
glm::vec3 m_pos;
};
struct Triangle
{
public:
//Constructor
Triangle()
{
}
//Constructor
Triangle(int point1, int point2, int point3)
{
SetTriangle(point1, point2, point3);
}
int* GetTriangle()
{
return m_points;
}
void SetTriangle(int point1, int point2, int point3)
{
m_points[0] = point1;
m_points[1] = point2;
m_points[2] = point3;
}
protected:
private:
int m_points[3];
};
Camera functions
Camera::Camera(const glm::vec3 pos, float fov, float aspect, float zNear, float zFar)
{
m_perspectiveMatrix = glm::perspective(fov, aspect, zNear, zFar);
m_pos = pos;
m_forward = glm::vec3(0, 0, 1);
m_up = glm::vec3(0, 1, 0);
}
glm::mat4 Camera::GetViewProjection() const
{
return m_perspectiveMatrix * glm::lookAt(m_pos, m_pos + m_forward, m_up);
}
Note that in the cube constructor I'm only creating one triangle which should be bottom left, top left, top right yet this is the result:
Another note is that my camera rotation seems to be off as well. Changing the y rotation actually rotates it on the x axis and the changing the x rotation rotates on the y axis.
Also if anyone had a better way of creating and rendering the cube I would be grateful. Once I can do that I'll most likely look into letsmakeavoxelengine tutorials.
Edit: It feels like the x and y axis are inverted. I could just invert all my functions to counter that but that's kind of a hacky way around it and it still doesn't fix the underlying issue which could cause more trouble later on.
Edit2: Transform.h
#pragma once
#include <glm\glm.hpp>
#include <glm\gtx\transform.hpp>
#include "Camera.h"
struct Transform
{
public:
//Constructor
Transform(const glm::vec3& pos = glm::vec3(), const glm::vec3& rot = glm::vec3(), const glm::vec3& scale = glm::vec3(1.0f, 1.0f, 1.0f))
{
this->m_pos = pos;
this->m_rot = rot;
this->m_scale = scale;
}
//Get the model matrix
inline glm::mat4 GetModelMatrix() const
{
//Create all the transform matrices
//Position matrix
glm::mat4 posMatrix = glm::translate(m_pos);
//Scale matrix
glm::mat4 scaleMatrix = glm::scale(m_scale);
//Rotation matrix X
glm::mat4 rotXMatrix = glm::rotate(m_rot.x, glm::vec3(1.0f, 0.0f, 0.0f));
//Rotation matrix Y
glm::mat4 rotYMatrix = glm::rotate(m_rot.y, glm::vec3(0.0f, 1.0f, 0.0f));
//Rotation matrix Z
glm::mat4 rotZMatrix = glm::rotate(m_rot.z, glm::vec3(0.0f, 0.0f, 1.0f));
//Combined rotation matrix
glm::mat4 rotMatrix = rotXMatrix * rotYMatrix * rotZMatrix;
return posMatrix * rotMatrix * scaleMatrix;
}
inline glm::mat4 GetMVP(const Camera& camera) const
{
glm::mat4 ViewProjection = camera.GetViewProjection();
glm::mat4 ModelMatrix = GetModelMatrix();
return ViewProjection * ModelMatrix;//camera.GetViewProjection() * GetModel();
}
//Get position
inline glm::vec3* GetPosition() { return &m_pos; }
//Get rotation
inline glm::vec3* GetRotation() { return &m_rot; }
//Get scale
inline glm::vec3* GetScale() { return &m_scale; }
//Set Position
inline void SetPosition(const glm::vec3& pos) { this->m_pos = pos; }
//Set Rotation
inline void SetRotation(const glm::vec3& rot) { this->m_rot = rot; }
//Set Scale
inline void SetScale(const glm::vec3& scale) { this->m_scale = scale; }
private:
//Transform position
glm::vec3 m_pos;
//Transform rotation
glm::vec3 m_rot;
//Transform scale
glm::vec3 m_scale;
};
Cube, Transform and Camera calls in main.cpp:
Cube cube;
Transform transform;
Camera camera(glm::vec3(0.0f, 0.0f, -3.0f), 70.0f, (float)display.GetWidth()/(float)display.GetHeight(), 0.01f, 100.0f);
Edit3: 100% inverted on X Axis. New cube.cpp code:
m_vertices[0] = Vertex(glm::vec3(-0.5, -0.5, 0)); //BottomLeftFront
m_vertices[1] = Vertex(glm::vec3(-0.5, 0.5, 0)); //TopLeftFront
m_vertices[2] = Vertex(glm::vec3(0.5, 0.5, 0)); //TopRightFront
m_vertices[3] = Vertex(glm::vec3(0.5, -0.5, 0)); //BottomRightFront
m_vertices[4] = Vertex(glm::vec3(-0.5, -0.5, 1)); //BottomLeftBack
m_vertices[5] = Vertex(glm::vec3(-0.5, 0.5, 1)); //TopLeftBack
m_vertices[6] = Vertex(glm::vec3(0.5, 0.5, 1)); //TopRightBack
m_vertices[7] = Vertex(glm::vec3(0.5, -0.5, 1)); //BottomRightBack
m_triangles[0] = Triangle(0, 1, 2); //Front
m_triangles[1] = Triangle(0, 2, 3); //Front
//m_triangles[2] = Triangle(1, 5, 6); //Top
//m_triangles[3] = Triangle(1, 6, 2); //Top
m_triangles[4] = Triangle(3, 5, 4); //Left //BottomLeftFront, TopRightBack, BottomRightBack
//m_triangles[5] = Triangle(3, 5, 4); //Left
//m_triangles[6] = Triangle(3, 2, 7); //Right
//m_triangles[7] = Triangle(3, 3, 7); //Right
//m_triangles[8] = Triangle(7, 6, 4); //Back
//m_triangles[9] = Triangle(5, 6, 7); //Back
//m_triangles[10] = Triangle(0, 4, 7); //Bottom
//m_triangles[11] = Triangle(0, 3, 7); //Bottom
I've put a comment next to the new triangle that tells you what the actual resulting triangle points were. The triangle that I set should have been BottomRightFront, TopLeftBack, BottomLeftBack according to the code. I'll also add a screenshot of what it looks like.
Your description of the X and Y axes sounds right, but the Z axis seems to be reversed. For example, in the code you posted there's a variable m_forward whose value is (0, 0, 1); that could be right, but I usually call that direction "backwards".
Conventionally, OpenGL programs use a right-handed coordinate system, so if X points right and Y points up then Z points out of the screen, towards the eye. If you keep that in mind and review your code, checking the sign of the Z component of each position and direction vector, you should find the mistake(s). Good luck!
Using Windows7, VS2013, Qt 5.6, OpenGL 4.4
The code in question is as follows
void TestClass::paintGL()
{
/*
* Clear the screen
*/
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glClearColor(0, 0.25, 1, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glClearDepth(0.0);
glDepthFunc(GL_LEQUAL);
GLenum error = glGetError();
if (error != GL_NO_ERROR)
{
int i = 0;
i++;
}
/*
*/
//*************************************************
//double xx = mRotation.x() * mRotation.x();
//double xy = mRotation.x() * mRotation.y();
//double xz = mRotation.x() * mRotation.z();
//double xw = mRotation.x() * mRotation.scalar();
//double yy = mRotation.y() * mRotation.y();
//double yz = mRotation.y() * mRotation.z();
//double yw = mRotation.y() * mRotation.scalar();
//double zz = mRotation.z() * mRotation.z();
//double zw = mRotation.z() * mRotation.scalar();
//double m00 = 1 - 2 * (yy + zz);
//double m01 = 2 * (xy - zw);
//double m02 = 2 * (xz + yw);
//double m03 = 0;
//double m10 = 2 * (xy + zw);
//double m11 = 1 - 2 * (xx + zz);
//double m12 = 2 * (yz - xw);
//double m13 = 0;
//double m20 = 2 * (xz - yw);
//double m21 = 2 * (yz + xw);
//double m22 = 1 - 2 * (xx + yy);
//double m23 = 0;
//double m30 = 0;
//double m31 = 0;
//double m32 = -30;
//double m33 = 1;
//double blarg[] = {m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33};
//glLoadMatrixd(&blarg[0]);
//*************************************************
QMatrix4x4 yaw;
yaw.rotate(mYPR.x(), QVector3D(0, -1, 0));
QMatrix4x4 pitch;
pitch.rotate(mYPR.y(), QVector3D(1, 0, 0));
QMatrix4x4 roll;
roll.rotate(mYPR.z(), QVector3D(0, 0, -1));
QMatrix4x4 translate;
translate.translate(0, 0, -30);
//QMatrix4x4 vnToOpenGL(0, 0, -1, 0,
// 1, 0, 0, 0,
// 0, -1, 0, 0,
// 0, 0, 0, 1);
QMatrix4x4 vnToOpenGL(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
QMatrix4x4 openGLMatrix = vnToOpenGL * translate * yaw * pitch * roll * vnToOpenGL.transposed();
glLoadMatrixf(reinterpret_cast<const float*>(openGLMatrix.constData()));
int depth;
glGetIntegerv(GL_DEPTH_BITS, &depth);
QString ytext(QString("yaw : ") + QString::number(mYPR.x()));
QString ptext(QString("pitch : ") + QString::number(mYPR.y()));
QString rtext(QString("roll : ") + QString::number(mYPR.z()));
QString dtext(QString("depth : ") + QString::number(depth));
QPainter painter(this);
painter.drawText(10, 10, ytext);
painter.drawText(10, 25, ptext);
painter.drawText(10, 40, rtext);
painter.drawText(10, 55, dtext);
painter.end();
//*************************************************
//double scalar = std::acos(mRotation.scalar()) * 2;
//double X = std::asin(mRotation.x()) * 2;
//double Y = std::asin(mRotation.y()) * 2;
//double Z = std::asin(mRotation.z()) * 2;
//glRotated(scalar * radToDeg, X * radToDeg, Y * radToDeg, Z * radToDeg);
glScaled(mScale, mScale, mScale);
QVector3D orthogonalPoint;
QVector3D orthogonalVector;
QVector3D currentNormal;
for (size_t index = 0; index < mSphere->getTriangles().size(); index++)
{
// BEGIN CODE IN QUESTION
glEnable(GL_DEPTH_TEST);
//glDepthMask(GL_TRUE);
//glClearDepth(0.0);
//glDepthFunc(GL_LEQUAL);
// END CODE IN QUESTION
currentNormal = mSphere->getTriangles()[index].getNormal();
orthogonalVector = QVector3D::crossProduct(QVector3D(currentNormal.x(), currentNormal.y(), currentNormal.z()),
QVector3D(currentNormal.x(), currentNormal.y(), 0));
orthogonalVector.normalize();
glLineWidth(1.0);
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
glVertex3f(0, 0, 0);
glVertex3f(currentNormal.x(), currentNormal.y(), currentNormal.z());
glEnd();
glLineWidth(3.0);
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_LINE_STRIP);
glVertex3f(0, 0, 0);
glVertex3f(0, 0, -1);
glEnd();
glLineWidth(3.0);
glColor3f(0.0, 1.0, 0.0);
glBegin(GL_LINE_STRIP);
glVertex3f(0, 0, 0);
glVertex3f(1, 0, 0);
glEnd();
glLineWidth(3.0);
glColor3f(0.0, 1.0, 1.0);
glBegin(GL_LINE_STRIP);
glVertex3f(0, 0, 0);
glVertex3f(0, 1, 0);
glEnd();
glColor3f(0.0, 0.0, 0.0);
glBegin(GL_TRIANGLE_FAN);
glVertex3d(currentNormal.x(), currentNormal.y(), currentNormal.z());
for (size_t fanIndex = 0; fanIndex < 9; ++fanIndex)
{
double degrees = static_cast<double>(fanIndex) * (360.0 / mTargetTesselations);
QMatrix4x4 matrix;
matrix.rotate(degrees, currentNormal.x(), currentNormal.y(), currentNormal.z());
orthogonalPoint = (orthogonalVector * matrix) / 20.0;
glVertex3d(currentNormal.x() + orthogonalPoint.x(),
currentNormal.y() + orthogonalPoint.y(),
currentNormal.z() + orthogonalPoint.z());
}
glEnd();
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_TRIANGLE_FAN);
glVertex3d(currentNormal.x(), currentNormal.y(), currentNormal.z());
for (size_t fanIndex = 0; fanIndex < 9; ++fanIndex)
{
double degrees = 360.0 - (static_cast<double>(fanIndex) * (360.0 / mTargetTesselations));
QMatrix4x4 matrix;
matrix.rotate(degrees, currentNormal.x(), currentNormal.y(), currentNormal.z());
orthogonalPoint = (orthogonalVector * matrix) / 20.0;
glVertex3d(currentNormal.x() + orthogonalPoint.x(),
currentNormal.y() + orthogonalPoint.y(),
currentNormal.z() + orthogonalPoint.z());
}
glEnd();
}
/*
* Don't forget about the model-view matrix
*/
glPopMatrix();
}
I had bee searching basically all night to figure out why the depth test was not working. Things in the distance were being drawn over the things in the foreground. Finally, after writing halfway through a question here on stackoverflow I placed the code
glEnable(GL_DEPTH_TEST);
//glDepthMask(GL_TRUE);
//glClearDepth(0.0);
//glDepthFunc(GL_LEQUAL);
at the start of my for loop in the paintGL function. Low and behold the depth was correct. So, now that I have it working I'd really like to know WHY it is working. Why does this work properly because I enabled 'GL_DEPTH_TEST' in the loop? If I don't manually change it shouldn't the state remain the same? I'd like to know because knowing is half the battle.
Im trying to rotate my cube using quaternion to matrix rotation based on arcball mouse movement. My cube is rendering, and it has movement/rotation, but it isnt simply just rotating around an axis, it is also moving in the direction of my mouse, so i think that either the data that im getting from my trackball is a bit off, or the way im transforming my quaternion into a rotation matrix is slightly off. trackball_ptov is where i translate mouse location to an arcball. mouseMotion() is where im creating the rotation matrix from the quaternion.
The entirety of my main:
#include "Angel.h"
#include <gl/glew.h>
#include <glut.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
using namespace std;
#define bool int /* if system does not support bool type */
#define false 0
#define true 1
#define M_PI 3.14159265358 /* if not in math.h */
int winWidth, winHeight;
float angle = 0.0, axis[3], trans[3];
bool trackingMouse = false;
float lastPos[3] = {0.0, 0.0, 0.0};
float aspect = 1.0;
int curx, cury;
int startX, startY;
int modelInit=1;
//the program object
GLuint program = 0;
glm::mat4 model;
typedef Angel::vec4 color4;
typedef Angel::vec4 point4;
const int NumVertices = 36; //(6 faces)(2 triangles/face)(3 vertices/triangle)
point4 points[NumVertices];
color4 colors[NumVertices];
enum { Xaxis = 0, Yaxis = 1, Zaxis = 2, NumAxes = 3 };
int Axis = Xaxis;
GLuint mvpUniform;
GLuint shaderaxis;
int Index = 0;
// Vertices of a unit cube centered at origin, sides aligned with axes
point4 vertices[8]={
point4(-0.5, -0.5, 0.5, 1.0),
point4(-0.5, 0.5, 0.5, 1.0),
point4(0.5, 0.5, 0.5, 1.0),
point4(0.5, -0.5, 0.5, 1.0),
point4(-0.5, -0.5, -0.5, 1.0),
point4(-0.5, 0.5, -0.5, 1.0),
point4(0.5, 0.5, -0.5, 1.0),
point4(0.5, -0.5, -0.5, 1.0)
};
// RGBA colors
color4 vertex_colors[8] = {
color4(0.0, 0.0, 0.0, 1.0), // black
color4(1.0, 0.0, 0.0, 1.0), // red
color4(1.0, 1.0, 0.0, 1.0), // yellow
color4(0.0, 1.0, 0.0, 1.0), // green
color4(0.0, 0.0, 1.0, 1.0), // blue
color4(1.0, 0.0, 1.0, 1.0), // magenta
color4(1.0, 1.0, 1.0, 1.0), // white
color4(0.0, 1.0, 1.0, 1.0) // cyan
};
// quad generates two triangles for each face and assigns colors to the vertices
void quad(int a, int b, int c, int d) {
colors[Index] = vertex_colors[a]; points[Index] = vertices[a]; Index++;
colors[Index] = vertex_colors[b]; points[Index] = vertices[b]; Index++;
colors[Index] = vertex_colors[c]; points[Index] = vertices[c]; Index++;
colors[Index] = vertex_colors[a]; points[Index] = vertices[a]; Index++;
colors[Index] = vertex_colors[c]; points[Index] = vertices[c]; Index++;
colors[Index] = vertex_colors[d]; points[Index] = vertices[d]; Index++;
}
// generate 12 triangles: 36 vertices and 36 colors
void colorcube(void) {
quad(1, 0, 3, 2);
quad(2, 3, 7, 6);
quad(3, 0, 4, 7);
quad(6, 5, 1, 2);
quad(4, 5, 6, 7);
quad(5, 4, 0, 1);
}
// OpenGL initialization
void init(void) {
colorcube();
// Load shaders and use the resulting shader program
GLuint program = InitShader("vshader36.glsl", "fshader36.glsl");
glUseProgram(program);
// Create a vertex array object
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// Create and initialize a buffer object
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(points) + sizeof(colors), NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(points), points);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(points), sizeof(colors), colors);
// set up vertex arrays
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
model = glm::translate(glm::mat4(1.0), glm::vec3(0.0, 0.0, -5.0));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(sizeof(points)));
mvpUniform = glGetUniformLocation(program, "mvp");
glEnable(GL_DEPTH_TEST);
glClearColor(1.0, 1.0, 1.0, 1.0);
}
void trackball_ptov(int x, int y, int width, int height, float v[3]) {
float d, a;
/* project x, y onto a hemisphere centered within width, height , note z is up here*/
v[0] = (2.0*x - width) / width;
v[1] = (height - 2.0F*y) / height;
d = sqrt(v[0]*v[0] + v[1]*v[1]);
v[2] = cos((M_PI/2.0) * ((d < 1.0) ? d : 1.0));
a = 1.0 / sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
v[0] *= a;
v[1] *= a;
v[2] *= a;
}
void display() {
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glm::mat4 proj = glm::perspective(90.F, 1.F, 0.1F, 100.F);
glm::mat4 view = glm::translate(glm::mat4(1.0), glm::vec3(0.0, 0.0, 0.0)); //apply mouse translation
//view = glm::rotate(view, 0.2*mousediff.x, glm::vec3(0.0, 1.0, 0.0)); //apply mouse rotation
//view = glm::rotate(view, 0.2*mousediff.y, glm::vec3(1.0, 0.0, 0.0));
//model = glm::translate(glm::mat4(1.0), glm::vec3(0.0, 0.0, -5.0));
//glUniformMatrix4fv(mvpUniform, 1, false, glm::value_ptr(proj));
glUniformMatrix4fv(mvpUniform, 1, false, glm::value_ptr(proj*view*model));
//glUniformMatrix4fv(mvpUniform, 1, false, glm::value_ptr(model*view*proj));
glDrawArrays(GL_TRIANGLES, 0, NumVertices);
glutSwapBuffers();
}
void keyboard(unsigned char key, int x, int y) {
switch(key) {
case 033: // Escape Key
case 'q': case 'Q':
exit(EXIT_SUCCESS);
break;
}
}
void mouseButton(int button, int state, int x, int y) {
if(button==GLUT_RIGHT_BUTTON) exit(0);
/* holding down left button allows user to rotate cube */
if(button==GLUT_LEFT_BUTTON)
switch(state) {
case GLUT_DOWN:
trackingMouse = true;
startX = x;
startY = y;
curx = x;
cury = y;
trackball_ptov(x, y, 512, 512, lastPos);
break;
case GLUT_UP:
trackingMouse = false;
angle = 0.0;
break;
}
}
void mouseMotion(int x, int y) {
float curPos[3], dx, dy, dz;
/* compute position on hemisphere */
if(trackingMouse) {
/* compute the change in position on the hemisphere */
trackball_ptov(x, y, 512, 512, curPos);
dx = curPos[0] - lastPos[0];
dy = curPos[1] - lastPos[1];
dz = curPos[2] - lastPos[2];
if (dx || dy || dz) {
/* compute theta and cross product */
angle = 90.0 * sqrt(dx*dx + dy*dy + dz*dz);
axis[0] = lastPos[1]*curPos[2] - lastPos[2]*curPos[1];
axis[1] = lastPos[2]*curPos[0] - lastPos[0]*curPos[2];
axis[2] = lastPos[0]*curPos[1] - lastPos[1]*curPos[0];
/* update position */
lastPos[0] = curPos[0];
lastPos[1] = curPos[1];
lastPos[2] = curPos[2];
}
float w = angle;
float x = axis[0];
float y = axis[1];
float z = axis[2];
glm::mat4 xform = glm::mat4((1.F - (2.F * ( y*y + z*z ))),(2.F * ( x*y - z*w )),( x*z + y*w ),0.F,
(2.F * ( x*y + z*w )),(1.F - (2.F * ( x*x + z*z ))),(2.F * ( y*z - x*w )),0.F,
(2.F * ( x*z - y*w )),(2.F * ( y*z + x*w )),(1.F - (2.F * ( x*x + y*y ))),0.F,
0.F,0.F,0.F,1.F);
model = xform*model;
}
glutPostRedisplay();
}
int main(int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(512, 512);
glutCreateWindow("Color Cube");
glewInit();
init();
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutIdleFunc(display);
glutMouseFunc(mouseButton);
glutMotionFunc(mouseMotion);
glutMainLoop();
return 0;
}
Here is the fshader36
#version 150
in vec4 color;
out vec4 fColor;
void main()
{
fColor = color;
}
here is VSHADER
#version 330
uniform mat4 mvp;
layout(location=0) in vec4 vPosition;
layout(location=1) in vec4 vColor;
out vec4 color;
void main() {
color = vColor;
gl_Position = mvp * vPosition;
}