Mostrando entradas con la etiqueta 3D. Mostrar todas las entradas
Mostrando entradas con la etiqueta 3D. Mostrar todas las entradas

Simulación del movimiento de los electrones en un campo electrico


Espectacular simulación realizada con OpenGL del movimiento de los electrones cuando atraviesan un campo eléctrico.

Como muestra la imagen se ven los electrones saliendo en tiempo real de la placa de color gris hacia la placa de color rojo acelerándose cuando atraviesan el campo eléctrico.

El escenario se puede girar o rotar en todas direcciones para apreciar mejor el movimiento.


Autor:
Email: systemhalt@nm.ru
www.systemhalt.nm.ru


Codigo fuente



Rotaciones en 3D con RAD Studio


En este post veremos cómo crear y rotar una figura en 3D,  utilizando Delphi RAD Studio.

Empezamos desde File – New – Multi-Device Application y
seleccionamos “3D Application”





A continuación vamos añadiendo los componentes básicos:

-     tViewPort3D : será el lienzo donde se mostrará la
animación en 3D.

-    tColorMaterialSource, en la propiedad “Color”
selecciono “Snow”
.
-  tLightMaterialSource, dejamos marcadas las
propiedades como se indica en la imagen: 



Ahora tenemos que indicar la orientación de la luz y la
orientación del observador de la escena.

Añadimos 2 componentes más:

tLight


tCamera






Estos dos últimos componentes tienen unos botones de control
que permiten moverlos en los 3 ejes del espacio.




Y otro botón más que permite cambiar el tamaño de cada una
de las caras del objeto.



Por último sólo nos queda añadir los bloques de la figura
que queremos crear.

En nuestro caso utilizaremos los siguientes:

tRoundcube (el bloque principal), tPlane (será la pantalla)
y dos tDisk correspondientes a las cámaras




Utilizando los controles de orientación tendremos que mover
cada bloque
hasta conseguir lo siguiente:



Ahora sólo nos queda conseguir el movimiento de la figura

Lo haremos utilizando un tTimer y variaremos el ángulo de
rotación.


PROCEDURE TForm1.Timer1Timer(Sender: TObject);


BEGIN


  WITH
RoundCube1.RotationAngle DO


    Point := Point +
Point3D(2, 0, 0);




END;





Activamos el Timer y 
¡¡ ya lo tenemos !!





UNIT Unit1;

INTERFACE

USES
System.SysUtils, System.Types, System.UITypes, System.Classes,
System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
System.Math.Vectors, FMX.Types3D, System.Sensors, System.Sensors.Components,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, FMX.MaterialSources,
FMX.Objects3D, FMX.Controls3D, FMX.Viewport3D;

TYPE
TForm1 = CLASS(TForm)
Viewport3D1: TViewport3D;
Dummy1: TDummy;
Camera1: TCamera;
Light1: TLight;
RoundCube1: TRoundCube;
Plane1: TPlane;
Disk1: TDisk;
LightMaterialSource1: TLightMaterialSource;
ColorMaterialSource1: TColorMaterialSource;
Disk2: TDisk;
Timer1: TTimer;
PROCEDURE Timer1Timer(Sender: TObject);
PRIVATE
{ Private declarations }
PUBLIC
{ Public declarations }
END;

VAR
Form1: TForm1;

IMPLEMENTATION

{$R *.fmx}


PROCEDURE TForm1.Timer1Timer(Sender: TObject);
BEGIN

WITH RoundCube1.RotationAngle DO
Point := Point + Point3D(2, 0, 0);

END;

END.




Programas Delphi que utilizan OPENGL






1) Generador de curvas de Bezier



Esta aplicación genera un array de vértices utilizados para construir curvas de Bezier. Requiere que la tarjeta gráfica sea compatible con OpenGL.



Codigo fuente y programa








2) Utilización de una Webcam






Utilizando las primitivas OpenGL, permite ver la imagen de una webcam en una rejilla tridimensional, con la posibilidad de cambiar el ángulo de la cámara, la distancia del observador y la rotación de la imagen.











3) Bandera















4) Simulación del movimiento de los electrones































Simulador de circuitos lógicos



Simulador de circuitos con código fuente que permite diseñar circuitos basándose en puertas lógicas: Y, AND, OR, etc.



Una vez que se termina el diseño se puede activar virtualmente para ver su resultado.
Utilizando o no una tabla es posible realizar funciones básicas: sumador, comparador, etc y simular cualquier expresión booleana de una forma fácil.
El autor incluye varios ejemplos:
-Un display de 7 segmentos
-Un multiplicador binario.

Descargar codigo

Autor: Montero-Ribas

Web: ADMR.CAD.Free.fr





Movimiento de un octoedro



Movimiento de un octaedro en pantalla. (No utiliza OpenGL, GlScene)





Codigo fuente










LIBROS:














Rotación de poliedros en el espacio








Simula el movimiento de un objeto sobre los 3 ejes X,Y,Z





Una app para visualizar moléculas







A continuación os presento una app llamada "Molecule Hero" que permite ver moléculas en 3D , su autor es  Pawel Glowacki





