20 000 virtuelle Zellen

Übersicht BlitzBasic Blitz3D

Neue Antwort erstellen

 

Starling

Betreff: 20 000 virtuelle Zellen

BeitragDi, Feb 26, 2013 19:40
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo liebe BB-Community,

ich studiere eigentlich Medizin, aber habe gerade wieder mein Faible fürs Programmieren gefunden bzw. will für unser physiologisches Institut eine Herzanimation programmieren.

Meine Frage ist wie ich optimal die Rechenleistung ausnutzen kann bzw. die Grafik möglichst schonend programmiere...

In erster Linie geht es darum zu zeigen wie sich die elektrische Erregung von einem kleinen Bereich im Vorhof, dem Sinusknoten, über die ganzen Zellen des Vorhofs ausbreitet, dann nochmal modifiziert wird, bis sie schließlich auch auf die beiden Ventrikel übergreift und für deren Erregung und damit Kontraktion sorgt.

Ich habe mir erstmal anhand von MRT-Bildern ein virtuelles Herz aus 20 000 Zellen in BlitzBasic erstellt.
Das ist auch etwa das Minimum, weil ich mit dem Programm bestimmte Dinge darstellen will, für die es eben mindestens eine solche Massenverteilung geben muss bzw. will ich auch eine gewisse Ästhetik erreichen Wink
Entscheidend ist auf jeden Fall, dass manche Zellen ihr Erregung schneller, andere langsamer weitergeben, so dass sich die Erregung in den beiden Kammern erstmal rechts ganz ausbreitet (dort gibt es insgesamt viel weniger Zellen, die linke Kammer ist dagegen für den großen Körperkreislauf, das Hochdrucksystem verantwortlich und benötigt viel mehr Zellen/Muskelkraft); außerdem verläuft die Erregungsausbreitung von innen nach außen bzw. werden die letzten Zellen zunächst "unten" an der Herzspitze und dann "oben" an der Basis der Ventrikel erregt.
Das nur kurz, um zu erklären, wieso ich viele verschiedene Arten von Zellen brauche und diese in der richtigen Geometrie anordnen muss: Letztlich will ich auch ein EKG anzeigen lassen, für das ja die verschiedenen Zacken charakteristisch ist und eben letztlich Ausdruck von der wechselnden Erregungsrichtung...

So weit zum physiologischen Hintergrund, ich hoffe Ihr könnt mir bei meinen Fragen bezüglich einer guten Programmierung helfen. Da bin ich ziemlich naiv:

Ich hatte angefangen, diese 20 000 Zellen über Types zu ordnen und diese als Cubes anzeigen zu lassen.
Komischerweise streikt mein BlitzBasic, wenn ich mehr als 15 000 Sprites erstelle? Kennt ihr das Problem?

Um Kapazität zu sparen, bin ich von den Cubes auf selbsterstellte Dreiecke aus Vertexpunkten gekommen, damit spare ich ja erstmal Polymere ein: Pro Zelle werden nicht mehr 6 Dreiecke gezeichnet, sondern eben nur noch eins, oder? Allerdings sind die nur in eine Richtung sichtbar sind, sodass ich die noch kopieren und flipen musste. Das habe ich mir bisher nur bei den 10 000 Zellen des Vorhofs erlaubt. Die Vorhöfe bestehten aus einer nur einschichtigen Zellschicht, deswegen wirkt er sonst ziemlich "brüchig". Außerdem hab ich viele dann nochnochmal zum Rechteck verdoppelt - also letztlich gegenüber den Cubes nur ein Gewinn von 2 Polymeren.

Naja, lange Rede kurzer Sinn, bei mir werden jetzt 60 000 Dreiecke gezeichnet und ich merke deutlich wie die Erregungsleitung dadurch langsamer erfolgt: Von den Types bin ich auch weg: ich arbeite jetzt mit Feldern, so dass ich die einzelnen Zelle über ihre Id direkt ansprechen kann und nicht erst über eine Schleife suchen muss.

Problem nach wie vor die Rechen/Grafikleistung die ich an den Grenzen ist.
Meine Beobachtung war, dass wenn ich die Kamera so manövrier, dass bspw. nur der Vorhof zu sehen ist, mir nur k.A. 20 000 Dreiecke gezeichnet werden ich fast keine Zeitverzögerung wahrnehme. Insofern suche ich nach einer Lösung die Zellen, die ich selbst gar nicht sehe bzw. für die Erregungsweiterleitung nicht relevant sind einfach auszublenden.

Mit entityinview, oder wie heisst der Befehl? entityvisible?, kann ich aber nicht arbeiten, weil die Kamera ja quasie durch die löcher durch schauen kann... es auch irgendwie zum Raumgefühl beiträgt, dass man die Rückwand sieht. Wenn ich diese Lücken stopfen könnte, wär mir sicher auch geholfen, aber die Zellfläche hochzuscalen war nicht wirklich die Lösung ....

Habt vielleicht eine Idee wie ich irgendwie irgendwas einsparen kann?
Licht hab ich z.B. ausgemacht und mich schon sehr gefreut, dass es geholfen hat. Insofern bringen vielleicht auch ganz simple Vorschläge etwas !

Im Anhang seht Ihr ein Screenshot, wenns hilft, kann ich Euch auch den Code zukommen lassen.

Erstmal Vielen Dank fürs Durchlesen und vielleicht den ein oder anderen Ratschlag !!

liebe Grüße aus Freiburg
V

http://www.pic-upload.de/view-...5.jpg.html
<-- Vorhöfe zur Hälfte erregt

user posted image
  • Zuletzt bearbeitet von Starling am Di, Feb 26, 2013 22:45, insgesamt einmal bearbeitet

Tennisball

BeitragDi, Feb 26, 2013 20:09
Antworten mit Zitat
Benutzer-Profile anzeigen
Hi,

Du könntest auch komplett auf 3D-Darstellung verzichten, wenn das für dich geht.
Wenn nicht, erstellst du für jede Zelle eine eigene "Surface"? Wenn ja, solltest du dir mal das Thema "SingleSurface" anschauen, das sollte deutlich schneller laufen.

