Assimp animation bone transformation - animation

Recently I'm working on bone animation import, so I made a 3d minecraft-like model with some IK technique to test Assimp animation import. Ouput format is COLLADA(*.dae),and the tool I used is Blender. On the programming side, my enviroment is opengl/glm/assimp. I think these information for my problem is enough.One thing, the animation of the model, I just record 7 unmove keyframe for testing assimp animation.
First, I guess my transformation except local transform part is correct, so let the function only return glm::mat4(1.0f), and the result show the bind pose(not sure) model. (see below image)
Second, Turn back the value glm::mat4(1.0f) to bone->localTransform = transform * scaling * glm::mat4(1.0f);, then the model deform. (see below image)
Test image and model in blender:
(bone->localTransform = glm::mat4(1.0f) * scaling * rotate; : this image is under ground :( )
The code here:
void MeshModel::UpdateAnimations(float time, std::vector<Bone*>& bones)
{
for each (Bone* bone in bones)
{
glm::mat4 rotate = GetInterpolateRotation(time, bone->rotationKeys);
glm::mat4 transform = GetInterpolateTransform(time, bone->transformKeys);
glm::mat4 scaling = GetInterpolateScaling(time, bone->scalingKeys);
//bone->localTransform = transform * scaling * glm::mat4(1.0f);
//bone->localTransform = glm::mat4(1.0f) * scaling * rotate;
//bone->localTransform = glm::translate(glm::mat4(1.0f), glm::vec3(0.5f));
bone->localTransform = glm::mat4(1.0f);
}
}
void MeshModel::UpdateBone(Bone * bone)
{
glm::mat4 parentTransform = bone->getParentTransform();
bone->nodeTransform = parentTransform
* bone->transform // assimp_node->mTransformation
* bone->localTransform; // T S R matrix
bone->finalTransform = globalInverse
* bone->nodeTransform
* bone->inverseBindPoseMatrix; // ai_mesh->mBones[i]->mOffsetMatrix
for (int i = 0; i < (int)bone->children.size(); i++) {
UpdateBone(bone->children[i]);
}
}
glm::mat4 Bone::getParentTransform()
{
if (this->parent != nullptr)
return parent->nodeTransform;
else
return glm::mat4(1.0f);
}
glm::mat4 MeshModel::GetInterpolateRotation(float time, std::vector<BoneKey>& keys)
{
// we need at least two values to interpolate...
if ((int)keys.size() == 0) {
return glm::mat4(1.0f);
}
if ((int)keys.size() == 1) {
return glm::mat4_cast(keys[0].rotation);
}
int rotationIndex = FindBestTimeIndex(time, keys);
int nextRotationIndex = (rotationIndex + 1);
assert(nextRotationIndex < (int)keys.size());
float DeltaTime = (float)(keys[nextRotationIndex].time - keys[rotationIndex].time);
float Factor = (time - (float)keys[rotationIndex].time) / DeltaTime;
if (Factor < 0.0f)
Factor = 0.0f;
if (Factor > 1.0f)
Factor = 1.0f;
assert(Factor >= 0.0f && Factor <= 1.0f);
const glm::quat& startRotationQ = keys[rotationIndex].rotation;
const glm::quat& endRotationQ = keys[nextRotationIndex].rotation;
glm::quat interpolateQ = glm::lerp(endRotationQ, startRotationQ, Factor);
interpolateQ = glm::normalize(interpolateQ);
return glm::mat4_cast(interpolateQ);
}
glm::mat4 MeshModel::GetInterpolateTransform(float time, std::vector<BoneKey>& keys)
{
// we need at least two values to interpolate...
if ((int)keys.size() == 0) {
return glm::mat4(1.0f);
}
if ((int)keys.size() == 1) {
return glm::translate(glm::mat4(1.0f), keys[0].vector);
}
int translateIndex = FindBestTimeIndex(time, keys);
int nextTranslateIndex = (translateIndex + 1);
assert(nextTranslateIndex < (int)keys.size());
float DeltaTime = (float)(keys[nextTranslateIndex].time - keys[translateIndex].time);
float Factor = (time - (float)keys[translateIndex].time) / DeltaTime;
if (Factor < 0.0f)
Factor = 0.0f;
if (Factor > 1.0f)
Factor = 1.0f;
assert(Factor >= 0.0f && Factor <= 1.0f);
const glm::vec3& startTranslate = keys[translateIndex].vector;
const glm::vec3& endTrabslate = keys[nextTranslateIndex].vector;
glm::vec3 delta = endTrabslate - startTranslate;
glm::vec3 resultVec = startTranslate + delta * Factor;
return glm::translate(glm::mat4(1.0f), resultVec);
}
The code idea is referenced from Matrix calculations for gpu skinning and Skeletal Animation With Assimp.
Overall, I fectch all the information from assimp to MeshModel and save it to the bone structure, so I think the information is alright?
The last thing, my vertex shader code:
#version 330 core
#define MAX_BONES_PER_VERTEX 4
in vec3 position;
in vec2 texCoord;
in vec3 normal;
in ivec4 boneID;
in vec4 boneWeight;
const int MAX_BONES = 100;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 boneTransform[MAX_BONES];
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;
out float Visibility;
const float density = 0.007f;
const float gradient = 1.5f;
void main()
{
mat4 boneTransformation = boneTransform[boneID[0]] * boneWeight[0];
boneTransformation += boneTransform[boneID[1]] * boneWeight[1];
boneTransformation += boneTransform[boneID[2]] * boneWeight[2];
boneTransformation += boneTransform[boneID[3]] * boneWeight[3];
vec3 usingPosition = (boneTransformation * vec4(position, 1.0)).xyz;
vec3 usingNormal = (boneTransformation * vec4(normal, 1.0)).xyz;
vec4 viewPos = view * model * vec4(usingPosition, 1.0);
gl_Position = projection * viewPos;
FragPos = vec3(model * vec4(usingPosition, 1.0f));
Normal = mat3(transpose(inverse(model))) * usingNormal;
TexCoords = texCoord;
float distance = length(viewPos.xyz);
Visibility = exp(-pow(distance * density, gradient));
Visibility = clamp(Visibility, 0.0f, 1.0f);
}
If my question above, lack of code or describe vaguely, please let me know, Thanks!
Edit:(1)
In additional, my bone information like this(code fetching part):
for (int i = 0; i < (int)nodeAnim->mNumPositionKeys; i++)
{
BoneKey key;
key.time = nodeAnim->mPositionKeys[i].mTime;
aiVector3D vec = nodeAnim->mPositionKeys[i].mValue;
key.vector = glm::vec3(vec.x, vec.y, vec.z);
currentBone->transformKeys.push_back(key);
}
had some transformation vector, so my code above glm::mat4 transform = GetInterpolateTransform(time, bone->transformKeys);,Absloutely, get the same value from it. I'm not sure I made a nomove keyframe animation that provide the transform values is true or not (of course it has 7 keyframe).
A keyframe contents like this(debug on head bone):
7 different keyframe, same vector value.
Edit:(2)
If you want to test my dae file, I put it in jsfiddle, come and take it :). Another thing, in Unity my file work correctly, so I think maybe not my local transform occurs the problem, it seems the problem could be some other like parentTransform or bone->transform...etc? I aslo add local transform matrix with all bone, But can not figure out why COLLADA contains these value for my unmove animation...