Tiene entre otras opciones la posibilidad de mostrar el "modelo de alambre"  p.ej, en el caso de la molécula de la Cafeína sería el siguiente:






La app está disponible en Play Store (abajo tenéis el link) y  el código fuente está en GitHub bajo licencia MIT de código abierto por lo que se puede reutilizar el código indicando su procedencia.

Utiliza la renderización de esferas (para mostrar los átomos de la molécula) en tiempo real, además permite girar desde todos los ángulos el modelo y ver las conexiones entre los diferentes átomos.

Los datos los obtiene desde el "Protein Data Bank" PDB que es el formato estándar de archivo para el intercambio de datos de estructuras de moléculas.

Así que si queréis ver cómo es la molécula de la cafeína, del colesterol o del diamante, entre otras muchas, esta es vuestra app.


Pawel Glowacki formó parte  del equipo de Embarcadero como "Technical Lead for Developer Tools". (D.E.P.)



Link de la app Molecule Hero en Play Store







Modelado 3D con Delphi




Para los entusiastas del modelado 3D aquí tienen un soft realizado en Delphi que permite construir piezas basándose en primitivas simples cilindros, cubos, etc y con las operaciones topológicas habituales como fusión, intersección, recorte, etc...


Permite hacer figuras de alambre, moverla y rotarla desde todos los ejes de coordenadas, hacer simulación del movimiento y pintar con colores cada cara del objeto.



Autor: Montero-Ribas

Licencia: GNU GPL



Descargar programa



Descargar codigo fuente






LIBROS:

Autocad 3D, DIBUJO Y MODELADO

3D con Photoshop

Aprender Maya, nivel avanzado, ejemplos y ejercicios

Aprender 3DS Max 2017 con 100 ejercicios prácticos

Programación en 3D con Java

Aprende Blender, diseño y creatividad











































Modelando en 3D con Firemonkey

Firemonkey proporciona la capacidad de utilizar modelos de alambre en 3D en los formatos OBJ, DAE y ASE, que son los más utilizados por el software diseñado para ese fin (p.ej blender...)

Para ahorrar tiempo, en vez de crear los modelos nosotros mismos, hay sitios web que los ofrecen de forma gratuita y sin pagar royalties.

Uno de los muchos sites que hay en la red  es www.turbosquid.com









En esa página hay miles de modelos que podremos utilizar en nuestro programa.

Para empezar tenemos que abrir RAD Studio y crear una nueva Multi-Device Application 3D (tienen los pasos a seguir en este post)

La estructura de los componentes será la siguiente:









El componente tModel3D (Model3D1) deberá colgar de un tDummy (DummyScene) y el objeto de malla en formato OBJ lo tendremos que indicar desde la propiedad MeshCollection (lo indico en color amarillo en la imagen superior).

Nos encontraremos con el incoveniente que el componente tModel3D no tiene asignada la propiedad "MaterialSource" por defecto, por lo que la tenemos que asignar en el evento OnCreate:





procedure TForm1.FormCreate(Sender: TObject);
var mesh : TMesh;
begin
for mesh in Model3D1.MeshCollection do
mesh.MaterialSource := LightMaterialSource1;
end;






También tendremos que añadir un manejador de eventos para conseguir que la figura de malla se mueva según la dirección del ratón y haga un zoom cuando se mueva la rueda del ratón:





procedure TForm1.FormGesture(Sender: TObject;
const EventInfo: TGestureEventInfo; var Handled: Boolean);
var delta: integer;
begin
if EventInfo.GestureID = igiZoom then
begin
if (not (TInteractiveGestureFlag.gfBegin in EventInfo.Flags))
and (not (TInteractiveGestureFlag.gfEnd in EventInfo.Flags)) then
begin
delta := EventInfo.Distance - UltimaDistancia;
DoZoom(delta > 0);
end;
UltimaDistancia := EventInfo.Distance;
end;
end;






Igualmente hay que manejar los eventos OnMouseDown, OnMouseMove y OnMouseWheel del componente ViewPort3D1





procedure TForm1.MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
Punto := PointF(X, Y);
end;

procedure TForm1.MouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Single);
begin
if (ssLeft in Shift) then
begin
DummyCamera.RotationAngle.X := DummyCamera.RotationAngle.X - ((Y - Punto.Y) * ROTATION_STEP);
DummyCamera.RotationAngle.Y := DummyCamera.RotationAngle.Y + ((X - Punto.X) * ROTATION_STEP);
Punto := PointF(X, Y);
end;
end;

procedure TForm1.MouseWheel(Sender: TObject;
Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean);
begin
DoZoom(WheelDelta > 0);
end;





Después ejecutaremos la aplicación y podremos ver la figura en 3D desde todos los ángulos posibles,

se puede rotar, ampliar, girar, etc... realmente es espectacular lo que hemos conseguido con unas pocas líneas de código.























Descargar el programa







Creando aplicaciones en 3D con Firemonkey

En este post vamos a ver lo fácil que es realizar una aplicación simple en 3D con Firemonkey.

Comenzamos desde el menú File - New - Multi Device Application Delphi - 3D Application