Gruß auch aus FR Wink ,
Tennisball

Midimaster

BeitragDi, Feb 26, 2013 21:55
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich denke mal diese "Erregungsleitung" ist ja ein Vorgang, der eigentlich mehr rechnerisch erfolgt. Teste doch mal, ob dein Programm wesentlich schneller wird, wenn Du probehalber gar nichts zeichnen läßt.

Die Idee, an die ich denke ist, dass Du auf der Rechenseite mit viel mehr Zellen arbeiten könntest, wenn Du sie nicht alle zeichnest. Beim Zeichnen arbeitest Du dann mit Objekten die stellvertretend für 3x3 Zellen oder gar 10x10 Zellen stehen. So reduziert sich die Objektzahl bei z.B. 270.000 Zellen auf 30.000 oder gar nur 2.700 Flächen

Es ist sowieso unwahrscheinlich, dass 60.000 Flächen unterscheidbar auf den Bildschirm passen. Für den Betrachter wird die Reduzierung kaum erkennbar sein.

"Zoomt" man in das Herz, könnte man die Genauigkeit von 10x10 auf 3x3 oder gar 1x1 erhöhen und nur aus den Zellen Flächen erstellen, die im Blickfeld wären.
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe
 

Starling

BeitragDi, Feb 26, 2013 22:33
Antworten mit Zitat
Benutzer-Profile anzeigen
Danke für die raschen Antworten!

Also viel "gerechnet" wird bei mir bis jetzt nur insofern, als ich die Zellen ihre Erregung an die Nachbarzellen weitergeben lasse, so wie bei nem Dominoeffekt. Anfangs hatte ich noch in meiner Mainloop die Distanzen ausmessen lassen und quasi alle Nachbarszellen in einem bestimmten Abstand erregen lassen, jetzt habe ich die rechenfrendlichere Methode gewählt: Vor Eintritt in die Hauptschleife weise ich jeder Zelle 10 bis 20 Nachbarszellen zu, um die dann direkt aufzurufen und nach einer bestimmten Verzögerung zu erregen.

Insofern haperts bei meinem Programm wirklich noch an der richtigen Grafikprogrammierung:

Ich habe wie gesagt diese 20 000 Koordinaten von Zellen und will die jetzt räumlich eben zum Herzen abbilden. Auf 2D umzusteigen ist für mich keine Alternative, es geht letztlich gerade um die dritte Dimension und überhaupt ist die Anschaulichkeit wichtig z.B. wie Midimaster fantasiert, wenn man in die Kammern reinfliegt und sich das Ventrikelseptum anschaut, die Wand zwischen den beiden Kammern, an denen z.B. die Tawaraschenkel entlang laufen, eine besonders schnelle Leitstrecke.
Ich bin für jeden Vorschlag empfänglich wie ich nur die äußeren Koordinaten mit Grafik fülle, sodass aber eben der Körper abgeschlossen aussieht. Sicher könnte ich viel Leistung einsparen, wenn ich die Muskelmassezellen einfach nicht anzeigen lasse, nur hatte ich bisher darauf nicht verzichten wollen, der Ästhetik wegen Confused

Ich habe jetzt mal ein wenig recherchiert was den Begriff Single Surface angeht - leider ist ja hier im deutschen Forum wirklich nicht soviel zu finden, nur immer der Hinweis, dass es für Fortgeschritte sei und man sich doch auch im englischen Forum umschauen möge, nun das werde ich morgen nachholen! Erstmal:
Soweit ich das verstanden habe, kettet man alle Objekte zu einem Mesh zusammen, damit der Grafikspeicher nicht verschiedene Surfaces laden muss, sondern nur eine?! Schwierig wird es, wenn man dann die Objekte einzeln modifizieren will, z.B. unterschiedlich texturieren.
Für meine Belange können ruhig alle Zellen bspw. des linken Ventrikels grün sein. Nur sollen sie sich hervorheben, wenn sie erregt sind. Außerdem muss ich eben immer noch die Zellen mit ihren individuellen Koordinaten vergleichen können (eben wegen dieser Dominoerregung).

Nach meiner Odyssee von den Spheres, über die Cubes bin ja letztlich an den Sprites gescheitert und hatte mir aus der Hilfe so n paar Schnipsel zusammengebastelt um selbst Dreiecke zu zeichnen. Im Moment erstelle ich für jede Zelle eine Surface, bestehend aus 2 Dreiecken zu einem Rechteck gelegt, kopiert und geflipt, also 4 Dreiecke:

Code: [AUSKLAPPEN]
      z1\objekt = CreateMesh()
         If z1\Zellart= 1 Then brushtouse = brushtexturevorhoflinks
         If z1\Zellart= 6 Then brushtouse = brushtexturevorhofrechts
               
         z1\surface = CreateSurface(z1\objekt, brushtouse )
         z1\v0 = AddVertex(z1\surface, 0, 0, 0)
         z1\v1 = AddVertex(z1\surface, 1, 0, -1)
         z1\v2 = AddVertex(z1\surface, -1, 0, -1)

         z1\v02 = AddVertex(z1\surface, -1, 0, -1)
         z1\v12 = AddVertex(z1\surface, 1, 0, -1)
         z1\v22 = AddVertex(z1\surface, 0, 0, -2)          
         
         AddTriangle(z1\surface, z1\v0, z1\v1, z1\v2)
         AddTriangle(z1\surface, z1\v02, z1\v12, z1\v22)
                  
         z1\objekt2 = CopyMesh( z1\objekt, z1\objekt)         
         FlipMesh z1\objekt2
   
         UpdateNormals z1\objekt
         UpdateNormals z1\objekt2
         
         PositionEntity z1\objekt, Readx#*faktor, Ready#*faktor, Readz#*faktor
         ScaleEntity z1\objekt, faktor2, faktor2*faktory, faktor2



Kann ich also hier ansetzen und alle Surfaces zusammenfügen? Oder verliere ich dann die Möglichkeit auf die Zellen einzeln zuzugreifen?

mfg
V

Xeres

Moderator