For amounts of testing, and finally found the problem is the UpdateBone() part.
Before I point out my problem, I need to say the series of matrix multiplication let me confuse, but when I found the solution, it just make me totally (maybe just 90%) realize all the matrix doing.
The problem comes from the article,Matrix calculations for gpu skinning. I assumed the answer code is absolutely right and don't think any more about the matrix should be used. Thus, misusing matrix terribly transfer my look into the local transform matrix. Back to the result image in my question section is bind pose when I change the local transform matrix to return glm::mat4(1.0f).
So the question is why the changed make the bind pose? I assumed the problem must be local transform in bone space, but I'm wrong. Before I give the answer, look at the code below:
void MeshModel::UpdateBone(Bone * bone)
{
glm::mat4 parentTransform = bone->getParentTransform();
bone->nodeTransform = parentTransform
* bone->transform // assimp_node->mTransformation
* bone->localTransform; // T S R matrix
bone->finalTransform = globalInverse
* bone->nodeTransform
* bone->inverseBindPoseMatrix; // ai_mesh->mBones[i]->mOffsetMatrix
for (int i = 0; i < (int)bone->children.size(); i++) {
UpdateBone(bone->children[i]);
}
}
And I make the change as below:
void MeshModel::UpdateBone(Bone * bone)
{
glm::mat4 parentTransform = bone->getParentTransform();
if (boneName == "Scene" || boneName == "Armature")
{
bone->nodeTransform = parentTransform
* bone->transform // when isn't bone node, using assimp_node->mTransformation
* bone->localTransform; //this is your T * R matrix
}
else
{
bone->nodeTransform = parentTransform // This retrieve the transformation one level above in the tree
* bone->localTransform; //this is your T * R matrix
}
bone->finalTransform = globalInverse // scene->mRootNode->mTransformation
* bone->nodeTransform //defined above
* bone->inverseBindPoseMatrix; // ai_mesh->mBones[i]->mOffsetMatrix
for (int i = 0; i < (int)bone->children.size(); i++) {
UpdateBone(bone->children[i]);
}
}
I don't know what the assimp_node->mTransformation give me before, only the description "The transformation relative to the node's parent" in the assimp documentation. For some testing, I found that the mTransformation is the bind pose matrix which the current node relative to parent if I use these on bone node. Let me give a picture that captured the matrix on head bone.
The left part is the transform which is fetched from assimp_node->mTransformation.The right part is my unmove animation's localTransform which is calculated by the keys from nodeAnim->mPositionKeys, nodeAnim->mRotationKeys and nodeAnim->mScalingKeys.
Look back what I did, I made a bind pose tranformation twice, so the image in my question section look just seperate but not spaghetti :)
On the last, let me show what I did before the unmove animation testing and correct animation result.
(For everyone, If my concept is wrong , please point me out! Thx.)

