Coordenadas oculares.
Se sitúan en el punto de vista del observador, sin importar las transformaciones que tengan lugar. Por tanto, estas coordenadas representan un sistema virtual de coordenadas fijo usado como marco de referencia común.
Transformaciones.
Son las que hacen posible la proyección de coordenadas 3D sobre superficies 2D. También son las encargadas de mover, rotar y escalar objetos.
Modelador.
En esta sección se recogen las transformaciones del observador y del modelado puesto que, constituyen, al fin y al cabo, la misma transformación.
Transformaciones del Observador.
Es la primera que se aplica a la escena, y se usa para determinar el punto más ventajoso de la escena. Por defecto, el punto de vista está en el origen (0,0,0) mirando en dirección negativa del eje z. La transformación del observador permite colocar y apuntar la cámara donde y hacia donde se quiera. Todas las transformaciones posteriores tienen lugar basadas en el nuevo sistema de coordenadas modificado.
Transformaciones del modelo.
Se usan para situar, rotar y escalar los objetos de la escena. La apariencia final de los objetos depende en gran medida del orden con el que se hayan aplicado las transformaciones.
Transformaciones de la proyección.
Se aplica a la orientación final del modelador. Esta proyección define el volumen de visualización y establece los planos de trabajo. A efectos prácticos, esta translación especifica cómo se traslada una escena finalizada a la imagen final de la pantalla.
Transformaciones de la vista.
En el momento en que se ha terminado todo el proceso de transformaciones, solo queda un último paso: proyectar lo que hemos dibujado en 3D al 2D de la pantalla, en la ventana en la que estamos trabajando. Esta es la denominada transformación de la vista.
Matrices.
Cada una de las transformaciones de las que se acaba de hablar puede conseguirse multiplicando una matriz que contenga los vértices por una matriz que describa la transformación. Por tanto todas las transformaciones ejecutables con ogl pueden describirse como la multiplicación de dos o más matrices.
Canal de Transformaciones.
deben modificarse dos matrices: la matriz del Modelador y la matriz de Proyección. OpenGL proporciona muchas funciones de alto nivel que hacen muy sencillo la construcción de matrices para transformaciones. Éstas se aplican sobre la matriz que este activa en ese instante. Para activar una de las dos matrices utilizamos la función glMatrixMode. Hay dos parámetros posibles:
glMatrixMode(GL_PROJECTION);
Que es el que activa la matriz de proyección.
glMatrixMode(GL_MODELVIEW);
Que es el que activa el modelador.
Matriz del modelador.
Es una matriz 4x4 que representa el sistema de coordenadas transformado que se está usando para colocar y orientar los objetos. Si se multiplica la matriz del vértice (de tamaño 1x4) por ésta se obtiene otra matriz 1x4 con los vértices transformados sobre ese sistema de coordenadas.
glTranslatef(0.0f, 10.0f, 0.0f);glutSolidSphere(3.0f, 8, 8);glTranslate(10.0f, 0.0f, 0.0f);glutSolidsphere(3.0f, 8, 8);glTranslatef(0.0f, 10.0f, 0.0f);
glutSolidSphere(3.0f, 8, 8);
glLoadIdentity();
glTranslate(10.0f, 0.0f, 0.0f);
glutSolidsphere(3.0f, 8, 8); Una proyección ortográfica es cuadrada en todas sus caras. Esto produce una proyección paralela, útil para aplicaciones de tipo CAD o dibujos arquitectónicos, o también para tomar medidas, ya que las dimensiones de lo que representan no se ven alteradas por la proyección.
Para definir la matriz de proyección ortográfica y multiplicarla por la matriz activa (que debería ser en ese momento la de proyección, GL_PROJECTION), se utiliza la función glOrtho, que se define de la siguiente forma:
glOrtho(limiteIzquierdo, limiteDerecho, limiteAbajo, limiteArriba, znear, zfar)
Proyecciones perspectivas.
Reduce y estirar los objetos más alejados del observador. Es importante saber que las medidas de la proyección de un objeto no tienen por qué coincidir con las del objeto real, ya que han sido deformadas.
Void gluPerspective(angulo, aspecto, znear, zfar); El codigo para crear un cubo en 3D y girarlo es:#pragma comment( lib, "openGL32.lib" ) // Estas directivas del preprocesador #pragma comment( lib, "glu32.lib" ) // le dicen a el enlazador (linker o // linkador o ...) que incluya estas // librerias. Esto se puede hacer en // la configuracion del proyecto en el // Visual C++ pero esto me parece mas // claro. #include <stdio.h> #include <windows.h> // Fichero de inclusion para programas Windows #include <GL/gl.h> // Los ficheros de inclusion de OpenGL. #include <GL/glu.h> // Estrictamente solo es necesario el primero. // El segundo es de funciones utiles que se podrian // hacer de otra manera. No nos vamos a complicar la // vida y la usaremos. HINSTANCE IdAplicacion; // Para guardar el identificador del programa HWND IdVentana; // Para guardar el identificador de la ventana HDC DevContex; // Device Context para conectar la ventana con OpenGL. HGLRC OGLrc; // Render Context para la ventana OpenGL. int AnchoVentana = 600; // Lo que dice el nombre de la variable int AltoVentana = 400; // Lo que dice el nombre de la variable //-------------------------------------------------------------- // Funcion para pintar algo y que se repite continuamente. Se suele // llamar bucle de juego o principal. void Pinta() { // Borro el buffer de atras (BackBuffer), donde voy a pintar. // Tambien el buffer de profundidad para que no pinte lo que // esta detras de otra cosa. glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); // Le digo a OpenGL que voy a cambiar la matriz de modelo glMatrixMode(GL_MODELVIEW); // Cargo la matriz identidad en la modelview, // con eso me aseguro de que ningun cambio anterior modifica // lo que voy ha hacer despues con esta matriz. Empiezo de cero. glLoadIdentity(); // Muevo para atras el objeto. El punto de vista esta // en la posicion 0,0,0 porque no lo he cambiado, asi que // alejo el objeto para poder verlo. glTranslatef(0,0,-4.0f); // Giro el objeto 30 grados en el eje x, luego otros // 30 en el eje y. Es para que quede bonito. glRotatef(30,1,0,0); glRotatef(30,0,1,0); // y pinto el objeto con coordenadas genericas alrededor // del eje de coordenadas. Estas coordenadas que pongo // ahora son modificadas por las modificaciones que // hemos hecho en la matriz modelview (glTranslate, glRotate). // Le digo a OpenGL que voy a pintar y con cuadrados: glBegin(GL_QUADS); // Cara de arriba glColor3f(1,0,0); // rojo glVertex3f( 1.0f, 1.0f,-1.0f); glVertex3f(-1.0f, 1.0f,-1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Cara de abajo glColor3f(1,0,0); // rojo glVertex3f( 1.0f,-1.0f, 1.0f); glVertex3f(-1.0f,-1.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f); glVertex3f( 1.0f,-1.0f,-1.0f); // Cara frontal glColor3f(0,0,1); // azul glVertex3f( 1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f,-1.0f, 1.0f); glVertex3f( 1.0f,-1.0f, 1.0f); // Cara trasera glColor3f(0,0,1); // azul glVertex3f( 1.0f,-1.0f,-1.0f); glVertex3f(-1.0f,-1.0f,-1.0f); glVertex3f(-1.0f, 1.0f,-1.0f); glVertex3f( 1.0f, 1.0f,-1.0f); // Cara izquierda glColor3f(0,1,0); // verde glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f,-1.0f); glVertex3f(-1.0f,-1.0f,-1.0f); glVertex3f(-1.0f,-1.0f, 1.0f); // Cara derecha glColor3f(0,1,0); // verde glVertex3f( 1.0f, 1.0f,-1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glVertex3f( 1.0f,-1.0f, 1.0f); glVertex3f( 1.0f,-1.0f,-1.0f); glEnd(); // Cambio los bufferes de modo que presento lo que he dibujado en pantalla: SwapBuffers(DevContex); } //-------------------------------------------------------------- // Funcion para inicializar OpenGL. void IniciaGL() { // Definimos una estructura de tipo PIXELFORMATDESCRIPTOR para definir // las caracteristicas graficas que queremos usar (dentro de las que nos // permite el OpenGL de nuestra tarjeta de video) static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // Tama�o de este descriptor 1, // N�mero de versi�n PFD_DRAW_TO_WINDOW | // El formato debe soportar ventana PFD_SUPPORT_OPENGL | // El formato debe soportar OpenGL PFD_DOUBLEBUFFER | // Debe soportar Doble Buffer PFD_TYPE_RGBA, // Tambi�n debe soportar RGBA 32, // Bits por pixels seleccionados 0,0,0,0,0,0, // Bits de color ignorados 0, // Sin buffer alpha 0, // Shift bit ignorado 0, // Buffer de acumulaci�n ignorado 0,0,0,0, // Bits de acumulaci�n ignorados 32, // Z-Buffer de 32 bits 0, // Sin buffer de pincel (Stencil) 0, // Sin buffer auxiliar PFD_MAIN_PLANE, // Layer de dibujo principal 0, // Reservado 0,0,0, // Layers de m�scara ignorados }; // Estas funciones son las que hacen que la ventana acepte el modo grafico que // queremos dado el descriptor de pixel anterior. (en el que uno de los parametros // es que funcione con OpenGL). DevContex=GetDC(IdVentana); // Obtengo el "device context" de la ventana. int PixF=ChoosePixelFormat(DevContex,&pfd); // Busco un indice de una conbinacion // que coincida con mis especificaciones SetPixelFormat(DevContex,PixF,&pfd); // Pongo la ventana en el formato que quiero. // Dos funciones de OpenGL por fin (aunque sean especificas de windows). OGLrc=wglCreateContext(DevContex); // Indico a OpenGL que la ventana esta disponible para el. wglMakeCurrent(DevContex,OGLrc); // Le digo a OpenGL que nuestra ventana es donde tiene // que dibujar a partir de ahora. // A partir de ahora podemos considerar OpenGL inicializado. // Aun nos queda decirle los parametros del modo en que dibujar, aunque eso se puede, // y de hecho se hace continuamente, cambiar durante el programa. // Ahora pondremos algunos parametro puramente de OpenGL de inicio. glClearDepth(1.0f); // Profundidad del buffer de profundidad. Hace que lo que esta // mas cerca se vea (dibuje) encima de lo que esta mas lejos. glDepthFunc(GL_LEQUAL); // Comparacion del buffer de profundidad. glEnable(GL_DEPTH_TEST); // Habilita test de profundidad. A partir de ahora, lo que // esta mas cerca se pinta encima. glClearColor(0,0,0,1.0f); // Color del fondo. Color con el que se borra la pantalla, o la // ventana donde pintamos. Cuando usemos la funcion glClear. glShadeModel(GL_SMOOTH); // Renderizado suave. Cuanta mejor calidad mas lento. Nosotros // tenemos un ordenador reciente(+ o -) y queremos que se vea bien. glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST); // Calidad buena de perspectiva. //Definimos un rectangulo (programacion windows) RECT rect; // Extraemos el area cliente de nuestra ventana en rect. GetClientRect(IdVentana, &rect); // Usamos rect (el area cliente o espacio util dentro de los bordes de nuestra ventana) // para definir los limites del espacio que va a usar OpenGL para dibujar (viewport). // asi ajustamos nuestra ventana y OpenGL. glViewport(rect.left,rect.top,rect.right,rect.bottom); // Le dice a OpenGL el tama�o en el que va a pintar en pixels. // Coincidira, en principio, con el tama�o de nuestra ventana y // empezando por 0,0 (la esquina de nuestra ventana). // Le digo a OpenGL que voy a cambiar la matriz de proyeccion glMatrixMode(GL_PROJECTION); // Le digo a OpenGL que use proyeccion perspectiva. // Uso al ancho y alto de mi viewport para calcular el segundo parametro gluPerspective(60.0f, (float)rect.right/(float)rect.bottom, 0.5f, 50.0f); // Definimos una cadena de caracteres para poner en el titulo de nuestra ventana // el tama�o del area cliente (a mi me gusta saber el tama�o de mi ventana de dibujo) char cad[50]; // 50 seran suficientes caracteres. // Relleno mi cadena con lo que voy a poner en la ventana. sprintf(cad,"UnSitioWeb %i x %i",rect.right,rect.bottom); // Modifico el titulo de mi ventana. SetWindowTextA(IdVentana,cad); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); // Borramos la ventana con el color antes establecido // (en realidad solo el backbuffer (la pantalla oculta // donde en realidad estamos pintando) // y borramos tambien el buffer de profundidad SwapBuffers(DevContex); // Le digo a nuestro espacio de dibujo que cambie "muy rapido" el buffer de // dibujo por el de visualizacion y viceversa (suichear en Espanglish) } // Fin IniciaGL //------------------------------------------------------------------ // Funcion para controlar lo que ocurre en la ventana segun los eventos // que vienen de Windows. Mas tarde se asocia a la ventana que crearemos. // De momento solo interceptamos la tecla ESC para poder salir del // programa cuando queramos y el evento de destruccion de ventana con el // que terminamos la aplicacion LRESULT FAR PASCAL ProcesaMensajes(HWND Ventana, UINT Mensaje, WPARAM wParam, LPARAM lParam) { switch(Mensaje) // Seg�n el mensaje recibido { case WM_KEYDOWN: // Caso de mensaje de pulsaci�n de una tecla switch(wParam) // y segun el contenido de la variable wParam { case VK_ESCAPE: // ESC causa la salida del programa // Funcion para enviar mensaje de cerrado a la ventana // y despues de la aplicacion PostMessage(Ventana, WM_CLOSE, 0, 0); break; } break; case WM_DESTROY: // Mensaje de destrucci�n de la ventana (provocado por // nosotros al pulsar ESC o cerrar la ventana. PostQuitMessage(0); // Funcion para salir del programa break; } // Efectuar el proceso por defecto del mensaje (si viene cualquier mensaje // que no hemos usado, querremos que haga lo que suele hacer) return DefWindowProc(Ventana, Mensaje, wParam, lParam); } // fin ProcesaMansajes //------------------------------------------------------------------ // Funcion para crear la ventana de nuestro programa, que asociaremos // al OpenGL para pintar en ella void CreaVentana() { WNDCLASS ClaseVentana; // Declaramos un tipo de ventana, el nuestro. //Definimos nuestro tipo de ventana... ClaseVentana.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Se redibujara si // cambia el tama�o de la ventana horizontal y verticalmente, y un solo // DC (device context) para cada ventana (para cuando empezemos con OpenGL) ClaseVentana.lpfnWndProc = ProcesaMensajes; //La definimos antes para contolar // los mansajes como los del teclado ClaseVentana.cbClsExtra = 0; //Sin memoria extra para la clase ClaseVentana.cbWndExtra = 0; //Sin memoria extra para la ventana ClaseVentana.hInstance = IdAplicacion; // Identificador del programa para asociar // esta ventana con este programa. ClaseVentana.hIcon = NULL; // De momento pasamos de icono ClaseVentana.hCursor = LoadCursor(NULL, IDC_ARROW); // Flecha normal de raton ClaseVentana.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // Por ahora lo ponemos blanco ClaseVentana.lpszMenuName = NULL; // Pasamos de menu ClaseVentana.lpszClassName = L"UnSitioWeb"; // Nombre de la clase (la "L" antes de la cadena es // por que solo admite cadenas de caracteres // unicode -LPCWSTR o TCHAR- y con la "L" se convierte). RegisterClass(&ClaseVentana); // Registramos nuestro tipo de ventana IdVentana = CreateWindowEx( // Funcion que crea la ventana. Guardamos el identificativo. WS_EX_APPWINDOW, // estilo extendido de ventana L"UnSitioWeb", // Nombre de la clase puesto antes (la "L" antes para convertir a UNICODE) L"Un Sitio Web", // Titulo que aparecera en la ventana. WS_POPUPWINDOW|WS_CAPTION|WS_MINIMIZEBOX, // Parametros de como sera la ventana // se pueden combinar varios. 100, // Posicion Horizontal. 100, // Posicion Vertical. AnchoVentana, // Ancho de la ventana. AltoVentana, // Alto de la ventana. (HWND) NULL, // No depende de otra ventana. (HMENU) NULL, // No le damos un menu diferente al de la clase (que es ninguno). IdAplicacion, // Identificador del programa al que pertenece la ventana. Al // empezar lo guardamos en una variable para usarlo despues. (LPVOID) NULL ); // Puntero a "no se que" datos (pasamos del tema, es para // aplicaciones MIDI). ShowWindow(IdVentana, SW_SHOW); // Mostramos nuestra ventana. UpdateWindow(IdVentana); // La actualizo para que muestre lo que tenga que mostrar (ahora nada). } // Fin CreaVentana //------------------------------------------------------------------ // Funcion principal de un programa Windows (como main en C normal, aqui WinMain) // el prorama empieza a ejecutarse a partir de esta funcion. int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(lpszCmdLine); // Para que no moleste al compilar, no lo usamos. IdAplicacion = hInstance; // Guardo el identificador del programa, luego lo usamos. CreaVentana(); // Funcion que crea la ventana. Definida mas arriba. IniciaGL(); // Funcion para inicializar OpenGL. Definida mas arriba. // Este es el bucle habitual de windows que se esta ejecutando continuamente hasta que recibe // el mensaje de acabar (ya lo hemos preparado en la funcion "ProcesaMensajes" asociada a // la ventana, tambien asociada a este programa atraves de su identificativo). MSG Mensaje; // Varible para contener los mensajes que van llegando. while(TRUE) // Se ejecuta continuamente. { Pinta(); // Funcion que pinta algo y se repite continuamente al estar aqui. if(PeekMessage(&Mensaje, NULL, 0, 0, PM_NOREMOVE)) // Exploramos la cola de mensajes. {// proces�ndolos adecuadamente if(!GetMessage(&Mensaje, NULL, 0, 0)) return (int)Mensaje.wParam; // En este caso terminamos. TranslateMessage(&Mensaje); DispatchMessage(&Mensaje); } else WaitMessage(); // en caso contrario esperamos un mensaje } } // fin WinMain //------------------------------------------------------------------
Muy buena información, Gracias!!
ResponderEliminar