BeitragDi, Feb 26, 2013 22:50
Antworten mit Zitat
Benutzer-Profile anzeigen
Im Screenshot ist es nicht all zu gut zu erkennen; brauchst du eine volumetrische Darstellung oder würde dir auch eine Oberfläche genügen? Soll das ganze animiert werden, oder ist das eine starres Gebilde, in dem nur gewisse Zellen aufleuchten?
Je nach dem böten sich vielleicht Optimierungen an...

Single Surface funktioniert so: Ein Mesh mit einem Surface erstellen und die Geometrie in dieses Surface packen, bis du ~16000 Vertices erreichst, dann ein neues Mesh erstellen. Wenn du dann etwas bewegen willst, muss du den richtigen Mesh mit den richtigen Gitterkoordinaten wiederfinden und die Koordinaten ändern - oder das komplette Surface leeren und neu erstellen.

Obwohl BB in seiner Datenverwaltung arg beschränkt ist, kann man ein paar Tricks benutzen, um nicht all zu lange herum zu rechnen - dafür müsstest du aber etwas vertiefen, was für Daten du hast und wie die interagieren sollen.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)

BladeRunner

Moderator

BeitragDi, Feb 26, 2013 22:54
Antworten mit Zitat
Benutzer-Profile anzeigen
Hach, die Reizleitung ist doch ein Riesenspaß Smile
Ich bin gespannt wie sich das hier weiter entwickelt- hast Du einen speziellen Verwendungszweck für dein Modell vorgesehen?
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92
 

Starling

BeitragDi, Feb 26, 2013 23:04
Antworten mit Zitat
Benutzer-Profile anzeigen
Ne, animieren und bewegen will ich nichts - also nicht zeigen, wie der Herzmuskel kontrahiert und so der Pumpvorgang vonstatten geht. Es geht erstmal nur darum in welcher Reihenfolge die Zellen erregt werden.

Ich habe mir ein 3dimensionales Objekt gebastelt, so wie auf dem Screenshot zu erahnen: auf 20 000 Koordinaten habe ich diese 2dimensionalen Blättchen, besteht aus 4 Polymeren, gesetzt. Ich lade gleich mal den Code hoch, dann kann man sich das besser anschauen. Es wirkt schon recht plastisch, wenn man die Erregung darüber hinweggleiten sieht bzw. wenn man die Kamera bewegt.

Xeres

Moderator

BeitragDi, Feb 26, 2013 23:15
Antworten mit Zitat
Benutzer-Profile anzeigen
Wenn sich überhaupt nichts bewegen soll, kann man die nächstgelegenen Zellen einem Gitterpunkt zuordnen und aufleuchten lassen - und sehr viel überflüssige Geometrie einsparen.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)
 

Starling

BeitragDi, Feb 26, 2013 23:44
Antworten mit Zitat
Benutzer-Profile anzeigen
Ja, aber nur die Gitterpunkte ergeben doch nicht meine Herzform, die ich plastisch dann als Organ einordnen kann?! Das ist ja mein Problem: Wie füge ich welche speicherarmen Grafiken an die Koordinaten, dass ich einerseits die Erregung sichtbar darüber laufen lasse und andererseits noch immer das Herz als Objekt bildlich begreifen kann.

Hier ist mal der Code zum starten und anschauen, wie sich das Ganze bisher gestaltet:

Es ist auch noch n Screenshot aus dem anderen Programm, mit dem ich die die 3D Koordinaten aus 100 verschiedenen schematisierten, transversale MRT Bildern, Querschnitten, erstellt habe: Manuell quasi mit nem Pinsel abgefahren, jeder Punkt eine Koordinate. Links oben sieht man aus einer fernen Kamera das ganze Objekt, auf dem Hauptschirm sieht man den Querschnitt einer bestimmten Höhe, das ist einfach ein Bild als Textur auf einen "Teller" gelegt, den man hoch und runterfahren kann und dabei eben seine Textur in der Reihenfolge=Tiefe/Höhe ändert. Man kann hier auch schön die unterschiedliche dicke der verschiedn gefärbten Kammern sehen bzw. das subendokardial gelegene Mykoard nochmal in der linken Kammer (im Bild unten rechts) in weißgrauer Farbe hervorgehoben.
Die Koordinaten werden dann in eine txt abgelegt und von dem Viewer geladen: Man wartet anfangs bei schwarzen Screen, dann kann man nur einmal die Erregung mit Enter über die Vorhöfe ablaufen lassen. Alles nur Demo, der Code ist auch total in Überarbeitung! Letztlich will ich von den Types komplett weg kommen

In erster Linie solls demonstrieren wie ich im Moment mit den Koordinaten umgehe... und den Grafikspeicher malträtiere

Herzviewer.zip

Xeres

Moderator

BeitragMi, Feb 27, 2013 0:44
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich glaube nicht, dass das entfernen der Types irgendetwas grundlegend verbessern wird.
Was ich denke, was du brauchst ist ein Modell von dem Herz aus wenigen Polygonen. Das kann man als Wireframe darstellen und einzelne Vertices den nächst gelegenen Zellen zuordnen. Das Problem ist, dass die ganzen Daten für das Herz nicht dazu geeignet scheinen, trianguliert zu werden - jedenfalls kann ich mir nicht wirklich vorstellen, wie das mit den Ausgangsdaten geschehen sollte... Wenn du eine Beschreibung des Datenformats mitlieferst, kann man dir besser helfen.

Das einzig Sinnvolle was mir einfällt ist, nur jedes 10 oder 20 Objekt auch tatsächlich zu rendern um etwas Geschwindigkeit zu erreichen. Dein Code ist sicherlich verbesserbar - wenn man auch da überall wüsste, was die Intention des Autors ist - bar jeder medizinischen Fachterminologie. Ich experimentiere nochmal, wenn ich mehr Zeit habe.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)

PSY

BeitragMi, Feb 27, 2013 5:37
Antworten mit Zitat
Benutzer-Profile anzeigen
Hoi,

also die Types entfernen wird Dir nichts bringen. Erstens wird der Code total unuebersichtlich, und speedtechnisch holste auch wenig bis gar nichts raus.

