Sign in to follow this  
SingularOne

OpenGL Rotation Matrix about point

Recommended Posts

Hey, would you help me a bit? I'm struggling with making matrices to rotate about point. My rotation part of the matrix construction looks like that:

[code]

boneMatrices[offs + 0] = cos(rx) * cos(ry);


boneMatrices[offs + 1] = -cos(rz) * sin(rx) - cos(rx)*sin(ry)*sin(rz);
boneMatrices[offs + 2] = cos(rx) * cos(rz) * sin(ry) + sin(rx) * sin(rz);

boneMatrices[offs + 4] = cos(ry) * sin(rx);
boneMatrices[offs + 5] = cos(rx) * cos(rz) - sin(rx)*sin(ry)*sin(rz);
boneMatrices[offs + 6] = cos(rz) * sin(rx) * sin(ry) - cos(rx) * sin(rz);

boneMatrices[offs + 8] = -sin(ry);
boneMatrices[offs + 9] = cos(ry) * sin(rz);
boneMatrices[offs + 10] = cos(ry) * cos(rz);
[/code]

I pass origin for rotation instead of scaling part of the matrix, and then, in vertex shader i do:

1) subtract origin from vertex

2) rotate vertex

3) add origin to vertex

It works, but it sucks. too much operations, ugly code;

I want to pass to my shader matrices that already do rotation about specific point. but really i need someone's help here. how can i make above matrix to rotate about specific point? i couldn't find any descriprion adoptable in my case.




i use opengl, if it matters.

Share this post


Link to post
Share on other sites
Assuming that you use column vectors, if [b]T[/b]([b]c[/b]) is the translation matrix to the center of rotation and [b]R[/b] is the rotation matrix, then the product
[b]M[/b] := [b]T[/b]([b]c[/b]) * [b]R[/b] * [b]T[/b](-[b]c[/b])
defines the desired new rotation matrix. It translates by -[b]c[/b], rotates, and translates back by [b]c[/b] like you do now separately.

EDIT:
You can take advantage from knowing the structures of the matrices when you compute the above product. Think of [b]M[/b] having a 3x3 sub-marix [b]M[/b][sub]R[/sub] on the upper left and a 1x3 sub-matrix [b]M[/b][sub]T[/sub] on the upper right, and doing so with [b]R[/b] and [b]T[/b] as well, then
[b]M[/b][sub]R[/sub] := [b]R[/b][sub]R[/sub]
[b]M[/b][sub]T[/sub] := [b]R[/b][sub]R[/sub] * [b]T[/b][sub]T[/sub](-[b]c[/b]) + [b]T[/b][sub]T[/sub]([b]c[/b])
means the minimum computations to be done for yielding in [b]M[/b].

Share this post


Link to post
Share on other sites
[quote name='SingularOne' timestamp='1313475830' post='4849719']
1) subtract origin from vertex

2) rotate vertex

3) add origin to vertex

[/quote]

I know you might not want to hear this, but the way you listed above is the correct way. If you want your code to still look good, you can write it like this (writen in psuedocode of course):

[code]
function RotateAboutPoint(Point,Rotation){
return MoveToAxis * Rotate * MoveFromAxis;
}
[/code]

It might even perform better than your "beautiful" code since matrix multiplication is accelerated while multiplying every element in your matrix separately is not... (Not sure about this one though...)

Share this post


Link to post
Share on other sites
Thanks for your replies.

Well i've tried to do it like [b]haegarr [/b]described:




[code]

//Origins

oMat.make_identity();
oMat2.make_identity();

oMat.element(0, 3) = -ox;
oMat.element(1, 3) = -oy;
oMat.element(2, 3) = -oz;

oMat2.element(0, 3) = ox;
oMat2.element(1, 3) = oy;
oMat2.element(2, 3) = oz;


//Rotation(tested)
bMat.make_identity();

bMat.element(0, 0) = cos(rx) * cos(ry);
bMat.element(1, 0) = -cos(rz) * sin(rx) - cos(rx)*sin(ry)*sin(rz);
bMat.element(2, 0) = cos(rx) * cos(rz) * sin(ry) + sin(rx) * sin(rz);;
bMat.element(0, 1) = cos(ry) * sin(rx);
bMat.element(1, 1) = cos(rx) * cos(rz) - sin(rx)*sin(ry)*sin(rz);
bMat.element(2, 1) = cos(rz) * sin(rx) * sin(ry) - cos(rx) * sin(rz);
bMat.element(0, 2) = -sin(ry);
bMat.element(1, 2) = cos(ry) * sin(rz);
bMat.element(2, 2) = cos(ry) * cos(rz);

