问题
I've made the following code:
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#define WIDTH 400
#define HEIGTH 400
#define ORIGIN_X 50
#define ORIGIN_Y 50
#define move(x,y) glTranslatef(x, y, 0.0f);
#define enlarge(y) glScalef(1, y, 1);
#define rotateX(angle) glRotatef(angle, 1,0,0);
#define rotateY(angle) glRotatef(angle, 0,1,0);
#define rotateZ(angle) glRotatef(angle, 0,0,1);
// Variables que definen la rotación del brazo entero (de hombro a mano)
static GLfloat shoulder_Xangle, shoulder_Yangle, shoulder_Zangle;
// Variables que definen sólo la rotación del antebrazo (de codo a mano)
static GLfloat elbow_Xangle, elbow_Yangle, elbow_Zangle;
void keyboardHandler(unsigned char key, int x, int y ){
switch(key){
case 'q': shoulder_Zangle++; break;
case 'e': shoulder_Zangle--; break;
case 'a': shoulder_Yangle++; break;
case 'd': shoulder_Yangle--; break;
case 'w': shoulder_Xangle++; break;
case 's': shoulder_Xangle--; break;
case 'r': elbow_Zangle++; break;
case 'y': elbow_Zangle--; break;
case 'f': elbow_Yangle++; break;
case 'h': elbow_Yangle--; break;
case 't': elbow_Xangle++; break;
case 'g': elbow_Xangle--; break;
default: break;
}
glutPostRedisplay(); // Avisa que la ventana ha de refrescarse
}
void init() {
glutKeyboardFunc(keyboardHandler); // Asociar handler a eventos procedentes del teclado
glClearColor(0.0,0.0,0.0,0.0); // Fijar el color por defecto a negro en el formato RGBA
}
void rotate(GLfloat Xangle, GLfloat Yangle, GLfloat Zangle) {
rotateX(Xangle); // Rotar Xangle grados sobre el eje X
rotateY(Yangle); // Rotar Yangle grados sobre el eje Y
rotateZ(Zangle); // Rotar Zangle grados sobre el eje Z
}
void draw_sphere(GLdouble radius) {
GLint slices = 360;
GLint stacks = 360;
glutWireSphere(radius, slices, stacks);
}
void draw_cube() {
glBegin(GL_QUADS);
glColor3f ( 0.0, 0.7, 0.1); // Parte anterior: verde
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f( 0.5, 0.5, 0.5);
glVertex3f( 0.5, -0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glColor3f ( 1.0, 0.0, 0.0); // Parte posterior: rojo
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f( 0.5, 0.5, -0.5);
glVertex3f( 0.5, -0.5, -0.5);
glVertex3f(-0.5, -0.5, -0.5);
glColor3f ( 1.0, 1.0, 1.0); // Resto: blanco
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f( 0.5, 0.5, 0.5);
glVertex3f( 0.5, 0.5, -0.5);
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f( 0.5, -0.5, 0.5);
glVertex3f( 0.5, -0.5, -0.5);
glVertex3f(-0.5, -0.5, -0.5);
glEnd();
}
void draw_shoulder() { draw_sphere(0.5); }
void draw_elbow() {
move(0, -3.0) // 3) Colocar en su posición final
rotate(elbow_Xangle, elbow_Yangle, elbow_Zangle); // 2) Rotamiento del codo
draw_sphere(0.5); // 1) Dibujar 1 esfera (codo)
}
void draw_arm() {
move(0.0, -1.5); // 3) Colocar en su posición final
enlarge(2.0); // 2) Escalar el brazo
draw_cube(); // 1) Dibujar 1 cubo (brazo)
}
void draw_forearm() {
move(0.0, -3.0); // 5) Colocar en su posición final
rotate(elbow_Xangle, elbow_Yangle, elbow_Zangle); // 4) Rotamiento del codo
move(0.0, -1.5); // 3) Mover hacia abajo para que el radio de rotación = tamaño codo
enlarge(2.0); // 2) Escalar el antebrazo
draw_cube(); // 1) Dibujar 1 cubo (antebrazo)
}
void draw_hand() {
move(0, -3.0); // 4) Colocar en su posición final
rotate(elbow_Xangle, elbow_Yangle, elbow_Zangle); // 3) Rotamiento del codo
move(0.0, -2.5) // 2) Mover hacia abajo el tamaño del codo+antebrazo = (1.0+2.0)-0.5
rotateX(90); // 1) Poner la mano en su sitio
glutSolidCone(0.5, 1.5, 360, 360);
}
void display() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Borrado del FrameBuffer
glEnable(GL_DEPTH_TEST);
glLoadIdentity(); // Cargar la matriz identidad en la matriz de proyección
rotate(shoulder_Xangle, shoulder_Yangle, shoulder_Zangle); // Movimiento del hombro
draw_shoulder(); // Dibujar el hombro
glLoadIdentity();
rotate(shoulder_Xangle, shoulder_Yangle, shoulder_Zangle); // Movimiento del hombro
draw_arm(); // Dibujar el brazo
glLoadIdentity();
rotate(shoulder_Xangle, shoulder_Yangle, shoulder_Zangle); // Movimiento del hombro
draw_elbow(); // Dibujar el codo
glLoadIdentity();
rotate(shoulder_Xangle, shoulder_Yangle, shoulder_Zangle); // Movimiento del hombro
draw_forearm(); // Dibujar el antebrazo
glLoadIdentity();
rotate(shoulder_Xangle, shoulder_Yangle, shoulder_Zangle); // Movimiento del hombro
draw_hand(); // Dibujar la mano
// Forzar renderizado
glutSwapBuffers();
}
void reshape(int w, int h) {
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION); // Activar las modificaciones en la cámara
glLoadIdentity();
glOrtho(-8, 8, -12, 4, -8, 8);
glMatrixMode(GL_MODELVIEW); // Activar las modificaciones en el modelo
}
int main(int argc, char** argv) {
glutInit(&argc, argv); // Cargar el teclado y el ráton
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); // Búffer doble, con color RGB y profundidad
glutInitWindowSize(WIDTH, HEIGTH); // Tamaño de la ventana
glutInitWindowPosition(ORIGIN_X, ORIGIN_Y); // Posición del extremo superior-izquierdo de la ventana
glutCreateWindow("Brazo Articulado"); // Crear la ventana
init();
glutDisplayFunc(display); // Activar la función de pintado
glutReshapeFunc(reshape); // Activar la función de escalado
glutMainLoop(); // Arrancar el bucle de OpenGL
return 0;
}
Basically, I have an arm made up of a shoulder, arm, elbow, forearm and hand. When the shoulder rotates, all those components have to rotate aswell. However, when the elbow rotates, only the elbow, the forearm and the hand are the ones that rotate.
From my ignorance, to achieve this, I've basically put a "shoulder rotate" for every single component of my arm, and an "elbow rotate" for my last 3 components, as you can see in my display function and my "draw_component" functions.
I've been told you can achieve the same functionality by just adding 2 "rotate" sentences (one for the shoulder and one for the elbow), and not like I did for every single component.
Any ideas how could this be made?
回答1:
Note, that Drawing by glBegin
/glEnd
sequences is deprecated since several years.
Read about Fixed Function Pipeline and see Vertex Specification and Shader for a state of the art way of rendering.
But if you want to do it like this, then you can push and pop matrices to the matrix stack by glPushMatrix/glPupMatrix.
If you want to imagine how the matrix operations change the model, then you need to "read" the operations in the reverse order. This is, because the current matrix of the matrix stack is multiplied by the matrix which is specified by the new operation and the matrices are stored in column-major order (fixed function pipeline).
See also OpenGL translation before and after a rotation
If you add the following code to the display
function, then you get a second arm, which behaves the same as the first:
void display() {
// your original code
....
glLoadIdentity();
move( 3.0, 0.0 )
// rotate sholder
rotate(shoulder_Xangle, shoulder_Yangle, shoulder_Zangle);
draw_sphere(0.5); // shoulder
move(0.0, -1.5);
glPushMatrix();
enlarge(2.0);
draw_cube(); //arm
glPopMatrix();
move(0.0, -1.5)
// rotate elbow
rotate(elbow_Xangle, elbow_Yangle, elbow_Zangle);
draw_sphere(0.5); // elbow
move(0.0, -1.5)
glPushMatrix();
enlarge(2.0);
draw_cube(); // forearm
glPopMatrix();
move(0.0, -1.0)
rotateX(90);
glutSolidCone(0.5, 1.5, 360, 360); // hand
// Forzar renderizado
glutSwapBuffers();
}
Preview:
In this case the glPushMatrix/glPupMatrix is needed for the glScalef
(enlarge
) operation only.
Instead of push and pop the matrix you can do the inverse operation too. This means enlarge(2.0)
can be reversed by enlarge(0.5)
:
// rotate sholder
rotate(shoulder_Xangle, shoulder_Yangle, shoulder_Zangle);
draw_sphere(0.5); // shoulder
move(0.0, -1.5);
enlarge(2.0);
draw_cube(); //arm
enlarge(0.5);
move(0.0, -1.5)
// rotate elbow
rotate(elbow_Xangle, elbow_Yangle, elbow_Zangle);
draw_sphere(0.5); // elbow
move(0.0, -1.5)
enlarge(2.0);
draw_cube(); // forearm
enlarge(0.5);
move(0.0, -1.0)
rotateX(90);
glutSolidCone(0.5, 1.5, 360, 360); // hand
来源:https://stackoverflow.com/questions/52652098/opengl-arm-that-rotates-shoulder-and-elbow