20k einzelne Type-Knoten mit B3D darstellen sollte drin sein (mit nem halbwegs aktuellen Rechner), wenn Du's quasi nur statisch brauchst. An SingleSurface kommst Du natuerlich nicht vorbei. In Deinem Beispiel erstellst Du ne Unmenge an Meshes, das geht natuerlich total auf die Performance.

Hab hier mal ein Beispiel gemacht mit 16000 Types, Singlesurface. Bei jedem Schleifendurchlauf werden alle 16k Types durchlaufen, jeder einzelne Type hat eine andere Rotation und andere RGB Werte und wird getrennt behandelt. Unterschiedliche Groessen und weitere Eigenschaften waeren auch kein Ding.
Alle 16k Quads werden bei jedem Schleifendurchlauf auf die Cam ausgerichtet.

Steuerung:
Keine. War zu faul Wink Cam zoomed halt mit Sinus automatisch rein/raus

Tasten:
ESC: Quit
SPACE: Blendmode umschalten (1/3)
Return: Wireframe ON/OFF (Sieht witzig aus, die Tris sind verfremdet wegen kleiner Textur)

Die Zahl links oben sind die FPS, laueft mit Flip 1 (VSYNC).

Ohne Pics saug ich nix: user posted image
Download: http://pheryllt.de/_misc/Blitzforum/16k/16k.exe
Notwendige Textur: http://pheryllt.de/_misc/Blitzforum/16k/tex.png (ins gleiche Verzeichnis wie die .exe kopieren)


Hab den Code nur hingesaut, falls Du was brauchst, sag Bescheid, dann kommentier ich n bischen und poste ihn.


Cheers,
PSY
PSY LABS Games
Coders don't die, they just gosub without return
 

Starling

BeitragMi, Feb 27, 2013 12:02
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo, wieder erstmal Danke für die Antworten !

@Xeres: Also die Daten hab ich eben aus einzelnen 2D Bildern erstellt. Der andere Screenshot in der zip zeigt wie ich MRT Bilder quasi abgefhren und mit 3DKoordinaten bepunktet habe. Der Datensatz für eine Zelle -wie er in der txt abgespeichert und ausgelesen wird- besteht dann aus:

ReadEbene = Int(ReadLine$(datei)) - Im Prinzip die Y Koordinate: alle Koordinaten auf einer Querschnittshöhe

ReadZellart= Int(ReadLine$(datei)) - Die Zellart, also deren Zuordnung zum linken/rechten Vorhof/Ventrikel etc.

Readx#= Int(ReadLine$(datei)) - X-Koordinate als Integer
Ready#= Int(ReadLine$(datei)) - Y-Koordinate als Integer
Readz#= Int(ReadLine$(datei)) - Z-Koordinate als Integer

Readmp = Int(ReadLine$(datei)) - Das sind extra Punkte, die ich pro Querschnitt in die Mitte der Kammern gesetzt habe. Danach richte ich im Code meine Zellen/Scheiben/Polymere aus (PointEntity), sodass sie eine gleichmäßige Wand ergeben und nicht alle querorientiert sind. Kann man in meinem Programm schön an den Vorhöfen sehen.

Was heisst nur 10 von 20 Objekte zu rendern? Werden sie dann noch angezeigt?


allg.: Ich hatte vergessen anzugeben wie man sich bei meinem Vorführprogramm Herzviewer.zip bewegt: Mit Mausdown links/rechts kann man ran- und raus fahren, A und D drehen um einen Pivot der im Mittelpunkt des Herzens gesetzt ist, Cursortasten für sonstiges Manövrieren in Richtung X- und Y-Achse.
Die einzelnen Kammern kann man mit den Tasten 1-4 an- und ausknipsen, mit der Taste (54) kann man die Erregung auf Anfang setzen. Insofern noch weitere Durchläufe über die Vorhöfe jagen...

@PSY: Also dein Screenshot sieht ja vielversprechend aus ! Leider seh ich nichts, wenn ich das Pogramm starte - nur schwarz und oben die FPS: bei mir so um die 40 (Grafik in gleichem Verzeichnis).
Aber das suche ich im Prinzip: Particles, speicherarme Grafiken die ich an die Positionen meine Zellkoordinaten setze und auch noch ansteuern kann: Also einmal ihre Koordinaten im Vergleich habe und dann eben auch ihren Look einzeln ändern, um eben die Erregung darzustellen. - Nur muss ich eben auch noch das Herz als 3dimensionales Objekt sehen können, nicht nur gepunktetes Chaos... !
Insofern wär ich Dir natürlich dankbar, wenn ich deinen Code sehen kann, wenn er denn bei mir auch was zeigt Smile natürlich am besten kommentiert, diese Grafikprogrammierung ist doch Neuland für mich.

@Bladerunner: Naja, das dient uns Studenten dann zum Verständnis, nutzen wir dann im Physiologiepraktikum. Letztlich wird das ganze noch komplexer, wenns dann um die verschiedenen Ionenkonzentrationen -kanäle geht, die Einfluss auf die Erregungsbildung/-weiterleitung haben.
Mein Prof is von der Grafik allgemein begeistert, der hat sonst nur so Dos-Programme, insofern kann ich damit auch gut bei ihm punkten Wink Und natürlich verweise ich auch auf die Sprache und dieses hilfreiche Forum !

Als Quad bezeichnet man ein Quadrat? Also zwei zu einem Rechteck gelegte Polymere?

DAK

BeitragMi, Feb 27, 2013 12:40
Antworten mit Zitat
Benutzer-Profile anzeigen
Die Idee mit dem "1-2 von 20 rendern" nennt man LOD (Level of Detail) und geht davon aus, dass, wenn man weiter weg ist, genaue Details sowieso nicht sehen kann. Das wird vor Allem bei Terrains aber auch bei Texturen und "normalen" Meshes gern gemacht. Wenn du da 20k Zellen im Blick hast, dann wird es kaum einen Unterschied für den Beobachter machen, ob du jetzt wirklich alle 20k renderst, oder nur jedes 10te davon, und immer welche dazwischen nicht zeichnest.

