Related
I'm trying to draw in OpenGL 2 vectors with a given angle (in radians) between them, something like this:
I managed to draw the vectors but I'm not sure how to place them at the specific angle:
glBegin(GL_LINES); // Vx
glColor4f(1, .5, 0, 1);
glVertex3f(0, 0, 0);
glVertex3f(0, vectorYRScalingValue, 0); // vectorYRScalingValue is 5.0
glEnd();
glBegin(GL_LINES); // Vy
glColor4f(1, .5, 0, 1);
glVertex3f(0, 0, 0);
glVertex3f(0, vectorYRScalingValue, 0);
glEnd();
If β is the angle to be rotated in radians.
We rotate this vector anticlockwise around the origin.
float c = cos(β);
float s = sin(β);
NewX = x * c - y * s;
NewY = x * s + y * c;
I performed an MVP transformation on the vertices of the model. In theory, I must apply the inverse transpose matrix of the MVP transformation to the normal.
This is the derivation process:
(A, B, C) is the normal of the plane where the point (x, y, z) lies
For a vector, such as (x0, y0, z0), it is (x0, y0, z0, 0) in homogeneous coordinates. After transformation, it should still be a vector, like (x1, y1, z1, 0), This requires that the last row of the 4 * 4 transformation matrix is all 0 except for the elements in the last column, otherwise it will become (x1, y1, z1, n) after the transformation.
In fact, my MVP transformation matrix cannot satisfy this point after undergoing inverse transpose transformation.
Code:
Mat<4, 4> View(const Vec3& pos){
Mat<4, 4> pan{1, 0, 0, -pos.x,
0, 1, 0, -pos.y,
0, 0, 1, -pos.z,
0, 0, 0, 1};
Vec3 v = Cross(camera.lookAt, camera.upDirection).Normalize();
Mat<4, 4> rotate{v.x, v.y, v.z, 0,
camera.upDirection.x, camera.upDirection.y, camera.upDirection.z, 0,
-camera.lookAt.x, -camera.lookAt.y, -camera.lookAt.z, 0,
0, 0, 0, 1};
return rotate * pan;
}
Mat<4, 4> Projection(double near, double far, double fov, double aspectRatio){
double angle = fov * PI / 180;
double t = -near * tan(angle / 2);
double b = -t;
double r = t * aspectRatio;
double l = -r;
Mat<4, 4> zoom{2 / (r - l), 0, 0, 0,
0, 2 / (t - b), 0, 0,
0, 0, 2 / (near - far), 0,
0, 0, 0, 1};
Mat<4, 4> pan{1, 0, 0, -(l + r) / 2,
0, 1, 0, -(t + b) / 2,
0, 0, 1, -(near + far) / 2,
0, 0, 0, 1};
Mat<4, 4> extrusion{near, 0, 0, 0,
0, near, 0, 0,
0, 0, near + far, -near * far,
0, 0, 1, 0};
Mat<4, 4> ret = zoom * pan * extrusion;
return ret;
}
Mat<4, 4> modelMatrix = Mat<4, 4>::identity();
Mat<4, 4> viewMatrix = View(camera.position);
Mat<4, 4> projectionMatrix = Projection(-0.1, -50, camera.fov, camera.aspectRatio);
Mat<4, 4> mvp = projectionMatrix * viewMatrix * modelMatrix;
Mat<4, 4> mvpInverseTranspose = mvp.Inverse().Transpose();
mvp:
-2.29032 0 0.763441 -2.68032e-16
0 -2.41421 0 0
-0.317495 0 -0.952486 2.97455
0.316228 0 0.948683 -3.16228
mvpInverseTranspose:
-0.392957 0 0.130986 0
0 -0.414214 0 0
-4.99 0 -14.97 -4.99
-4.69377 0 -14.0813 -5.01
I seem to understand the problem. The lighting should be calculated in world space, so I only need to apply the inverse transpose matrix of the model transformation to the normal.
I am practicing DirectX 11 following Frank Luna's book.
I have implemented a demo that renders a cube, but the result is not correct.
https://i.imgur.com/2uSkEiq.gif
As I hope you can see from the image (I apologize for the low quality), it seems like the camera is "trapped" inside the cube even when I move it away. There is also a camera frustum clipping problem.
I think the problem is therefore in the definition of the projection matrix.
Here is the cube vertices definition.
std::vector<Vertex> vertices =
{
{XMFLOAT3(-1, -1, -1), XMFLOAT4(1, 1, 1, 1)},
{XMFLOAT3(-1, +1, -1), XMFLOAT4(0, 0, 0, 1)},
{XMFLOAT3(+1, +1, -1), XMFLOAT4(1, 0, 0, 1)},
{XMFLOAT3(+1, -1, -1), XMFLOAT4(0, 1, 0, 1)},
{XMFLOAT3(-1, -1, +1), XMFLOAT4(0, 0, 1, 1)},
{XMFLOAT3(-1, +1, +1), XMFLOAT4(1, 1, 0, 1)},
{XMFLOAT3(+1, +1, +1), XMFLOAT4(0, 1, 1, 1)},
{XMFLOAT3(+1, -1, +1), XMFLOAT4(1, 0, 1, 1)},
};
Here is how I calculate the view and projection matrices.
void TestApp::OnResize()
{
D3DApp::OnResize();
mProj = XMMatrixPerspectiveFovLH(XM_PIDIV4, AspectRatio(), 1, 1000);
}
void TestApp::UpdateScene(float dt)
{
float x = mRadius * std::sin(mPhi) * std::cos(mTheta);
float y = mRadius * std::cos(mPhi);
float z = mRadius * std::sin(mPhi) * std::sin(mTheta);
XMVECTOR EyePosition = XMVectorSet(x, y, z, 1);
XMVECTOR FocusPosition = XMVectorZero();
XMVECTOR UpDirection = XMVectorSet(0, 1, 0, 0);
mView = XMMatrixLookAtLH(EyePosition, FocusPosition, UpDirection);
}
And here is how I update the camera position on mouse move.
glfwSetCursorPosCallback(mMainWindow, [](GLFWwindow* window, double xpos, double ypos)
{
TestApp* app = reinterpret_cast<TestApp*>(glfwGetWindowUserPointer(window));
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS)
{
float dx = 0.25f * XMConvertToRadians(xpos - app->mLastMousePos.x);
float dy = 0.25f * XMConvertToRadians(ypos - app->mLastMousePos.y);
app->mTheta += dx;
app->mPhi += dy;
app->mPhi = std::clamp(app->mPhi, 0.1f, XM_PI - 0.1f);
}
else if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS)
{
float dx = 0.05f * XMConvertToRadians(xpos - app->mLastMousePos.x);
float dy = 0.05f * XMConvertToRadians(ypos - app->mLastMousePos.y);
app->mRadius += (dx - dy);
app->mRadius = std::clamp(app->mRadius, 3.f, 15.f);
}
app->mLastMousePos = XMFLOAT2(xpos, ypos);
});
Thanks.
The root problem here was in the constant buffer vs. CPU update.
HLSL defaults to column-major matrix definitions per Microsoft Docs. DirectXMath uses row-major matrices, so you have to transpose while updating the Constant Buffer.
Alternatively, you can declare the HLSL matrix with the row_major keyword, #pragma pack_matrix, or the /Zpr compiler switch.
I have a rotating plane and a box on it. When the plane rotates and tilts at an angle, I want the box to stay on the same position on the plane.
Here the plane is not tilted,
while here the plane is tilted,
but the box does not follow the plane downwards.
At every update I render the box, and translate it by the given vec3 in glm::translate:
{
glm::mat4 modelMatrix = glm::mat4(1);
modelMatrix = glm::translate(modelMatrix, glm::vec3(boxOX, boxOY, boxOZ));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOX), glm::vec3(1, 0, 0));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOY), glm::vec3(0, 1, 0));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOZ), glm::vec3(0, 0, 1));
modelMatrix = glm::scale(modelMatrix, glm::vec3(0.2f));
RenderSimpleMesh(meshes["box"], shaders["ShaderLab8"], modelMatrix, glm::vec3(1, 0, 0));
}
The plane moves by hitting WASD keys:
if (window->KeyHold(GLFW_KEY_W) && (anglePlaneOX > -90.0f)) {
anglePlaneOX -= deltaTime * DELTA_SLOPE;
}
if (window->KeyHold(GLFW_KEY_S) && (anglePlaneOX < 90.0f)) {
anglePlaneOX += deltaTime * DELTA_SLOPE;
}
if (window->KeyHold(GLFW_KEY_D) && (anglePlaneOZ > -90.0f)) {
anglePlaneOZ -= deltaTime * DELTA_SLOPE;
}
if (window->KeyHold(GLFW_KEY_A) && (anglePlaneOZ < 90.0f)) {
anglePlaneOZ += deltaTime * DELTA_SLOPE;
}
I tried the following
when pressing the A key:
boxOY += deltaTime * (1 - (float)cos((double)anglePlaneOZ * PI / 180));
when pressing the W key:
boxOX -= deltaTime * sinf(anglePlaneOX * PI / 180);
But none of those seem to work.
What are the mathematical relations in order to move the box accordingly to the plane?
You have to do the translation "before" the rotation:
glm::mat4 modelMatrix = glm::mat4(1);
modelMatrix = glm::translate(modelMatrix, glm::vec3(boxOX, boxOY, boxOZ));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOX), glm::vec3(1, 0, 0));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOY), glm::vec3(0, 1, 0));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOZ), glm::vec3(0, 0, 1));
modelMatrix = glm::translate(modelMatrix, glm::vec3(boxOX, boxOY, boxOZ)); // translate here
modelMatrix = glm::scale(modelMatrix, glm::vec3(0.2f));
RenderSimpleMesh(meshes["box"], shaders["ShaderLab8"], modelMatrix, glm::vec3(1, 0, 0));
Explanation:
The translation matrix looks like this:
glm::mat4 translate;
translate[0] : ( 1, 0, 0, 0 )
translate[1] : ( 0, 1, 0, 0 )
translate[2] : ( 0, 0, 1, 0 )
translate[3] : ( tx, ty, tz, 1 )
And the rotation matrix around Y-Axis looks like this:
mat4 rotate;
float angle;
rotate[0] : ( cos(angle), 0, sin(angle), 0 )
rotate[1] : ( 0, 1, 0, 0 )
rotate[2] : ( -sin(angle), 0, cos(angle), 0 )
rotate[3] : ( 0, 0, 0, 1 )
The result of translate * rotate is this:
model[0] : ( cos(angle), 0, sin(angle), 0 )
model[1] : ( 0, 1, 0, 0 )
model[2] : ( -sin(angle), 0, cos(angle), 0 )
model[3] : ( tx, ty, tz, 1 )
Note, the result of rotate * translate would be:
model[0] : ( cos(angle), 0, sin(angle), 0 )
model[1] : ( 0, 1, 0, 0 )
model[2] : ( -sin(angle), 0, cos(angle), 0 )
model[3] : ( cos(angle)*tx - sin(angle)*tx, ty, sin(angle)*tz + cos(angle)*tz, 1 )
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.