Related

Optimize WebGL shader?

I wrote the following shader to render a pattern with a bunch of concentric circles. Eventually I want to have each rotating sphere be a light emitter to create something along these lines.
Of course right now I'm just doing the most basic part to render the different objects.
Unfortunately the shader is incredibly slow (16fps full screen on a high-end macbook). I'm pretty sure this is due to the numerous for loops and branching that I have in the shader. I'm wondering how I can pull off the geometry I'm trying to achieve in a more performance optimized way:
EDIT: you can run the shader here: https://www.shadertoy.com/view/lssyRH
One obvious optimization I am missing is that currently all the fragments are checked against the entire 24 surrounding circles. It would be pretty quick and easy to just discard these checks entirely by checking if the fragment intersects the outer bounds of the diagram. I guess I'm just trying to get a handle on how the best practice is of doing something like this.
#define N 10
#define M 5
#define K 24
#define M_PI 3.1415926535897932384626433832795
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
float aspectRatio = iResolution.x / iResolution.y;
float h = 1.0;
float w = aspectRatio;
vec2 uv = vec2(fragCoord.x / iResolution.x * aspectRatio, fragCoord.y / iResolution.y);
float radius = 0.01;
float orbitR = 0.02;
float orbiterRadius = 0.005;
float centerRadius = 0.002;
float encloseR = 2.0 * orbitR;
float encloserRadius = 0.002;
float spacingX = (w / (float(N) + 1.0));
float spacingY = h / (float(M) + 1.0);
float x = 0.0;
float y = 0.0;
vec4 totalLight = vec4(0.0, 0.0, 0.0, 1.0);
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
// compute the center of the diagram
vec2 center = vec2(spacingX * (float(i) + 1.0), spacingY * (float(j) + 1.0));
x = center.x + orbitR * cos(iGlobalTime);
y = center.y + orbitR * sin(iGlobalTime);
vec2 bulb = vec2(x,y);
if (length(uv - center) < centerRadius) {
// frag intersects white center marker
fragColor = vec4(1.0);
return;
} else if (length(uv - bulb) < radius) {
// intersects rotating "light"
fragColor = vec4(uv,0.5+0.5*sin(iGlobalTime),1.0);
return;
} else {
// intersects one of the enclosing 24 cylinders
for(int k = 0; k < K; k++) {
float theta = M_PI * 2.0 * float(k)/ float(K);
x = center.x + cos(theta) * encloseR;
y = center.y + sin(theta) * encloseR;
vec2 encloser = vec2(x,y);
if (length(uv - encloser) < encloserRadius) {
fragColor = vec4(uv,0.5+0.5*sin(iGlobalTime),1.0);
return;
}
}
}
}
}
}
Keeping in mind that you want to optimize the fragment shader, and only the fragment shader:
Move the sin(iGlobalTime) and cos(iGlobalTime) out of the loops, these remain static over the whole draw call so no need to recalculate them every loop iteration.
GPUs employ vectorized instruction sets (SIMD) where possible, take advantage of that. You're wasting lots of cycles by doing multiple scalar ops where you could use a single vector instruction(see annotated code)
[Three years wiser me here: I'm not really sure if this statement is true in regards to how modern GPUs process the instructions, however it certainly does help readability and maybe even give a hint or two to the compiler]
Do your radius checks squared, save that sqrt(length) for when you really need it
Replace float casts of constants(your loop limits) with a float constant(intelligent shader compilers will already do this, not something to count on though)
Don't have undefined behavior in your shader(not writing to gl_FragColor)
Here is an optimized and annotated version of your shader(still containing that undefined behavior, just like the one you provided). Annotation is in the form of:
// annotation
// old code, if any
new code
#define N 10
// define float constant N
#define fN 10.
#define M 5
// define float constant M
#define fM 5.
#define K 24
// define float constant K
#define fK 24.
#define M_PI 3.1415926535897932384626433832795
// predefine 2 times PI
#define M_PI2 6.28318531
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
float aspectRatio = iResolution.x / iResolution.y;
// we dont need these separate
// float h = 1.0;
// float w = aspectRatio;
// use vector ops(2 divs 1 mul => 1 div 1 mul)
// vec2 uv = vec2(fragCoord.x / iResolution.x * aspectRatio, fragCoord.y / iResolution.y);
vec2 uv = fragCoord.xy / iResolution.xy;
uv.x *= aspectRatio;
// most of the following declarations should be predefined or marked as "const"...
float radius = 0.01;
// precalc squared radius
float radius2 = radius*radius;
float orbitR = 0.02;
float orbiterRadius = 0.005;
float centerRadius = 0.002;
// precalc squared center radius
float centerRadius2 = centerRadius * centerRadius;
float encloseR = 2.0 * orbitR;
float encloserRadius = 0.002;
// precalc squared encloser radius
float encloserRadius2 = encloserRadius * encloserRadius;
// Use float constants and vector ops here(2 casts 2 adds 2 divs => 1 add 1 div)
// float spacingX = w / (float(N) + 1.0);
// float spacingY = h / (float(M) + 1.0);
vec2 spacing = vec2(aspectRatio, 1.0) / (vec2(fN, fM)+1.);
// calc sin and cos of global time
// saves N*M(sin,cos,2 muls)
vec2 stct = vec2(sin(iGlobalTime), cos(iGlobalTime));
vec2 orbit = orbitR * stct;
// not needed anymore
// float x = 0.0;
// float y = 0.0;
// was never used
// vec4 totalLight = vec4(0.0, 0.0, 0.0, 1.0);
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
// compute the center of the diagram
// Use vector ops
// vec2 center = vec2(spacingX * (float(i) + 1.0), spacingY * (float(j) + 1.0));
vec2 center = spacing * (vec2(i,j)+1.0);
// Again use vector opts, use precalced time trig(orbit = orbitR * stct)
// x = center.x + orbitR * cos(iGlobalTime);
// y = center.y + orbitR * sin(iGlobalTime);
// vec2 bulb = vec2(x,y);
vec2 bulb = center + orbit;
// calculate offsets
vec2 centerOffset = uv - center;
vec2 bulbOffset = uv - bulb;
// use squared length check
// if (length(uv - center) < centerRadius) {
if (dot(centerOffset, centerOffset) < centerRadius2) {
// frag intersects white center marker
fragColor = vec4(1.0);
return;
// use squared length check
// } else if (length(uv - bulb) < radius) {
} else if (dot(bulbOffset, bulbOffset) < radius2) {
// Use precalced sin global time in stct.x
// intersects rotating "light"
fragColor = vec4(uv,0.5+0.5*stct.x,1.0);
return;
} else {
// intersects one of the enclosing 24 cylinders
for(int k = 0; k < K; k++) {
// use predefined 2*PI and float K
float theta = M_PI2 * float(k) / fK;
// Use vector ops(2 muls 2 adds => 1 mul 1 add)
// x = center.x + cos(theta) * encloseR;
// y = center.y + sin(theta) * encloseR;
// vec2 encloser = vec2(x,y);
vec2 encloseOffset = uv - (center + vec2(cos(theta),sin(theta)) * encloseR);
if (dot(encloseOffset,encloseOffset) < encloserRadius2) {
fragColor = vec4(uv,0.5+0.5*stct.x,1.0);
return;
}
}
}
}
}
}
I did a little more thinking ... I realized the best way to optimize it is to actually change the logic so that before doing intersection tests on the small circles it checks the bounds of the group of circles. This got it to run at 60fps:
Example here:
https://www.shadertoy.com/view/lssyRH