//Bone translation

bMat.element(0, 3) = translation.x;
bMat.element(1, 3) = translation.y;
bMat.element(2, 3) = translation.z;


//M := R * T(-c) + T© ??
bMat *= oMat;
bMat += oMat2;[/code]


[b]and rotation\translation is now rigth, but for some reason i[u][color="#8b0000"]t's about 2x weaker than it should be and it looks like it scales vertices a bit then rotating[/color][/u][/b]

Share this post


Link to post
Share on other sites
Ok, fixed by


[code]
bMat *= oMat;
oMat2 *= bMat;
bMat = oMat2;//(or just pass oMat2 to shader)[/code]

(equialent of -T * R * T)

but i'm not sure if it's ok to use such a matrix for tangent and normal? looks ok, but just want to know.

Share this post


Link to post
Share on other sites
[quote name='SillyCow' timestamp='1313504494' post='4849857']
[quote name='SingularOne' timestamp='1313485221' post='4849759']
[code]
bMat *= oMat;
bMat += oMat2;[/code]
[/quote]

Why are you adding? All matrix chaining transformations should be multiplications...
[/quote]
[font="arial, verdana, tahoma, sans-serif"][size="2"]Please let me clarify what
[color="#1C2837"][size="2"][b]M[/b][sub]T[/sub] := [b]R[/b][sub]R[/sub] * [b]T[/b][sub]T[/sub](-[b]c[/b]) + [b]T[/b][sub]T[/sub]([b]c[/b])[/size][/color][/size][/font]
actually means. As mentioned my post above, [color="#1C2837"][size="2"][b]R[/b][sub]R[/sub][/size][/color] is a 3x3 matrix and [color="#1C2837"][size="2"][b]T[/b][sub]T[/sub][/size][/color] is a 1x3 matrix (a.k.a. column vector). Multiplying a 3x3 matrix on the left and a 1x3 matrix on the right gives you a 1x3 matrix. Adding a 1x3 matrix onto a 1x3 matrix gives you a 1x3 matrix.

So notice that the result [color="#1C2837"][size="2"][b]M[/b][sub]T[/sub][/size][/color] is a 1x3 matrix (and that [color="#1C2837"][size="2"][b]M[/b][sub]R[/sub][/size][/color] is a 3x3 matrix), while [b]M[/b] itself is a usual homogeneous 4x4 matrix. The correct assembly then looks like
[code]
bMat.make_identity();
// MR
bMat.element(0, 0) = cos(rx) * cos(ry);
bMat.element(1, 0) = -cos(rz) * sin(rx) - cos(rx)*sin(ry)*sin(rz);
bMat.element(2, 0) = cos(rx) * cos(rz) * sin(ry) + sin(rx) * sin(rz);;
bMat.element(0, 1) = cos(ry) * sin(rx);
bMat.element(1, 1) = cos(rx) * cos(rz) - sin(rx)*sin(ry)*sin(rz);
bMat.element(2, 1) = cos(rz) * sin(rx) * sin(ry) - cos(rx) * sin(rz);
bMat.element(0, 2) = -sin(ry);
bMat.element(1, 2) = cos(ry) * sin(rz);
bMat.element(2, 2) = cos(ry) * cos(rz);
// MT = MR * TT(-c) + TT(c)
bMat.element(0, 3) = bMat.element(0, 0) * (-ox) + bMat.element(0, 1) * (-oy) + bMat.element(0, 2) * (-oz) + ox;
bMat.element(1, 3) = bMat.element(1, 0) * (-ox) + bMat.element(1, 1) * (-oy) + bMat.element(1, 2) * (-oz) + oy;
bMat.element(2, 3) = bMat.element(2, 0) * (-ox) + bMat.element(2, 1) * (-oy) + bMat.element(2, 2) * (-oz) + oz;
[/code]
if I have interpreted the indexing scheme correctly.


EDIT: It is for sure possible to compose the desired rotation simply by computing [color="#1C2837"][size="2"][b]T[/b]([b]c[/b]) * [b]R[/b] * [b]T[/b](-[b]c[/b])[/size][/color]. The above way just shows (as mentioned) the minimal computational effort to do; it avoids all that nasty scalar products with 0 and 1. However, this kind of optimization will probably not be noticeable.