Añadimos un componente llamado tDummy que se comportará como un contenedor de otros componentes en Firemonkey 3D. Este componente es el equivalente al tLayout en el mundo de Firemonkey 2D.


Con tDummy podremos mover, girar, rotar y escalar los objetos que le asociemos y será el objeto del que descenderán casi todos los que coloquemos en el form, esto nos dará una forma muy sencilla de manipular una escena.









Ahora tenemos que seleccionar el componente tSphere y colocarlo de tal forma que cuelgue del componente tDummy.


Seleccionamos tDummy y cambiamos la propiedad Scale X,Y,Z al valor 5.


Desde la vista Structure tiene que quedar así:









Seguidamente seleccionamos el componente tTextureMaterialSource y lo colocamos en el form.


Después hacemos clic en tSphere y asociamos la propiedad "MaterialSource" al componente tTextureMaterialSource.










Ahora tenemos que crear un bitmap para la textura. Este puede ser una imagen en formato jpg, png, bmp, un color sólido, etc.


Pero para crear un efecto espectacular vamos a cargar la imagen de la tierra vista desde el espacio.


Para ello hay que seleccionar el componente tTextureMaterialSource1 y dentro de él hacer clic en Texture - Edit y seleccionar la imagen en formato *.jpg









 Yo la he descargado desde aquí: http://visibleearth.nasa.gov/


Ahora ya podremos ejecutar la aplicación y veremos  el globo terráqueo en 3D,


 


Pero para dar más realismo y hacer que gire  en 3D lo que hay que hacer es crear una animación, que se hace de una forma muy sencilla, añadiendo el componente tFloatAnimation y teniendo la precaución de quede "colgado" del tShpere en la vista de Structure.









Dentro del componente tFloatAnimation modificamos las propiedades que os he marcado en amarillo:


-Duration = 2


-Enabled = True


-Loop = true


-PropertyName = RotationAngle.Y


-StartValue=1


-StopValue=-360





Y eso es todo,  ¡ya tenemos nuestra primera aplicación en 3D ¡



Clic aquí para descargar el programa






























Modelo 3D del vehículo Perseverance realizado con Firemonkey

 


A continuación les muestro un ejemplo sobre cómo cargar el modelo de malla del rover Perseverance que actualmente está en Marte para obtener información del clima y del suelo de este planeta.
Este modelo lo ha puesto a disposición de todos la NASA y está en formato glb.
Si les interesa este tema y quieren ampliar más información sobre el programa de exploración de marte lo pueden consultar aquí
Los archivos glb contienen un modelo de un objeto 3D guardado en el formato de transmisión GL (glTF).
Almacena información sobre un modelo 3D, como la jerarquía de nodos, cámaras y materiales, animaciones y mallas en formato binario. Los archivos GLb son la versión binaria de los archivos GLTF.
Los archivos GLB pueden utilizarse para guardar y compartir activos digitales entre diferentes herramientas de modelado 3D, de forma similar a los archivos .DAE. Sin embargo, también están optimizados para la velocidad de descarga y el tiempo de carga en tiempo de ejecución, lo que hace que sean más fáciles de usar en programas de modelado 3D basados en móviles y en la web. También son un formato más racionalizado para cargar y descargar desde bases de datos de activos digitales en línea, como 3D Warehouse y Remix 3D.

Para descargar el modelo usa el componente tNetHTTPClient, ( también se podría utilizar tIdHTTP ), que lo que hace descargar el modelo desde la URL

https://mars.nasa.gov/system/resources/gltf_files/25042_Perseverance.glb

y almacenarlo en un tMemoryString.
Luego utiliza la clase TGLBModel para crear los nodos, el escalado, los materiales con el fin de crear la hoja de malla que visualizaremos en el tViewPort y que nos permitirá girar, rotar o escalar el modelo del rover.

Dentro del ViewPort se ha definido un componente tLight y un tDummy que contendrá toda la imagen del vehículo.

Lo bueno de utilizar Firemonkey es que de una forma transparente al usuario utiliza la capacidad de cálculo de la GPU del equipo para renderizar la imagen prácticamente en tiempo real.

El código es el siguiente:



unit Perseverance.FMX.Main;

interface

uses
System.SysUtils,
System.Types,
System.UITypes,
System.Classes,
System.Variants,
System.JSON,
FMX.Types,
FMX.Controls,
FMX.Forms,
FMX.Graphics,
FMX.Dialogs,
FMX.StdCtrls,
FMX.Controls.Presentation,
FMX.Edit,
FMX.Objects,
FMX.Layouts,
FMX.Types3D,
System.Net.URLClient,
System.Net.HttpClient,
System.Net.HttpClientComponent,
System.IOUtils,
System.Math.Vectors,
FMX.Controls3D,
FMX.Objects3D,
FMX.Viewport3D,
FMX.MaterialSources,
Execute.GLB.FMX, FMX.TreeView;