Sprite Kit Shader Uniforms Ignored

The shader that I'm using relies upon the position of the tiles in my game. I haven't found anything on using attribute variables with SKShader objects, so I went with updating the uniform variables. But it would seem that the shader won't communicate with the variables, especially once their values have been updated and changed. I am trying to make a basic lighting effect, but I can't get anything out of the shader at all. Any help? My code for the shader and for the Objective C classes are below.
Shader
uniform float midX, midY;
uniform float posX;
uniform float posY;
void main()
{
vec4 temp = SKDefaultShading(); // get the default shading
float lightRad = 200.0; // Light radius
float dist = distance(vec2(posX, posY), vec2(midX, midY)); // location of the light on the screen
vec4 color = vec4(1.0, 0, 0.0, (float)(dist / lightRad)); // creates an alpha gradient for the light. (falloff)
if (dist < lightRad) // only applies the light color if the distance from the light to the tile is smaller than the radius of the light
{
gl_FragColor = temp * color; // applies the color
}
else // otherwise, do nothing
{
gl_FragColor = temp;
}
}
Code
- (void) loadShaders
{
SKUniform* posX = [SKUniform uniformWithName:#"posX" float: 0.0f]; // adds the x position (with a placeholder value)
SKUniform* posY = [SKUniform uniformWithName:#"posY" float: 0.0f]; // adds the y position (with a placeholder value)
[_shader addUniform:posX];
[_shader addUniform:posY];
}
-(void)update:(CFTimeInterval)currentTime
{
for (int i = 0; i < _array.count; i++) // Loop through all tiles
{
float x = ((i % 100) - 13.5f) * 15.0f; // Calculate x pos of the tile
float y = ((1 - (i / 100)) + 6.5f) * 15.0f; // Calculate y pos of the tile
SKUniform* uniX = [[_tMap getShader] uniformNamed:#"posX"]; // get the uniform with the name posX
uniX.floatValue = x; // set the value of that uniform
SKUniform* uniY = [[_tMap getShader] uniformNamed:#"posY"]; // get the uniform with the name posY
uniY.floatValue = y; // set the value of that uniform
}
}
I'm fairly new to sprite kit, and I'm also new to GLSL.

2D topdown Water Ripple Effect (Fragment Shader)

So here's the code I found:
RippleSprite.cpp
void RippleEffectSprite::update(float delta) { //called per frame
updateRippleParams();
// TODO: improve
float rippleSpeed = 0.25f;
float maxRippleDistance = 1;
m_rippleDistance += rippleSpeed * delta;
m_rippleRange = (1 - m_rippleDistance / maxRippleDistance) * 0.02f;
if (m_rippleDistance > maxRippleDistance) {
updateRippleParams();
unscheduleUpdate(); //stop updating
}
}
void RippleEffectSprite::updateRippleParams() {
getGLProgramState()->setUniformFloat("u_rippleDistance", m_rippleDistance);
getGLProgramState()->setUniformFloat("u_rippleRange", m_rippleRange);
}
Fragment Shader
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform float u_rippleDistance;
uniform float u_rippleRange;
float waveHeight(vec2 p) {
float ampFactor = 2;
float distFactor = 2;
float dist = length(p);
float delta = abs(u_rippleDistance - dist);
if (delta <= u_rippleRange) {
return cos((u_rippleDistance - dist) * distFactor) * (u_rippleRange - delta) * ampFactor;
}
else {
return 0;
}
}
void main() {
vec2 p = v_texCoord - vec2(0.5, 0.5);
vec2 normal = normalize(p);
// offset texcoord along dist direction
vec2 v_texCoord2 = v_texCoord + normal * waveHeight(p);
gl_FragColor = texture2D(CC_Texture0, v_texCoord2) * v_fragmentColor;
}
Now i'll try my best to describe it in English, when run this creates a small circle (well not really circle, more like oval) at the middle of the Sprite, then it slowly expands outward, the textures below get distorted a bit, like a wave.
I've been reading stuff about Shaders for a week now and I understand how they work, but i don't understand this algorithm, can anyone explain to me how it created a oval and made it 'evenly',slowly expand?
here's the link of the tutorial: http://www.cocos.com/doc/tutorial/show?id=2121

Depth Map is white - webgl

I am using the shaders to draw the depth map in my image.
Here is my shader code :
vertex shader:
void main(void) {
gl_PointSize = aPointSize;
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vColor = aVertexColor;
visdepth = aisdepth;
vHasTexture = aHasTexture;
if (aHasTexture > 0.5)
vTextureCoord = aTextureCoord;
}
Fragement Shader:
void main(void) {
if (vHasTexture < 0.5 && visdepth < 0.5)
gl_FragColor = vColor;
if (vHasTexture > 0.5) {
vec4 textureColor = texture2D(uTexture, vec2(vTextureCoord.s, vTextureCoord.t));
gl_FragColor = vec4(textureColor.rgb, textureColor.a * uTextureAlpha);
}
if (visdepth > 0.5){
float ndcDepth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far) /
(gl_DepthRange.far - gl_DepthRange.near);
float clipDepth = ndcDepth /gl_FragCoord.w;
gl_FragColor = vec4((clipDepth*0.5)+0.5);
}
}
I used the following link as reference for my calculations : draw the depth value in opengl using shaders
I am getting all my values to be white as shown below:
From the two images above, it is clearly seen that points to the far right of the image are behind. This is not reflected in the image I downloaded. After using drawArrays function, I use the toDataUrl function to download the canvas data. The images are a result of the download. Does anyone know of any possible reasons for this?
for anyone who seeks an answer to that question , here's a little hint :
if you want to view the depth map , you have to linearize it...
float linearize_Z(float depth , float zNear , float zFar){
return (2*zNear ) / (zFar + zNear - depth*(zFar -zNear)) ;
}

Setting up perspective projection on Opengl ES 2.0 makes objects disappear

I'm working on a project using opengl-es 2.0, and I'm having some trouble setting up perspective projection.
If I don't set up the perspective projection and simply multiply the object-to-world matrix (I believe it's also called model matrix) by the vertex positions, the objects on screen are rendered correctly, they appear stretched, but as far as I know, that's something the projection matrix would fix. The problem is, whenever I set the perspective matrix and use it, the objects on screen disappear, and no matter how much I move them around they never show up in screen.
The calculations to get the Model-View-Projection matrix are done in CPU and the last multiplication the MVP-Matrix by the actual object-space vertex data is done in the vertex shader, this is why I believe the problem might be on the process to get that MVP-Matrix. I've run a bunch of unit tests, but according to those tests (and my basic knowledge of linear algebra) those matrices are being correctly calculated, and my internet-research throughout the day isn't helping for now. :-/
This is the code I use to calculate the MVP-Matrix:
Matrix4D projection_matrix;
projection_matrix.makePerspective(45.0f, 0.001f, 100.0f, 480.0f/320.0f);
Matrix4D view_matrix;
view_matrix.makeIdentity(); //Should be a real view matrix. TODO.
Matrix4D model_matrix(getWorldMatrix());
Matrix4D mvp_matrix(projection_matrix);
mvp_matrix *= view_matrix;
mvp_matrix *= model_matrix;
mMesh->draw(time, mvp_matrix.getRawData());
I think this code is pretty self-explanatory, but just in case, those Matrix4D are 4x4 matrices, and calling makePerspective/makeIdentity on them will make that matrix the Perspective or Identity matrix. The getRawData() call on Matrix4D objects returns the matrix data as a float array in column-major notation, and the mMesh variable is another object which, when draw is called, will simply send all the vertex and material data to the shaders.
The makePerspective function's code is the following:
Matrix4D& Matrix4D::makePerspective(const float field_of_view,
const float near, const float far, const float aspect_ratio) {
float size = near * tanf(DEGREES_TO_RADIANS(field_of_view) / 2.0f);
return this->makeFrustum(-size, size, -size / aspect_ratio,
size / aspect_ratio, near, far);
}
Matrix4D& Matrix4D::makeFrustum(const float left, const float right,
const float bottom, const float top, const float near,
const float far) {
this->mRawData[0] = 2.0f * near / (right - left);
this->mRawData[1] = 0.0f;
this->mRawData[2] = 0.0f;
this->mRawData[3] = 0.0f;
this->mRawData[4] = 0.0f;
this->mRawData[5] = 2.0f * near / (top - bottom);
this->mRawData[6] = 0.0f;
this->mRawData[7] = 0.0f;
this->mRawData[8] = (right + left) / (right - left);
this->mRawData[9] = (top + bottom) / (top - bottom);
this->mRawData[10] = - (far + near) / (far - near);
this->mRawData[11] = -1.0f;
this->mRawData[12] = 0.0f;
this->mRawData[13] = 0.0f;
this->mRawData[14] = -2.0f * far * near / (far - near);
this->mRawData[15] = 0.0f;
return *this;
}
And the getWorldMatrix() call does this(with some related code):
const Matrix4D& getWorldMatrix() {
return mWorldMatrix =
getTranslationMatrix() *
getRotationMatrix() *
getScaleMatrix();
}
const Matrix4D& getRotationMatrix() {
return this->mRotationMatrix.makeRotationFromEuler(this->mPitchAngle,
this->mRollAngle, this->mYawAngle);
}
const Matrix4D& getTranslationMatrix() {
return this->mTranslationMatrix.makeTranslation(this->mPosition.x,
this->mPosition.y, this->mPosition.z);
}
const Matrix4D& getScaleMatrix() {
return this->mScaleMatrix.makeScale(this->mScaleX, this->mScaleY, this->mScaleZ);
}
///This code goes in the Matrix4D class.
Matrix4D& Matrix4D::makeTranslation(const float x, const float y,
const float z) {
this->mRawData[0] = 1.0f;
this->mRawData[1] = 0.0f;
this->mRawData[2] = 0.0f;
this->mRawData[3] = 0.0f;
this->mRawData[4] = 0.0f;
this->mRawData[5] = 1.0f;
this->mRawData[6] = 0.0f;
this->mRawData[7] = 0.0f;
this->mRawData[8] = 0.0f;
this->mRawData[9] = 0.0f;
this->mRawData[10] = 1.0f;
this->mRawData[11] = 0.0f;
this->mRawData[12] = x;
this->mRawData[13] = y;
this->mRawData[14] = z;
this->mRawData[15] = 1.0f;
return *this;
}
Matrix4D& Matrix4D::makeScale(const float x, const float y,
const float z) {
this->mRawData[0] = x;
this->mRawData[1] = 0.0f;
this->mRawData[2] = 0.0f;
this->mRawData[3] = 0.0f;
this->mRawData[4] = 0.0f;
this->mRawData[5] = y;
this->mRawData[6] = 0.0f;
this->mRawData[7] = 0.0f;
this->mRawData[8] = 0.0f;
this->mRawData[9] = 0.0f;
this->mRawData[10] = z;
this->mRawData[11] = 0.0f;
this->mRawData[12] = 0.0f;
this->mRawData[13] = 0.0f;
this->mRawData[14] = 0.0f;
this->mRawData[15] = 1.0f;
return *this;
}
Matrix4D& Matrix4D::makeRotationFromEuler(const float angle_x,
const float angle_y, const float angle_z) {
float a = cosf(angle_x);
float b = sinf(angle_x);
float c = cosf(angle_y);
float d = sinf(angle_y);
float e = cosf(angle_z);
float f = sinf(angle_z);
float ad = a * d;
float bd = b * d;
this->mRawData[0] = c * e;
this->mRawData[1] = -bd * e + a * f;
this->mRawData[2] = ad * e + b * f;
this->mRawData[3] = 0.0f;
this->mRawData[4] = -c * f;
this->mRawData[5] = bd * f + a * e;
this->mRawData[6] = -ad * f + b * e;
this->mRawData[7] = 0.0f;
this->mRawData[8] = -d;
this->mRawData[9] = -b * c;
this->mRawData[10] = a * c;
this->mRawData[11] = 0.0f;
this->mRawData[12] = 0.0f;
this->mRawData[13] = 0.0f;
this->mRawData[14] = 0.0f;
this->mRawData[15] = 1.0f;
return *this;
}
Finally, the vertex shader is pretty much this:
#version 110
const float c_one = 1.0;
const float c_cero = 0.0;
uniform float time;
uniform mat4 mvp_matrix;
attribute vec3 position;
attribute vec3 normal;
attribute vec2 texture_coordinate;
varying vec2 v_texture_coordinate;
void main()
{
gl_Position = mvp_matrix * vec4(position, c_one);
v_texture_coordinate = texture_coordinate;
}
Just in case, the object being rendered is rendered on position (0.0f, 0.0f, -3.0f) with 0.5f scale applied to all the three axis's.
I don't really know what could be wrong, I'm hoping someone can spot what I may be missing, and any help would be appreciated. Debugging this would be a lot easier if I could get per-vertex results on the shader :-/.
As a side note, I'm having doubts on how to calculate the View or camera matrix, as far as I know it's simply a matrix with the inverted transformations the camera has to do, by which I understand something like, if I want to move the camera 100 units to the right, I move it 100 units to the left, is that right?
EDIT: Just trying to give more information, maybe that way someone will be able to help me. I've noticed the model matrix is incorrect with the code above, mostly because of the matrix order, I've changed it to the following and now the model matrix seems good:
const Matrix4D& getWorldMatrix() {
return mWorldMatrix =
getScaleMatrix() * getRotationMatrix() * getTranslationMatrix();
}
Despite this, still no luck. The matrices resulting from my test data are these:
Projection matrix:
[1.609506,0.000000,0.000000,0.000000]
[0.000000,2.414258,0.000000,0.000000]
[0.000000,0.000000,-1.000020,-0.002000]
[0.000000,0.000000,-1.000000,0.000000]
Model matrix:
[0.500000,0.000000,0.000000,0.000000]
[0.000000,0.500000,0.000000,0.000000]
[0.000000,0.000000,0.500000,-3.000000]
[0.000000,0.000000,0.000000,1.000000]
MVP matrix:
[0.804753,0.000000,0.000000,0.000000]
[0.000000,1.207129,0.000000,0.000000]
[0.000000,0.000000,2.499990,-0.001000]
[0.000000,0.000000,-1.000000,0.000000]
And the mesh I'm using to test all this is a simple cube going from 1.0f to -1.0f on each axis, centered on the origin. As far as I know, this should position the vertex closest to the near limit (0.0001f) on position -2.0f along the z axis, so the cube is in front of the camera and withing the view frustum. Any clues someone?

Resources