Share this post


Link to post
Share on other sites
[color="#1C2837"][size="2"][b] [url="http://www.gamedev.net/user/90444-haegarr/"]haegarr[/url],[/b]
i don't know how to thank you for your effort, your method works fine and it's much more efficient[/size][/color]
[size="2"][color="#1c2837"]
[b][url="http://www.gamedev.net/user/144405-sillycow/"]SillyCow[/url],[/b]
thank you too for pointing out the part i misunderstood.[/color][/size]
[size="2"] [/size]
[size="2"][color="#1c2837"]and yeah, i've alredy noticed, that it's not really good to rotate normals.[/color][/size]

Share this post


Link to post
Share on other sites
[quote name='SingularOne' timestamp='1313506514' post='4849879']
...
(equialent of -T * R * T)

but i'm not sure if it's ok to use such a matrix for tangent and normal? looks ok, but just want to know.
[/quote]
Well, please notice that -[b]T[/b] is not the same as [b]T[/b](-[b]c[/b]), because the elements on the main diagonal will be negated in -[b]T[/b] but not in [b]T[/b](-[b]c[/b])!

However, you can apply [b]T[/b](-[b]c[/b]) * [b]R[/b] * [b]T[/b]([b]c[/b]) to a normal / tangent because
a) normals and tangents are direction vectors and are hence invariant to translations, and
b) there is no scaling or shearing in this formula.
Hence for normals and tangents the above formula does the same as the lonely [b]R[/b] does: It simply rotates the vector.

Share this post


Link to post
Share on other sites
yes it looks like problem is in my shader.

here shader that i found in nvidia example of hardware skinning:


[code]
attribute vec4 position;
attribute vec3 normal;
attribute vec4 weight;
attribute vec4 index;
attribute float numBones;

uniform mat4 boneMatrices[30];
uniform vec4 color;
uniform vec4 lightPos;

void main()
{
vec4 transformedPosition = vec4(0.0);
vec3 transformedNormal = vec3(0.0);

vec4 curIndex = index;
vec4 curWeight = weight;

for (int i = 0; i < int(numBones); i++)
{
mat4 m44 = boneMatrices[int(curIndex.x)];

// transform the offset by bone i
transformedPosition += m44 * position * curWeight.x;

mat3 m33 = mat3(m44[0].xyz,
m44[1].xyz,
m44[2].xyz);

// transform normal by bone i
transformedNormal += m33 * normal * curWeight.x;

// shift over the index/weight variables, this moves the index and
// weight for the current bone into the .x component of the index
// and weight variables
curIndex = curIndex.yzwx;
curWeight = curWeight.yzwx;
}

gl_Position = gl_ModelViewProjectionMatrix * transformedPosition;

transformedNormal = normalize(transformedNormal);
gl_FrontColor = dot(transformedNormal, lightPos.xyz) * color;
}[/code]


and significant part of my adoption(maximum 2 bones affecting vertex, 1st one is always most effective, so 2nd affecting bone might exist only if 1st one is):

[code] V = gl_Vertex;
vec3 n2 = gl_Normal;
vec3 t2 = Tangent;
if(Bones.x >= 0.0)//Bone1 ID
{
mat4 tmat = BonesMat[int(Bones.x)]; //Bone matrix
mat3 nmat = mat3(tmat[0].xyz, tmat[1].xyz, tmat[2].xyz);

V = tmat * gl_Vertex * Bones.z; //Bones.z - Bone 1 weight
n2 = nmat * gl_Normal * Bones.z;
t2 = nmat * Tangent * Bones.z;

if(Bones.y >= 0.0)//Bone2 ID
{
tmat = BonesMat[int(Bones.y)];
nmat = mat3(tmat[0].xyz, tmat[1].xyz, tmat[2].xyz);

V += tmat * gl_Vertex * Bones.w; //Bones.w - Bone2 weight
n2 += nmat * gl_Normal * Bones.w;
t2 += nmat * Tangent * Bones.w;
}
}
[/code]
Further - using V,N,T as regular;

result: rotation\translation is alright.
problem: lighting glitches. then i move camera away from object - vertices that affected by 2 bones become dark with the distance(lambert decreasing).

Share this post