type
TMainForm = class(TForm)
Layout1: TLayout;
Text1: TText;
edURL: TEdit;
btGo: TButton;
NetHTTPClient: TNetHTTPClient;
Viewport3D1: TViewport3D;
Light1: TLight;
root: TDummy;
procedure btGoClick(Sender: TObject);
procedure Viewport3D1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Viewport3D1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Single);
procedure Viewport3D1MouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; var Handled: Boolean);
procedure FormCreate(Sender: TObject);
private
{ Déclarations privées }
FDown: TPointF;
FModel: TGLBFMXModel;
FMesh: TMesh;
FMat: TMaterialSource;
public
{ Déclarations publiques }
end;

var
MainForm: TMainForm;

implementation

{$R *.fmx}

procedure TMainForm.FormCreate(Sender: TObject);
//var
// Stream: TFileStream;
begin
FModel := TGLBFMXModel.Create(Self);
FModel.Parent := root;

// Stream := TFileStream.Create('..\..\..\VCL\win32\debug\TEST.GLB', fmOpenRead);
// FModel.LoadFromStream(Stream);
// Stream.Free;
end;

procedure TMainForm.btGoClick(Sender: TObject);
var
GLB: TMemoryStream;
begin
GLB := TMemoryStream.Create;
try
{$IFDEF DEBUG}
if FileExists('Perseverance.glb') then
begin
GLB.LoadFromFile('Perseverance.glb');
end else begin
{$ENDIF}
NetHTTPClient.Get(edURL.Text, GLB);
{$IFDEF DEBUG}
GLB.SaveToFile('Perseverance.glb')
end;
{$ENDIF}
GLB.Position := 0;
FModel.LoadFromStream(GLB);
finally
GLB.Free;
end;
end;

procedure TMainForm.Viewport3D1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
FDown := PointF(X, Y);
end;

procedure TMainForm.Viewport3D1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Single);
begin
if (ssLeft in Shift) then
begin
root.RotationAngle.X := root.RotationAngle.X + (Y - FDown.Y) * 0.3;
root.RotationAngle.Y := root.RotationAngle.Y + (X - FDown.X) * 0.3;
FDown.X := X;
FDown.Y := Y;
end;
if (ssRight in Shift) then
begin
root.Position.Z := root.Position.Z - (Y - FDown.Y) * 0.3;
FDown.Y := Y;
end;
end;

procedure TMainForm.Viewport3D1MouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; var Handled: Boolean);
begin
root.Position.Z := root.Position.Z - ((WheelDelta / 120) * 0.3);
end;

end.



------------------ 

Este sería el diseño del form:

Estructura del programa:


 

Programa terminado en Firemonkey

 


El programa se ha realizado con Delphi 10.4.1

El autor del código es Paul Toth (MVP Embarcadero).

El programa (ejecutable y código fuente) está en el github de Paul Toth

Para descargarlo pulsar el botón CODE y en el menú emergente seleccionar "Download.zip"

 



 








Diseñando hypercubos de N dimensiones en Delphi













Este programa te permite dibujar un hypercubo utilizando opengl. 

Hay varios modos de representación, estructura alámbrica, iluminación, estructura alámbrica con líneas ocultas, iluminación con líneas ocultas ... 

También hay una función que permite exportar los vértices calculados en un archivo * .inc que se puede usar con POV-Ray.






 




unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, Opengl, StdCtrls, ComCtrls, Buttons;

const
MaxDim=10; //Dimension maximale de travail
GL_POLYGON_OFFSET_FILL=$8037;

type
TVect=array[1..MaxDim] of Single; //vecteur
TMatrix=array[1..MaxDim,1..MaxDim] of Single; //matrice

TForm1 = class(TForm)
Timer1: TTimer;
Panel1: TPanel;
ComboBox1: TComboBox;
GroupBox1: TGroupBox;
RadioGroup1: TRadioGroup;
CheckBox1: TCheckBox;
SpeedButton1: TSpeedButton;
SpeedButton2: TSpeedButton;
Panel2: TPanel;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ComboBox1Change(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
procedure SpeedButton2Click(Sender: TObject);
private
FGLRC:HGLRC; //contexte opengl
FVertexes:array of TVect; //les sommets des faces
FDim:Integer; //la dimension courante (inférieure à MaxDim)
FMatrix:TMatrix; //la matrice des rotations

procedure TrackBarChange(Sender:TObject); //événement lorsqu'un angle a changé
procedure MakeMatrix; //génération de la matrice à partir des angles
procedure DrawScene; //tracé de la scène
procedure Animate; //procédure appelée pour changer les angles lorsque l'animation est activée
public
end;

var
Form1: TForm1;

procedure glPolygonOffset(factor:GLfloat;units:GLfloat);stdcall;external Opengl32; //fonction Opengl non standard

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject); //initialisation d'opengl et des diverses variables
var
PFd:TPixelFormatDescriptor;
c:Integer;
begin
WindowState:=wsMaximized;
ZeroMemory(@PFD,SizeOf(PFD));
PFD.nSize:=SizeOf(PFD);
PFD.nVersion:=1;
PFD.dwFlags:=PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER; //on se met en mode doublebuffered pour éviter les clignotements
PFD.iPixelType:=PFD_TYPE_RGBA;
PFD.cColorBits:=GetDeviceCaps(Canvas.Handle,BITSPIXEL);
PFD.cAlphaBits:=8; //8 bits pour le canal alpha
PFD.cDepthBits:=16; //16 pour le test de profondeur (Z-buffer)
PFD.cStencilBits:=16; //on va effectuer de la géométrie cnstructive en utilisant le stencil buffer
c:=ChoosePixelFormat(Canvas.Handle,@PFD);
Assert(c<>0,'No valid pixel foprmat is supported: '+SysErrorMessage(GetLastError));
Assert(SetPixelFormat(Canvas.Handle,c,@PFD),'Couldnt set the pixel format: '+SysErrorMessage(GetLastError)); //on active le pixel format du canvas pour supporter opengl
FGLRC:=wglCreateContext(Canvas.Handle); //on crée un contexte opengl
Assert(FGLRC<>0,'Couldnt create a valid GL context: '+SysErrorMessage(GetLastError));
Assert(wglMakeCurrent(Canvas.Handle,FGLRC),'Couldnt create use the GL context: '+SysErrorMessage(GetLastError)); //on active le contexte
SetLength(FVertexes,0);
ComboBox1Change(nil); //on initialise le premier cube
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
DrawScene; //on trace la scène
glFlush; //on notifie opengl que le tracé est fini
SwapBuffers(Canvas.Handle); //on "swap" les 2 buffers (puisqu'on est en mode doublebuffered)
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
Assert(wglMakeCurrent(Canvas.Handle,0),'Couldnt unlock the GL context: '+SysErrorMessage(GetLastError)); //on désactive le contexte
Assert(wglDeleteContext(FGLRC),'Couldnt delete the GL context: '+SysErrorMessage(GetLastError)); //on le détruit
SetLength(FVertexes,0);
end;