Als einfachere Verdeutlichung: Stell dir vor, du hast ein Foto mit 10 Megapixel Auflösung und schaust das auf einem kleinen alten Handy mit 160x240 Bildschirm-Auflösung an. Damit das Bild auf den Schirm passt, wird kräftig runterskaliert und die ganze tolle Pixelvielfalt verschwindet, da das Display es eh nicht darstellen kann. Hättest du das Bild schon vorher auf 160x240 runterskaliert und so auf das Handy geschickt, dann würde das Endergebnis genau gleich ausschauen, aber nur ein paar kb statt mehrere mb groß sein.

Hier gibts ein Video, welches ein automatisches LOD-Terrain so zeigt, dass man den Prozess sieht. Wenn du es richtig machst, dann kannst du es auch beinahe unsichtbar machen.


Noch mal als kleines Beispiel: angenommen, du hast dieses Rechteck an zeichenbaren Objekten:

X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X

Wenn die Kamera ganz weit weg ist, dann zeichnest du nur die Objekte, die mit O gekennzeichnet werden, aber so groß, dass sie das gesamte Rechteck überspannen:

Weit weg:
O X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X

Näher:

O X X X O X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
O X X X O X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X

Noch näher:
O X O X O X O X
X X X X X X X X
O X O X O X O X
X X X X X X X X
O X O X O X O X
X X X X X X X X
O X O X O X O X
X X X X X X X X

Und so weiter. Wie genau du dein Muster machst, musst du selber ausprobieren.

Dadurch, dass dann eben wesentlich weniger Objekte gezeichnet werden, sparst du dir damit extrem viel Rechenleistung.

In Computerspielen verwendet man das oft dafür, dass man Terrains in der Größe eines kleinen Planeten beinahe ohne nennenswerte Rechenleistung darstellen kann.
Gewinner der 6. und der 68. BlitzCodeCompo

PSY

BeitragDo, Feb 28, 2013 0:43
Antworten mit Zitat
Benutzer-Profile anzeigen
@Starling

Hmm komisch. Laeuft bei mir einwandfrei. Scheint wohl ein Kompatibilitaetsprob zu sein.
Hab hier Win7 64bit + NVidia. Welches BS hast Du?

Hab zum Testen hier mal 5 verschiedene .exes erstellt. (Fullscreen, windowed, 32bit Farben, tris statt quads, Anzahl tris/quads halbiert, andern Blendmode, Alpha deaktiviert).

Sind alle 5 in nem .rar file. Einfach entpacken und nacheinander ausprobieren.
Bitte bei jeder .exe 1x Space und 1x Return (nacheinander) druecken. Schaltet Blendmode um und Wireframe ON/OFF.

Wuerd mich mal interessiern obde bei keiner was siehst...
http://pheryllt.de/_misc/Blitz...urface.rar

(edit) Habn bischen rumgespielt. Zu Testzwecken hab ich mal Deine Datei eingelesen und einfach 8 verschiedene Farben genommen. Zusaetzlich hab ich alpha komplett deaktiviert, damit z-buffering korrekt funzt.
Video: http://pheryllt.de/_misc/Blitz...7quads.mp4
Qualitaet und FPS vom Video sind nicht aussagekraeftig, sieht original natuerlich besser aus.
Guck einfach mal, ob eine der o.g. .exe bei Dir laeuft, dann bau ich den Code dementsprechend um und kommentier ihn.


PSY
 

Starling

BeitragDo, Feb 28, 2013 11:32
Antworten mit Zitat
Benutzer-Profile anzeigen
WOW ! PSY, das ist echt beeindruckend ! Jetzt läufts bzw. die mit 8K, bei den beiden mit 16K nur schwarz.
(Hab auf meinem MacBook Air ne Win7 64Bit über BootCamp installiert)

Die Kubusform kommt ja auch super rüber, insofern bräuchte ich das für mein Herzmodell... das wäre perfekt, auch mit der Textur sieht super aus.

Wenn Du mir den Code zugänglich machst, wär ich Dir sehr verbunden! Hab selbst schon viel Zeit investiert, aber Dein Ergebnis nicht annähernd erzielt. Das fliegt so luftig umher, scheint kein bißchen auf die Leistung zu drücken ! - Heisst das aber 8K ist das Maximum? Worauf bezieht sich das denn? Ich hatte ja in meinem ersten Beitrag schon geschrieben, ich habe beobachtet, dass mein Computer bei etwa 15 tausend erstellen Spriteobjekten streikt: Fehler bei RenderWorld (). Aber wenn ich das richtig verstanden habe, sind deine tausende Texturen jetzt in einem einzigen Mesh integriert? Sprich, ich könnte weitere Meshes erstellen mit jeweils 8K?

Vielen Dank,
mfg
V
 

Matthias

BeitragDo, Feb 28, 2013 17:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo.

Da ich dein Thema sehr interessant fand habe ich mich mal daran gemacht, was in dieser Richtung zu programmieren.

Das Herz besteht aus kleine Würfel. Natürlich SinglesSurface.
Allerdings werden nur die Seiten des Würfels dargestellt die auch wirklich zu sehen sind.
Das spart Polygone.

Die Welt ist 64x64x64 Einheiten große Blöcke unterteilt wobei jeder Block(Chuck) sein eigendes Mesh hat.
Das spart speicher.

Da nicht alle Meshes in einem Schleifendurchgang aktualiesiert werden können (Geschwindigkeitsgründe)
werden die Veränderung über mehrere Schleifendurchgänge verteilt.

Natürlich werde ich daran noch ein wenig weiter programmieren aber vlt. kannst du jetzt schon was damit anfangen.

Steuerung. Mausmitteltaste=Camera drehen. Mausrollrad oder Links/Rechts=vor/zurück
user posted image
https://www.blitzforum.de/upload/file.php?id=12283

Gruß Matthias.

PSY