Link to post
Share on other sites
IMHO the shown adopted code snippet is missing a normalization of both n2 and t2 (assuming that "using V,N,T as regular" doesn't include it). The code snippet probably only works well if Bones.z == 1 and Bones.y < 0. In all other cases the lengths of n2 and t2 may be anything but are later expected to be 1.

Share this post


Link to post
Share on other sites
yes, i've noticed before, it works ok if bone1 weight = 1.0 and bone2 is ineffective, but i do normalize resulting normal and tangent


[code]

vec3 T = normalize(gl_NormalMatrix * normalize(t2));
vec3 N = normalize(gl_NormalMatrix * normalize(n2));
vec3 B = cross(N, T);[/code]


and even if i remove normal rotation, problem doesn't go away.
also i modified code so second bone weight = 1.0 - FirstBoneWeight (because outside shader my program actually calculates weights for all affecting bones, but in examples i use to test it's always <= 2 bones affecting vertex);

and to avoid wasting your time with snippets:
full code of stuff that actually affects that problem,
[spoiler]
[code]#version 110
attribute vec4 Bones;
attribute vec3 Tangent;
uniform mat4 BonesMat[30];
varying vec3 ld;
varying vec3 eye;
varying vec4 V;

void main()
{
V = gl_Vertex;
vec3 n2 = gl_Normal;
vec3 t2 = Tangent;
if(Bones.x >= 0.0)
{
mat4 tmat = BonesMat[int(Bones.x)];
mat3 nmat = mat3(tmat[0].xyz, tmat[1].xyz, tmat[2].xyz);

V = tmat * gl_Vertex * Bones.z;
n2 = nmat * gl_Normal * Bones.z;
t2 = nmat * Tangent * Bones.z;

if(Bones.y >= 0.0)
{
tmat = BonesMat[int(Bones.y)];
nmat = mat3(tmat[0].xyz, tmat[1].xyz, tmat[2].xyz);

V += tmat * gl_Vertex * (1.0-Bones.z);
n2 += nmat * gl_Normal * (1.0-Bones.z);
t2 += nmat * Tangent * (1.0-Bones.z);
}
}

gl_TexCoord[0] = gl_MultiTexCoord0;
V = gl_ModelViewMatrix * V;

vec3 T = -normalize(gl_NormalMatrix * normalize(t2));
vec3 N = normalize(gl_NormalMatrix * normalize(n2));
vec3 B = cross(N, T);

vec3 lv = (gl_LightSource[0].position - V).xyz;
ld.x = dot(lv, B);
ld.y = dot(lv, T);
ld.z = dot(lv, N);

vec3 vt = -V.xyz;
eye.x = dot(vt, B);
eye.y = dot(vt, T);
eye.z = dot(vt, N);
gl_Position = gl_ProjectionMatrix * V;
}[/code]

[/spoiler]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Announcements

  • Forum Statistics

    • Total Topics
      628378
    • Total Posts
      2982347
  • Similar Content

    • By test opty
      Hi all,
       
      I'm starting OpenGL using a tut on the Web. But at this point I would like to know the primitives needed for creating a window using OpenGL. So on Windows and using MS VS 2017, what is the simplest code required to render a window with the title of "First Rectangle", please?
       
       
    • By DejayHextrix
      Hi, New here. 
      I need some help. My fiance and I like to play this mobile game online that goes by real time. Her and I are always working but when we have free time we like to play this game. We don't always got time throughout the day to Queue Buildings, troops, Upgrades....etc.... 
      I was told to look into DLL Injection and OpenGL/DirectX Hooking. Is this true? Is this what I need to learn? 
      How do I read the Android files, or modify the files, or get the in-game tags/variables for the game I want? 
      Any assistance on this would be most appreciated. I been everywhere and seems no one knows or is to lazy to help me out. It would be nice to have assistance for once. I don't know what I need to learn. 
      So links of topics I need to learn within the comment section would be SOOOOO.....Helpful. Anything to just get me started. 
      Thanks, 
      Dejay Hextrix 
    • By mellinoe
      Hi all,
      First time poster here, although I've been reading posts here for quite a while. This place has been invaluable for learning graphics programming -- thanks for a great resource!
      Right now, I'm working on a graphics abstraction layer for .NET which supports D3D11, Vulkan, and OpenGL at the moment. I have implemented most of my planned features already, and things are working well. Some remaining features that I am planning are Compute Shaders, and some flavor of read-write shader resources. At the moment, my shaders can just get simple read-only access to a uniform (or constant) buffer, a texture, or a sampler. Unfortunately, I'm having a tough time grasping the distinctions between all of the different kinds of read-write resources that are available. In D3D alone, there seem to be 5 or 6 different kinds of resources with similar but different characteristics. On top of that, I get the impression that some of them are more or less "obsoleted" by the newer kinds, and don't have much of a place in modern code. There seem to be a few pivots:
      The data source/destination (buffer or texture) Read-write or read-only Structured or unstructured (?) Ordered vs unordered (?) These are just my observations based on a lot of MSDN and OpenGL doc reading. For my library, I'm not interested in exposing every possibility to the user -- just trying to find a good "middle-ground" that can be represented cleanly across API's which is good enough for common scenarios.
      Can anyone give a sort of "overview" of the different options, and perhaps compare/contrast the concepts between Direct3D, OpenGL, and Vulkan? I'd also be very interested in hearing how other folks have abstracted these concepts in their libraries.
    • By aejt
      I recently started getting into graphics programming (2nd try, first try was many years ago) and I'm working on a 3d rendering engine which I hope to be able to make a 3D game with sooner or later. I have plenty of C++ experience, but not a lot when it comes to graphics, and while it's definitely going much better this time, I'm having trouble figuring out how assets are usually handled by engines.
      I'm not having trouble with handling the GPU resources, but more so with how the resources should be defined and used in the system (materials, models, etc).
      This is my plan now, I've implemented most of it except for the XML parts and factories and those are the ones I'm not sure of at all:
      I have these classes:
      For GPU resources:
      Geometry: holds and manages everything needed to render a geometry: VAO, VBO, EBO. Texture: holds and manages a texture which is loaded into the GPU. Shader: holds and manages a shader which is loaded into the GPU. For assets relying on GPU resources:
      Material: holds a shader resource, multiple texture resources, as well as uniform settings. Mesh: holds a geometry and a material. Model: holds multiple meshes, possibly in a tree structure to more easily support skinning later on? For handling GPU resources:
      ResourceCache<T>: T can be any resource loaded into the GPU. It owns these resources and only hands out handles to them on request (currently string identifiers are used when requesting handles, but all resources are stored in a vector and each handle only contains resource's index in that vector) Resource<T>: The handles given out from ResourceCache. The handles are reference counted and to get the underlying resource you simply deference like with pointers (*handle).  
      And my plan is to define everything into these XML documents to abstract away files:
      Resources.xml for ref-counted GPU resources (geometry, shaders, textures) Resources are assigned names/ids and resource files, and possibly some attributes (what vertex attributes does this geometry have? what vertex attributes does this shader expect? what uniforms does this shader use? and so on) Are reference counted using ResourceCache<T> Assets.xml for assets using the GPU resources (materials, meshes, models) Assets are not reference counted, but they hold handles to ref-counted resources. References the resources defined in Resources.xml by names/ids. The XMLs are loaded into some structure in memory which is then used for loading the resources/assets using factory classes:
      Factory classes for resources:
      For example, a texture factory could contain the texture definitions from the XML containing data about textures in the game, as well as a cache containing all loaded textures. This means it has mappings from each name/id to a file and when asked to load a texture with a name/id, it can look up its path and use a "BinaryLoader" to either load the file and create the resource directly, or asynchronously load the file's data into a queue which then can be read from later to create the resources synchronously in the GL context. These factories only return handles.
      Factory classes for assets:
      Much like for resources, these classes contain the definitions for the assets they can load. For example, with the definition the MaterialFactory will know which shader, textures and possibly uniform a certain material has, and with the help of TextureFactory and ShaderFactory, it can retrieve handles to the resources it needs (Shader + Textures), setup itself from XML data (uniform values), and return a created instance of requested material. These factories return actual instances, not handles (but the instances contain handles).
       
       
      Is this a good or commonly used approach? Is this going to bite me in the ass later on? Are there other more preferable approaches? Is this outside of the scope of a 3d renderer and should be on the engine side? I'd love to receive and kind of advice or suggestions!
      Thanks!
    • By nedondev
      I 'm learning how to create game by using opengl with c/c++ coding, so here is my fist game. In video description also have game contain in Dropbox. May be I will make it better in future.
      Thanks.
  • Popular Now