function PowerOfTwo(n:Integer):Integer; //calcule une puissance de 2 (fonction peu optimisée mais peu utilisée)
begin
Result:=Round(Exp(Ln(2)*n));
end;

procedure TForm1.ComboBox1Change(Sender: TObject); //Changement de dimension
var
a,b,c,n:Integer;
t:TTrackBar;

procedure MakeSubFaces; //On crée tous les quadrilatères possibles qui sont situés dans les plans de directions numéro a et b. Il y en a 2^(FDim-2) en tout.
var
i,j,u,v:Integer;
begin
for i:=0 to n-1 do begin //pour a et b fixés, il y a n quadrilatères possibles, correspondant à toutes les combinaisons de coordonnées 1 et -1
ZeroMemory(@FVertexes[c+4*i],SizeOf(TVect)); //dans les directions orthogonales à a et b. il suffit alors de prendre la représentation en base 2 de i variant
ZeroMemory(@FVertexes[c+4*i+1],SizeOf(TVect)); //de 0 à n-1, et de dire que 0 correspond à la coordonnée -1, et 1 correspond à la coordonnée 1, ainsi on aura bien
ZeroMemory(@FVertexes[c+4*i+2],SizeOf(TVect)); //toutes les combinaisons possibles de 1 et de -1 pour les directions orthogonales à a et b.
ZeroMemory(@FVertexes[c+4*i+3],SizeOf(TVect));
u:=1;
for j:=1 to FDim do begin //on parcourt toutes les coordonnées du vecteur
if (j=a) or (j=b) then //si ce sont les coordonnées a ou b, on ne fait rien
Continue;
if (u and i)=u then //sinon, si le bit de i vaut 1, la coordonnée est 1, sinon elle vaut -1
v:=1
else
v:=-1;
FVertexes[c+4*i,j]:=v; //on modifie la coordonnée correspondante pour les 4 sommets du quadrilatère
FVertexes[c+4*i+1,j]:=v;
FVertexes[c+4*i+2,j]:=v;
FVertexes[c+4*i+3,j]:=v;
u:=u*2;
end;
FVertexes[c+4*i,a]:=1; //pour les coordonnées numéro a et b, on prend celles du carré usuel dans l'ordre: (1,1) (-1,1) (-1,-1) (1,-1) pour avoir une courbe fermée
FVertexes[c+4*i+1,a]:=1;
FVertexes[c+4*i+2,a]:=-1;
FVertexes[c+4*i+3,a]:=-1;
FVertexes[c+4*i,b]:=1;
FVertexes[c+4*i+1,b]:=-1;
FVertexes[c+4*i+2,b]:=-1;
FVertexes[c+4*i+3,b]:=1;
end;
c:=c+4*n; //on a calculé en tout 4*n coordonnées supplémentaires, on décale l'indice de base
end;

