Difference between revisions of "OpenGL Tutorial/es"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting)
 
(15 intermediate revisions by 5 users not shown)
Line 1: Line 1:
 
{{OpenGL Tutorial}}
 
{{OpenGL Tutorial}}
[[category:Español|x]][[category:Castellano|x]]
+
[[category:Español|?]][[category:Castellano|?]]
  
   OpenGL es el principal entorno para el diseño de aplicaciones 2D y 3D portables. Desde su introducción en 1992, OpenGL ha llegado a ser el interface de programación de aplicaciones (API) gráficas 2D y 3D más ampliamente utilizado en la industria, aportando miles de aplicaciones a una gran variedad de plataformas de computación. OpenGL promuebe la innovación y acelera el diseño de aplicaciones mediante la incorporación de un amplio conjunto de renderizados, mapeo de texturas, efectos especiales y otras potentes funciones de visualización. Los desarrolladores pueden llevar el potencial de OpenGL a través de todos los escritorios más extendidos y plataformas de estaciones de trabjo, asegurando un amplio desarrollo de aplicaciones.
+
   OpenGL es el principal entorno para el diseño de aplicaciones 2D y 3D portables. Desde su introducción en 1992, OpenGL ha llegado a ser la interfaz de programación de aplicaciones (API) gráficas 2D y 3D más ampliamente utilizadao en la industria, aportando miles de aplicaciones a una gran variedad de plataformas de computación. OpenGL promueve la innovación y acelera el diseño de aplicaciones mediante la incorporación de un amplio conjunto de acabados, aplicACIÓN de texturas, efectos especiales y otras potentes funciones de visualización. Los desarrolladores pueden llevar la potencia de OpenGL a través de todos los escritorios más extendidos y plataformas de estaciones de trabajo, asegurando un amplio desarrollo de aplicaciones.
  
 
   Puedes encontrar más información acerca de OpenGL [http://www.opengl.org/about/overview/ aquí].
 
   Puedes encontrar más información acerca de OpenGL [http://www.opengl.org/about/overview/ aquí].
Line 8: Line 8:
 
= GLUT =
 
= GLUT =
  
   GLUT (pronunciado como el glut en glotonería) es el conjunto de utilidades OpenGL, un kit de utilidades de ventana independiente para escribir programas OpenGL. Implementa un interface simple de programación de aplicaciones (API) de ventana para OpenGL. GLUT hace que sea considerablemente más fácil de aprender y explorar la programación con OpenGL aportando una API portable de forma que permite escribir un programa que funcionará en todos los PC y estaciones de trabajo en las diferentes plataformas existentes.
+
   GLUT es el conjunto de utilidades OpenGL, un conjunto de utilidades de ventana independiente para escribir programas OpenGL. Implementa una interfaz gráfica simple de programación de aplicaciones (API) para OpenGL. GLUT hace que sea considerablemente más fácil de aprender y explorar la programación con OpenGL aportando una API portable de forma que permite escribir un programa que funcionará en todos los PC y estaciones de trabajo en las diferentes plataformas existentes.
  
Se puede encontrar más información acerca de GLUT [http://www.opengl.org/resources/libraries/glut/ aquí].
+
   Se puede encontrar más información acerca de GLUT [http://www.opengl.org/resources/libraries/glut/ aquí].
  
Algunos Sistemas Operativos llevan preinstalado GLUT, pero en el caso de que no sea así se puede encontrar fácilmente utilizando [http://www.google.com/ Google].
+
   Algunos Sistemas Operativos llevan preinstalado GLUT, pero en el caso de que no sea así se puede encontrar fácilmente utilizando [http://www.google.com/ Google].
  
Los binarios para Windows se pueden descargar desde [http://www.xmission.com/~nate/glut.html www.xmission.com].
+
   Los binarios para Windows se pueden descargar desde [http://www.xmission.com/~nate/glut.html www.xmission.com].
  
La información de las units para GLUT bajo FPC se encuentran aquí [[OpenGL]].
+
   La información de las unidades de FPC para GLUT se encuentran aquí [[OpenGL]].
  
 
= LCL =
 
= LCL =
  
La librería de componente de Lazarus también se puede utilizar con OpenGL. Lazarus incluye un control "TOpenGLControl" - un control LCL con un contexto OpenGL. Se puede encontrar el paquete LazOpenGLContext en lazarus/components/opengl/lazopenglcontext.lpk. Además podemos encontrar un ejemplo en lazarus/examples/openglcontrol/openglcontrol_demo.lpi.
+
   La librería de componente de Lazarus también se puede utilizar con OpenGL. Lazarus incluye un control "TOpenGLControl" - un control LCL con un contexto OpenGL. Se puede encontrar el paquete LazOpenGLContext en lazarus/components/opengl/lazopenglcontext.lpk. Además podemos encontrar un ejemplo en lazarus/examples/openglcontrol/openglcontrol_demo.lpi.
  
 
= LCL / GLUT =
 
= LCL / GLUT =
  
¿Cuando utilizar GLUT, y cuando LCL?
+
   ¿Cuando utilizar GLUT, y cuando LCL?
  
 
*GLUT es mejor si deseas dibujar todo tu mismo.
 
*GLUT es mejor si deseas dibujar todo tu mismo.
*LCL es mejor para las aplicaciones habituales. Por ejemplo un editor 3D necesita una pocas ventanas OpenGL y el resto es una aplicación normal utilizando buttons, comboboxes, windows, modal windows, etc.
+
*LCL es mejor para las aplicaciones habituales. Por ejemplo un editor 3D necesita una pocas ventanas OpenGL y el resto es una aplicación normal utilizando botones, cajas de edición, ventanas, ventanas modales, etc.
 
 
La parte OpenGL es otro tanto de lo mismo. GLUT necesita una dll bajo windows, donde LCL typically runs out of the box, pero el ejecutable LCL es bastante más grande.
 
  
 +
   La parte OpenGL es otro tanto de lo mismo. GLUT necesita una dll en windows, mientras la LCL no requiere ninguna, pero el ejecutable LCL es bastante más grande.
  
 
= Creando tu primer programa GLUT =
 
= Creando tu primer programa GLUT =
  
Para utilizar GLUT, primero hay que inicializarlo. Esto se hace utilizando la función '''glutInit'''. Esta función puede pasar la línea de mandatos y sus parámetros a la ventana principal, pero espera su entrada en el estilo de C/C++. Tendrás que escribir tu propia función para realizar la conversión de ParamCount (cuenta de parámetros) y ParamStr (cadena de parámetros) al estilo parámetros de línea de mandato tipo C/C++.
+
   Para utilizar GLUT, primero hay que iniciarlo. Esto se hace utilizando la función '''glutInit'''. Esta función analiza la línea de ordenes y pasa los parámetros a la ventana principal, pero espera la entrada en estilo C/C++. Tendrás que escribir tu propia función para realizar la conversión de ''ParamCount'' y ''ParamStr'' a parámetros al estilo C/C++ de línea de ordenes.
  
<delphi>
+
<syntaxhighlight lang=pascal> procedure glutInitPascal(ParseCmdLine: Boolean);  
procedure glutInitPascal(ParseCmdLine: Boolean);  
 
 
  var
 
  var
 
   Cmd: array of PChar;
 
   Cmd: array of PChar;
Line 50: Line 48:
 
     Cmd[I] := PChar(ParamStr(I));
 
     Cmd[I] := PChar(ParamStr(I));
 
   glutInit(@CmdCount, @Cmd);
 
   glutInit(@CmdCount, @Cmd);
  end;
+
  end;</syntaxhighlight>
</delphi>
 
  
En esencia lo que hace es crear una matriz y rellenarla con las cadenas (strings) introducidos en ParamStr. Esta función además toma un parámetro que puede controlar que se pasa a glutInit -- bien sea la línea de mandatos completa o bien sólamente el nombre del fichero ejecutable.
+
&nbsp;&nbsp;&nbsp;En esencia lo que hace es crear una matriz y rellenarla con las cadenas (strings) de ParamStr. Este procedimiento recibe un parámetro que permite controlar lo que se pasa a glutInit, bien la línea de mandatos completa o bien sólo el nombre del fichero ejecutable.
  
ToDo: probablemente '''glutInit(@argc, @argv);''' sea suficiente.
+
&nbsp;&nbsp;&nbsp;ToDo: probablemente '''glutInit(@argc, @argv);''' sea suficiente.
  
 +
&nbsp;&nbsp;&nbsp;Más acerca de glutInit: http://www.opengl.org/resources/libraries/glut/spec3/node10.html
  
Más acerca de glutInit: http://www.opengl.org/resources/libraries/glut/spec3/node10.html
+
&nbsp;&nbsp;&nbsp;Ahora, necesitas crear una ventana principal. Ajusta el modo de visualización de la ventana principal con '''glutInitDisplayMode'''. Únicamente recibe un parámetro, que es una combinación de indicadores. En general todo lo que se necesita es '''GLUT_DOUBLE''' o '''GLUT_RGB o GLUT_DEPTH'''.
  
Next, you need to create a main window.
+
&nbsp;&nbsp;&nbsp;Más acerca de glutInitDisplayMode: http://www.opengl.org/resources/libraries/glut/spec3/node12.html
Set the display mode for the main window using '''glutInitDisplayMode'''. Esto solamente toma un parámetro, el cual es una combinación de flags. Con frecuencia todo lo que se necesita es únicamente '''GLUT_DOUBLE o GLUT_RGB o GLUT_DEPTH'''.
 
  
Más acerca de glutInitDisplayMode: http://www.opengl.org/resources/libraries/glut/spec3/node12.html
+
&nbsp;&nbsp;&nbsp;La posición y el tamaño de la ventana se controla utilizando '''glutInitWindowPosition''' y '''glutInitWindowSize'''. Toman 2 parámetros. Las coordenada X e Y en el former, y width (ancho) y height (alto) en el latter. Se puede utilizar '''glutGet''' para obtener el tamaño de pantalla y centrar la ventana.
  
La posición y el tamaño de la ventana se controla utilizando '''glutInitWindowPosition''' y '''glutInitWindowSize'''. Toman 2 parámetros. Las coordenada X e Y en el former, y width (ancho) y height (alto) en el latter. Se puede utilizar '''glutGet''' para obtener el tamaño de pantalla y centrar la ventana.
+
&nbsp;&nbsp;&nbsp;Más acerca de glutInitWindowPosition, glutInitWindowSize y glutGet: http://www.opengl.org/resources/libraries/glut/spec3/node11.html  
 
 
Más acerca de glutInitWindowPosition, glutInitWindowSize y glutGet: http://www.opengl.org/resources/libraries/glut/spec3/node11.html  
 
 
http://www.opengl.org/documentation/specs/glut/spec3/node70.html  
 
http://www.opengl.org/documentation/specs/glut/spec3/node70.html  
  
Finalmente se debería crear la ventana utilizando la función '''glutCreateWindow'''. Esto generará la ventana y establecerá su caption a través de un parámetro. Como resultado se retornará el manejador (handler) de ventana. Esto se puede utilizar con otras funciones que lo requieran.
+
&nbsp;&nbsp;&nbsp;Finalmente se debería crear la ventana utilizando la función '''glutCreateWindow'''. Esto generará la ventana y establecerá su caption a través de un parámetro. Como resultado se retornará el manejador (handler) de ventana. Esto se puede utilizar con otras funciones que lo requieran.
  
Más acerca de glutCreateWindow: http://www.opengl.org/resources/libraries/glut/spec3/node16.html  
+
&nbsp;&nbsp;&nbsp;Más acerca de glutCreateWindow: http://www.opengl.org/resources/libraries/glut/spec3/node16.html  
  
Antes de que el programa pueda entrar en el bucle principal, se deben establecer algunas callbacks. Se necesitarán las callback para dibujar la ventana, para su redimensionado y para obtener las entradas desde el teclado. Estas callbacks se establecen utilizando '''glutDisplayFunc''', '''glutReshapeFunc''' y '''glutKeyboardFunc'''.
+
&nbsp;&nbsp;&nbsp;Antes de que el programa pueda entrar en el bucle principal, se deben establecer algunas callbacks. Se necesitarán las callback para dibujar la ventana, para su redimensionado y para obtener las entradas desde el teclado. Estas callbacks se establecen utilizando '''glutDisplayFunc''', '''glutReshapeFunc''' y '''glutKeyboardFunc'''.
  
Más acerca de las callbacks: http://www.opengl.org/resources/libraries/glut/spec3/node45.html#SECTION00080000000000000000  
+
&nbsp;&nbsp;&nbsp;Más acerca de las callbacks: http://www.opengl.org/resources/libraries/glut/spec3/node45.html#SECTION00080000000000000000  
  
Tu función de dibujo podría parecerse a lo siguiente:
+
&nbsp;&nbsp;&nbsp;Tu función de dibujo podría parecerse a lo siguiente:
  
<delphi> procedure DrawGLScene; cdecl;
+
<syntaxhighlight lang=pascal> procedure DrawGLScene; cdecl;
 
  begin
 
  begin
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glutSwapBuffers;
 
   glutSwapBuffers;
  end;</delphi>
+
  end;</syntaxhighlight>
  
Esto solamente borra la ventana con el color de fondo y realiza un reset del zbuffer (no te preocupes acerca de que puede ser zbuffer...más acerca de esto más adelante).
+
&nbsp;&nbsp;&nbsp;Esto solamente borra la ventana con el color de fondo y realiza un reset del zbuffer (no te preocupes acerca de que puede ser el zbuffer...más acerca de esto más adelante).
  
La función de redimensionado podría parecerse a esto:
+
&nbsp;&nbsp;&nbsp;La función de redimensionado podría parecerse a esto:
  
<delphi> procedure ReSizeGLScene(Width, Height: Integer); cdecl;
+
<syntaxhighlight lang=pascal> procedure ReSizeGLScene(Width, Height: Integer); cdecl;
 
  begin
 
  begin
 
   if Height = 0 then
 
   if Height = 0 then
Line 102: Line 97:
 
   glMatrixMode(GL_MODELVIEW);
 
   glMatrixMode(GL_MODELVIEW);
 
   glLoadIdentity;
 
   glLoadIdentity;
  end;</delphi>
+
  end;</syntaxhighlight>
  
Con este código, se le indica a OpenGL donde debe dibujar en la ventana y establecer las matrices a los valores deseados (las funciones de matriz se explicarán más adelante).
+
&nbsp;&nbsp;&nbsp;Con este código, se le indica a OpenGL donde debe dibujar en la ventana y establecer las matrices a los valores deseados (las funciones de matriz se explicarán más adelante).
  
La entrada de teclado (Keyboard) se evalúa con la siguiente callback:
+
&nbsp;&nbsp;&nbsp;La entrada de teclado (Keyboard) se evalúa con la siguiente callback:
  
<delphi> procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
+
<syntaxhighlight lang=pascal> procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 
  begin
 
  begin
 
   if Key = 27 then
 
   if Key = 27 then
 
     Halt(0);
 
     Halt(0);
  end;</delphi>
+
  end;</syntaxhighlight>
  
Esta función indica al programa que debe salir del mismo si se presiona la tecla ESC. GLUT está orientado a eventos y el único medio de terminar el programa es llamar '''Halt''' desde dentro de una de las funciones callback. Si se cierra la ventana de algún otro modo, desaparecerá, pero el programa continuará en el bucle dentro de la rutina principal indefinidamente. Para comenzar el bucle principal hay que llamar a la función '''glutMainLoop'''. Esto iniciará un bucle que nunca finalizará y que realizará las llamadas a todas las funciones callback.
+
&nbsp;&nbsp;&nbsp;Esta función indica al programa que debe salir del mismo si se presiona la tecla ESC. GLUT está orientado a eventos y el único medio de salir del programa es llamar '''Halt''' desde dentro de una de las funciones callback. Si se cierra la ventana de algún otro modo, desaparecerá, pero el programa continuará en el bucle dentro de la rutina principal indefinidamente. Para comenzar el bucle principal hay que llamar a la función '''glutMainLoop'''. Esto iniciará un bucle que nunca finalizará y que realizará las llamadas a todas las funciones callback.
  
La porción principal del programa puede parecerse a lo siguiente:
+
&nbsp;&nbsp;&nbsp;La porción principal del programa puede parecerse a lo siguiente:
  
<delphi> const  
+
<syntaxhighlight lang=pascal> const  
 
   AppWidth = 640;  
 
   AppWidth = 640;  
 
   AppHeight = 480;  
 
   AppHeight = 480;  
Line 146: Line 141:
 
   
 
   
 
   glutMainLoop;  
 
   glutMainLoop;  
  end.</delphi>
+
  end.</syntaxhighlight>
  
The next tutorial will add some code that will draw a simple shape.  
+
&nbsp;&nbsp;&nbsp;La siguiente tutoría añade un poco de código que dibuja una figura sencilla.
  
Download source code or a linux/windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
&nbsp;&nbsp;&nbsp;Descarga el código fuente o un  ejecutable  Linux / Windows desde [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
  
=Dibujando una forma simple=
+
=Dibujando una figura simple=
  
'''Nota:''' The following parts are almost only OpenGL, so they run under GLUT and LCL. You can recognize GLUT specific functions with the prefix 'glu'.
+
'''Nota:''' Las siguientes partes tratan principalmente sobre OpenGL, por lo que utilizan las librerías GLUT y LCL. Las funciones específicas de GLUT se reconocen porque llevan el prefijo 'glu'.
  
This time we shall add just a few lines of code and focus on explanation of some of the OpenGL functions.
+
&nbsp;&nbsp;&nbsp;Esta vez solo añadiremos unas pocas líneas de código y nos centraremos en explicar algunas de las funciones de OpenGL.
  
Let us explain code you allready have.
+
&nbsp;&nbsp;&nbsp;Expliquemos el código que ya tenemos escrito.
 
   
 
   
<delphi>  .
+
<syntaxhighlight lang=pascal>  .
 
   .
 
   .
 
   .
 
   .
Line 169: Line 164:
 
   glMatrixMode(GL_MODELVIEW);
 
   glMatrixMode(GL_MODELVIEW);
 
   glLoadIdentity;
 
   glLoadIdentity;
  end;</delphi>
+
  end;</syntaxhighlight>
  
Using '''glMatrixMode''' function you chose which matrix you want to change. OpenGL works with 3 matrices:
+
&nbsp;&nbsp;&nbsp;Usando la función '''glMatrixMode''' elegimos que matriz queremos cambiar. OpenGL trabaja con 3 matrices:
'''GL_MODELVIEW''': this one is used to move vertex to model space.
+
'''GL_MODELVIEW''': Se usa para mover el vértice al espacio del modelo.
'''GL_PROJECTION''': this one is used to convert 3d coordinate to 2d coordinate for finall pixel position.
+
'''GL_PROJECTION''': Se usa para convertir las coordenadas 3d en coordenadas 2d para determinar la posición final del pixel.
'''GL_TEXTURE''': this one is used to alter texture coordinates.
+
'''GL_TEXTURE''': Se usa para alterar las coordenadas de la textura.
  
Once you chose matrix you want to change, you can call functions that affect matrix values. '''glLoadIdentity''' will reset matrix so it doesn't affect vertex position. Since almost all matrix functions multiply current matrix with a generated one, you sometimes need to clear matrix with this function.
+
&nbsp;&nbsp;&nbsp;Una vez que elegimos la matriz que queremos cambiar, podemos llamar a las funciones que afectan a los valores de la matriz. '''glLoadIdentity''' reinicializa la matriz por lo que no afectará a la posición del vértice. Puesto que casi todas las funciones de matrices multiplican la matriz actual por una generada, a veces necesitaremos limpiar la matriz usando esta función.
  
In order to set perspective matrix, you can use '''gluPerspective''' function. Four parameters present the field of view, aspect ratio, near and far plane. It's that simple.
+
&nbsp;&nbsp;&nbsp;Para establecer una matriz de perspectiva, podemos usar la función '''gluPerspective'''. Cuatro parámetros presentan el punto de vista, aspecto (aspect radio), distancia la plano. Es así de simple.
  
Now, you'll change model matrix... for this time, you just set it to identity.
+
&nbsp;&nbsp;&nbsp;Ahora, vamos a cambiar la matriz modelo... esta vez la ajustamos a la identidad.
  
OK... y ahora, el código para dibujar la primera forma:
+
&nbsp;&nbsp;&nbsp;Bien... y ahora, el código para dibujar la primera figura:
  
<delphi> procedure DrawGLScene; cdecl;
+
<syntaxhighlight lang=pascal> procedure DrawGLScene; cdecl;
 
  begin
 
  begin
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
Line 203: Line 198:
 
   
 
   
 
   glutSwapBuffers;
 
   glutSwapBuffers;
  end;</delphi>
+
  end;</syntaxhighlight>
  
We have allready used glClear function. It will just reset buffers. We'll skip next two functions and head for drawing ones.
+
&nbsp;&nbsp;&nbsp;Ya hemos usado la función glClear, que reseteará los buffers. Vamos a saltarnos las dos siguientes funciones, y vayamos directamente a las de dibujo.
  
'''glBegin''' marks beginning of drawing block. After this function you can start entering vertices. Parameter describes how are vertices used when drawing:
+
&nbsp;&nbsp;&nbsp;'''glBegin''' marca el inicio de un bloque de dibujo. Después de esta función puedes empezar a introducir vértices. Los parámetros describen como van a ser usados los vértices cuando se dibujen.
GL_POINTS: Treats each vertex as a single point. Vertex n defines point n. N points are drawn.
+
*GL_POINTS: Trata cada vértices como un único punto. Vertex n define el punto n. Pueden dibujarse n.
 +
*GL_LINES: Trata cada par de vérices como un segmento independiente. Los vértices 2n-1 y 2n definen la línea n. Se dibujan n/2 lineas.
 +
*GL_LINE_STRIP: Dibuja un grupo de segmentos de línea conectados entre sí. Se dibujan n-1 lineas.
 +
*GL_LINE_LOOP: Dibuja un grupo de segmentos de lineas conectados entre sí. El primer vértice y el último también están conectados, formando un lazo. Los vertices n y n+1 definen la línea n. La última linea es definida por los vertices n y 1. Se dibujan n líneas.
 +
*GL_TRIANGLES: Trata cada triplete de vértices como un triángulo independiente. Los vértices 3n-2, 3n-1 y 3n definen el triángulo. Se dibujan n/3 triángulos.
 +
*GL_TRIANGLE_STRIP: Dibuja un grupo de triángulos conectados. Un triángulo es definido por cada vértice creado después de los dos primeros vértices. Para valores inpares de n, vertices n, n+1 and n+2 define triangle n. For even n, vertices n+1, n and n+2 define triangle n. Se dibujan n-2 triángulos.
 +
*GL_TRIANGLE_FAN: Dibuja un grupo de triángulos conectados. Un triángulo se define con cada vértice creado después de los dos primeros vértices. Los vértices 1. n+1 y n+2 definen el triángulo n. Se dibujan n-2 triángulos.
 +
*GL_QUADS: Trata cada grupo de 4 vértices como un cuadrilátero independiente. Los vértices 4n-3, 4n-2, 4n-1 y 4n definen el cuadrilátero n. Se dibujan n/4 cuadriláteros.
 +
*GL_QUAD_STRIP: Dibuja un grupo de cuadriláteros conectados. Un cuadrilátero es definido por cada pa de vértices creados después del primer par. Los vértices 2n-1, 2n, 2n+2 y 2n+1 definen el cuadrilátero n. Se dibujan n/2-1 cuadriláteros. Tenga en cuenta que el orden en el que los vértices se usan para crear un cuadrilátero a partir de strip data es diferente from that used with independent data.
 +
*GL_POLYGON: Dibuja un polígono convexo. Los vértices del 1 al n definen este polígono.
  
GL_LINES: Treats each pair of vertices as an independent line segment. Vertices 2n-1 and 2n define line n. n/2 lines are drawn.
+
[[Image:SimpleShapePic1.jpg|thumb]]&nbsp;&nbsp;&nbsp; You'll draw single triangle and for that GL_TRIANGLES flag will do the trick. La función '''glVertex3f''' define la posición del vértice que queremos dibujar. Hay más funciones glVertex*. Solo se diferencian en el número y tipo de los parámetros que introducimos. For instance... glVertex2i toma dos parámetros (X e Y) de tipo integer. Casi siempre glVertex3f será la función que más se use.
  
GL_LINE_STRIP: Draws a connected group of line segments from the first vertex to the last. n-1 lines are drawn.
+
&nbsp;&nbsp;&nbsp;Before glVertex you can set color, material, textura... Por simplicidad you'll just specify color para cada vértice en este tutorial. El color se establece usando la función '''glColor3f'''. glColor tambien puede tomar un conjunto de parámetros diferentes como glVertex.
  
GL_LINE_LOOP: Draws a connected group of line segments from the first vertex to the last, then back to the first. Vertices n and n+1 define line n. The last line, however, is defined by vertices n and 1. n lines are drawn.
+
&nbsp;&nbsp;&nbsp;As we look through code podemos ver que asignamos a Z el valor 0 en todos los vértices. Since you set near plane to 0.1, triangle will not be visible. That is where those two functions we skipped in the beginning jump in. ya sabemos que glLoadIdentity resetea la matriz. '''glTranslatef''' moves triangles by X, Y and Z values you provide. Since you set Z to -5 (negative Z is farther from camera) todos los vertices se dibujarán 5 unidades far from point of view and will be visible.
  
GL_TRIANGLES: Treats each triplet of vertices as an independent triangle. Vertices 3n-2, 3n-1 and 3n define triangle n. n/3 triangles are drawn.
+
&nbsp;&nbsp;&nbsp;Al final llamamos a la función '''glEnd''' que termina el proceso de dibujado. Si queremos, podemos comenzar otro bloque de dibujado usando glBegin.
  
GL_TRIANGLE_STRIP: Draws a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. For odd n, vertices n, n+1 and n+2 define triangle n. For even n, vertices n+1, n and n+2 define triangle n. n-2 triangles are drawn.
+
&nbsp;&nbsp;&nbsp;Descargue el código fuente, el ejecutable de linux o windows desde [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
  
GL_TRIANGLE_FAN: Draws a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. Vertices 1. n+1 and n+2 define triangle n. n-2 triangles are drawn.
+
=Usando [http://es.wikipedia.org/wiki/Lista_de_despliegue Listas de despliegue]=
  
GL_QUADS: Treats each group of four vertices as an independent quadrilateral. Vertices 4n-3, 4n-2, 4n-1 and 4n define quadrilateral n. n/4 quadrilaterals are drawn.
+
&nbsp;&nbsp;&nbsp;A veces necesitaras dibujar algunos objetos varias veces en la escena. OpenGL tiene la habiliad de construir '''Listas de despliegue (''display lists'')''' con las cuales dibujar se hace un poco más rápido. Crear una lista de despliegue es muy fácil... dibuja los vértices como hiciste en el tutorial anterior, y enciérralos con las llamadas '''glNewList''' y '''glEndList'''.
  
GL_QUAD_STRIP: Draws a connected group of quadrilaterals. One quadrilateral is defined for each pair of vertices presented after the first pair. Vertices 2n-1, 2n, 2n+2 and 2n+1 define quadrilateral n. n/2-1 quadrilaterals are drawn. Note that the order in which vertices are used to construct a quadrilateral from strip data is different from that used with independent data.
+
<syntaxhighlight lang=pascal> const
 
 
GL_POLYGON: Draws a single, convex polygon. Vertices 1 through n define this polygon.
 
 
 
[[Image:SimpleShapePic1.jpg|thumb]] You'll draw single triangle and for that GL_TRIANGLES flag will do the trick. '''glVertex3f''' function defines the position of a vertex you want to draw. There are more glVertex* functions. Only difference is number and type of parameters they take. For instance... glVertex2i takes two parameters (x and y) of integer type. glVertex3f will almost always be just what you need.
 
 
 
Before glVertex you can set color, material, texture... For simplicity you'll just specify color for each vertex in this tutorial. Color is set using '''glColor3f''' function. glColor can also take different set of parameters like glVertex.
 
 
 
As we look through code we can see that Z is set to 0 for all vertices. Since you set near plane to 0.1, triangle will not be visible. That is where those two functions we skipped in the beginning jump in. We already know that glLoadIdentity reset matrix. '''glTranslatef''' moves triangles by X, Y and Z values you provide. Since you set Z to -5 (negative Z is farther from camera) all vertices will be drawn 5 units far from point of view and will be visible.
 
 
 
In the end you call '''glEnd''' functions that finishes drawing. You could now start another drawing block with new glBegin function if you wish.
 
 
 
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
 
 
 
=Using display lists=
 
 
 
Sometimes you'll need to draw some object multiple times on scene. OpenGL has ability to build '''display lists''' which make drawing a bit faster. Creating display list is very easy... just draw vertices as you did in previous tutorial and enclose them with '''glNewList''' and '''glEndList''' calls.
 
 
 
<delphi> const
 
 
   LIST_OBJECT = 1;
 
   LIST_OBJECT = 1;
 
   
 
   
Line 285: Line 271:
 
     glEnd;
 
     glEnd;
 
   glEndList;
 
   glEndList;
  end;</delphi>
+
  end;</syntaxhighlight>
 +
 
 +
&nbsp;&nbsp;&nbsp;'''glNewList''' crea una nueva lista de despliegue y todas las funciones de dibujo serán grabadas hasta que se llame a '''glEndList'''. El primer parámetro para la función glNewList es list ID. Toda lista es definida por su ID. Si una lista con un ID dado ya está creada, será borrada antes de grabar. Si el segundo parámetro es GL_COMPILE entonces todas las funciones solamente son grabadas, pero si es GL_COMPILE_AND_EXECUTE entonces son guardadas y ejecutadas automáticamente.
  
'''glNewList''' creates new display list and all drawing functions will be recorded until '''glEndList''' is called. The first parameter for glNewList function is list ID. Every list is defined by it's ID. If list with given ID is already created it fill be cleared before recording. If the second parameter is GL_COMPILE then all drawing functions are just recorded, but if it is GL_COMPILE_AND_EXECUTE then they are recorded and executed automaticly.
+
&nbsp;&nbsp;&nbsp;'''glIsList''' es una función que puede ayudar con tus listas de despliegue. Puede decirte si un ID de una lista ya contiene datos.
  
'''glIsList''' function can help you with display lists. It can tell if some list ID is already filled with data.
+
&nbsp;&nbsp;&nbsp;Otra función útil es ''glGenLists'''. Creará múltiples listas. Puedes pasar el número de listas de despliegue que necesitas y obtendrás el ID del primero. Si necesitas n listas, y obtienes un ID, listas de despliegue son: r, r+1, r+2,..., r+n-1
Another useful function is '''glGenLists'''. It will create multiple empty display lists. You pass number of display lists you need and you get ID of the first one. If you require n lists, and get r ID, generated display lists are: r, r+1, r+2,..., r+n-1
 
  
All created lists should be deleted. You will do that before program exits:
+
&nbsp;&nbsp;&nbsp;Todas las listas deben ser borradas antes de que el programa acabe:
  
<delphi> procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
+
<syntaxhighlight lang=pascal> procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 
  begin
 
  begin
 
   if Key = 27 then
 
   if Key = 27 then
Line 301: Line 288:
 
     Halt(0);
 
     Halt(0);
 
   end;
 
   end;
  end;</delphi>
+
  end;</syntaxhighlight>
  
'''glDeleteLists''' takes 2 parameters, ID of display list and number of lists to delete. If ID is r, and number of lists to delete is n, deleted lists are: r, r+1, r+2,..., r+n-1
+
&nbsp;&nbsp;&nbsp;'''glDeleteLists''' tiene 2 parámetros, ID de lista de despliegue y un número de listas a borrar. Si el ID es r, y el número de listas a borrar es n, las litas borradas son: r, r+1, r+2,..., r+n-1
  
Now you know how to create and delete display lists... let's see how to draw them:
+
&nbsp;&nbsp;&nbsp;Ahora conoces como crear y borrar listas, vamos a ver como dibujarlas:
  
<delphi> procedure DrawGLScene; cdecl;
+
<syntaxhighlight lang=pascal> procedure DrawGLScene; cdecl;
 
  begin
 
  begin
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
Line 327: Line 314:
 
   
 
   
 
   glutSwapBuffers;
 
   glutSwapBuffers;
  end;</delphi>
+
  end;</syntaxhighlight>
 
 
[[Image:DisplayListsPic1.jpg|thumb]] Using '''glCallList''' you can draw only one display list. In this tutorial, before drawing display list, you change model matrix and draw object in different places.
 
Some times you would like to draw multiple lists at once. That is possible using '''glCallLists''' function. It takes number of lists you want to draw, type of array that contains display list IDs and array with display list IDs. Type of list can be one of the following:
 
 
 
GL_BYTE: list is treated as an array of signed bytes, each in the range -128 through 127.
 
 
 
GL_UNSIGNED_BYTE: list is treated as an array of unsigned bytes, each in the range 0 through 255.
 
  
GL_SHORT: list is treated as an array of signed two-byte integers, each in the range -32768 through 32767.
+
[[Image:DisplayListsPic1.jpg|thumb]]&nbsp;&nbsp;&nbsp;Usando '''glCallList''' puedes dibujar solo un display list. En este tutorial, antes de display list, puedes cambiar la matriz modelo, y dibujar en diferentes lugares.
 +
&nbsp;&nbsp;&nbsp;A veces te gustaria dibujar multiples listas de una vez. Esto es posible usando la función '''glCallLists'''. Coge el número de listas que quieres dibujar, es un array que contiene los ID de display list. El tipo de lista puede ser una de los siguientes:
 +
*GL_BYTE: list is treated as an array of signed bytes, each in the range -128 through 127.
 +
*GL_UNSIGNED_BYTE: list is treated as an array of unsigned bytes, each in the range 0 through 255.
 +
*GL_SHORT: list is treated as an array of signed two-byte integers, each in the range -32768 through 32767.
 +
*GL_UNSIGNED_SHORT: list is treated as an array of unsigned two-byte integers, each in the range 0 through 65535.
 +
*GL_INT: lists is treated as an array of signed four-byte integers.
 +
*GL_UNSIGNED_INT: list is treated as an array of unsigned four-byte integers.
 +
*GL_FLOAT: list is treated as an array of four-byte floating-point values.
 +
*GL_2_BYTES: list is treated as an array of unsigned bytes. Each pair of bytes specifies a single display list ID. The value of the pair is computed as 256 times the unsigned value of the first byte plus the unsigned value of the second byte.
 +
*GL_3_BYTES: list is treated as an array of unsigned bytes. Each triplet of bytes specifies a single display list ID. The value of the triplet is computed as 65536 times the unsigned value of the first byte, plus 256 times the unsigned value of the second byte, plus the unsigned value of the third byte.
 +
*GL_4_BYTES: list is treated as an array of unsigned bytes. Each quadruplet of bytes specifies a single display list ID. The value of the quadruplet is computed as 16777216 times the unsigned value of the first byte, plus 65536 times the unsigned value of the second byte, plus 256 times the unsigned value of the third byte, plus the unsigned value of the fourth byte.
  
GL_UNSIGNED_SHORT: list is treated as an array of unsigned two-byte integers, each in the range 0 through 65535.
+
&nbsp;&nbsp;&nbsp;Esto es todo por ahora. En el siguiente tutorial, mostraré como crear un pequeño sistema planetario. Hablaremos sobre las matrices y como hacer una escena animada que no dependa del número de cuadros por segundo.
  
GL_INT: lists is treated as an array of signed four-byte integers.
+
&nbsp;&nbsp;&nbsp;Descarga el codido fuente, linux ejecutable o windows ejecutable de [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
 
 
GL_UNSIGNED_INT: list is treated as an array of unsigned four-byte integers.
 
 
 
GL_FLOAT: list is treated as an array of four-byte floating-point values.
 
 
 
GL_2_BYTES: list is treated as an array of unsigned bytes. Each pair of bytes specifies a single display list ID. The value of the pair is computed as 256 times the unsigned value of the first byte plus the unsigned value of the second byte.
 
 
 
GL_3_BYTES: list is treated as an array of unsigned bytes. Each triplet of bytes specifies a single display list ID. The value of the triplet is computed as 65536 times the unsigned value of the first byte, plus 256 times the unsigned value of the second byte, plus the unsigned value of the third byte.
 
 
 
GL_4_BYTES: list is treated as an array of unsigned bytes. Each quadruplet of bytes specifies a single display list ID. The value of the quadruplet is computed as 16777216 times the unsigned value of the first byte, plus 65536 times the unsigned value of the second byte, plus 256 times the unsigned value of the third byte, plus the unsigned value of the fourth byte.
 
 
 
That is for now. Next tutorial will show how to create little planetary system. We'll talk about matrices and how to make animated scene that doesn't depend of number of frames per second.
 
 
 
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
 
  
 
=Full screen animation=
 
=Full screen animation=
  
Entering full screen mode is easy with GLUT. Let's change main part of the program:
+
&nbsp;&nbsp;&nbsp;Entrar en modo pantalal completa es fácil con. Hagamos cambios en la parte "main del programa:the program:
  
<delphi> const
+
<syntaxhighlight lang=pascal> const
 
   FSMode = '800x600:32@75';
 
   FSMode = '800x600:32@75';
 
   
 
   
Line 378: Line 355:
 
   
 
   
 
   glutMainLoop;
 
   glutMainLoop;
  end.</delphi>
+
  end.</syntaxhighlight>
 +
 
 +
&nbsp;&nbsp;&nbsp;Since we don't want GLUT to parse command line this time we call glutInitPascal with False parameter. As you can see, there is no code for window creation. GLUT have '''glutEnterGameMode''' that create full screen window. To specify what kind of full screen mode you want, you call '''glutGameModeString''' function which takes string that defines mode you like.
  
Since we don't want GLUT to parse command line this time we call glutInitPascal with False parameter. As you can see, there is no code for window creation. GLUT have '''glutEnterGameMode''' that create full screen window. To specify what kind of full screen mode you want, you call '''glutGameModeString''' function which takes string that defines mode you like.
+
&nbsp;&nbsp;&nbsp;Format of that string is:
Format of that string is:
 
  
 
  [width "x" height][":" bpp]["@" hertz]
 
  [width "x" height][":" bpp]["@" hertz]
  
In FSMode string we declared that full screen mode should be 800x600, with 32bit pallete and 75Hz refresh. It is possible to skip one of the group. If you omit size, GLUT will try to use current one or first smaller that can work. That policy is used and for other parameters.
+
&nbsp;&nbsp;&nbsp;In FSMode string we declared that full screen mode should be 800x600, with 32bit pallete and 75Hz refresh. It is possible to skip one of the group. If you omit size, GLUT will try to use current one or first smaller that can work. That policy is used and for other parameters.
  
Usually in full screen mode cursor is not visible. To hide cursor you use '''glutSetCursor''' function. It takes only one parameter which describes cursor you would like to see:
+
&nbsp;&nbsp;&nbsp;Usually in full screen mode cursor is not visible. To hide cursor you use '''glutSetCursor''' function. It takes only one parameter which describes cursor you would like to see:
<delphi> GLUT_CURSOR_RIGHT_ARROW
+
<syntaxhighlight lang=pascal> GLUT_CURSOR_RIGHT_ARROW
 
  GLUT_CURSOR_LEFT_ARROW
 
  GLUT_CURSOR_LEFT_ARROW
 
  GLUT_CURSOR_INFO
 
  GLUT_CURSOR_INFO
Line 410: Line 388:
 
  GLUT_CURSOR_FULL_CROSSHAIR
 
  GLUT_CURSOR_FULL_CROSSHAIR
 
  GLUT_CURSOR_NONE
 
  GLUT_CURSOR_NONE
  GLUT_CURSOR_INHERIT</delphi>
+
  GLUT_CURSOR_INHERIT</syntaxhighlight>
  
'''glutIdleFunc''' defines callback function that you want to be called every time you program has no messages to process. Since we just want to render new frame if there is nothing to do, just set idle function to DrawGLScene.
+
&nbsp;&nbsp;&nbsp;'''glutIdleFunc''' defines callback function that you want to be called every time you program has no messages to process. Since we just want to render new frame if there is nothing to do, just set idle function to DrawGLScene.
 
Some other tutorials show that idle function should send refresh message insted of drawing, but that way I have 50-100 frames less than using method I described.
 
Some other tutorials show that idle function should send refresh message insted of drawing, but that way I have 50-100 frames less than using method I described.
  
Now, let's look at the program termination where you need to exit full screen mode:
+
&nbsp;&nbsp;&nbsp;Now, let's look at the program termination where you need to exit full screen mode:
  
<delphi> procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
+
<syntaxhighlight lang=pascal> procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 
  begin
 
  begin
 
   if Key = 27 then
 
   if Key = 27 then
Line 424: Line 402:
 
     Halt(0);
 
     Halt(0);
 
   end;
 
   end;
  end;</delphi>
+
  end;</syntaxhighlight>
  
As you can see, all you need to do is to call '''glutLeaveGameMode'''.
+
&nbsp;&nbsp;&nbsp;As you can see, all you need to do is to call '''glutLeaveGameMode'''.
  
Now, we'll introduce some new matrix functions. First, let's change ReSizeGLScene function:
+
&nbsp;&nbsp;&nbsp;Now, we'll introduce some new matrix functions. First, let's change ReSizeGLScene function:
  
<delphi> procedure ReSizeGLScene(Width, Height: Integer); cdecl;
+
<syntaxhighlight lang=pascal> procedure ReSizeGLScene(Width, Height: Integer); cdecl;
 
  begin
 
  begin
 
   .
 
   .
Line 439: Line 417:
 
   glLoadIdentity;
 
   glLoadIdentity;
 
   gluLookAt(0, 20, 25, 0, 0, 0, 0, 1, 0);
 
   gluLookAt(0, 20, 25, 0, 0, 0, 0, 1, 0);
  end;</delphi>
+
  end;</syntaxhighlight>
  
'''gluLookAt''' create matrix that will define from where are you look to objects. First 3 parameters are X, Y and Z coordinate of position of camera. Next 3 parameters are X, Y and Z coordinate of point where camera look at, and last 3 parameters defines "up" vector (where is "up" for the camera). Usually, up is positive y axis.
+
&nbsp;&nbsp;&nbsp;'''gluLookAt''' create matrix that will define from where are you look to objects. First 3 parameters are X, Y and Z coordinate of position of camera. Next 3 parameters are X, Y and Z coordinate of point where camera look at, and last 3 parameters defines "up" vector (where is "up" for the camera). Usually, up is positive y axis.
  
OK, let's draw now. Since you set matrix with gluLookAt that should be used with all objects, you can't just use glLoadIdentity to reset matrix for next object... you'll save previous matrix state and restore it after object is drawn:
+
&nbsp;&nbsp;&nbsp;OK, let's draw now. Since you set matrix with gluLookAt that should be used with all objects, you can't just use glLoadIdentity to reset matrix for next object... you'll save previous matrix state and restore it after object is drawn:
  
<delphi> procedure DrawGLScene; cdecl;
+
<syntaxhighlight lang=pascal> procedure DrawGLScene; cdecl;
 
  var
 
  var
 
   T: Single;
 
   T: Single;
Line 484: Line 462:
 
   
 
   
 
   glutSwapBuffers;
 
   glutSwapBuffers;
  end;</delphi>
+
  end;</syntaxhighlight>
  
[[Image:FullScreenAnimationPic1.jpg|thumb]] '''glPushMatrix''' i '''glPopMatrix''' are used to save and restore matrix state. As you can see, we save matrix state, then change matrix in order to draw object in right place, and then restore old matrix state.
+
[[Image:FullScreenAnimationPic1.jpg|thumb]]&nbsp;&nbsp;&nbsp;'''glPushMatrix''' i '''glPopMatrix''' are used to save and restore matrix state. As you can see, we save matrix state, then change matrix in order to draw object in right place, and then restore old matrix state.
  
You may wonder what is '''T''' variable for. Well, it is used to determen animation speed. Every change that depends on time is multiplied with T. That way animation speed is constant on every frame rate.
+
&nbsp;&nbsp;&nbsp;You may wonder what is '''T''' variable for. Well, it is used to determen animation speed. Every change that depends on time is multiplied with T. That way animation speed is constant on every frame rate.
 
'''glutGet''' function with '''GLUT_ELAPSED_TIME''' parameter returns time in milliseconds from glutInit is called. By dividing that value with 1000, we get time in seconds.
 
'''glutGet''' function with '''GLUT_ELAPSED_TIME''' parameter returns time in milliseconds from glutInit is called. By dividing that value with 1000, we get time in seconds.
  
'''glRotatef''' function create rotation matrix. First parameter is angle in degrees, and last 3 parameters defines axis around which rotation will be done.
+
&nbsp;&nbsp;&nbsp;'''glRotatef''' function create rotation matrix. First parameter is angle in degrees, and last 3 parameters defines axis around which rotation will be done.
 
Since you multiplied angle with T, object will be rotated by that angle in exactly 1 second.
 
Since you multiplied angle with T, object will be rotated by that angle in exactly 1 second.
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
&nbsp;&nbsp;&nbsp;Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
  
=Light=
+
=Luz=
  
This tutorial will introduce some light to the scene. You'll make rotating cube and one light which will add some realism to the scene, but first let's make some utility unit.
+
&nbsp;&nbsp;&nbsp;Este tutorial introducirá algo de luz en la escena. You'll make rotating cube and one light which will add some realism to the scene, but first let's make some utility unit.
  
Por el momento tendremos sólamente funciones básicas para ayudarnos a obtener los tiempos current y delta (tiempo transcurrido desde la llamada de un render a otro) y para calcular los frames por segundo.  
+
&nbsp;&nbsp;&nbsp;Por el momento tendremos sólamente funciones básicas para ayudarnos a obtener los tiempos current y delta (tiempo transcurrido desde la llamada de un render a otro) y para calcular los frames por segundo.  
  
<delphi> unit utils;
+
<syntaxhighlight lang=pascal> unit utils;
 
   
 
   
 
  {$mode objfpc}{$H+}
 
  {$mode objfpc}{$H+}
Line 554: Line 532:
 
  end;
 
  end;
 
   
 
   
  end.</delphi>
+
  end.</syntaxhighlight>
  
Como puedes observar, no hay complejidades en esta unit. Se salva simplente el tiempo entre las llamadas y se retorna la diferencia. Se debe llamar FrameRendered cada vez que se dibuja una escena de forma que la función pueda calcular los FPS (Frames por segundo:Frames Per Second).
+
&nbsp;&nbsp;&nbsp;Como puedes observar, no hay complejidades en esta unit. Se salva simplente el tiempo entre las llamadas y se retorna la diferencia. Se debe hacer una llamada a FrameRendered cada vez que se dibuja una escena de manera que la función pueda calcular los FPS (Frames por segundo:Frames Per Second).
  
Now, let's have fun with lights.
+
&nbsp;&nbsp;&nbsp;Ahora, divirtámonos con las luces.
  
OpenGL tiene varios tipos de luz... ambiente, difuso, point, spot, especular y emissive.  
+
&nbsp;&nbsp;&nbsp;OpenGL tiene varios tipos de luz... ambiente, difusa, point, spot, especular y emissive.  
  
La luz ambiente es algo así como la del Sol. Cuando los rayos solares pasan a través de la ventana de una habitación inciden sobre las paredes y tiene lugar su reflexión y su dispersión en todas las direcciones lo cual averagely brightens up la habitación completa. Todos los vertices son lit con luz ambiente.
+
&nbsp;&nbsp;&nbsp;La luz ambiente es algo así como la del Sol. Cuando los rayos solares pasan a través de la ventana de una habitación inciden sobre las paredes y tiene lugar su reflexión y su dispersión en todas las direcciones lo cual averagely brightens up la habitación completa. Todos los vertices son lit con luz ambiente.
  
La Luz Difusa puede representarse como rayos de luz paralelos llegando desde puntos lejanos. They will lit only vertices that are oriented towards the light source.
+
&nbsp;&nbsp;&nbsp;La Luz Difusa puede representarse como rayos de luz paralelos llegando desde puntos lejanos. They will lit only vertices that are oriented towards the light source.
  
Point light lights all around it. It is like a fire ball, it send light rays all around it and lights vertices that are oriented towards light source and that are close enough.
+
&nbsp;&nbsp;&nbsp;Point light lights all around it. Es como una bola de fuego, it send light rays all around it and lights vertices that are oriented towards light source and that are close enough.
  
Spot light is like light from flashlight. It is simply a point light source with a small light cone radius. All vertices that falls inside of cone and are close enough are lit.
+
&nbsp;&nbsp;&nbsp;Spot light es como la luz de una linterna. Es simplemente un punto de luz source con un cono de luz de radio pequeño. All vertices that falls inside of cone and are close enough are lit.
  
Just like Diffuse light, Specular light is a directional type of light. It comes from one particular direction. The difference between the two is that specular light reflects off the surface in a sharp and uniform way. The rendering of specular light relies on the angle between the viewer and the light source. From the viewer’s standpoint specular light creates a highlighted area on the surface of the viewed object known as specular highlight or specular reflection.
+
&nbsp;&nbsp;&nbsp;Just like Diffuse light, Specular light is a directional type of light. It comes from one particular direction. The difference between the two is that specular light reflects off the surface in a sharp and uniform way. The rendering of specular light relies on the angle between the viewer and the light source. From the viewer’s standpoint specular light creates a highlighted area on the surface of the viewed object known as specular highlight or specular reflection.
  
Emissive light is a little different than any other previously explained light components. This light comes out of object you draw but don't lit other objects in nearby.
+
&nbsp;&nbsp;&nbsp;Emissive light is a little different than any other previously explained light components. This light comes out of object you draw but don't lit other objects in nearby.
  
Por simplicidad utilizaremos únicamente luz difusa en este tutorial. Con el tiempo puede que aparezcan otros tipos de luz pero por ahora nos centraremos en este. :)
+
&nbsp;&nbsp;&nbsp;Por simplicidad utilizaremos únicamente luz difusa en este tutorial. Con el tiempo puede que aparezcan otros tipos de luz pero por ahora nos centraremos en este. :)
  
Veamos como habilitar la luz en la escena:
+
&nbsp;&nbsp;&nbsp;Veamos como habilitar la luz en la escena:
  
<delphi> const
+
<syntaxhighlight lang=pascal> const
 
   DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);
 
   DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);
 
   
 
   
 
   glEnable(GL_LIGHTING);
 
   glEnable(GL_LIGHTING);
 
   glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
 
   glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
   glEnable(GL_LIGHT0);</delphi>
+
   glEnable(GL_LIGHT0);</syntaxhighlight>
  
As you see, we enable lighting in OpenGL so lights affect scene you are rendering. Light parameters are set with '''glLightfv''' function. It takes 3 parameters... one for light number you want to change (OpenGL suports up to 8 lights), next tells OpenGL what light parameter to change, and the last one is new parameter for light.
+
&nbsp;&nbsp;&nbsp;As you see, we enable lighting in OpenGL so lights affect scene you are rendering. Light parameters are set with '''glLightfv''' function. It takes 3 parameters... one for light number you want to change (OpenGL suports up to 8 lights), next tells OpenGL que parámetro de la luz cambiar, y este último es el nuevo parámetro para la luz.
You'll set just diffuse color for light in this tutorial. After that, you can enable light and there will be light in the scene... but... that is not all.
+
You'll set just diffuse color for light en este tutorial. After that, you can enable light and there will be light en la escena... pero... eso no es todo.
  
Para saber más sobre glLightfv: http://www.opengl.org//documentation/specs/man_pages/hardcopy/GL/html/gl/light.html
+
&nbsp;&nbsp;&nbsp;Para saber más sobre glLightfv: http://www.opengl.org//documentation/specs/man_pages/hardcopy/GL/html/gl/light.html
  
Si necesitas utilizar luces no basta con simplemente establecer el color de los vértices...
+
&nbsp;&nbsp;&nbsp;Si necesitas utilizar luces no basta simplemente con establecer el color de los vértices...
debes establecer material para los vértices. Establezcamos material para el dibujo:
+
debes establecer el material de los vértices. Establezcamos el material para el dibujo:
  
 
  glEnable(GL_COLOR_MATERIAL);
 
  glEnable(GL_COLOR_MATERIAL);
 
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
 
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
  
[[Image:LightPic1.jpg|thumb]] ¿Esperabas hacer algo más complicado? :) Bien, este código nos permitirá utilizar la función glColor to set material to vertices. Utilizando la función glEnable y la bandera GL_COLOR_MATERIAL, you can define what material properties will glColor change. '''glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)''' tells OpenGL that glColor changes ambient and diffuse material. Discutiremos los materiales más tarde en estos tutoriales.
+
[[Image:LightPic1.jpg|thumb]]&nbsp;&nbsp;&nbsp;¿Esperabas hacer algo más complicado? :) Bien, este código nos permitirá utilizar la función glColor para configurar los materiales de los vértices. Utilizando la función glEnable y la bandera GL_COLOR_MATERIAL, puedes definir que propiedades de los materiales, cambiará glColor. '''glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)''' le dice a OpenGL que glColor cambia las propiedades del material ambient y diffuse. Discutiremos los materiales más tarde en estos tutoriales.
  
Una cosa más que es importante cuando utilizamos luces...cada vértice debe tener tener asociado un "normal". "Normal" se utiliza para encontrar la dirección del vértice de forma que la luz se puede calcular de la forma apropiada. Utilizarás la función GLUT para dibujar un cubo que nos aportará "normals" de por sí, so this time we'll just walk by normals.
+
&nbsp;&nbsp;&nbsp;Una cosa más que es importante cuando utilizamos luces... cada vértice debe tener asociada una "normal". La "Normal" se utiliza para encontrar la dirección del vértice de forma que la luz se puede calcular de la forma apropiada. Utilizarás la función GLUT para dibujar un cubo que nos aportará las "normales" de por sí, so this time we'll just walk by normals.
  
Después de establecer todo esto, la luz brillará en el cubo :)
+
&nbsp;&nbsp;&nbsp;Después de establecer todo esto, la luz brillará en el cubo :)
  
Parte de lo que sigue está copiado de [http://www.falloutsoftware.com/tutorials/gl/gl8.htm The OpenGL Light Bible]
+
&nbsp;&nbsp;&nbsp;Parte de lo que sigue está copiado de [http://www.falloutsoftware.com/tutorials/gl/gl8.htm The OpenGL Light Bible]
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
&nbsp;&nbsp;&nbsp;Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
  
 
=Fuentes Bitmap=
 
=Fuentes Bitmap=
  
Games and programs usually need to write some text on screen. GLUT provides several functions for drawing chars that are platform independent.
+
&nbsp;&nbsp;&nbsp;Games and programs usually need to write some text on screen. GLUT provides several functions for drawing chars that are platform independent.
  
First, we'll show how to use default bitmap fonts. Almost all code additions will be made to utils.pas unit.
+
&nbsp;&nbsp;&nbsp;First, we'll show how to use default bitmap fonts. Almost all code additions will be made to utils.pas unit.
  
Since text will be drawn in 2D, we'll need to know width and height of viewport... so, we'll write two functions for that:
+
&nbsp;&nbsp;&nbsp;Since text will be drawn in 2D, we'll need to know width and height of viewport... so, we'll write two functions for that:
  
<delphi> function glGetViewportWidth: Integer;
+
<syntaxhighlight lang=pascal> function glGetViewportWidth: Integer;
 
  var
 
  var
 
   Rect: array[0..3] of Integer;
 
   Rect: array[0..3] of Integer;
Line 628: Line 606:
 
   glGetIntegerv(GL_VIEWPORT, @Rect);
 
   glGetIntegerv(GL_VIEWPORT, @Rect);
 
   Result := Rect[3] - Rect[1];
 
   Result := Rect[3] - Rect[1];
  end;</delphi>
+
  end;</syntaxhighlight>
  
We just get left/right, top/bottom and calculate width/height by subtracting them.
+
&nbsp;&nbsp;&nbsp;We just get left/right, top/bottom and calculate width/height by subtracting them.
  
There must be functions for entering and leaving 2D mode:
+
&nbsp;&nbsp;&nbsp;There must be functions for entering and leaving 2D mode:
  
<delphi> procedure glEnter2D;
+
<syntaxhighlight lang=pascal> procedure glEnter2D;
 
  begin
 
  begin
 
   glMatrixMode(GL_PROJECTION);
 
   glMatrixMode(GL_PROJECTION);
Line 656: Line 634:
 
   
 
   
 
   glEnable(GL_DEPTH_TEST);
 
   glEnable(GL_DEPTH_TEST);
  end;</delphi>
+
  end;</syntaxhighlight>
  
When entering 2D mode, we save current matrices and set 2D matrix using '''gluOrtho2D''' function. This way if we draw some thing on 100, 100 it will be drawn on exactly 100 pixels from left edge of window, and 100 pixels form bottom edge (positive Y is up). Also, we disable ZBuffer. This way text won't alter ZBuffer.
+
&nbsp;&nbsp;&nbsp;When entering 2D mode, we save current matrices and set 2D matrix using '''gluOrtho2D''' function. This way if we draw some thing on 100, 100 it will be drawn on exactly 100 pixels from left edge of window, and 100 pixels form bottom edge (positive Y is up). Also, we disable ZBuffer. This way text won't alter ZBuffer.
  
Leaving 2D mode just returns old matrices and enable ZBuffer.
+
&nbsp;&nbsp;&nbsp;Leaving 2D mode just returns old matrices and enable ZBuffer.
  
Now, we can create function for text drawing:
+
&nbsp;&nbsp;&nbsp;Now, we can create function for text drawing:
  
<delphi> procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);
+
<syntaxhighlight lang=pascal> procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);
 
  var
 
  var
 
   I: Integer;
 
   I: Integer;
Line 671: Line 649:
 
   for I := 1 to Length(Text) do
 
   for I := 1 to Length(Text) do
 
     glutBitmapCharacter(Font, Integer(Text[I]));
 
     glutBitmapCharacter(Font, Integer(Text[I]));
  end;</delphi>
+
  end;</syntaxhighlight>
  
'''glutBitmapCharacter''' can draw only one character of selected font. First parameter is desired font (GLUT_BITMAP_9_BY_15, GLUT_BITMAP_8_BY_13, GLUT_BITMAP_TIMES_ROMAN_10, GLUT_BITMAP_TIMES_ROMAN_24, GLUT_BITMAP_HELVETICA_10, GLUT_BITMAP_HELVETICA_12 or GLUT_BITMAP_HELVETICA_18) and other one is character.
+
&nbsp;&nbsp;&nbsp;'''glutBitmapCharacter''' can draw only one character of selected font. First parameter is desired font (GLUT_BITMAP_9_BY_15, GLUT_BITMAP_8_BY_13, GLUT_BITMAP_TIMES_ROMAN_10, GLUT_BITMAP_TIMES_ROMAN_24, GLUT_BITMAP_HELVETICA_10, GLUT_BITMAP_HELVETICA_12 or GLUT_BITMAP_HELVETICA_18) and other one is character.
  
Character will be drawn at current raster position. To set desired raster position we call '''glRasterPos''' function. glRasterPos can handle different number and types of parameters just like glVertex function. Coordinate specified is transformed by model and projection matrix to get 2D coordinate where new raster position will be. Since we entered 2D mode, X and Y coordinates are actual 2D coordinates where drawing will occur.
+
&nbsp;&nbsp;&nbsp;Character will be drawn at current raster position. To set desired raster position we call '''glRasterPos''' function. glRasterPos can handle different number and types of parameters just like glVertex function. Coordinate specified is transformed by model and projection matrix to get 2D coordinate where new raster position will be. Since we entered 2D mode, X and Y coordinates are actual 2D coordinates where drawing will occur.
  
This new functions will make text drawing very easy:
+
&nbsp;&nbsp;&nbsp;This new functions will make text drawing very easy:
  
<delphi> procedure DrawGLScene; cdecl;
+
<syntaxhighlight lang=pascal> procedure DrawGLScene; cdecl;
 
  begin
 
  begin
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
Line 715: Line 693:
 
   
 
   
 
   FrameRendered;
 
   FrameRendered;
  end;</delphi>
+
  end;</syntaxhighlight>
  
[[Image:BitmapFontsPic1.jpg|thumb]]
+
[[Image:BitmapFontsPic1.jpg|thumb]]&nbsp;&nbsp;&nbsp;We draw red cube and rotate it, and some text to show how various bitmap fonts look like.
We draw red cube and rotate it, and some text to show how various bitmap fonts look like.
 
 
'''glutBitmapLength''' function is used to find width of string so it could be aligned to right. Code can easily be altered to center text.
 
'''glutBitmapLength''' function is used to find width of string so it could be aligned to right. Code can easily be altered to center text.
  
Note: See how cube looks without light.
+
&nbsp;&nbsp;&nbsp;Note: See how cube looks without light.
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
&nbsp;&nbsp;&nbsp;Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
  
 
=Texturas=
 
=Texturas=
  
Es tiempo de usar texturas :)
+
&nbsp;&nbsp;&nbsp;Es tiempo de usar texturas :)
  
Este tutorial mostrará como dibujar polígonos con textura y como to blend textures using multipass technic.
+
&nbsp;&nbsp;&nbsp;Este tutorial mostrará como dibujar polígonos con textura y como to blend textures using multipass technic.
 
Since OpenGL has no builtin mechanism for loading textures, we'll use external library: [http://imaginglib.sourceforge.net/ Vampyre Imaging Library].
 
Since OpenGL has no builtin mechanism for loading textures, we'll use external library: [http://imaginglib.sourceforge.net/ Vampyre Imaging Library].
 
We'll use just OpenGL helper functions, but you may find this lib handy for some other things to.
 
We'll use just OpenGL helper functions, but you may find this lib handy for some other things to.
  
Let's get started... we'll create display list for drawing textured rectangle:
+
&nbsp;&nbsp;&nbsp;Let's get started... we'll create display list for drawing textured rectangle:
  
<delphi> procedure CreateList;
+
<syntaxhighlight lang=pascal> procedure CreateList;
 
  begin
 
  begin
 
   glNewList(LIST_OBJECT, GL_COMPILE);
 
   glNewList(LIST_OBJECT, GL_COMPILE);
Line 749: Line 726:
 
     glEnd;
 
     glEnd;
 
   glEndList;
 
   glEndList;
  end;</delphi>
+
  end;</syntaxhighlight>
  
Notice '''glTexCoord''' functions. They are used to specify which part of texture is assigned to vertex. Coordinates defined in this functions are from 0 to 1 (values greater than 1 are allowed but can generate different results). 0 is first pixel and 1 is last pixel. So, 0.5 will be right in the middle of texture.
+
&nbsp;&nbsp;&nbsp;Notice '''glTexCoord''' functions. They are used to specify which part of texture is assigned to vertex. Coordinates defined in this functions are from 0 to 1 (values greater than 1 are allowed but can generate different results). 0 is first pixel and 1 is last pixel. So, 0.5 will be right in the middle of texture.
  
Texture loading is extremely easy with Vampyre Imaging Library:
+
&nbsp;&nbsp;&nbsp;Texture loading is extremely easy with Vampyre Imaging Library:
  
<delphi> var
+
<syntaxhighlight lang=pascal> var
 
   Tex1, Tex2: GLuint;
 
   Tex1, Tex2: GLuint;
 
   
 
   
Line 764: Line 741:
 
   Tex2 := LoadGLTextureFromFile('Flare.bmp');
 
   Tex2 := LoadGLTextureFromFile('Flare.bmp');
 
   glEnable(GL_TEXTURE_2D);
 
   glEnable(GL_TEXTURE_2D);
  end;</delphi>
+
  end;</syntaxhighlight>
  
'''LoadGLTextureFromFile''' loads texture from file and returns it's ID. When texture is loaded it is allready setup for rendering.
+
&nbsp;&nbsp;&nbsp;'''LoadGLTextureFromFile''' loads texture from file and returns it's ID. When texture is loaded it is allready setup for rendering.
Last line just enables 2D textures.
+
&nbsp;&nbsp;&nbsp;Last line just enables 2D textures.
  
To draw textured polygon you have to bind texture and setup texture coordinations (texture coordinations are set in display list in this tutorial):
+
&nbsp;&nbsp;&nbsp;To draw textured polygon you have to bind texture and setup texture coordinations (texture coordinations are set in display list in this tutorial):
  
<delphi>  ...
+
<syntaxhighlight lang=pascal>  ...
 
   glLoadIdentity;
 
   glLoadIdentity;
 
   glTranslatef(-5, 0, -15);
 
   glTranslatef(-5, 0, -15);
 
   glBindTexture(GL_TEXTURE_2D, Tex1);
 
   glBindTexture(GL_TEXTURE_2D, Tex1);
 
   glCallList(LIST_OBJECT);
 
   glCallList(LIST_OBJECT);
   ...</delphi>
+
   ...</syntaxhighlight>
  
'''glBindTexture''' function is used to select texture. When you draw polygins they will have selected texture on them. It's that easy :)
+
&nbsp;&nbsp;&nbsp;'''glBindTexture''' function is used to select texture. When you draw polygins they will have selected texture on them. It's that easy :)
  
So, using one texture is easy... but how to blend two textures. Basicly you draw polygon once with one texture, setup blending parameters, and draw polygon once more time with other texture. You can blend houndreds of textures this way. Let's see how code for this looks:
+
&nbsp;&nbsp;&nbsp;So, using one texture is easy... but how to blend two textures. Basicly you draw polygon once with one texture, setup blending parameters, and draw polygon once more time with other texture. You can blend houndreds of textures this way. Let's see how code for this looks:
  
<delphi>  ...
+
<syntaxhighlight lang=pascal>  ...
 
   glLoadIdentity;
 
   glLoadIdentity;
 
   glTranslatef(5, 0, -15);
 
   glTranslatef(5, 0, -15);
Line 795: Line 772:
 
   glCallList(LIST_OBJECT);
 
   glCallList(LIST_OBJECT);
 
   glDisable(GL_BLEND);
 
   glDisable(GL_BLEND);
  ...</delphi>
+
  ...</syntaxhighlight>
  
As you can see, polygon is drawn first time like we allready know. Before second drawing we enable blending by calling '''glEnable(GL_BLEND)'''. Blending means that finall pixel color is calculated like this:
+
&nbsp;&nbsp;&nbsp;As you can see, polygon is drawn first time like we allready know. Before second drawing we enable blending by calling '''glEnable(GL_BLEND)'''. Blending means that finall pixel color is calculated like this:
  
DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND
+
*DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND
  
 
SRCBLEND and DESTBLEND are defined using '''glBlendFunc''' function. In this tutorial we set SRCBLEND to GL_ZERO (zero) and DESTBLENT to GL_SRC_COLOR (DrawingColor) and finall color is then:
 
SRCBLEND and DESTBLEND are defined using '''glBlendFunc''' function. In this tutorial we set SRCBLEND to GL_ZERO (zero) and DESTBLENT to GL_SRC_COLOR (DrawingColor) and finall color is then:
  
DrawingColor * 0 + BackgroundColor * DrawingColor
+
&nbsp;&nbsp;&nbsp;DrawingColor * 0 + BackgroundColor * DrawingColor
 
[[Image:TexturesPic1.jpg|thumb]]
 
[[Image:TexturesPic1.jpg|thumb]]
  
This means that background will get darker when you draw with dark colors... when you draw with white color, background color will not be changed. The result will look like this
+
&nbsp;&nbsp;&nbsp;This means that background will get darker when you draw with dark colors... when you draw with white color, background color will not be changed. The result will look like this
  
Next time, we'll use extensions to show how to use singlepass multitexturing.
+
&nbsp;&nbsp;&nbsp;Next time, we'll use extensions to show how to use singlepass multitexturing.
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
&nbsp;&nbsp;&nbsp;Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
  
 
=Multitexturing (extensions)=
 
=Multitexturing (extensions)=
  
When youknow multipass multi texturing, singlepass is very easy. Texturing is separated in stages. First stage setup and draw first texture, second stage draws another one and so on. Todo lo que hay que hacer es configurar las etapas de textura y renderizar el objeto.
+
&nbsp;&nbsp;&nbsp;When youknow multipass multi texturing, singlepass is very easy. Texturing is separated in stages. First stage setup and draw first texture, second stage draws another one and so on. Todo lo que hay que hacer es configurar las etapas de textura y renderizar el objeto.
  
Veamos como queda el código:
+
&nbsp;&nbsp;&nbsp;Veamos como queda el código:
  
<delphi> procedure InitializeGL;
+
<syntaxhighlight lang=pascal> procedure InitializeGL;
 
  begin
 
  begin
 
   Load_GL_ARB_multitexture;
 
   Load_GL_ARB_multitexture;
Line 830: Line 807:
 
   glEnable(GL_TEXTURE_2D);
 
   glEnable(GL_TEXTURE_2D);
 
   glBindTexture(GL_TEXTURE_2D, Tex2);
 
   glBindTexture(GL_TEXTURE_2D, Tex2);
  end;</delphi>
+
  end;</syntaxhighlight>
  
Primero necesitamos cargar la extensión de OpenGL que permitirá utilizar las funciones de multitextura. '''Load_GL_ARB_multitexture''' tratará de cargar estas extensiones retornando TRUE en caso de éxito.
+
&nbsp;&nbsp;&nbsp;Primero necesitamos cargar la extensión de OpenGL que permitirá utilizar las funciones de multitextura. '''Load_GL_ARB_multitexture''' tratará de cargar estas extensiones retornando TRUE en caso de éxito.
  
Para seleccionar la etapa de textura que queremos establecer utilizamos la función '''glActiveTextureARB'''. Toma únicamente un parámetro para definir la etapa que se necesita. Después de esto todas las funciones de textura (enabling, disabling, binding, creating...) afectarán esta etapa.
+
&nbsp;&nbsp;&nbsp;Para seleccionar la etapa de textura que queremos establecer utilizamos la función '''glActiveTextureARB'''. Toma únicamente un parámetro para definir la etapa que se necesita. Después de esto todas las funciones de textura (enabling, disabling, binding, creating...) afectarán esta etapa.
  
Después de establecer todo en la función de inicialización, todo lo que resta por hacer es dibujar el objeto:
+
&nbsp;&nbsp;&nbsp;Después de establecer todo en la función de inicialización, todo lo que resta por hacer es dibujar el objeto:
  
<delphi> procedure DrawGLScene; cdecl;
+
<syntaxhighlight lang=pascal> procedure DrawGLScene; cdecl;
 
  begin
 
  begin
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
Line 861: Line 838:
 
   
 
   
 
   glutSwapBuffers;
 
   glutSwapBuffers;
  end;</delphi>
+
  end;</syntaxhighlight>
  
[[Image:MultitexturePic1.jpg|thumb]]
+
[[Image:MultitexturePic1.jpg|thumb]]&nbsp;&nbsp;&nbsp;Como puedes observar, la diferencia está únicamente en la definiciónde las coordenadas de la textura. We now use '''glMultiTexCoord2fARB''' function that takes texture stage and texture coordinations. Every thing else is unchanged.
Como puedes observar, la diferencia está únicamente en la definiciónde las coordenadas de la textura. We now use '''glMultiTexCoord2fARB''' function that takes texture stage and texture coordinations. Every thing else is unchanged.
 
  
Hoy días prácticamente todas las tarjetas gráficas soportan al menos 2 etapas de textura.
+
&nbsp;&nbsp;&nbsp;Hoy días prácticamente todas las tarjetas gráficas soportan al menos 2 etapas de textura.
 
Using singlepass multitexturing is faster than multipass version since you draw objects only once. If hardware supports singlepass multitexturing (Load_GL_ARB_multitexture returns TRUE) use it.
 
Using singlepass multitexturing is faster than multipass version since you draw objects only once. If hardware supports singlepass multitexturing (Load_GL_ARB_multitexture returns TRUE) use it.
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
&nbsp;&nbsp;&nbsp;Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
  
 
=Render to texture=
 
=Render to texture=
  
This one will be short. OpenGL can capture current scene to texture so you can use it for texturing other objects (TV screen, mirror or some thing else). Well just render scene to texture and apply it to rotating plane.
+
&nbsp;&nbsp;&nbsp;This one will be short. OpenGL can capture current scene to texture so you can use it for texturing other objects (TV screen, mirror or some thing else). Well just render scene to texture and apply it to rotating plane.
  
First, we must create empty texture which we'll use to capture scene:
+
&nbsp;&nbsp;&nbsp;First, we must create empty texture which we'll use to capture scene:
  
<delphi> procedure SetupRenderTexture;
+
<syntaxhighlight lang=pascal> procedure SetupRenderTexture;
 
  var
 
  var
 
   Data: Pointer;
 
   Data: Pointer;
Line 888: Line 864:
 
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 
   FreeMem(Data);
 
   FreeMem(Data);
  end;</delphi>
+
  end;</syntaxhighlight>
  
Buffer for 256*256 RGB image is created and it is used to setup 2D texture.
+
&nbsp;&nbsp;&nbsp;Buffer for 256*256 RGB image is created and it is used to setup 2D texture.
  
Main part is in drawing function:
+
&nbsp;&nbsp;&nbsp;Main part is in drawing function:
  
<delphi> procedure DrawGLScene; cdecl;
+
<syntaxhighlight lang=pascal> procedure DrawGLScene; cdecl;
 
  var
 
  var
 
   TotalTime: Single;
 
   TotalTime: Single;
Line 940: Line 916:
 
   
 
   
 
   glutSwapBuffers;
 
   glutSwapBuffers;
  end;</delphi>
+
  end;</syntaxhighlight>
  
[[Image:RenderToTexturePic1.jpg|thumb]]
+
[[Image:RenderToTexturePic1.jpg|thumb]]&nbsp;&nbsp;&nbsp;First, everything is setup for scene that will be captured. Viewport is reduced to 256*256 so it will fit into texture and scene is drawn. '''glCopyTexImage2D''' is used to capture scene to currently selected texture.
First, everything is setup for scene that will be captured. Viewport is reduced to 256*256 so it will fit into texture and scene is drawn. '''glCopyTexImage2D''' is used to capture scene to currently selected texture.
 
  
When we have scene captured to texture, everything can be cleared again, viewport can be returned to original size and final scene is drawn using previous scene as texture.
+
&nbsp;&nbsp;&nbsp;When we have scene captured to texture, everything can be cleared again, viewport can be returned to original size and final scene is drawn using previous scene as texture.
  
P.S. Captured texture can be saved using '''SaveGLTextureToFile''' function from [http://imaginglib.sourceforge.net/ Vampyre Imaging Library].
+
&nbsp;&nbsp;&nbsp;P.S. Captured texture can be saved using '''SaveGLTextureToFile''' function from [http://imaginglib.sourceforge.net/ Vampyre Imaging Library].
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
&nbsp;&nbsp;&nbsp;Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
  
 
=Vertex array=
 
=Vertex array=
  
OpenGL is capable of rendering primitives using data that is stored in buffers insted of calling glVertex. Buffers can be used to define vertex and texture coordinates, and colors (index and RGBA), normals and edge flags.
+
&nbsp;&nbsp;&nbsp;OpenGL is capable of rendering primitives using data that is stored in buffers insted of calling glVertex. Buffers can be used to define vertex and texture coordinates, and colors (index and RGBA), normals and edge flags.
  
In this tutorial well use only vertex and color buffers, and we'll show non-indexed and indexed drawing. Non-indexed mode draws buffers as streams. Indexed mode will draw buffer elements in order that is defined in index buffer. But enough talking... let's start coding.
+
&nbsp;&nbsp;&nbsp;In this tutorial well use only vertex and color buffers, and we'll show non-indexed and indexed drawing. Non-indexed mode draws buffers as streams. Indexed mode will draw buffer elements in order that is defined in index buffer. But enough talking... let's start coding.
  
First, let's define some types and constants:
+
&nbsp;&nbsp;&nbsp;First, let's define some types and constants:
  
<delphi> type
+
<syntaxhighlight lang=pascal> type
 
   TVertex3f = record
 
   TVertex3f = record
 
     X, Y, Z: Single;
 
     X, Y, Z: Single;
Line 983: Line 958:
 
     (R : 0; G : 1; B : 0),
 
     (R : 0; G : 1; B : 0),
 
     (R : 1; G : 1; B : 0)
 
     (R : 1; G : 1; B : 0)
   );</delphi>
+
   );</syntaxhighlight>
  
We have two buffers. One for vertex coordinates and one for vertex colors. This 6 vertices defines 2 triangles that forms rectangle.
+
&nbsp;&nbsp;&nbsp;We have two buffers. One for vertex coordinates and one for vertex colors. This 6 vertices defines 2 triangles that forms rectangle.
  
Drawing primitives using buffers is easy:
+
&nbsp;&nbsp;&nbsp;Drawing primitives using buffers is easy:
  
<delphi>  glEnableClientState(GL_VERTEX_ARRAY);
+
<syntaxhighlight lang=pascal>  glEnableClientState(GL_VERTEX_ARRAY);
 
   glEnableClientState(GL_COLOR_ARRAY);
 
   glEnableClientState(GL_COLOR_ARRAY);
 
   glVertexPointer(3, GL_FLOAT, 0, @VertexBuffer[0]);
 
   glVertexPointer(3, GL_FLOAT, 0, @VertexBuffer[0]);
Line 997: Line 972:
 
   
 
   
 
   glDisableClientState(GL_VERTEX_ARRAY);
 
   glDisableClientState(GL_VERTEX_ARRAY);
   glDisableClientState(GL_COLOR_ARRAY);</delphi>
+
   glDisableClientState(GL_COLOR_ARRAY);</syntaxhighlight>
 +
 
 +
&nbsp;&nbsp;&nbsp;First we enable buffers we want to use using '''glEnableClientState''' function. Than we can select buffers we want to use. Every buffer type has own function for selecting ('''glColorPointer''', '''glEdgeFlagPointer''', '''glIndexPointer''', '''glNormalPointer''', '''glTexCoordPointer''', '''glVertexPointer''').
 +
 
 +
&nbsp;&nbsp;&nbsp;First parameter in those functions defines how many numbers every element contains. For example, let's take vertex buffer. If this parameter is 2 than OpenGL expects that every element in buffer contains x and y coordinate. If this parameter is, for example, 4, than every element should contains x, y, z and w coordinate.
 +
 
 +
&nbsp;&nbsp;&nbsp;Next parameter defines what type of data element contains (GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT or GL_DOUBLE).
  
First we enable buffers we want to use using '''glEnableClientState''' function. Than we can select buffers we want to use. Every buffer type has own function for selecting ('''glColorPointer''', '''glEdgeFlagPointer''', '''glIndexPointer''', '''glNormalPointer''', '''glTexCoordPointer''', '''glVertexPointer''').
+
&nbsp;&nbsp;&nbsp;Next one defines how many bytes are between each element. This way you can have buffer that contains vertex coordinates and some custom data. For arbitrary data type, this parameter can be calculated like this:
First parameter in those functions defines how many numbers every element contains. For example, let's take vertex buffer. If this parameter is 2 than OpenGL expects that every element in buffer contains x and y coordinate. If this parameter is, for example, 4, than every element should contains x, y, z and w coordinate.
 
Next parameter defines what type of data element contains (GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT or GL_DOUBLE).
 
Next one defines how many bytes are between each element. This way you can have buffer that contains vertex coordinates and some custom data. For arbitrary data type, this parameter can be calculated like this:
 
  
<delphi> type
+
<syntaxhighlight lang=pascal> type
 
   TBufferData = record
 
   TBufferData = record
 
     DataBefore: TDataBefore;
 
     DataBefore: TDataBefore;
 
     Vertex: TVertex;
 
     Vertex: TVertex;
 
     DataAfter: TDataAfter;
 
     DataAfter: TDataAfter;
   end;</delphi>
+
   end;</syntaxhighlight>
 
   
 
   
 
  Bytes between elements = SizeOf(TDataBefore) + SizeOf(TDataAfter)
 
  Bytes between elements = SizeOf(TDataBefore) + SizeOf(TDataAfter)
  
Last parameter if pointer to the begginig of buffer.
+
&nbsp;&nbsp;&nbsp;Last parameter if pointer to the begginig of buffer.
  
When buffers are selected we can draw them using '''glDrawArrays''' functions. All enabled buffers are used to draw primitives. What kind of polygons are being generated is defined in first parameter (same as in glBegin function). Next two defines subset of buffer which is used for drawing (start and count).
+
&nbsp;&nbsp;&nbsp;When buffers are selected we can draw them using '''glDrawArrays''' functions. All enabled buffers are used to draw primitives. What kind of polygons are being generated is defined in first parameter (same as in glBegin function). Next two defines subset of buffer which is used for drawing (start and count).
  
When buffers are not needed you can disable them.
+
&nbsp;&nbsp;&nbsp;When buffers are not needed you can disable them.
  
To demonstrate indexed mode, I made some simple mesh class that can load vertex, color and index data from external files:
+
&nbsp;&nbsp;&nbsp;To demonstrate indexed mode, I made some simple mesh class that can load vertex, color and index data from external files:
  
<delphi> type
+
<syntaxhighlight lang=pascal> type
 
   TMesh = class
 
   TMesh = class
 
   private
 
   private
Line 1,033: Line 1,011:
 
     procedure LoadMesh(FileName: String);
 
     procedure LoadMesh(FileName: String);
 
     procedure DrawMesh;
 
     procedure DrawMesh;
   end;</delphi>
+
   end;</syntaxhighlight>
  
FVertices will contain data about vertices, FColors data about color and FIndices data about indices when external file is loaded.
+
&nbsp;&nbsp;&nbsp;FVertices will contain data about vertices, FColors data about color and FIndices data about indices when external file is loaded.
  
First we'll write some code that deals with creation and destruction of class:
+
&nbsp;&nbsp;&nbsp;First we'll write some code that deals with creation and destruction of class:
  
<delphi> procedure TMesh.FreeBuffers;
+
<syntaxhighlight lang=pascal> procedure TMesh.FreeBuffers;
 
  begin
 
  begin
 
   FVertices := nil;
 
   FVertices := nil;
Line 1,055: Line 1,033:
 
   FreeBuffers;
 
   FreeBuffers;
 
   inherited Destroy;
 
   inherited Destroy;
  end;</delphi>
+
  end;</syntaxhighlight>
  
File that will contain mesh data is simple text file. First row will contain number of vertices and indices separated by space character. After that row will come rows for every vertex and color. X, Y, Z, R, G and B all separated by space character. In the end, there will be rows for indices... every index number is written in its own row... so, for one triangle, data file will look like this:
+
&nbsp;&nbsp;&nbsp;File that will contain mesh data is simple text file. First row will contain number of vertices and indices separated by space character. After that row will come rows for every vertex and color. X, Y, Z, R, G and B all separated by space character. In the end, there will be rows for indices... every index number is written in its own row... so, for one triangle, data file will look like this:
  
 
  3 3
 
  3 3
Line 1,071: Line 1,049:
 
&nbsp;&nbsp;&nbsp;Code for loading this data will loke like this:
 
&nbsp;&nbsp;&nbsp;Code for loading this data will loke like this:
  
<delphi> procedure TMesh.LoadMesh(FileName: String);
+
<syntaxhighlight lang=pascal> procedure TMesh.LoadMesh(FileName: String);
 
  var
 
  var
 
   MeshFile: TextFile;
 
   MeshFile: TextFile;
Line 1,097: Line 1,075:
 
   
 
   
 
   CloseFile(MeshFile);
 
   CloseFile(MeshFile);
  end;</delphi>
+
  end;</syntaxhighlight>
  
 
&nbsp;&nbsp;&nbsp;After loading data, we have everything for drawing:
 
&nbsp;&nbsp;&nbsp;After loading data, we have everything for drawing:
  
<delphi> procedure TMesh.DrawMesh;
+
<syntaxhighlight lang=pascal> procedure TMesh.DrawMesh;
 
  begin
 
  begin
 
   glEnableClientState(GL_VERTEX_ARRAY);
 
   glEnableClientState(GL_VERTEX_ARRAY);
Line 1,112: Line 1,090:
 
   glDisableClientState(GL_VERTEX_ARRAY);
 
   glDisableClientState(GL_VERTEX_ARRAY);
 
   glDisableClientState(GL_COLOR_ARRAY);
 
   glDisableClientState(GL_COLOR_ARRAY);
  end;</delphi>
+
  end;</syntaxhighlight>
  
 
&nbsp;&nbsp;&nbsp;As you can see, allmost everything is the same as for non-indexed drawing, except function that actually draw polygons. In this case we use '''glDrawElements''' function. For this one we specify what kind of polygons we want, how many indices are in index buffer, type of data in index buffer and pointer to the beginning of index buffer.
 
&nbsp;&nbsp;&nbsp;As you can see, allmost everything is the same as for non-indexed drawing, except function that actually draw polygons. In this case we use '''glDrawElements''' function. For this one we specify what kind of polygons we want, how many indices are in index buffer, type of data in index buffer and pointer to the beginning of index buffer.
Line 1,135: Line 1,113:
  
  
[[category:Example programs]]
+
[[category:Example programs/es]]
  
 
-------
 
-------
 
[[Creating bindings for C libraries]]
 
[[Creating bindings for C libraries]]

Latest revision as of 06:15, 23 February 2020

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) 한국어 (ko) русский (ru) 中文(中国大陆)‎ (zh_CN)

   OpenGL es el principal entorno para el diseño de aplicaciones 2D y 3D portables. Desde su introducción en 1992, OpenGL ha llegado a ser la interfaz de programación de aplicaciones (API) gráficas 2D y 3D más ampliamente utilizadao en la industria, aportando miles de aplicaciones a una gran variedad de plataformas de computación. OpenGL promueve la innovación y acelera el diseño de aplicaciones mediante la incorporación de un amplio conjunto de acabados, aplicACIÓN de texturas, efectos especiales y otras potentes funciones de visualización. Los desarrolladores pueden llevar la potencia de OpenGL a través de todos los escritorios más extendidos y plataformas de estaciones de trabajo, asegurando un amplio desarrollo de aplicaciones.

   Puedes encontrar más información acerca de OpenGL aquí.

GLUT

   GLUT es el conjunto de utilidades OpenGL, un conjunto de utilidades de ventana independiente para escribir programas OpenGL. Implementa una interfaz gráfica simple de programación de aplicaciones (API) para OpenGL. GLUT hace que sea considerablemente más fácil de aprender y explorar la programación con OpenGL aportando una API portable de forma que permite escribir un programa que funcionará en todos los PC y estaciones de trabajo en las diferentes plataformas existentes.

   Se puede encontrar más información acerca de GLUT aquí.

   Algunos Sistemas Operativos llevan preinstalado GLUT, pero en el caso de que no sea así se puede encontrar fácilmente utilizando Google.

   Los binarios para Windows se pueden descargar desde www.xmission.com.

   La información de las unidades de FPC para GLUT se encuentran aquí OpenGL.

LCL

   La librería de componente de Lazarus también se puede utilizar con OpenGL. Lazarus incluye un control "TOpenGLControl" - un control LCL con un contexto OpenGL. Se puede encontrar el paquete LazOpenGLContext en lazarus/components/opengl/lazopenglcontext.lpk. Además podemos encontrar un ejemplo en lazarus/examples/openglcontrol/openglcontrol_demo.lpi.

LCL / GLUT

   ¿Cuando utilizar GLUT, y cuando LCL?

  • GLUT es mejor si deseas dibujar todo tu mismo.
  • LCL es mejor para las aplicaciones habituales. Por ejemplo un editor 3D necesita una pocas ventanas OpenGL y el resto es una aplicación normal utilizando botones, cajas de edición, ventanas, ventanas modales, etc.

   La parte OpenGL es otro tanto de lo mismo. GLUT necesita una dll en windows, mientras la LCL no requiere ninguna, pero el ejecutable LCL es bastante más grande.

Creando tu primer programa GLUT

   Para utilizar GLUT, primero hay que iniciarlo. Esto se hace utilizando la función glutInit. Esta función analiza la línea de ordenes y pasa los parámetros a la ventana principal, pero espera la entrada en estilo C/C++. Tendrás que escribir tu propia función para realizar la conversión de ParamCount y ParamStr a parámetros al estilo C/C++ de línea de ordenes.

 procedure glutInitPascal(ParseCmdLine: Boolean); 
 var
   Cmd: array of PChar;
   CmdCount, I: Integer;
 begin
   if ParseCmdLine then
     CmdCount := ParamCount + 1
   else
     CmdCount := 1;
   SetLength(Cmd, CmdCount);
   for I := 0 to CmdCount - 1 do
     Cmd[I] := PChar(ParamStr(I));
   glutInit(@CmdCount, @Cmd);
 end;

   En esencia lo que hace es crear una matriz y rellenarla con las cadenas (strings) de ParamStr. Este procedimiento recibe un parámetro que permite controlar lo que se pasa a glutInit, bien la línea de mandatos completa o bien sólo el nombre del fichero ejecutable.

   ToDo: probablemente glutInit(@argc, @argv); sea suficiente.

   Más acerca de glutInit: http://www.opengl.org/resources/libraries/glut/spec3/node10.html

   Ahora, necesitas crear una ventana principal. Ajusta el modo de visualización de la ventana principal con glutInitDisplayMode. Únicamente recibe un parámetro, que es una combinación de indicadores. En general todo lo que se necesita es GLUT_DOUBLE o GLUT_RGB o GLUT_DEPTH.

   Más acerca de glutInitDisplayMode: http://www.opengl.org/resources/libraries/glut/spec3/node12.html

   La posición y el tamaño de la ventana se controla utilizando glutInitWindowPosition y glutInitWindowSize. Toman 2 parámetros. Las coordenada X e Y en el former, y width (ancho) y height (alto) en el latter. Se puede utilizar glutGet para obtener el tamaño de pantalla y centrar la ventana.

   Más acerca de glutInitWindowPosition, glutInitWindowSize y glutGet: http://www.opengl.org/resources/libraries/glut/spec3/node11.html http://www.opengl.org/documentation/specs/glut/spec3/node70.html

   Finalmente se debería crear la ventana utilizando la función glutCreateWindow. Esto generará la ventana y establecerá su caption a través de un parámetro. Como resultado se retornará el manejador (handler) de ventana. Esto se puede utilizar con otras funciones que lo requieran.

   Más acerca de glutCreateWindow: http://www.opengl.org/resources/libraries/glut/spec3/node16.html

   Antes de que el programa pueda entrar en el bucle principal, se deben establecer algunas callbacks. Se necesitarán las callback para dibujar la ventana, para su redimensionado y para obtener las entradas desde el teclado. Estas callbacks se establecen utilizando glutDisplayFunc, glutReshapeFunc y glutKeyboardFunc.

   Más acerca de las callbacks: http://www.opengl.org/resources/libraries/glut/spec3/node45.html#SECTION00080000000000000000

   Tu función de dibujo podría parecerse a lo siguiente:

 procedure DrawGLScene; cdecl;
 begin
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
   glutSwapBuffers;
 end;

   Esto solamente borra la ventana con el color de fondo y realiza un reset del zbuffer (no te preocupes acerca de que puede ser el zbuffer...más acerca de esto más adelante).

   La función de redimensionado podría parecerse a esto:

 procedure ReSizeGLScene(Width, Height: Integer); cdecl;
 begin
   if Height = 0 then
     Height := 1;
 
   glViewport(0, 0, Width, Height);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity;
   gluPerspective(45, Width / Height, 0.1, 1000);
 
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity;
 end;

   Con este código, se le indica a OpenGL donde debe dibujar en la ventana y establecer las matrices a los valores deseados (las funciones de matriz se explicarán más adelante).

   La entrada de teclado (Keyboard) se evalúa con la siguiente callback:

 procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 begin
   if Key = 27 then
     Halt(0);
 end;

   Esta función indica al programa que debe salir del mismo si se presiona la tecla ESC. GLUT está orientado a eventos y el único medio de salir del programa es llamar Halt desde dentro de una de las funciones callback. Si se cierra la ventana de algún otro modo, desaparecerá, pero el programa continuará en el bucle dentro de la rutina principal indefinidamente. Para comenzar el bucle principal hay que llamar a la función glutMainLoop. Esto iniciará un bucle que nunca finalizará y que realizará las llamadas a todas las funciones callback.

   La porción principal del programa puede parecerse a lo siguiente:

 const 
   AppWidth = 640; 
   AppHeight = 480; 
 
 procedure InitializeGL; 
 begin 
   glClearColor(0.18, 0.20, 0.66, 0); 
 end; 
 
 var 
   ScreenWidth, ScreenHeight: Integer; 
 begin 
   glutInitPascal(True); 
   glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH); 
   glutInitWindowSize(AppWidth, AppHeight); 
   ScreenWidth := glutGet(GLUT_SCREEN_WIDTH); 
   ScreenHeight := glutGet(GLUT_SCREEN_HEIGHT); 
   glutInitWindowPosition((ScreenWidth - AppWidth) div 2,
     (ScreenHeight - AppHeight) div 2); 
   glutCreateWindow('OpenGL Tutorial 1'); 
 
   InitializeGL; 
 
   glutDisplayFunc(@DrawGLScene); 
   glutReshapeFunc(@ReSizeGLScene); 
   glutKeyboardFunc(@GLKeyboard); 
 
   glutMainLoop; 
 end.

   La siguiente tutoría añade un poco de código que dibuja una figura sencilla.

   Descarga el código fuente o un ejecutable Linux / Windows desde Lazarus CCR SourceForge.

Dibujando una figura simple

Nota: Las siguientes partes tratan principalmente sobre OpenGL, por lo que utilizan las librerías GLUT y LCL. Las funciones específicas de GLUT se reconocen porque llevan el prefijo 'glu'.

   Esta vez solo añadiremos unas pocas líneas de código y nos centraremos en explicar algunas de las funciones de OpenGL.

   Expliquemos el código que ya tenemos escrito.

   .
   .
   .
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity;
   gluPerspective(45, Width / Height, 0.1, 1000);
 
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity;
 end;

   Usando la función glMatrixMode elegimos que matriz queremos cambiar. OpenGL trabaja con 3 matrices: GL_MODELVIEW: Se usa para mover el vértice al espacio del modelo. GL_PROJECTION: Se usa para convertir las coordenadas 3d en coordenadas 2d para determinar la posición final del pixel. GL_TEXTURE: Se usa para alterar las coordenadas de la textura.

   Una vez que elegimos la matriz que queremos cambiar, podemos llamar a las funciones que afectan a los valores de la matriz. glLoadIdentity reinicializa la matriz por lo que no afectará a la posición del vértice. Puesto que casi todas las funciones de matrices multiplican la matriz actual por una generada, a veces necesitaremos limpiar la matriz usando esta función.

   Para establecer una matriz de perspectiva, podemos usar la función gluPerspective. Cuatro parámetros presentan el punto de vista, aspecto (aspect radio), distancia la plano. Es así de simple.

   Ahora, vamos a cambiar la matriz modelo... esta vez la ajustamos a la identidad.

   Bien... y ahora, el código para dibujar la primera figura:

 procedure DrawGLScene; cdecl;
 begin
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glLoadIdentity;
   glTranslatef(0, 0, -5);
 
   glBegin(GL_TRIANGLES);
     glColor3f(1, 0, 0);
     glVertex3f(-1, -1, 0);
 
     glColor3f(0, 1, 0);
     glVertex3f(1, -1, 0);
 
     glColor3f(0, 0, 1);
     glVertex3f(0, 1, 0);
   glEnd;
 
   glutSwapBuffers;
 end;

   Ya hemos usado la función glClear, que reseteará los buffers. Vamos a saltarnos las dos siguientes funciones, y vayamos directamente a las de dibujo.

   glBegin marca el inicio de un bloque de dibujo. Después de esta función puedes empezar a introducir vértices. Los parámetros describen como van a ser usados los vértices cuando se dibujen.

  • GL_POINTS: Trata cada vértices como un único punto. Vertex n define el punto n. Pueden dibujarse n.
  • GL_LINES: Trata cada par de vérices como un segmento independiente. Los vértices 2n-1 y 2n definen la línea n. Se dibujan n/2 lineas.
  • GL_LINE_STRIP: Dibuja un grupo de segmentos de línea conectados entre sí. Se dibujan n-1 lineas.
  • GL_LINE_LOOP: Dibuja un grupo de segmentos de lineas conectados entre sí. El primer vértice y el último también están conectados, formando un lazo. Los vertices n y n+1 definen la línea n. La última linea es definida por los vertices n y 1. Se dibujan n líneas.
  • GL_TRIANGLES: Trata cada triplete de vértices como un triángulo independiente. Los vértices 3n-2, 3n-1 y 3n definen el triángulo. Se dibujan n/3 triángulos.
  • GL_TRIANGLE_STRIP: Dibuja un grupo de triángulos conectados. Un triángulo es definido por cada vértice creado después de los dos primeros vértices. Para valores inpares de n, vertices n, n+1 and n+2 define triangle n. For even n, vertices n+1, n and n+2 define triangle n. Se dibujan n-2 triángulos.
  • GL_TRIANGLE_FAN: Dibuja un grupo de triángulos conectados. Un triángulo se define con cada vértice creado después de los dos primeros vértices. Los vértices 1. n+1 y n+2 definen el triángulo n. Se dibujan n-2 triángulos.
  • GL_QUADS: Trata cada grupo de 4 vértices como un cuadrilátero independiente. Los vértices 4n-3, 4n-2, 4n-1 y 4n definen el cuadrilátero n. Se dibujan n/4 cuadriláteros.
  • GL_QUAD_STRIP: Dibuja un grupo de cuadriláteros conectados. Un cuadrilátero es definido por cada pa de vértices creados después del primer par. Los vértices 2n-1, 2n, 2n+2 y 2n+1 definen el cuadrilátero n. Se dibujan n/2-1 cuadriláteros. Tenga en cuenta que el orden en el que los vértices se usan para crear un cuadrilátero a partir de strip data es diferente from that used with independent data.
  • GL_POLYGON: Dibuja un polígono convexo. Los vértices del 1 al n definen este polígono.
SimpleShapePic1.jpg

    You'll draw single triangle and for that GL_TRIANGLES flag will do the trick. La función glVertex3f define la posición del vértice que queremos dibujar. Hay más funciones glVertex*. Solo se diferencian en el número y tipo de los parámetros que introducimos. For instance... glVertex2i toma dos parámetros (X e Y) de tipo integer. Casi siempre glVertex3f será la función que más se use.

   Before glVertex you can set color, material, textura... Por simplicidad you'll just specify color para cada vértice en este tutorial. El color se establece usando la función glColor3f. glColor tambien puede tomar un conjunto de parámetros diferentes como glVertex.

   As we look through code podemos ver que asignamos a Z el valor 0 en todos los vértices. Since you set near plane to 0.1, triangle will not be visible. That is where those two functions we skipped in the beginning jump in. ya sabemos que glLoadIdentity resetea la matriz. glTranslatef moves triangles by X, Y and Z values you provide. Since you set Z to -5 (negative Z is farther from camera) todos los vertices se dibujarán 5 unidades far from point of view and will be visible.

   Al final llamamos a la función glEnd que termina el proceso de dibujado. Si queremos, podemos comenzar otro bloque de dibujado usando glBegin.

   Descargue el código fuente, el ejecutable de linux o windows desde Lazarus CCR SourceForge.

Usando Listas de despliegue

   A veces necesitaras dibujar algunos objetos varias veces en la escena. OpenGL tiene la habiliad de construir Listas de despliegue (display lists) con las cuales dibujar se hace un poco más rápido. Crear una lista de despliegue es muy fácil... dibuja los vértices como hiciste en el tutorial anterior, y enciérralos con las llamadas glNewList y glEndList.

 const
   LIST_OBJECT = 1;
 
 procedure CreateList;
 begin
   glNewList(LIST_OBJECT, GL_COMPILE);
     glBegin(GL_TRIANGLE_FAN);
       glColor3f(1, 0, 0);
       glVertex3f(0, 0.5, 0);
 
       glColor3f(1, 1, 0);
       glVertex3f(-0.5, -0.5, 0.5);
 
       glColor3f(1, 1, 1);
       glVertex3f(0.5, -0.5, 0.5);
 
       glColor3f(0, 1, 1);
       glVertex3f(0.5, -0.5, -0.5);
 
       glColor3f(0, 0, 1);
       glVertex3f(-0.5, -0.5, -0.5);
 
       glColor3f(0, 1, 0);
       glVertex3f(-0.5, -0.5, 0.5);
     glEnd;
 
     glBegin(GL_QUADS);
       glColor3f(1, 1, 0);
       glVertex3f(-0.5, -0.5, 0.5);
 
       glColor3f(1, 1, 1);
       glVertex3f(0.5, -0.5, 0.5);
 
       glColor3f(0, 1, 1);
       glVertex3f(0.5, -0.5, -0.5);
 
       glColor3f(0, 0, 1);
       glVertex3f(-0.5, -0.5, -0.5);
 
       glColor3f(0, 1, 0);
       glVertex3f(-0.5, -0.5, 0.5);
     glEnd;
   glEndList;
 end;

   glNewList crea una nueva lista de despliegue y todas las funciones de dibujo serán grabadas hasta que se llame a glEndList. El primer parámetro para la función glNewList es list ID. Toda lista es definida por su ID. Si una lista con un ID dado ya está creada, será borrada antes de grabar. Si el segundo parámetro es GL_COMPILE entonces todas las funciones solamente son grabadas, pero si es GL_COMPILE_AND_EXECUTE entonces son guardadas y ejecutadas automáticamente.

   glIsList es una función que puede ayudar con tus listas de despliegue. Puede decirte si un ID de una lista ya contiene datos.

   Otra función útil es glGenLists'. Creará múltiples listas. Puedes pasar el número de listas de despliegue que necesitas y obtendrás el ID del primero. Si necesitas n listas, y obtienes un ID, listas de despliegue son: r, r+1, r+2,..., r+n-1

   Todas las listas deben ser borradas antes de que el programa acabe:

 procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 begin
   if Key = 27 then
   begin
     glDeleteLists(LIST_OBJECT, 1);
     Halt(0);
   end;
 end;

   glDeleteLists tiene 2 parámetros, ID de lista de despliegue y un número de listas a borrar. Si el ID es r, y el número de listas a borrar es n, las litas borradas son: r, r+1, r+2,..., r+n-1

   Ahora conoces como crear y borrar listas, vamos a ver como dibujarlas:

 procedure DrawGLScene; cdecl;
 begin
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glLoadIdentity;
   glTranslatef(-2, 0, -5);
   glRotatef(40, 1, 0, 1);
   glCallList(LIST_OBJECT);
 
   glLoadIdentity;
   glTranslatef(1, -2, -10);
   glRotatef(62, 0, 1, 0);
   glCallList(LIST_OBJECT);
 
   glLoadIdentity;
   glTranslatef(-4, 0.5, -15);
   glRotatef(200, 1, 0, 0);
   glCallList(LIST_OBJECT);
 
   glutSwapBuffers;
 end;
DisplayListsPic1.jpg

   Usando glCallList puedes dibujar solo un display list. En este tutorial, antes de display list, puedes cambiar la matriz modelo, y dibujar en diferentes lugares.

   A veces te gustaria dibujar multiples listas de una vez. Esto es posible usando la función glCallLists. Coge el número de listas que quieres dibujar, es un array que contiene los ID de display list. El tipo de lista puede ser una de los siguientes:

  • GL_BYTE: list is treated as an array of signed bytes, each in the range -128 through 127.
  • GL_UNSIGNED_BYTE: list is treated as an array of unsigned bytes, each in the range 0 through 255.
  • GL_SHORT: list is treated as an array of signed two-byte integers, each in the range -32768 through 32767.
  • GL_UNSIGNED_SHORT: list is treated as an array of unsigned two-byte integers, each in the range 0 through 65535.
  • GL_INT: lists is treated as an array of signed four-byte integers.
  • GL_UNSIGNED_INT: list is treated as an array of unsigned four-byte integers.
  • GL_FLOAT: list is treated as an array of four-byte floating-point values.
  • GL_2_BYTES: list is treated as an array of unsigned bytes. Each pair of bytes specifies a single display list ID. The value of the pair is computed as 256 times the unsigned value of the first byte plus the unsigned value of the second byte.
  • GL_3_BYTES: list is treated as an array of unsigned bytes. Each triplet of bytes specifies a single display list ID. The value of the triplet is computed as 65536 times the unsigned value of the first byte, plus 256 times the unsigned value of the second byte, plus the unsigned value of the third byte.
  • GL_4_BYTES: list is treated as an array of unsigned bytes. Each quadruplet of bytes specifies a single display list ID. The value of the quadruplet is computed as 16777216 times the unsigned value of the first byte, plus 65536 times the unsigned value of the second byte, plus 256 times the unsigned value of the third byte, plus the unsigned value of the fourth byte.

   Esto es todo por ahora. En el siguiente tutorial, mostraré como crear un pequeño sistema planetario. Hablaremos sobre las matrices y como hacer una escena animada que no dependa del número de cuadros por segundo.

   Descarga el codido fuente, linux ejecutable o windows ejecutable de Lazarus CCR SourceForge.

Full screen animation

   Entrar en modo pantalal completa es fácil con. Hagamos cambios en la parte "main del programa:the program:

 const
   FSMode = '800x600:32@75';
 
 begin
   glutInitPascal(False);
   glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);
   glutGameModeString(FSMode);
   glutEnterGameMode;
   glutSetCursor(GLUT_CURSOR_NONE);
 
   InitializeGL;
 
   glutDisplayFunc(@DrawGLScene);
   glutReshapeFunc(@ReSizeGLScene);
   glutKeyboardFunc(@GLKeyboard);
   glutIdleFunc(@DrawGLScene);
 
   glutMainLoop;
 end.

   Since we don't want GLUT to parse command line this time we call glutInitPascal with False parameter. As you can see, there is no code for window creation. GLUT have glutEnterGameMode that create full screen window. To specify what kind of full screen mode you want, you call glutGameModeString function which takes string that defines mode you like.

   Format of that string is:

[width "x" height][":" bpp]["@" hertz]

   In FSMode string we declared that full screen mode should be 800x600, with 32bit pallete and 75Hz refresh. It is possible to skip one of the group. If you omit size, GLUT will try to use current one or first smaller that can work. That policy is used and for other parameters.

   Usually in full screen mode cursor is not visible. To hide cursor you use glutSetCursor function. It takes only one parameter which describes cursor you would like to see:

 GLUT_CURSOR_RIGHT_ARROW
 GLUT_CURSOR_LEFT_ARROW
 GLUT_CURSOR_INFO
 GLUT_CURSOR_DESTROY
 GLUT_CURSOR_HELP
 GLUT_CURSOR_CYCLE
 GLUT_CURSOR_SPRAY
 GLUT_CURSOR_WAIT
 GLUT_CURSOR_TEXT
 GLUT_CURSOR_CROSSHAIR
 GLUT_CURSOR_UP_DOWN
 GLUT_CURSOR_LEFT_RIGHT
 GLUT_CURSOR_TOP_SIDE
 GLUT_CURSOR_BOTTOM_SIDE
 GLUT_CURSOR_LEFT_SIDE
 GLUT_CURSOR_RIGHT_SIDE
 GLUT_CURSOR_TOP_LEFT_CORNER
 GLUT_CURSOR_TOP_RIGHT_CORNER
 GLUT_CURSOR_BOTTOM_RIGHT_CORNER
 GLUT_CURSOR_BOTTOM_LEFT_CORNER
 GLUT_CURSOR_FULL_CROSSHAIR
 GLUT_CURSOR_NONE
 GLUT_CURSOR_INHERIT

   glutIdleFunc defines callback function that you want to be called every time you program has no messages to process. Since we just want to render new frame if there is nothing to do, just set idle function to DrawGLScene. Some other tutorials show that idle function should send refresh message insted of drawing, but that way I have 50-100 frames less than using method I described.

   Now, let's look at the program termination where you need to exit full screen mode:

 procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 begin
   if Key = 27 then
   begin
     glutLeaveGameMode;
     Halt(0);
   end;
 end;

   As you can see, all you need to do is to call glutLeaveGameMode.

   Now, we'll introduce some new matrix functions. First, let's change ReSizeGLScene function:

 procedure ReSizeGLScene(Width, Height: Integer); cdecl;
 begin
   .
   .
   .

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity;
   gluLookAt(0, 20, 25, 0, 0, 0, 0, 1, 0);
 end;

   gluLookAt create matrix that will define from where are you look to objects. First 3 parameters are X, Y and Z coordinate of position of camera. Next 3 parameters are X, Y and Z coordinate of point where camera look at, and last 3 parameters defines "up" vector (where is "up" for the camera). Usually, up is positive y axis.

   OK, let's draw now. Since you set matrix with gluLookAt that should be used with all objects, you can't just use glLoadIdentity to reset matrix for next object... you'll save previous matrix state and restore it after object is drawn:

 procedure DrawGLScene; cdecl;
 var
   T: Single;
 begin
   T := glutGet(GLUT_ELAPSED_TIME) / 1000;
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glPushMatrix;
     glRotatef(5 * T, 0, 1, 0);
     glColor3f(1, 1, 0);
     glutWireSphere(2, 20, 20);
   glPopMatrix;
 
   glPushMatrix;
     glRotatef(90 * T, 0, 1, 0);
     glTranslatef(5, 0, 0);
     glRotatef(40 * T, 0, 1, 0);
     glColor3f(1, 0, 0);
     glutWireSphere(0.6, 10, 10);
   glPopMatrix;
 
   glPushMatrix;
     glRotatef(60 * T, 0, 1, 0);
     glTranslatef(-3, 0, 9);
     glRotatef(50 * T, 0, 1, 0);
     glColor3f(0, 1, 0);
     glutWireSphere(1, 16, 16);
 
     glPushMatrix;
       glRotatef(360 * T, 0, 1, 0);
       glTranslatef(-1.7, 0, 0);
       glRotatef(50 * T, 0, 1, 0);
       glColor3f(0, 0, 1);
       glutWireSphere(0.4, 10, 10);
     glPopMatrix;
 
   glPopMatrix;
 
   glutSwapBuffers;
 end;
FullScreenAnimationPic1.jpg

   glPushMatrix i glPopMatrix are used to save and restore matrix state. As you can see, we save matrix state, then change matrix in order to draw object in right place, and then restore old matrix state.

   You may wonder what is T variable for. Well, it is used to determen animation speed. Every change that depends on time is multiplied with T. That way animation speed is constant on every frame rate. glutGet function with GLUT_ELAPSED_TIME parameter returns time in milliseconds from glutInit is called. By dividing that value with 1000, we get time in seconds.

   glRotatef function create rotation matrix. First parameter is angle in degrees, and last 3 parameters defines axis around which rotation will be done. Since you multiplied angle with T, object will be rotated by that angle in exactly 1 second.

   Download source code, linux executable or windows executable from Lazarus CCR SourceForge.

Luz

   Este tutorial introducirá algo de luz en la escena. You'll make rotating cube and one light which will add some realism to the scene, but first let's make some utility unit.

   Por el momento tendremos sólamente funciones básicas para ayudarnos a obtener los tiempos current y delta (tiempo transcurrido desde la llamada de un render a otro) y para calcular los frames por segundo.

 unit utils;
 
 {$mode objfpc}{$H+}
 
 interface
 
 uses
   glut;
 
 function GetTotalTime: Single;
 function GetDeltaTime: Single;
 procedure FrameRendered(Count: Integer = 1);
 function GetFPS: Single;
 
 implementation
 
 var
   OldTime: Integer = 0;
   FPSTime: Integer = 0;
   FPSCount: Integer = 0;
 
 function GetTotalTime: Single;
 begin
   Result := glutGet(GLUT_ELAPSED_TIME) / 1000;
 end;
 
 function GetDeltaTime: Single;
 var
   NewTime: Integer;
 begin
   NewTime := glutGet(GLUT_ELAPSED_TIME);
   Result := (NewTime - OldTime) / 1000;
   OldTime := NewTime;
 end;
 
 procedure FrameRendered(Count: Integer);
 begin
   Inc(FPSCount, Count);
 end;
 
 function GetFPS: Single;
 var
   NewTime: Integer;
 begin
   NewTime := glutGet(GLUT_ELAPSED_TIME);
 
   Result := FPSCount / ((NewTime - FPSTime) / 1000);
 
   FPSTime := NewTime;
   FPSCount := 0;
 end;
 
 end.

   Como puedes observar, no hay complejidades en esta unit. Se salva simplente el tiempo entre las llamadas y se retorna la diferencia. Se debe hacer una llamada a FrameRendered cada vez que se dibuja una escena de manera que la función pueda calcular los FPS (Frames por segundo:Frames Per Second).

   Ahora, divirtámonos con las luces.

   OpenGL tiene varios tipos de luz... ambiente, difusa, point, spot, especular y emissive.

   La luz ambiente es algo así como la del Sol. Cuando los rayos solares pasan a través de la ventana de una habitación inciden sobre las paredes y tiene lugar su reflexión y su dispersión en todas las direcciones lo cual averagely brightens up la habitación completa. Todos los vertices son lit con luz ambiente.

   La Luz Difusa puede representarse como rayos de luz paralelos llegando desde puntos lejanos. They will lit only vertices that are oriented towards the light source.

   Point light lights all around it. Es como una bola de fuego, it send light rays all around it and lights vertices that are oriented towards light source and that are close enough.

   Spot light es como la luz de una linterna. Es simplemente un punto de luz source con un cono de luz de radio pequeño. All vertices that falls inside of cone and are close enough are lit.

   Just like Diffuse light, Specular light is a directional type of light. It comes from one particular direction. The difference between the two is that specular light reflects off the surface in a sharp and uniform way. The rendering of specular light relies on the angle between the viewer and the light source. From the viewer’s standpoint specular light creates a highlighted area on the surface of the viewed object known as specular highlight or specular reflection.

   Emissive light is a little different than any other previously explained light components. This light comes out of object you draw but don't lit other objects in nearby.

   Por simplicidad utilizaremos únicamente luz difusa en este tutorial. Con el tiempo puede que aparezcan otros tipos de luz pero por ahora nos centraremos en este. :)

   Veamos como habilitar la luz en la escena:

 const
   DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);
 
   glEnable(GL_LIGHTING);
   glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
   glEnable(GL_LIGHT0);

   As you see, we enable lighting in OpenGL so lights affect scene you are rendering. Light parameters are set with glLightfv function. It takes 3 parameters... one for light number you want to change (OpenGL suports up to 8 lights), next tells OpenGL que parámetro de la luz cambiar, y este último es el nuevo parámetro para la luz. You'll set just diffuse color for light en este tutorial. After that, you can enable light and there will be light en la escena... pero... eso no es todo.

   Para saber más sobre glLightfv: http://www.opengl.org//documentation/specs/man_pages/hardcopy/GL/html/gl/light.html

   Si necesitas utilizar luces no basta simplemente con establecer el color de los vértices... debes establecer el material de los vértices. Establezcamos el material para el dibujo:

glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
LightPic1.jpg

   ¿Esperabas hacer algo más complicado? :) Bien, este código nos permitirá utilizar la función glColor para configurar los materiales de los vértices. Utilizando la función glEnable y la bandera GL_COLOR_MATERIAL, puedes definir que propiedades de los materiales, cambiará glColor. glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE) le dice a OpenGL que glColor cambia las propiedades del material ambient y diffuse. Discutiremos los materiales más tarde en estos tutoriales.

   Una cosa más que es importante cuando utilizamos luces... cada vértice debe tener asociada una "normal". La "Normal" se utiliza para encontrar la dirección del vértice de forma que la luz se puede calcular de la forma apropiada. Utilizarás la función GLUT para dibujar un cubo que nos aportará las "normales" de por sí, so this time we'll just walk by normals.

   Después de establecer todo esto, la luz brillará en el cubo :)

   Parte de lo que sigue está copiado de The OpenGL Light Bible

   Download source code, linux executable or windows executable from Lazarus CCR SourceForge.

Fuentes Bitmap

   Games and programs usually need to write some text on screen. GLUT provides several functions for drawing chars that are platform independent.

   First, we'll show how to use default bitmap fonts. Almost all code additions will be made to utils.pas unit.

   Since text will be drawn in 2D, we'll need to know width and height of viewport... so, we'll write two functions for that:

 function glGetViewportWidth: Integer;
 var
   Rect: array[0..3] of Integer;
 begin
   glGetIntegerv(GL_VIEWPORT, @Rect);
   Result := Rect[2] - Rect[0];
 end;
 
 function glGetViewportHeight: Integer;
 var
   Rect: array[0..3] of Integer;
 begin
   glGetIntegerv(GL_VIEWPORT, @Rect);
   Result := Rect[3] - Rect[1];
 end;

   We just get left/right, top/bottom and calculate width/height by subtracting them.

   There must be functions for entering and leaving 2D mode:

 procedure glEnter2D;
 begin
   glMatrixMode(GL_PROJECTION);
   glPushMatrix;
   glLoadIdentity;
   gluOrtho2D(0, glGetViewportWidth, 0, glGetViewportHeight);
 
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix;
   glLoadIdentity;
 
   glDisable(GL_DEPTH_TEST);
 end;
 
 procedure glLeave2D;
 begin
   glMatrixMode(GL_PROJECTION);
   glPopMatrix;
   glMatrixMode(GL_MODELVIEW);
   glPopMatrix;
 
   glEnable(GL_DEPTH_TEST);
 end;

   When entering 2D mode, we save current matrices and set 2D matrix using gluOrtho2D function. This way if we draw some thing on 100, 100 it will be drawn on exactly 100 pixels from left edge of window, and 100 pixels form bottom edge (positive Y is up). Also, we disable ZBuffer. This way text won't alter ZBuffer.

   Leaving 2D mode just returns old matrices and enable ZBuffer.

   Now, we can create function for text drawing:

 procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);
 var
   I: Integer;
 begin
   glRasterPos2f(X, Y);
   for I := 1 to Length(Text) do
     glutBitmapCharacter(Font, Integer(Text[I]));
 end;

   glutBitmapCharacter can draw only one character of selected font. First parameter is desired font (GLUT_BITMAP_9_BY_15, GLUT_BITMAP_8_BY_13, GLUT_BITMAP_TIMES_ROMAN_10, GLUT_BITMAP_TIMES_ROMAN_24, GLUT_BITMAP_HELVETICA_10, GLUT_BITMAP_HELVETICA_12 or GLUT_BITMAP_HELVETICA_18) and other one is character.

   Character will be drawn at current raster position. To set desired raster position we call glRasterPos function. glRasterPos can handle different number and types of parameters just like glVertex function. Coordinate specified is transformed by model and projection matrix to get 2D coordinate where new raster position will be. Since we entered 2D mode, X and Y coordinates are actual 2D coordinates where drawing will occur.

   This new functions will make text drawing very easy:

 procedure DrawGLScene; cdecl;
 begin
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glLoadIdentity;
   glTranslatef(0, 0, -5);
   glRotatef(GetTotalTime * 10, 0, 0.5, 0.5);
 
   glColor3f(1, 0, 0);
   glutSolidCube(2);
 
   glEnter2D;
 
   glColor3f(0.2, 0.8 + 0.2 * Sin(GetTotalTime * 5), 0);
   glWrite(20, glGetViewportHeight - 20, GLUT_BITMAP_8_BY_13,
     Format('OpenGL Tutorial :: Bitmap Fonts :: FPS - %.2f FPS', [FPS]));
 
   glColor3f(1, 1, 1);
   glWrite(50, glGetViewportHeight - 60, GLUT_BITMAP_9_BY_15, 'GLUT_BITMAP_9_BY_15');
   glWrite(50, glGetViewportHeight - 90, GLUT_BITMAP_8_BY_13, 'GLUT_BITMAP_8_BY_13');
   glWrite(50, glGetViewportHeight - 120, GLUT_BITMAP_TIMES_ROMAN_10, 'GLUT_BITMAP_TIMES_ROMAN_10');
   glWrite(50, glGetViewportHeight - 150, GLUT_BITMAP_TIMES_ROMAN_24, 'GLUT_BITMAP_TIMES_ROMAN_24');
   glWrite(50, glGetViewportHeight - 180, GLUT_BITMAP_HELVETICA_10, 'GLUT_BITMAP_HELVETICA_10');
   glWrite(50, glGetViewportHeight - 210, GLUT_BITMAP_HELVETICA_12, 'GLUT_BITMAP_HELVETICA_12');
   glWrite(50, glGetViewportHeight - 240, GLUT_BITMAP_HELVETICA_18, 'GLUT_BITMAP_HELVETICA_18');
 
   glColor3f(0.5, 0.5, 1);
   glWrite(
     glGetViewportWidth - glutBitmapLength(GLUT_BITMAP_9_BY_15, LazText) - 5,
     10, GLUT_BITMAP_9_BY_15, LazText);
 
   glLeave2D;
 
   glutSwapBuffers;
 
   FrameRendered;
 end;
BitmapFontsPic1.jpg

   We draw red cube and rotate it, and some text to show how various bitmap fonts look like.

glutBitmapLength function is used to find width of string so it could be aligned to right. Code can easily be altered to center text.

   Note: See how cube looks without light.

   Download source code, linux executable or windows executable from Lazarus CCR SourceForge.

Texturas

   Es tiempo de usar texturas :)

   Este tutorial mostrará como dibujar polígonos con textura y como to blend textures using multipass technic. Since OpenGL has no builtin mechanism for loading textures, we'll use external library: Vampyre Imaging Library. We'll use just OpenGL helper functions, but you may find this lib handy for some other things to.

   Let's get started... we'll create display list for drawing textured rectangle:

 procedure CreateList;
 begin
   glNewList(LIST_OBJECT, GL_COMPILE);
     glBegin(GL_QUADS);
       glTexCoord2f(1, 0);
       glVertex3f( 2, 2, 0);
       glTexCoord2f(0, 0);
       glVertex3f(-2, 2, 0);
       glTexCoord2f(0, 1);
       glVertex3f(-2,-2, 0);
       glTexCoord2f(1, 1);
       glVertex3f( 2,-2, 0);
     glEnd;
   glEndList;
 end;

   Notice glTexCoord functions. They are used to specify which part of texture is assigned to vertex. Coordinates defined in this functions are from 0 to 1 (values greater than 1 are allowed but can generate different results). 0 is first pixel and 1 is last pixel. So, 0.5 will be right in the middle of texture.

   Texture loading is extremely easy with Vampyre Imaging Library:

 var
   Tex1, Tex2: GLuint;
 
 procedure InitializeGL;
 begin
   glClearColor(0, 0, 0, 0);
   Tex1 := LoadGLTextureFromFile('ashwood.bmp');
   Tex2 := LoadGLTextureFromFile('Flare.bmp');
   glEnable(GL_TEXTURE_2D);
 end;

   LoadGLTextureFromFile loads texture from file and returns it's ID. When texture is loaded it is allready setup for rendering.    Last line just enables 2D textures.

   To draw textured polygon you have to bind texture and setup texture coordinations (texture coordinations are set in display list in this tutorial):

   ...
   glLoadIdentity;
   glTranslatef(-5, 0, -15);
   glBindTexture(GL_TEXTURE_2D, Tex1);
   glCallList(LIST_OBJECT);
   ...

   glBindTexture function is used to select texture. When you draw polygins they will have selected texture on them. It's that easy :)

   So, using one texture is easy... but how to blend two textures. Basicly you draw polygon once with one texture, setup blending parameters, and draw polygon once more time with other texture. You can blend houndreds of textures this way. Let's see how code for this looks:

   ...
   glLoadIdentity;
   glTranslatef(5, 0, -15);
   glBindTexture(GL_TEXTURE_2D, Tex1);
   glCallList(LIST_OBJECT);
 
   glEnable(GL_BLEND);
   glBlendFunc(GL_ZERO, GL_SRC_COLOR);
   glLoadIdentity;
   glTranslatef(5, 0, -15);
   glBindTexture(GL_TEXTURE_2D, Tex2);
   glCallList(LIST_OBJECT);
   glDisable(GL_BLEND);
 ...

   As you can see, polygon is drawn first time like we allready know. Before second drawing we enable blending by calling glEnable(GL_BLEND). Blending means that finall pixel color is calculated like this:

  • DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND

SRCBLEND and DESTBLEND are defined using glBlendFunc function. In this tutorial we set SRCBLEND to GL_ZERO (zero) and DESTBLENT to GL_SRC_COLOR (DrawingColor) and finall color is then:

   DrawingColor * 0 + BackgroundColor * DrawingColor

TexturesPic1.jpg

   This means that background will get darker when you draw with dark colors... when you draw with white color, background color will not be changed. The result will look like this

   Next time, we'll use extensions to show how to use singlepass multitexturing.

   Download source code, linux executable or windows executable from Lazarus CCR SourceForge.

Multitexturing (extensions)

   When youknow multipass multi texturing, singlepass is very easy. Texturing is separated in stages. First stage setup and draw first texture, second stage draws another one and so on. Todo lo que hay que hacer es configurar las etapas de textura y renderizar el objeto.

   Veamos como queda el código:

 procedure InitializeGL;
 begin
   Load_GL_ARB_multitexture;
   glClearColor(0, 0, 0, 0);
   Tex1 := LoadGLTextureFromFile('Lazarus.bmp');
   Tex2 := LoadGLTextureFromFile('Mask.bmp');
   glActiveTextureARB(GL_TEXTURE0_ARB);
   glEnable(GL_TEXTURE_2D);
   glBindTexture(GL_TEXTURE_2D, Tex1);
   glActiveTextureARB(GL_TEXTURE1_ARB);
   glEnable(GL_TEXTURE_2D);
   glBindTexture(GL_TEXTURE_2D, Tex2);
 end;

   Primero necesitamos cargar la extensión de OpenGL que permitirá utilizar las funciones de multitextura. Load_GL_ARB_multitexture tratará de cargar estas extensiones retornando TRUE en caso de éxito.

   Para seleccionar la etapa de textura que queremos establecer utilizamos la función glActiveTextureARB. Toma únicamente un parámetro para definir la etapa que se necesita. Después de esto todas las funciones de textura (enabling, disabling, binding, creating...) afectarán esta etapa.

   Después de establecer todo en la función de inicialización, todo lo que resta por hacer es dibujar el objeto:

 procedure DrawGLScene; cdecl;
 begin
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glLoadIdentity;
   glTranslatef(0, 0, -5);
 
   glBegin(GL_QUADS);
     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
     glVertex3f(2.516, 2, 0);
     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
     glVertex3f(-2.516, 2, 0);
     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
     glVertex3f(-2.516,-2, 0);
     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
     glVertex3f(2.516,-2, 0);
   glEnd;
 
   glutSwapBuffers;
 end;
MultitexturePic1.jpg

   Como puedes observar, la diferencia está únicamente en la definiciónde las coordenadas de la textura. We now use glMultiTexCoord2fARB function that takes texture stage and texture coordinations. Every thing else is unchanged.

   Hoy días prácticamente todas las tarjetas gráficas soportan al menos 2 etapas de textura. Using singlepass multitexturing is faster than multipass version since you draw objects only once. If hardware supports singlepass multitexturing (Load_GL_ARB_multitexture returns TRUE) use it.

   Download source code, linux executable or windows executable from Lazarus CCR SourceForge.

Render to texture

   This one will be short. OpenGL can capture current scene to texture so you can use it for texturing other objects (TV screen, mirror or some thing else). Well just render scene to texture and apply it to rotating plane.

   First, we must create empty texture which we'll use to capture scene:

 procedure SetupRenderTexture;
 var
   Data: Pointer;
 begin
   GetMem(Data, 256*256*3);
   glGenTextures(1, @RenderTexture);
   glBindTexture(GL_TEXTURE_2D, RenderTexture);
   glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, Data);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   FreeMem(Data);
 end;

   Buffer for 256*256 RGB image is created and it is used to setup 2D texture.

   Main part is in drawing function:

 procedure DrawGLScene; cdecl;
 var
   TotalTime: Single;
 begin
   glClearColor(0, 0, 0, 0);
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
   glEnable(GL_LIGHTING);
   glDisable(GL_TEXTURE_2D);
   glViewport(0, 0, 256, 256);
 
   TotalTime := GetTotalTime;
 
   glLoadIdentity;
   glTranslatef(0, 0, -5);
   glRotatef(50 * TotalTime, 1, 0, 0);
   glRotatef(100 * TotalTime, 0, 1, 0);
   glRotatef(50 * TotalTime, 0, 0, 1);
 
   glColor3f(1, 1, 1);
   glutSolidCube(2);
 
   glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 256, 256, 0);
 
   glClearColor(0.18, 0.20, 0.66, 0);
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
   glDisable(GL_LIGHTING);
   glEnable(GL_TEXTURE_2D);
   glViewport(0, 0, AppWidth, AppHeight);
 
   glLoadIdentity;
   glTranslatef(0, 0, -7);
   glRotatef(20 * TotalTime, 1, 0, 0);
   glRotatef(50 * TotalTime, 0, 1, 0);
 
   glBegin(GL_QUADS);
     glTexCoord2f(1, 0);
     glVertex3f(2, 2, 0);
     glTexCoord2f(0, 0);
     glVertex3f(-2, 2, 0);
     glTexCoord2f(0, 1);
     glVertex3f(-2,-2, 0);
     glTexCoord2f(1, 1);
     glVertex3f(2,-2, 0);
   glEnd;
 
   glutSwapBuffers;
 end;
RenderToTexturePic1.jpg

   First, everything is setup for scene that will be captured. Viewport is reduced to 256*256 so it will fit into texture and scene is drawn. glCopyTexImage2D is used to capture scene to currently selected texture.

   When we have scene captured to texture, everything can be cleared again, viewport can be returned to original size and final scene is drawn using previous scene as texture.

   P.S. Captured texture can be saved using SaveGLTextureToFile function from Vampyre Imaging Library.

   Download source code, linux executable or windows executable from Lazarus CCR SourceForge.

Vertex array

   OpenGL is capable of rendering primitives using data that is stored in buffers insted of calling glVertex. Buffers can be used to define vertex and texture coordinates, and colors (index and RGBA), normals and edge flags.

   In this tutorial well use only vertex and color buffers, and we'll show non-indexed and indexed drawing. Non-indexed mode draws buffers as streams. Indexed mode will draw buffer elements in order that is defined in index buffer. But enough talking... let's start coding.

   First, let's define some types and constants:

 type
   TVertex3f = record
     X, Y, Z: Single;
   end;
 
   TColor3f = record
    R, G, B: Single;
   end;
 
   VertexBuffer: array [0..5] of TVertex3f = (
     (X : 1; Y : 1; Z : 0),
     (X : -1; Y : 1; Z : 0),
     (X : -1; Y : -1; Z : 0),
     (X : 1; Y : 1; Z : 0),
     (X : -1; Y : -1; Z : 0),
     (X : 1; Y : -1; Z : 0)
   );
   ColorBuffer: array [0..5] of TColor3f = (
     (R : 1; G : 0; B : 1),
     (R : 0; G : 0; B : 1),
     (R : 0; G : 1; B : 0),
     (R : 1; G : 0; B : 1),
     (R : 0; G : 1; B : 0),
     (R : 1; G : 1; B : 0)
   );

   We have two buffers. One for vertex coordinates and one for vertex colors. This 6 vertices defines 2 triangles that forms rectangle.

   Drawing primitives using buffers is easy:

   glEnableClientState(GL_VERTEX_ARRAY);
   glEnableClientState(GL_COLOR_ARRAY);
   glVertexPointer(3, GL_FLOAT, 0, @VertexBuffer[0]);
   glColorPointer(3, GL_FLOAT, 0, @ColorBuffer[0]);
 
   glDrawArrays(GL_TRIANGLES, 0, Length(VertexBuffer));
 
   glDisableClientState(GL_VERTEX_ARRAY);
   glDisableClientState(GL_COLOR_ARRAY);

   First we enable buffers we want to use using glEnableClientState function. Than we can select buffers we want to use. Every buffer type has own function for selecting (glColorPointer, glEdgeFlagPointer, glIndexPointer, glNormalPointer, glTexCoordPointer, glVertexPointer).

   First parameter in those functions defines how many numbers every element contains. For example, let's take vertex buffer. If this parameter is 2 than OpenGL expects that every element in buffer contains x and y coordinate. If this parameter is, for example, 4, than every element should contains x, y, z and w coordinate.

   Next parameter defines what type of data element contains (GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT or GL_DOUBLE).

   Next one defines how many bytes are between each element. This way you can have buffer that contains vertex coordinates and some custom data. For arbitrary data type, this parameter can be calculated like this:

 type
   TBufferData = record
     DataBefore: TDataBefore;
     Vertex: TVertex;
     DataAfter: TDataAfter;
   end;
Bytes between elements = SizeOf(TDataBefore) + SizeOf(TDataAfter)

   Last parameter if pointer to the begginig of buffer.

   When buffers are selected we can draw them using glDrawArrays functions. All enabled buffers are used to draw primitives. What kind of polygons are being generated is defined in first parameter (same as in glBegin function). Next two defines subset of buffer which is used for drawing (start and count).

   When buffers are not needed you can disable them.

   To demonstrate indexed mode, I made some simple mesh class that can load vertex, color and index data from external files:

 type
   TMesh = class
   private
     FVertices: array of TVertex3f;
     FColors: array of TColor3f;
     FIndices: array of Integer;
     procedure FreeBuffers;
   public
     constructor Create;
     destructor Destroy; override;
     procedure LoadMesh(FileName: String);
     procedure DrawMesh;
   end;

   FVertices will contain data about vertices, FColors data about color and FIndices data about indices when external file is loaded.

   First we'll write some code that deals with creation and destruction of class:

 procedure TMesh.FreeBuffers;
 begin
   FVertices := nil;
   FColors := nil;
   FIndices := nil;
 end;
 
 constructor TMesh.Create;
 begin
   FreeBuffers;
 end;
 
 destructor TMesh.Destroy;
 begin
   FreeBuffers;
   inherited Destroy;
 end;

   File that will contain mesh data is simple text file. First row will contain number of vertices and indices separated by space character. After that row will come rows for every vertex and color. X, Y, Z, R, G and B all separated by space character. In the end, there will be rows for indices... every index number is written in its own row... so, for one triangle, data file will look like this:

3 3
-1 -1 0 1 1 1
1 -1 0 1 1 1
0 1 0 1 1 1
0
1
2

   This means that there is 3 vertices and 3 indices defined in file. First vrtex is at -1, -1, 0 and has color 1, 1, 1 and so on. Indices defines that order in which vertices are drawn (in this case vertices are drawn in the same order as they are defined).

   Code for loading this data will loke like this:

 procedure TMesh.LoadMesh(FileName: String);
 var
   MeshFile: TextFile;
   VertexCount, IndexCount: Integer;
   iV, iI: Integer;
 begin
   FreeBuffers;
 
   AssignFile(MeshFile, FileName);
   Reset(MeshFile);
 
   ReadLn(MeshFile, VertexCount, IndexCount);
 
   SetLength(FVertices, VertexCount);
   SetLength(FColors, VertexCount);
   SetLength(FIndices, IndexCount);
 
   for iV := 0 to VertexCount - 1 do
     ReadLn(MeshFile,
       FVertices[iV].X, FVertices[iV].Y, FVertices[iV].Z,
       FColors[iV].R, FColors[iV].G, FColors[iV].B);
 
   for iI := 0 to IndexCount - 1 do
     ReadLn(MeshFile, FIndices[iI]);
 
   CloseFile(MeshFile);
 end;

   After loading data, we have everything for drawing:

 procedure TMesh.DrawMesh;
 begin
   glEnableClientState(GL_VERTEX_ARRAY);
   glEnableClientState(GL_COLOR_ARRAY);
   glVertexPointer(3, GL_FLOAT, 0, @FVertices[0]);
   glColorPointer(3, GL_FLOAT, 0, @FColors[0]);
 
   glDrawElements(GL_TRIANGLES, Length(FIndices), GL_UNSIGNED_INT, @FIndices[0]);
 
   glDisableClientState(GL_VERTEX_ARRAY);
   glDisableClientState(GL_COLOR_ARRAY);
 end;

   As you can see, allmost everything is the same as for non-indexed drawing, except function that actually draw polygons. In this case we use glDrawElements function. For this one we specify what kind of polygons we want, how many indices are in index buffer, type of data in index buffer and pointer to the beginning of index buffer.

   

VertexArrayPic1.jpg

Full source code comes with mesh data file that this class can use to generates rectangle that is identical with one that is drawn using non-indexed mode. Mesh data file looks like this:

4 6
1 1 0 1 0 1
-1 1 0 0 0 1
-1 -1 0 0 1 0
1 -1 0 1 1 0
0
1
2
0
2
3

   As you can see, there is data for only 4 vertices and 6 indices. So, first triangle is defined by vertices 0, 1 and 2, and the seccond one by vertices 0, 2 and 3. By using indexed mode we don't have to duplicate vertices.

   Download source code, linux executable or windows executable from Lazarus CCR SourceForge.


Creating bindings for C libraries