BeitragFr, März 01, 2013 2:12
Antworten mit Zitat
Benutzer-Profile anzeigen
Starling hat Folgendes geschrieben:
WOW ! PSY, das ist echt beeindruckend ! Jetzt läufts bzw. die mit 8K, bei den beiden mit 16K nur schwarz.
(Hab auf meinem MacBook Air ne Win7 64Bit über BootCamp installiert)
Danke Smile
Ah, ok. Dann liegts an der Anzahl der Tris.
Die beiden 16k-Versionen benutzen 32000 Tris PRO mesh, das packt Deine Graka wohl nicht.
Die 8k Quad-Version benutzt 16000 Tris, die beiden andern 8k halt dementsprechend 8k Tris.
Wo genau die Genze bei dir ist, kannst Du ganz einfach austesten, indem Du die Trianzahl stueckweise erhoest. (Musst bei dem Code von mir nur 1 Wert aendern)



Starling hat Folgendes geschrieben:
Die Kubusform kommt ja auch super rüber, insofern bräuchte ich das für mein Herzmodell... das wäre perfekt, auch mit der Textur sieht super aus.
Das Ding hat ne Kubusform, weil ich jeweils die x, y und z Coords in einem bestimmten Zufallsbereich generiert hab. Lineare Zufallswerte auf 3 Achsen --> automatische Kubusform Smile
Mit sin/cos und exp functions kannste da noch ganz andere Gebilde schaffen



Starling hat Folgendes geschrieben:

Wenn Du mir den Code zugänglich machst, wär ich Dir sehr verbunden!
Kein Ding, werd ihn entsprechend umbaun und kommentieren.



Starling hat Folgendes geschrieben:

Heisst das aber 8K ist das Maximum? Worauf bezieht sich das denn?
Das heisst, dass 16384 Tris das Maximum bei Deiner Graka sind. PRO MESH Smile
Kannst also problemlos weitere Meshs mit jeweils 16384 (schaetze das ist der Wert, kannst ja mal rumtesten) erstellen. Hab grad problemlos 128k Tris erstellt mit 4 Meshes (meine Graka packt 32768 Tris pro Mesh). Allerdings dropped dann die Framerate Smile
(edit) Die Framerate dropped natuerlich nur, weil pro loop 65536 Types durchlaufen werden. Wenn man die Tris/Quads einmal setzt, ist das kein Prob fuer die Grafikkarte. Da laufen auf ner 295GTX 128.000 Tris noch mit knapp 300 FPS, und 32.000 Tris mit mehr als 1000...



Starling hat Folgendes geschrieben:
...sind deine tausende Texturen jetzt in einem einzigen Mesh integriert? Sprich, ich könnte weitere Meshes erstellen mit jeweils 8K?
Right. Entweder mit rund 16k Tris/Mesh oder halt 8k Quads/Mesh. Der Vorteil von Quads ist halt, dass Du die komplette Textur verwenden kannst.
Ganz wichtig: Deine Texturen muessen in Graustufen vorliegen (Faerben uebernimmt die Engine) und quadratisch sein (Potenz von 2, also 32x32, 64x64, 128x128 usw.). Kennst das Spiel ja Wink



Werd vllt nachher noch dazu kommen, muss noch ein paar andere Sachen erledigen. Spaetestens heut abend (Freitag, ist schon nach 12 nu) hastes aber.


PSY




(edit2) Hatte leider kaum Zeit, aber zumindest den Code hab ich komplett umgebaut. Test mal, ob er bei Dir laeuft. Alle 3D-Punkte aus Deinem File sind eingebunden (29684) und als Quads auf 4 Meshes verteilt. Alpha hab ich komplett deaktiviert (wegen Z-buffering). Mit ',' und '.' kannste das Updateintervall aendern (wenn das zB auf 10 steht, werden bei jedem 10. loop alle Types durchlaufen). Man muss die Position/Rotation/Timer etc. ja nicht jeden loop updaten, kostet unnoetig Rechenzeit.
Wenn der Code bei Dir laeuft kommentier ich ihn nachm Aufstehn und lad ihn hoch.
Achja, benutz die alte tex.png. Oder halt irgendeine von Dir, egal. Hauptsache quadratisch, 2er Potenz und Graustufen.
Und Deine Datei "Herzdata53.txt" muss natuerlich auch im gleichen Verzeichnis liegen Smile



KOMPLETTPAKET (exe + texture + herzdaten)
http://pheryllt.de/_misc/Blitz...DHeart.rar

PICS
user posted image
user posted image
 

Starling

BeitragFr, März 01, 2013 21:13
Antworten mit Zitat
Benutzer-Profile anzeigen
Wow, wow ! Vielen Dank Matthias und vielen Dank PSY !
Das macht mich richtig glücklich, ich dachte die Leistung wäre schon ausgeschöpft, aber so wie ihr das hinbekommen habt, kann man ja noch viel mehr in das Programm integrieren !

PSY, das Prog läuft vorzüglich, die Plastizität ist auch wunderbar. Ich bin auf den Code gespannt ! Vielleicht kannst du an einer Stelle noch ein Beispiel einfügen wie ich eine einzelne Zelle anspreche?

Ich denke mit Euren beiden Codes findet sich jetzt hier im Forum auch ein super Tutorial was Single Engine angeht, insofern bedank ich mich im Rahmen aller, die davon profitieren !!

Ich halt Euch am Laufen wie sich das Projekt entwickelt, vielleicht kann ich bei Interesse ja auch ein paar medizinische Aspekte vermitteln Smile

lg
V

PSY

BeitragFr, März 01, 2013 23:05
Antworten mit Zitat
Benutzer-Profile anzeigen
Hoi,

einzelne Zellen kannste ansprechen, indem Du den jeweiligen Type bestimmst.
Da kannste aber noch einiges aendern, zB Anzahl der 3D Punkte verringern, oder einfach die Punkte weiter auseinandersetzen. Oder die Textur aendern (statt dem Herz nen kleinen Punkt, rundum schwarz - schwarz wird automatisch ausgeblendet). Probier einfach mal rum.

Hab den Code zusaetzlich hochgeladen, hier kommts beim Pasten oft zu Fehlern mit der Einrueckung...

Code zum DL: http://pheryllt.de/_misc/Blitz...eartv2.rar

Code direkt:

Code: [AUSKLAPPEN]
; SingleSurface engine by PSY
; Made Feb 2013
; http://psy-labs.com


; Aufloesung spielt fuer die SingleSurface Engine speedtechnisch ueberhaupt keine Rolle!
Graphics3D 1024,768,32,1
SetBuffer BackBuffer()

SeedRnd MilliSecs()

Global FPS_frames% ,FPS_fps% ,FPS_timer%, bMode%=0, wFrame%=0, cycle%=0, updateInterval%=10
Global pivot%, cam%, tex%, ghostmesh%, ghostsurface%
Local angle#=240.0 ; Init-Wert fuer Cam-Fahrt

; 4 Meshes und 4 Surfaces erstellen
; Auf jedem Mesh koennen eine bestimmte Anzahl Tris untergebracht werden
; Damit es auf aelteren/billigeren Grakas laeuft, sollte man 16k Tris pro Mesh nicht ueberschreiten.
; Bei neueren sind 32k Tris pro Mesh kein Thema
; Gesamtzahl der Tris(auf mehrere Meshes verteilt) wird letztendlich nur durch Grafikkartenpower bestimmt
Dim mesh%(3)
Dim surface%(3)

; Hier werden die einzelnen 3D Punkte gespeichert
Type TQuad
   Field x#, y#, z#   ; Position
   Field r%, g%, b%   ; Farben
   Field rotation#      ; Rotation (kannste auch rausnehmen, musst sehn was besser aussieht)
End Type

Global quad.TQuad


Init()          ; CAM / MESHES / SURFACES erstellen
LoadData3D()   ; Herzdaten laden und in den Type uebertragen







Repeat
   
   ; Die 3 Zeilen hier sind fuer die Kamerafahrt. Camera hat Pivot als Parent. Dient alles nur der Optik
   angle = (angle + .5 ) Mod 360
   PositionEntity cam, 0, 0, -200+100*Sin(angle)
   TurnEntity pivot, .2, .2, .2, 1
   
   Keys()          ; Usereingaben abfragen
   SingleSurface() ; SINGLE SURFACE ENGINE
   RenderWorld()
   
   Text 0, 00, "FPS:  " + fps()   
   Text 0, 20, "Tris: " + TrisRendered()
   Text 0, 40, "RETURN - Toggle WireFrame: " + wFrame
   Text 0, 60, "SPACE - Toggle Blendmode:  " + bMode
   Text 0, 80, "',' and '.' to toggle heart update interval:  " + updateInterval
   
   
   Flip 0
   
Until KeyHit(1)
End















; SINGLE SURFACE ENGINE
; FUNKTIONSWEISE:
; ---------------------
; 0. Alle Surfaces werden geloescht
; 1. Alle Types werden durchlaufen
; 2. Der GHOSTMESH wird an den jeweiligen Type-Koordinaten positioniert
; 3. Der GHOSTMESH wird per PointEntity auf die CAM ausgerichtet (und gedreht)
; 4. Die 3(bei Tris) oder 4(bei Quads) Vertexpunkte des GHOSTMESH werden per TFormPoint global bestimmt
; 5. Die bei Punkt 4 bestimmten Vertexpunkte werden nun per AddVertex auf das passende Surface der SingleSurface-Meshs uebertragen
; 6. Sie bekommen per Vertexcolor ihre Farbe zugewiesen
; 7. Zum Schluss werden mit AddTriangle die Vertexpunkte zu Tris verbunden
Function SingleSurface()
   Local x#, y#, z#, v%, surf%, c%=-1, i%
   
   ; TRICK: Die ganzen Types muessen nicht bei jedem Loop durchlaufen werde, das ist total unnoetig und kostet Unmengen an Rechenzeit
   ; Stattdessen wird einfach nur jeder x.te Cycle durchlaufen (x = Updateinterval)
   ; Damit kannst Du die FPS MASSIV steigern
   cycle = cycle + 1 : If cycle < updateInterval Then Return
   cycle = 0
   
   ; Alle 4 Surfaces loeschen
   For i=0 To 3
      ClearSurface surface(i), 1, 1
   Next
   
   For quad.TQuad = Each TQuad
      
      ; Diese beiden Zeilen sorgen einfach dafuer, dass per MOD die 3D Punkte auf die 4 Meshs/Surfaces verteilt werden
      c = c + 1
      i = c Mod 4
      
      PositionEntity ghostmesh, quad\x, quad\y, quad\z, 1 ; Ghostmesh an Typekoordinaten positionieren
      PointEntity ghostmesh, cam, quad\rotation         ; Ghostmesh auf Cam ausrichten (und drehen falls gewuenscht)
      
      ; Hier werden die 3(bei Tris) oder 4(bei Quads) Vertexpunkte des Ghostmesh global bestimmt und auf die Single Surface Meshs uebertragen
      TFormPoint VertexX(ghostsurface,0), VertexY(ghostsurface,0), VertexZ(ghostsurface,0), ghostmesh, 0
      x = TFormedX()
      y = TFormedY()
      z = TFormedZ()
      v = AddVertex (surface(i),x,y,z,0,0) ; Einmal den Vertexindex LINKS OBEN merken, braucht man fuer Vertexcolor und AddTriangle weiter unten
      TFormPoint VertexX(ghostsurface,1), VertexY(ghostsurface,1), VertexZ(ghostsurface,1), ghostmesh, 0
      x = TFormedX()
      y = TFormedY()
      z = TFormedZ()
      AddVertex  surface(i),x,y,z,1,0
      TFormPoint VertexX(ghostsurface,2), VertexY(ghostsurface,2), VertexZ(ghostsurface,2), ghostmesh, 0
      x = TFormedX()
      y = TFormedY()
      z = TFormedZ()
      AddVertex  surface(i),x,y,z,0,1
      TFormPoint VertexX(ghostsurface,3), VertexY(ghostsurface,3), VertexZ(ghostsurface,3), ghostmesh, 0    ; weglassen wenn statt Quads nur Tris verwendet werden sollen
      x = TFormedX()                                                                  ; weglassen wenn statt Quads nur Tris verwendet werden sollen
      y = TFormedY()                                                                  ; weglassen wenn statt Quads nur Tris verwendet werden sollen
      z = TFormedZ()                                                                  ; weglassen wenn statt Quads nur Tris verwendet werden sollen
      AddVertex  surface(i),x,y,z,1,1                                                      ; weglassen wenn statt Quads nur Tris verwendet werden sollen
      
      ; Farbe des Vertexpunkte setzen
      VertexColor surface(i), v  , quad\r, quad\g, quad\b, 1
      VertexColor surface(i), v+1, quad\r, quad\g, quad\b, 1
      VertexColor surface(i), v+2, quad\r, quad\g, quad\b, 1
      VertexColor surface(i), v+3, quad\r, quad\g, quad\b, 1                                    ; weglassen wenn statt Quads nur Tris verwendet werden sollen
      
      ; Punkte zu Dreiecken oder Quads (2 Dreiecke) verbinden
      AddTriangle surface(i), v  , v+1, v+2
      AddTriangle surface(i), v+1, v+3, v+2                                                ; weglassen wenn statt Quads nur Tris verwendet werden sollen
      
   Next
   