begin
Randomize; //réinitialisation de la fonction random
FDim:=ComboBox1.ItemIndex+3; //calcul de la nouvelle dimension
GroupBox1.DestroyComponents; //on détruit les trackbars de controle des angles
t:=nil;
for a:=0 to 3*(FDim-3)+2 do begin //on crée les nouvelles trackbar
t:=TTrackBar.Create(GroupBox1);
t.Align:=alTop;
t.Top:=10000;
t.Height:=26;
t.Min:=0;
t.Max:=3000;
t.Position:=Random(3000); //position aléatoire
t.TickMarks:=tmBoth;
t.TickStyle:=tsNone;
t.Tag:=a;
t.OnChange:=TrackBarChange; //événement pour calculer la matrice si l'utilisateur change un angle
t.Parent:=GroupBox1;
t.Tag:=Random(11)-5; //vitesse de déplacement pour le mode animation
end;
GroupBox1.ClientHeight:=t.Height+t.Top+3; //on ajuste la hauteur de la groupbox
SetLength(FVertexes,(FDim-1)*FDim*PowerOfTwo(FDim-1)); //cette formule donne le nombre de faces pour l'hypercube (multiplié par 4 car une face a 4 sommets)
c:=0;
n:=PowerOfTwo(FDim-2);
for a:=1 to FDim-1 do //on va parcourir toutes les directions de plan possibles en dimension n. Un plan est défini par 2 directions orthogonales ici, elles ont pour numéro
for b:=a+1 to FDim do //a et b par rapport à la base canonique
MakeSubFaces; //on crée pour la direction du plan, tous les carrés possibles en prenant toutes les combinaisons de 1 et de -1 pour les coordonnées de numéro différent de a et b
MakeMatrix; //on actualise la matrice de rotations
Caption:='Hypercube: '+IntToStr((High(FVertexes)+1) div 4)+' faces'; //nombre total de faces
end;

procedure TForm1.TrackBarChange(Sender: TObject); //cet événement est appelé lorsqu'une des trackbars créées automatiquement est modifiée
begin
MakeMatrix; //si l'utilisateur a changé une des trackbars, on recalcule la matrice de rotation.
end;

function MultMatrix(m,n:TMatrix):TMatrix; //multiplication de 2 matrices
var
a,b,c:Integer;
s:Single;
begin
for a:=1 to MaxDim do
for b:=1 to MaxDim do begin
s:=0;
for c:=1 to MaxDim do
s:=s+m[a,c]*n[c,b];
Result[a,b]:=s;
end;
end;

function GetIdentity:TMatrix; //la matrice identité
var
a:Integer;
begin
ZeroMemory(@Result,SizeOf(Result));
for a:=1 to MaxDim do
Result[a,a]:=1;
end;

function MakeRotation(ID,IDD:Integer;Angle:Single):TMatrix; //calcule la matrice de rotation dans le plan défini par les directions canoniques de numéros ID et IDD
begin
Result:=GetIdentity;
Result[ID,ID]:=Cos(Angle);
Result[IDD,IDD]:=Result[ID,ID];
Result[IDD,ID]:=Sin(Angle);
Result[ID,IDD]:=-Result[IDD,ID];
end;

procedure TForm1.MakeMatrix;
var
a:Integer;
begin
if Tag<>0 then //cette condition est utile pour le mode animation, pour ne pas recalculer à chaque fois la matrice
Exit;
FMatrix:=GetIdentity;
FMatrix:=MultMatrix(FMatrix,MakeRotation(2,3,TTrackBar(GroupBox1.Controls[0]).Position*2*pi/3000)); //les 3 premières rotations sont dans l'espace usuel à 3 dimensions
FMatrix:=MultMatrix(FMatrix,MakeRotation(3,1,TTrackBar(GroupBox1.Controls[1]).Position*2*pi/3000));
FMatrix:=MultMatrix(FMatrix,MakeRotation(1,2,TTrackBar(GroupBox1.Controls[2]).Position*2*pi/3000));
for a:=0 to 3*(FDim-3)-1 do
FMatrix:=MultMatrix(FMatrix,MakeRotation((a mod 3)+1,(a div 3)+4,TTrackBar(GroupBox1.Controls[a+3]).Position*2*pi/3000)); //les suivantes sont dans tous les plans qui contiennent un vecteur de l'espace usuel et un vecteur dans son orthogonal
end;

function MultVect(m:TMatrix;v:TVect):TVect; //multiplication d'un vecteur par une matrice
var
a,b:Integer;
s:Single;
begin
for a:=1 to MaxDim do begin
s:=0;
for b:=1 to MaxDim do
s:=s+m[a,b]*v[b];
Result[a]:=s;
end;
end;

function CrossP(u,v:TVect):TVect; //produit vectoriel en dimension 3 (les autres coordonnées sont ignorées)
begin
Result[1]:=u[2]*v[3]-u[3]*v[2];
Result[2]:=u[3]*v[1]-u[1]*v[3];
Result[3]:=u[1]*v[2]-u[2]*v[1];
end;

function Minus(u,v:TVect):TVect; //différence de 2 vecteurs
var
a:Integer;
begin
for a:=1 to MaxDim do
Result[a]:=u[a]-v[a];
end;

procedure TForm1.DrawScene; //la procédure qui trace l'hypercube
var
a:Integer;

procedure DrawVertex(v:TVect); //pour dessiner un vecteur
begin //ici, on fait une projection implicite: seule les 3 premières coordonnées du vecteur sont utilisées, ça correspond à projeter orthogonalement
glVertex3f(v[1],v[2],v[3]); //l'espace de dimension n sur l'espace usuel de dimension 3
end;

