Anfang mit 3D-Grafik.Programmierung
-
Hallo alle zusammen und frohe Weihnachten!
Ich hab mir vor einiger Zeit das Buch "3d-Grafik-Programmierung" von Marius Apetri gekauft (war ein Sonderangebot, weil es eine alte Ausgabe von 2003 ist). Dieses Buch fängt wirklich beim Urschleim an (also so, wie es sein soll), d.h. das erste kleine Programm soll nichts weiter tun, als in einem kleinen Fenster spaltenweise die Pixel entsprechend der Farbpalette bunt zu färben und nach Tastendruck durch den Benutzer das Fenster wieder zu schließen.
Doch es kam, wie es kommen musste: es geht natürlich nicht!
Ich habs mit dem DevC++-Compiler und mit dem Cygwin-Compiler von der Buch-CD versucht.Alles was passiert, ist, dass der Bildschirm kurz schwarz wird und dann schließt sich das Fenster auch schon wieder.
Hier ist das Programm:
#include <windows.h> #include "s2_1.h" uchar handle_input( MSG *msg ); int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow ) { surface.open_window( hInstance, 320, 200, 8 ); uchar *screen = (uchar *) surface.get_screen_pointer(); for( long x=0 ; x<256 ; x++ ) for( long y=0 ; y<200 ; y++ ) screen[ y * 320 + x ] = x; surface.release_screen_pointer(); MSG message; while( !handle_input( &message ) ); return message.wParam; } uchar handle_input( MSG *msg ) { if( PeekMessage( msg, NULL, 0, 0, PM_REMOVE ) ) { if( msg->message == WM_QUIT || msg->message == WM_KEYDOWN ) return 1; TranslateMessage( msg ); DispatchMessage( msg ); } return 0; }
Ich weiß schon so viel, dass es an der Zeile 14 "screen[ y * 320 + x ] = x;" liegt, also quasi an der Manipulation des Videospeichers, denn wenn ich diese Zeile auskommentiere, dann wird der Bildschirm schwarz (das wird wohl das Fenster sein, welches erzeugt wird) und das Programm wartet drauf, dass ich eine Taste drücke.
Das Rahmenprogramm funktioniert also, aber warum geht das mit dem Einfärben nicht?
Doof ist auch, sämtliche Beispiel-Programme sind ja auch als fertige .exe-Dateien mit auf der Buch-CD, nur passiert dort dasselbe!
Hier sind noch die beiden Header-Dateien:
//s2_1.h #ifndef SURFACE_H #define SURFACE_H #include <windows.h> #include <stdlib.h> // exit() #include <string.h> // memset(), memmove() #include <ddraw.h> #include "st2_1.h" // pixel_8 #define WIN32_LEAN_AND_MEAN #define x_res surface.get_x_resolution() #define y_res surface.get_y_resolution() void exit( char *message ); LRESULT CALLBACK main_window_procedure( HWND main_window_handle, UINT message, WPARAM wparam, LPARAM lparam ) { switch( message ) { case WM_CLOSE : PostQuitMessage( 0 ); return 0; } return DefWindowProc( main_window_handle, message, wparam, lparam ); } class directx_surface { private: long x_resolution, y_resolution; LPDIRECTDRAW main_dd_object; LPDIRECTDRAWSURFACE primary_surface; DDSURFACEDESC surface_description; HWND main_window_handle; void initialise_direct_draw( long x, long y, long bit_depth ); public: long get_x_resolution( void ) { return x_resolution; } long get_y_resolution( void ) { return y_resolution; } void open_window( HINSTANCE hInstance, long x, long y, long bit_depth ); void close_window( void ); void set_palette( pixel_8 *colors ); void *get_screen_pointer( void ); void release_screen_pointer( void ) { primary_surface->Unlock( surface_description.lpSurface ); } directx_surface( void ) : main_dd_object( NULL ), primary_surface( NULL ), main_window_handle( NULL ), x_resolution( 0 ), y_resolution( 0 ) { } ~directx_surface( void ) { close_window(); } } surface; void directx_surface::initialise_direct_draw( long x, long y, long bit_depth ) { if( DirectDrawCreate( NULL, &main_dd_object, NULL ) != DD_OK ) exit( "Fehler während der Ausführung von DirectDrawCreate().\n" ); if( main_dd_object->SetCooperativeLevel( main_window_handle, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX ) != DD_OK ) exit( "Fehler während der Ausführung von SetCooperativeLevel().\n" ); if( main_dd_object->SetDisplayMode( x, y, bit_depth ) != DD_OK ) exit( "Fehler während der Ausführung von SetDisplayMode().\n" ); memset( &surface_description, 0, sizeof( surface_description ) ); surface_description.dwSize = sizeof( surface_description ); surface_description.dwFlags = DDSD_CAPS; surface_description.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; if( main_dd_object->CreateSurface( &surface_description, &primary_surface, NULL ) != DD_OK ) exit( "Fehler während der Ausführung von CreateSurface().\n" ); } void directx_surface::open_window( HINSTANCE hInstance, long x, long y, long bit_depth ) { x_resolution = x; y_resolution = y; char *class_name = "Main Window"; WNDCLASS m_window; m_window.style = CS_OWNDC; m_window.lpfnWndProc = main_window_procedure; m_window.cbClsExtra = 0; m_window.cbWndExtra = 0; m_window.hInstance = hInstance; m_window.hIcon = LoadIcon( NULL, IDI_APPLICATION ); m_window.hCursor = LoadCursor( NULL, IDC_ARROW ); m_window.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH ); m_window.lpszMenuName = NULL; m_window.lpszClassName = class_name; if( RegisterClass( &m_window ) == 0 ) exit( "Fehler bei der Registrierung des Programmfensters.\n" ); if( !(main_window_handle = CreateWindowEx( WS_EX_TOPMOST, class_name, "Application", WS_VISIBLE | WS_POPUP, 0,0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ), NULL, NULL, hInstance, NULL )) ) exit( "Fehler beim Öffnen des Applikationsfensters.\n" ); ShowCursor( 0 ); initialise_direct_draw( x_resolution, y_resolution, bit_depth ); } void directx_surface::close_window( void ) { if( primary_surface ) { primary_surface->Release(); primary_surface = NULL; } if( main_dd_object ) { main_dd_object->Release(); main_dd_object = NULL; } DestroyWindow( main_window_handle ); } void directx_surface::set_palette( pixel_8 *colors ) { PALETTEENTRY palette_definition[ 256 ]; memset( palette_definition, 0, 256*sizeof( PALETTEENTRY ) ); for( ushort x=0 ; x<256 ; x++ ) { palette_definition[ x ].peRed = colors[ x ].red; palette_definition[ x ].peGreen = colors[ x ].green; palette_definition[ x ].peBlue = colors[ x ].blue; palette_definition[ x ].peFlags = PC_NOCOLLAPSE; } LPDIRECTDRAWPALETTE primary_palette_object = NULL; if( main_dd_object->CreatePalette( DDPCAPS_8BIT | DDPCAPS_ALLOW256, palette_definition, &primary_palette_object, NULL ) != DD_OK ) exit( "Fehler während der Ausführung von CreatePalette().\n" ); if( primary_surface->SetPalette( primary_palette_object ) != DD_OK ) exit( "Fehler bei der Veränderung der DirectX Palette.\n" ); } void *directx_surface::get_screen_pointer( void ) { primary_surface->Lock( NULL, &surface_description, DDLOCK_SURFACEMEMORYPTR, NULL ); return surface_description.lpSurface; } void exit( char *message ) { surface.close_window(); ShowCursor( 1 ); MessageBox( NULL, message, "Fehler:", MB_OK ); exit( 1 ); } #endif
//st2_1.h #ifndef SIMPLE_TYPES #define SIMPLE_TYPES typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned long ulong; struct pixel_8 { uchar red, green, blue; pixel_8( void ) : red( 0 ), green( 0 ), blue( 0 ) { } pixel_8( uchar r, uchar g, uchar b ) : red( r ), green( g ), blue( b ) { } }; #endif
Puh, das war gleich mal ne ganze Menge für meinen ersten Post hier im Forum, aber ich hoffe wirklich, jemand kann mir sagen, warum das nicht geht, weil ich würd das Buch schon gern durcharbeiten (bzw. erstmal anfangen zu arbeiten).
-
Guten Abend nochmal an alle!
Kann mir denn wirklich keiner sagen, woran das liegen könnte?
Oder sagt mir wenigstens jemand, woran es liegt, dass ich keine Antwort bekomme - ist das Problem zu speziell (kann ich mir eigentlich nicht vorstellen) oder ist es nicht angebracht, den kompletten Code zu posten (seh ich ja aber öfter mal) oder kam die Frage nicht deutlich genug rüber (Warum klappt das Einfärben der Pixel in dem Programm nicht?) oder warum sagt keiner was?
Würd mich wirklich freuen, eine hilfreiche Antwort zu bekommen.
mfg JKCody
-
unabhängig von deinem Problem: benutze Visual C++ - die Express Edition gibt’s kostenlos unter http://www.microsoft.com/express/download/
Anschließend kannst du das Projekt gerne Hochladen und ich debug es durch
zum setzen von einzelnen Pixel kannst du z. B. http://www.pixeltoaster.com/ verwenden - dann musst du dich nicht mehr mit dem DirectDraw zeug rumschlagen
warum dir hier niemand helfen will liegt vielleicht daran, dass sich niemand mit DirectDraw Code beschäftigen will
-
Der Support fuer Low-Resolution Modi wie 320x200 hat extrem nachgelassen.
Versuch' doch einfach mal 640x480.
Die Addressierung geht wahrscheinlich auch in die Hose (Scanlines sind auf Kilobytes aligned).
Vorschlag:int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow ) { surface.open_window( hInstance, 640, 480, 8); MSG message; int frame= 0; while( !handle_input( &message ) ) { unsigned char *screen = (unsigned char*) surface.get_screen_pointer(); int pitch= surface.get_screen_pitch(); for( long y=0 ; y<480 ; y++ ) { for( long x=0 ; x<640 ; x++ ) { screen[x]= x+frame; } screen+=pitch; // offset zur naechsten scanline } surface.release_screen_pointer(); frame++; } return message.wParam; }
und in s2_1.h in class directx_surface unter public: einfuegen:
int get_screen_pitch() { return surface_description.lPitch; }
-
Tach!
@Vertexwahn:
also ich hab jetzt "MS Visual C++ 2008 Express Edition" installiert. Hab dort auch fast das gleiche Ergebnis wie bisher: der Bildschirm wird kurz schwarz und gleich darauf kommt eine Standard-Windows-Meldung "... hat ein Problem festgestellt und muss beendet werden."Noch ne ganz doofe Frage: wie und wo lad ich denn hier was hoch? Hab ich nämlich noch nie gemacht (ja, so was gibts auch noch).
Thema DirectDraw - kann ich dann jetzt davon ausgehen, dass dieses Buch, was ich da hab, eine Fehlinvestition war (auch wenns stark reduziert war), da alle Beispiele im Buch ddraw verwenden
@hellihjb:
die Auflösung hat ich auch selbst schon mal geändert gehabt, nur dass das leider auch nix gebracht hat.
Hab deinen Vorschlag einfach mal so wie du gesagt hast übernommen und siehe da: es ändert so richtig gar nichts...Davon mal abgesehen, hab ich keine Ahnung, was du hiermit meinst:
Die Addressierung geht wahrscheinlich auch in die Hose (Scanlines sind auf Kilobytes aligned).
Aber danke erstmal, dass überhaupt jemand geantwortet hat.
Edit:
Kennt jemand dieses Buch:
http://www.amazon.de/DirectX-Grafikprogrammierung-mit-C-Alexander-Schunk/dp/3826681746/ref=sr_1_4?ie=UTF8&s=books&qid=1230669026&sr=1-4Das klingt imo auch ganz gut und ist wenigstens von 2006 (hab noch nicht geschaut, ob es schon eine neuere Ausgabe gibt)
mfg
-
Davon mal abgesehen, hab ich keine Ahnung, was du hiermit meinst
Die Pixel werden zeilenweise im Speicher abgelegt. Allerdings liegen aufeinanderfolgende Zeilen (Scanlines) nicht direkt hintereinander sondern besitzen einen Offset den sich der Grafikkartenhersteller aussuchen kann.
Aus diesem Grund muss dieser "Pitch" beim Lock des Surface ausgelesen und angewendet werden.und gleich darauf kommt eine Standard-Windows-Meldung
Debugger starten (F5), ggf beim Erstellen des Fensters topmost- und fullscreen-flags entfernen.
-
Wenn man es ehh grad anschaltet, im Fenstermodus sollte dann auch wieder 320x200 und sonst alles <= Desktop funktionieren. f'`8k
Gruß, TGGC (Der neue Game Star)
-
JKCody schrieb:
Thema DirectDraw - kann ich dann jetzt davon ausgehen, dass dieses Buch, was ich da hab, eine Fehlinvestition war (auch wenns stark reduziert war), da alle Beispiele im Buch ddraw verwenden
Ich besitze das Buch auch und kann dazu nur sagen, dass es kein Problem ist, die Beispiele so zu ändern, dass kein DDraw benutzt wird, sondern was anderes.
Du musst nur die Klasse directx_surface verändern, dass sie z.B. Pixeltoaster nimmt (oben erwähnt, www.pixeltoaster.com), um die Änderungen in den Grafikspeicher zu schreiben. Fertig.
-
hi!
@hellihjb:
was sind denn "topmost- und fullscreen-flags" in meinem Fall?Unabhängig davon hab ich den Debugger gestartet, der Bildschirm wurde schwarz und blieb es auch - hab dann die Anwendung brutal beendet und hatte dann folgende Meldung:
Unbehandelte Ausnahme bei 0x0001c080 in 3dbuch2.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x0001c080.
Hab dann auf weiter geklickt und als Ausgabe kam dann Folgendes:
Eine Ausnahme (erste Chance) bei 0x0001c080 in 3dbuch2.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x0001c080.
Unbehandelte Ausnahme bei 0x0001c080 in 3dbuch2.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x0001c080.
Das Programm "[2740] 3dbuch2.exe: Systemeigen" wurde mit Code 0 (0x0) beendet.@mad_martin:
Pixeltoaster werd ich dann gleich mal ausprobieren...Noch ne allgemeine Frage: ist es denn wirklich soooooo zu empfehlen, dass ich mich vorher mit der Windows-Programmierung beschäftige?
mfg
-
Darum wurden dir doch schon andere APIs empfohlen... f'`8k
AutocogitoGruß, TGGC (Der neue Game Star)
-
JKCody schrieb:
@hellihjb: was sind denn "topmost- und fullscreen-flags" in meinem Fall?
CreateWindowEx erzeugt durch das Flag WS_EX_TOPMOST ein Fenster das immer vor allen anderen ist, SetDisplayMode wechselt zudem die Bildschirmaufloesung was das Debuggen etwas behindert.
Vielleicht gibt es ja in Deinem Buch auch ein Beispiel das im Fenster arbeitet?ist es denn wirklich soooooo zu empfehlen, dass ich mich vorher mit der Windows-Programmierung beschäftige?
Es ist nicht unbedingt empfehlenswert die zugrundeliegenden Konzepte gaenzlich zu ignorieren.
-
Hallo und frohes neues Jahr erstmal noch (auch wenns schon bissl spät is
)!
Also ich hab das Beispiel, so wie es im Buch steht aufgegeben - ich bekomm es nicht zum Laufen.
ABER der Pixeltoaster funktioniert, d.h. ich hab erstmal wieder was zum Rumbasteln und bin beschäftigt, so lange ich die Beispiele aus dem Buch halberwegs umschreiben kann (auch wenns nicht schlecht wär, wenn die so funktionieren würden).
Allerdings hätte ich zu dem Pixeltoaster gleich noch zwei kleine Fragen:
1.) Kann ich die Konsole, die ja im Hintergrund zu sehen ist, irgendwie verstecken?
2.) Kann ich den Pixeltoaster auch aus einer WinMain-Funktion heraus verwenden, weil die Beispiele dazu sind ja alle in einer "normalen" main-Funktion?mfg JKCody
-
JKCody schrieb:
Allerdings hätte ich zu dem Pixeltoaster gleich noch zwei kleine Fragen:
1.) Kann ich die Konsole, die ja im Hintergrund zu sehen ist, irgendwie verstecken?
2.) Kann ich den Pixeltoaster auch aus einer WinMain-Funktion heraus verwenden, weil die Beispiele dazu sind ja alle in einer "normalen" main-Funktion?Die Konsole kannst du ausschalten, indem du den Anwendungstyp auf GUI-Anwendung stellst.
Wofür willst du denn WinMain verwenden? Ich sehe da keine Vorteile.
-
Die Konsole kannst du ausschalten, indem du den Anwendungstyp auf GUI-Anwendung stellst.
Und wo mach ich das?
mfg
-
Im Visual-Studio stellst Du in den Projekteinstellungen unter Linker->System auf Subsystem:Windows; dann brauchst Du auch WinMain anstatt main.
-
ok, werd ich bei Gelegenheit mal ausprobieren!
danke auch für die Antworten - ich denk, damit ist das Thema erledigt
mfg JKCody
-
hellihjb schrieb:
Im Visual-Studio stellst Du in den Projekteinstellungen unter Linker->System auf Subsystem:Windows; dann brauchst Du auch WinMain anstatt main.
Nein brauchst du nicht.
main
kannst du trotzdem verwenden. Du musst nur als Entry-Point mainCRTStartup eintragen. Wenn man eine Plattform unabhängige Bibliothek benutzt, muss man sich ja nicht durch so einen Blödsinn wieder mehr Arbeit machen.http://msdn.microsoft.com/en-us/library/f9t8842e(VS.71).aspx
-
-
Hi JKCody,
ich hatte ebenfalls dasselbe Problem mit dem Buch. So habe ich es gelöst: Das Hauptproblem ist die 8 Bit Farbtiefe, die ab Windows XP scheinbar nicht mehr funktioniert. Auf der CD des Buches gibt es aber auch Programme, die mit der Farbtiefe von 16 und 32 Bit arbeiten, ich glaube ab dem 6. Kapitel. Sollte die Auflösung 640x480 nicht arbeiten, probiere es mit 1024x768, aber die 32 Bit Farbtiefe ist wichtig. Diese erkennt man anhand von Befehlen wie:
initialise_world( hInstance, 640, 480, 32 );
pixel_32 *screen = (pixel_32
surface.get_screen_pointer();
Diese Programme laufen wie sie sollten, auf meinem Rechner wenigstens. Ich habe einfach eines dieser Programme genommen (a6_8), und ihn so weit wie möglich vereinfacht. Darauf konnte ich dann meine eigenen Versionen der Beispielprogramme aufbauen. Ein weißer Pixel an der Position (sx, sy) wird dann wie folgt gesetzt:
screen[ sy * 640 + sx ] = pixel_32( 255, 255, 255 );
Eine Farbpalette von gelb nach blau wird erstellt durch:
pixel_32 palette[ 256 ];
for( int i=0 ; i < 256 ; i++ ) palette[ i ] = pixel_32( 255-i, 255-i, i );und ein Pixel dann auf dem Bildschirm gesetzt:
long x = 200;
screen[ sy * 640 + sx ] = palette[ x ];Ich hoffe, ich konnte Dir helfen. Sag bitte, ob dieser Vorschlag funktioniert hat.
-
Hallo!
@Coding4fun:Ich war einige Tage nicht im Forum, deshalb hab ich deine Antwort jetzt erst gelesen. Also ich hab jetzt versucht, das Programm 6_8 (welches dieses tolle Grundgerüst von dem Raumschiff anzeigt) so umzugestalten, dass es das macht, was eigentlich das Programm 2_1 machen soll.
Hier mein Ergebnis:#include <windows.h> #include "s6_8.h" #include "m6_8.h" #include "t6_8.h" #include "gv6_8.h" uchar handle_input( MSG *msg ); int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow ) { initialise_world( hInstance, 640, 480, 32 ); pixel_32 *screen = (pixel_32 *) surface.get_screen_pointer(); for( long x=0 ; x<255 ; x++ ) for( long y=0 ; y<480 ; y++ ) screen[ y * 640 + x ] = pixel_32( 255, 255, 255 ); surface.release_screen_pointer(); MSG message; while( !handle_input( &message ) ) ; destroy_world(); return message.wParam; } uchar handle_input( MSG *msg ) { if( PeekMessage( msg, NULL, 0, 0, PM_REMOVE ) ) { if( msg->message == WM_QUIT || msg->message == WM_KEYDOWN ) return 1; TranslateMessage( msg ); DispatchMessage( msg ); } return 0; }
Folge ist, dass das Programm startet, der Bildschirm schwarz wird und dann das Programm auf einen Tastendruck zum Beenden wartet - aber von Farben weiterhin keine Spur
! (Und ja, ich hab auch verschiedene Auflösungen und Farbtiefen versucht.)
Hoffe, dass du mir weiterhelfen kannst, da du ja das gleiche Problem hattest. Übrigens, welchen Compiler hast du verwendet, weil mit nem anderen, als dem Mitgelieferten, krieg ich gleich gar kein funktionsfähiges Programm hin.
mfg JKCody