End Function




; Hier werden einfach nur Tastatureingaben abgefragt
Function Keys()
   Local i%
   
   ; Blendmodus 1(normal) oder 3(addieren)
   If KeyHit(57)
      bMode = 1 - bMode
      For i=0 To 3
         EntityBlend mesh(i), bMode*3
      Next
   EndIf
   
   If KeyHit(28) Then wFrame = 1 - wFrame : WireFrame wFrame ; Wireframe an/aus
   
   ; Hier wird ueber ',' und '.' das Updateintervall erhoeht bzw. verringert
   If KeyHit(52) Then updateInterval = updateInterval+1
   If KeyHit(51) And updateInterval>1 Then updateInterval = updateInterval-1
   
End Function




; Hier wird das SingleSurface System initialisiert
; Jeder Mesh bekommt dabei die gleiche Textur zugewiesen
; ALPHA IST DEAKTIVIERT, ANSONSTEN KOMMT ES ZU Z-BUFFER FEHLERN
; GENAUERES IN DER HILFE ZUM BEFEHL 'LOADTEXTURE'
Function Init()
   Local i%
   
   pivot   = CreatePivot()                  ; Nur fuer die Camfahrt
   cam    = CreateCamera(pivot)            ; Nur fuer die Camfahrt
   tex    = LoadTexture ("tex.png",4)
   
   For i=0 To 3
      mesh(i)      = CreateMesh()
      surface(i)    = CreateSurface(mesh(i))
      EntityBlend    mesh(i), 1            ; Normaler Blendmodus bei Start
      EntityFX       mesh(i), 1+2+4+8+16      ; leuchtend, Vertexfarbe, flache Schattierung, ohne Nebel, kein Backface culling. ALPHA NICHT AKTIVIEREN, SONST Z-BUFFER FEHLER
      EntityTexture   mesh(i), tex         ; Mesh mit Textur ueberziehen
   Next
   
   ghostmesh%       = CreateMesh()            ; Unser Geheimtrick, der Ghostmesh ;)
   ghostsurface%    = CreateSurface(ghostmesh)
   HideEntity  ghostmesh                  ; Verstecken das Teil, braucht man nicht zu sehn...
   
   AddVertex   ghostsurface, -1,  1, 0         ; QUAD (oder TRI) erstellen. Diese 3(4) Vertexpunkte werden wir nachher bestimmen und auf die SingleSurface Meshs uebertragen
   AddVertex   ghostsurface,  1,  1, 0
   AddVertex   ghostsurface, -1, -1, 0
   AddVertex   ghostsurface,  1, -1, 0       ; weglassen wenn statt Quads nur Tris verwendet werden sollen
   
End Function




; Hier werden Deine Herzdaten geladen...
Function LoadData3D()
   
   Local file%, plane%, cell%, mp%
   
   file% = OpenFile("Herzdata53.txt")
   While Not Eof(file)
      
      plane         = Int(ReadLine$(file))
      cell         = Int(ReadLine$(file))
      
      quad.TQuad       = New TQuad
      quad\x          = Int(ReadLine$(file))
      quad\y          = Int(ReadLine$(file))
      quad\z          = Int(ReadLine$(file))
      quad\rotation    = Rnd(359.0)
      
      mp            = Int(ReadLine$(file))
      
      ; Verschiedene Farben setzen. Hab hier einfach Farben vergeben. Musste fuer Deine Zwecke anpassen ^^
      Select cell
         Case 1 quad\r = 255 : quad\g = 255 : quad\b = 255
         Case 2 quad\r = 255 : quad\g = 000 : quad\b = 000
         Case 3 quad\r = 000 : quad\g = 255 : quad\b = 000
         Case 4 quad\r = 000 : quad\g = 000 : quad\b = 255
         Case 5 quad\r = 255 : quad\g = 255 : quad\b = 000
         Case 6 quad\r = 000 : quad\g = 255 : quad\b = 255
         Case 7 quad\r = 255 : quad\g = 000 : quad\b = 255
         Case 8 quad\r = 050 : quad\g = 150 : quad\b = 250
      End Select
      
   Wend
   CloseFile (file)
   
End Function




; Bloss die FPS...
Function fps ()
   FPS_frames = FPS_frames + 1
   If FPS_timer + 1000 < MilliSecs()
      FPS_fps    = FPS_frames
      FPS_frames    = 0
      FPS_timer    = MilliSecs()
   EndIf
   Return(FPS_fps)
   
End Function




Have fun,
PSY
PSY LABS Games
Coders don't die, they just gosub without return
 

Starling

BeitragSo, März 03, 2013 14:03
Antworten mit Zitat
Benutzer-Profile anzeigen
Klasse !

Wens interessiert:
Engine-1.0.zip

Jetzt kann ichs nach und nach zum Leben erwecken: Nach dem EKG ist die Kontraktion dran Smile BB und Eure Hilfe machts möglich Exclamation

Neue Antwort erstellen


Übersicht BlitzBasic Blitz3D

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group