procedure DrawFace(Id:Integer;Mode:Cardinal); //pour dessiner une face à 4 côtés en fonction d'un mode de primitive opengl (par exemple GL_QUADS ou GL_LINE_LOOP)
var
t,u,v,w,n:TVect;
begin
t:=MultVect(FMatrix,FVertexes[Id]); //on calcule la projection des 4 sommets de la face en utilisant la matrice de rotations
u:=MultVect(FMatrix,FVertexes[Id+1]);
v:=MultVect(FMatrix,FVertexes[Id+2]);
w:=MultVect(FMatrix,FVertexes[Id+3]);
n:=Crossp(Minus(t,v),Minus(u,w)); //on calcule le produit vectoriel pour avoir la normale
glBegin(Mode);
glNormal3f(n[1],n[2],n[3]); //la normale...
DrawVertex(t); //... et les 4 coordonnées
DrawVertex(u);
DrawVertex(v);
DrawVertex(w);
glEnd;
end;

procedure DrawFaces(Mode:Cardinal); //pour dessiner toutes les faces de l'hypercube en fonction d'un mode de primitive unique
var
a:Integer;
begin
for a:=0 to ((High(FVertexes)+1) div 4)-1 do
DrawFace(4*a,Mode);
end;

const
Specular:array[0..3] of GlFloat=(0.8,0.3,0.1,1.0);

begin
if CheckBox1.Checked then //si on est en mode animation, on fait varier les angles de rotation
Animate;
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); //on efface la scène d'avant
glViewport(0,0,ClientWidth-Panel1.Width,ClientHeight); //viewport en fonction de la zone de tracé
glMatrixMode(GL_PROJECTION); //calcul de la matrice de projection Opengl
glLoadIdentity;
gluPerspective(100,(ClientWidth-Panel1.Width)/ClientHeight,0.001,10); //calcul de la perspective, avec un angle de 100° pour la caméra
gluLookAt(0,0,3,0,0,0,0,1,0); //position de la caméra
glColor3f(1,1,1); //couleur par défaut
glEnable(GL_NORMALIZE); //renormalisation automatique des vecteurs normaux
glPushAttrib(GL_ALL_ATTRIB_BITS); //pour éviter d'avoir a réinitialiser les paramètres par défaut
glEnable(GL_LINE_SMOOTH); //plus joli
glEnable(GL_POLYGON_SMOOTH); //plus joli
case RadioGroup1.ItemIndex of //choix du mode de tracé
0:DrawFaces(GL_LINE_LOOP); //lignes simples
1:begin //faces éclairées
glColor3f(0.4,0.6,0.5); //couleur de base
glEnable(GL_DEPTH_TEST); //on doit utiliser le Z-buffer pour la gestion des faces cachées
glEnable(GL_LIGHTING); //on active la gestion de la lumière
glEnable(GL_LIGHT0); //on active la première lampe
glEnable(GL_COLOR_MATERIAL); //le matériau est assujeti à la couleur courante
glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE); //la couleur courante va modifier la diffusion uniquement
glMaterial(GL_FRONT_AND_BACK,GL_SHININESS,5); //pour faire des reflets avec la lampe (donne un aspect plus "métallique")
glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,@Specular); //couleur spéculaire du matériau
glLightModel(GL_LIGHT_MODEL_TWO_SIDE,1); //les faces doivent être éclairées des 2 côtés car on n'a pas gèré l'orientation de la normale
DrawFaces(GL_QUADS); //on dessine les faces pleines
end;
2:begin //lignes cachées
glDepthMask(GL_FALSE); //Z-buffer en lecture seule
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE); //color buffer en lecture seule
glEnable(GL_STENCIL_TEST); //activation du test de stencil
glClear(GL_STENCIL_BUFFER_BIT); //on vide le buffer de stencil
glStencilFunc(GL_ALWAYS,1,1); //on va mettre ddes 1 partout où l'on trace
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
DrawFaces(GL_LINE_LOOP); //on trace les lignes

glPolygonOffset(0.8,0.8); //permet d'éviter des effets gênants de crênelage
glEnable(GL_POLYGON_OFFSET_FILL);
glEnable(GL_DEPTH_TEST); //on active le test de profondeur
glDepthMask(GL_TRUE); //mode écriture pour le Z-Buffer
glStencilFunc(GL_EQUAL,1,1); //on ne tracera que là où le stencil vaut 1, c'est à dire où on a tracé les arêtes précédemment
glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP); //on ne modifie pas le stencil
DrawFaces(GL_QUADS); //on trace les faces pleines

glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE); //color buffer en mode écriture
DrawFaces(GL_LINE_LOOP); //cette fois on trace les lignes pour de vrai, le test de profondeur empèchera de tracer les morceaux de lignes recouvertes par des faces.
end;
3:begin
glLineWidth(3); //lignes de largeur 3
glDepthMask(GL_FALSE); //idem que précédemment: le stencil vaut 1 partout où on trace les lignes
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
glEnable(GL_STENCIL_TEST);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS,1,2);
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
DrawFaces(GL_LINE_LOOP);

glPolygonOffset(0.8,0.8); //idem que précédemment: on a mis à jour le Z-buffer partout où il y a des faces
glEnable(GL_POLYGON_OFFSET_FILL);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glStencilFunc(GL_EQUAL,1,2);
glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
DrawFaces(GL_QUADS);

glColor3f(1,0.5,0); //couleur rouge-orangé
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE); //idem que précédemment, on trace des lignes larges partout où le test de profondeur le permet...
glStencilOp(GL_KEEP,GL_KEEP,GL_INCR); //...mais cette fois on stocke 2 dans le stencil partout où les lignes sont visibles
DrawFaces(GL_LINE_LOOP);

glColor3f(0.4,0.6,0.5); //on trace les polygones avec la lumière comme précédemment...
glDepthFunc(GL_LEQUAL);
glStencilFunc(GL_NOTEQUAL,2,2); //...mais cette fois si uniquement où le stencil ne vaut pas 2, c'est à dire où il n'y a pas de lignes visibles
glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE);
glMaterial(GL_FRONT_AND_BACK,GL_SHININESS,5);
glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,@Specular);
glLightModel(GL_LIGHT_MODEL_TWO_SIDE,1);
DrawFaces(GL_QUADS);

glLineWidth(1); //cette fois on va tracer les lignes cachées (fines et avec de l'alpha)
glEnable(GL_BLEND);
glColor4F(1,0.5,1,0.5); //couleur bleu-violet avec transparence
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glStencilFunc(GL_EQUAL,1,2); //on aura le droit de tracer uniquement là où le stencil vaut 1
glStencilOp(GL_KEEP,GL_KEEP,GL_INCR); //et on modifie le stencil pour que les lignes ne puissent pas se recouvrir 2 fois
glDisable(GL_LIGHTING);
// glEnable(GL_LINE_STIPPLE);
glLineStipple(1,$1);
DrawFaces(GL_LINE_LOOP);
end;
4:begin
glLineWidth(3);
glDepthMask(GL_FALSE);
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
glEnable(GL_STENCIL_TEST);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS,1,1);
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
DrawFaces(GL_LINE_LOOP);

glPolygonOffset(0.8,0.8);
glEnable(GL_POLYGON_OFFSET_FILL);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glStencilFunc(GL_EQUAL,1,1);
glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
DrawFaces(GL_QUADS);

glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
DrawFaces(GL_LINE_LOOP); //jusque là c'est semblable au mode avec les lignes cachées

glDisable(GL_DEPTH_TEST);
glClear(GL_STENCIL_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
for a:=0 to ((High(FVertexes)+1) div 4)-1 do begin //cette fois on trace les lignes cachées en pointillé
glLineWidth(1); //largeur des lignes cachées: 1
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
glStencilFunc(GL_EQUAL,0,1);
glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
glEnable(GL_LINE_STIPPLE);
glLineStipple(1,$1); //motif en pointillé
DrawFace(4*a,GL_LINE_LOOP);

glLineWidth(3); //cette étape interdit de tracer les lignes qui se recouvrent 2 fois
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
glDisable(GL_LINE_STIPPLE);
glStencilFunc(GL_ALWAYS,1,1);
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
DrawFace(4*a,GL_LINE_LOOP);
end;
end;
end;
glPopAttrib;
end;

procedure TForm1.Animate;
var
a,b:Integer;
begin
Tag:=1; //pour ne pas recalculer la matrice à chaque fois
for a:=0 to GroupBox1.ControlCount-1 do //pour toutes les trackbars créées, on fait varier leur position en fonction de la vitesse de déplacement stockée dans Tag
with TTrackBar(GroupBox1.Controls[a]) do begin
b:=Position+Tag;
if bMax then
Position:=Min+(b-Max)
else
Position:=b;
end;
end;
Tag:=0;
MakeMatrix; //on calcule la matrice à la fin, en une seule fois
end;

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
ComboBox1Change(nil); //reset des angles
end;

function FloatToStr2(x:Single):string; //cette fonction transforme un nombre en une chaine dans un format utilisable par POV-Ray
begin
Str(x,Result);
end;

procedure TForm1.SpeedButton2Click(Sender: TObject); //on écrit le source pour POV-Ray dans le répertoire du programme
var
f:TextFile;
a,n:Integer;
v:TVect;
begin
AssignFile(f,ExtractFilePath(APplication.ExeName)+'Vertexes.inc'); //on ouvre le fichier...
ReWrite(f); //...en mode écriture
try
n:=(High(FVertexes)+1) div 4;
WriteLn(f,'#declare FacesCount='+IntToStr(n)+';');
WriteLn(f,'');
WriteLn(f,'#declare Vertexes=array['+IntToStr(4*n)+'][3]');
WriteLn(f,'{');
for a:=0 to High(FVertexes) do begin //on écrit toutes les coordonnées des sommets dans le fichier...
v:=MultVect(FMatrix,FVertexes[a]);
if a


Descargar la aplicación

 

Autor


 

 

 

 

 

 

 



Simulación del movimiento de los electrones en un campo electrico

Espectacular simulación realizada con OpenGL del movimiento de los electrones cuando atraviesan un campo eléctrico. Como muestra la image...