Raytracing - Ich brauche neue Ideen was ich an den Bild noch verbessern kann
-
ja, die meisten spiele nutzen zu 99% nur ein brdf. auch viele renderer nutzen nur wenige brdfs.
die idee ist, dass es eigentlich ein einziges brdf-model geben kann, das einzig durch verschiedene parameter die oberflaecheneingeschaften beschreibt. das problem ist, dass die simulation davon extrem aufwendig waere, denn dazu gehoert subsurface scatterin genau so wie atmospheric scattering etc.
deswegen spezialisiert man ein paar. oft
- metalle (specular ist bunt, basis material ist gray)
- nicht metalle (dielectrics) bunte basis, weisses specular
- subsurface scattering (meistens nur sehr duennes, z.b. haut, pflanzenblaetter etc.) BSSRDF
- participating media (rauch)
- atmospheric effects (meist in clouds und sky aufgeteilt)
- anisotropische materialien (ging zwar mit dielectrics, aber die parametrisierung ist ein wenig komplexer und das will man nicht grundsaetzlich haben.
- caustic materials (glass, wasser,...)
- fur (fell, bzw rasen, tepich, stoff gehoert da oft dazu). BRTFdazu gibt es oft analytische formel. beim path tracen nutzt man manchmal auch gemessene werte, im einfachsten fall http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter11.html
wenn du metalle und dielectrics erstmal implementierst, solltest du deine scene sehr gut ausleuchten koennen.
-
Sehr schön, deine Hinweise und Link hilft mir jetzt erstmal wieder weiter. Ich werde jetzt erstmal versuchen das weiter zu verstehen/umusetzen.
Dieses Thema mit der Anzeige von Kleidung/Kissen interessiert mich schon lange und ich wußte einfach nicht, unter welchen Begriff ich das googeln muss.
-
-
Ich habe am WE jetzt eine neue Szene angefangen. Hier nun erstmal die Abschlussbilder von der alten Szene.
Diffuses Material:
https://picload.org/image/rodpolir/saeulenbueromitgodraysundmotio.jpg
https://picload.org/image/rodpolia/saeulenbueroohnegodrays.jpgMit Specular Highlights
https://picload.org/image/rodpolci/god_phong.png
https://picload.org/image/rodpolcw/nogodbipath.pngMein nächstes Bild wird diesmal eine Nahaufnahme von kleinen Gegenständen sein. Sowas muss auch mal sein^^ Dort möchte ich mich um Subsurface Scattering kümmern.
Ohne Tiefenunschärfe:
https://picload.org/image/rodpowrg/test.pngMit Tiefenunschärfe
https://picload.org/image/rodwgpll/test.png
-
schnapp dir doch eine der vielen referenz szenen. dann kannst du deine resultate auch mit dem wie es aussehen sollte vergleichen
was ist das da links auf der Saeule? https://img1.picload.org/image/rodpolir/saeulenbueromitgodraysundmotio.jpg
Schaut ein wenig aus als ob du durchs objekt tracen wuerdest.
-
Wo finde ich diese Referenz-Bilder? Bei einer kurzen Google-Suche fand ich nichts.
Meinst du die vertikal verlaufende Rille bei der Säule?
-
ganz ganz ganz links, am unteren rand der dritten kachel von oben.
DIE REFENREZ ist die cornell box, weil die Wissenschaftlich diese box erst im echten Leben ausgemessen haben und dann mit dem rendering vergleichen:
http://www.graphics.cornell.edu/online/box/
http://www.graphics.cornell.edu/online/box/compare.html.Blender hat einige test szenen, dieser subd affen kopf, oder z.B. http://blenderdiplom.com/en/downloads/584-download-cycles-material-test-scene.html
oder
https://code.blender.org/2016/02/new-cycles-benchmark/einige nette szenen (z.B. ist Sponza eine sehr sehr gaengige szene zu der du viele renderings finden solltest):
http://graphics.cs.williams.edu/data/meshes.xml
-
Ok das mit diesen weißen Streusel ist mir auch schon aufgefallen und ich habe mich drüber gewundert^^ Ich nehms einfach erstmal in meine Liste der offenen Fragen/Fehler auf.
Klingt nach einer guten Idee sich an diesen Benchmarkt-Szenen zu testen. Die Cornellbox kenne ich natürlich aber ist mir jetzt gerade beim Verfassen von mein letzten Beitrag nicht sofort eingefallen.
Ich denke mal, bis ich diese ganzen 'Tests' geschaft habe, dürfte noch sehr viel Arbeit sein. Es geht hier ja nicht einfach nur um globale Beleuchtung sondern auch um das ganze Material-Zeug. Schön wärs noch, wenn ich auch noch gegen die Renderzeit arbeiten/vergleichen dürfte, nachdem der Bildvergleich geschaft ist.
Du hast mich mit dein Link für diese Blender-Datei noch auf eine weitere gute Idee gebracht. Ich könnte doch einfach mal anfangen mir Anzusehen, wie man den Raytracer von Blender benutzt und diesen dann nehmen, um Referenzbilder zu erzeugen. Bei Blender arbeiten doch Leute, die Ahnung haben^^
-
du kannst auch deinen raytracer in blender einbinden.
hm, ja, mir klingt es als ob dein tracer recht langsam ist. vielleicht lohnt es sich daran zu arbeiten? Mit jeder verdopplung der geschwindigkeit wirst du doppelt so schnell progress machen beim entwickeln, weil du nur die haelfte wartest
installier dir CodeXL, starte deinen tracer und dann auf "profile" und dort "attach to process" und profile deinen tracer mal 2minuten
vielleicht hast du funktionen die langsam sind, die du nicht erwartet haettest und du kannst mit einem fingerschnippen viel zeit sparen.
ansonsten poste hier die top offenders und wir koennen vielleicht tipps geben.
-
Das mit den Profiler kann ich gerne machen aber ich glaube kaum, dass ich da mit CodeXL arbeiten kann. Das ist ein reines C#-Projekt. Dafür kann ich bereits den Profiler von Visual Studio nutzen, um mir das mal anzusehen was da hängt.
Was ich z.B. mal rausgefunden habe ist, dass man bei der Vektor-Klasse sowas nicht machen darf:
class Vector { public float[] Numbers = new float[]{0,0,0}; }
Macht man es stattdessen so, dann ist die Raytracingzeit halb so viel:
class Vector { public float X = 0; public float Y = 0; public float Z = 0; }
D.h. einfach nur die Multiplikation + Addition von einer Adresse, um im Array auf ein Element zuzugreifen, macht das ganze schon so übelst langsamer.
-
XMAMan schrieb:
Wenn denn spectrales Raytraing auch 'normale' Alltagsszene verbessern könnte, dann würde ich das ja machen. Was mich stört ist, dass es nur für ganz bestimmte unscheinbare Effekte gut zu sein scheint.
In einem "echten" Szenario könnte man Staubpartikel sehen, und auch jedes einzelne Teil gut erkennen.
Eine sehr wichtige Besonderheit dazu ist, dass die Sonne wandert.
Abhängig davon ändert sich die Farbe (wie auch der Schattenverlauf), also je gerader der Winkel, bzw. je stärker die Horizontnähe, desto farbiger (und dunkler).Manchmal ist die Sonne von Wolken verdeckt, und es kommt nur ein einzelner Lichtstrahl durch (welcher ebenfalls wandert).
(dieser Effekt wird z.T. gezielt genutzt (z.B. Stonehenge oder Pyramiden))Am Gesamtbild selbst: Beim ersten Eindruck hatte ich gedacht, da hängt ein Möbelstück an der Decke bzw. ein abstraktes Bild?
-
An der Decke hängen nur die Lampen und zwei Ventilatoren.
Oder meinst du die Textur mit den schwarz/weißen Kacheln?
-
XMAMan schrieb:
An der Decke hängen nur die Lampen und zwei Ventilatoren.
Oder meinst du die Textur mit den schwarz/weißen Kacheln?
Die Ventilatoren. Außerdem würden in einem Zimmer mit Ventilatoren (und dieser Fensterbreite) höchstwahrscheinlich Jalousien vor den Fenstern hängen.
-
nachtfeuer schrieb:
XMAMan schrieb:
An der Decke hängen nur die Lampen und zwei Ventilatoren.
Oder meinst du die Textur mit den schwarz/weißen Kacheln?
Die Ventilatoren. Außerdem würden in einem Zimmer mit Ventilatoren (und dieser Fensterbreite) höchstwahrscheinlich Jalousien vor den Fenstern hängen.
jetzt stich nicht so auf sein innernarchitektenherz ein
ich hab wenig ahnung von C# optimierung. auf PSVita war das extrem langsam (ca 100x langsammer als c++ auf meinem handy), aber das schoben die leuete dem system zu.
Ich wuerde vermuten, dass du zumindestens an high level algorithmen optimieren kannst. z.B. wie partitionierst du die szene? AABBs? OcTree? kd-tree? grid? spheres?
-
Für die Schnittpunktabfrage zwischen Strahl-Kugel/Dreieck/... nutze ich eine Bounding Intervall Hierachy https://en.wikipedia.org/wiki/Bounding_interval_hierarchy
Für die Photonmap ein KD-Baum.
Für die Beammap(Line-Line-Intersection) (Dort sind meine Godyrays) ein Reguläres Grid.
Ansonsten versende ich die Photonen bei der Lichtquelle nur in die Richtungen/Bereiche, wo auch was ist und verwende Importancesampling für Lighrareasampling und Brdf-Sampling.
Was mir bei C#-Projekten zumindest aufgefallen ist, dass sie mehr Hauptspeicher als C++ brauchen. Bei der Geschwindigkeit wäre ich auch nicht überrascht, wenn es spürbar langsamer ist.
Ich kann ja mal ein von den Renderverfahren nehmen und ihn einmal als C++ und einmal als C#-Projekt schreiben und versuchen beide Projekte vom Aufbau her möglichst gleich zu machen. Dann ist nur noch die Programmiersprache der Unterschied und dann sehe ich ja, wer schneller ist.
-
BIH sind nicht ganz so toll (wenn sie auch viel gehypt wurden).
Fuer die beste performance solltest du etwas nutzen was den raum wirklich separiert (ohne ueberlappung) und adaptiv ist (also nicht sowas wie octree).z.B. kdtree oder bounding boxes die auch mit einer ebene den raum teilen.
der grund dass diese besser performen ist, dass du aufhoeren kannst du tracen, sobald du die erste node mit einem hit hast.
Das ist quasi dieselbe optimierung wie witty-raytracer bei schatten verwenden, wo das tracen abgebrochen wird beim ersten hit, weil dann klar ist dass die lichtquelle nicht erreicht wird.
bei split hierarchien ist auch klar, dass jede weitere 'node' weiter entfernt sein muss als die erste mit einem hit, weil nichts ueberlappt und du von vorne nach hinten traversierst.BIH auf der anderen seite, wenn du viele grosse ueberlappende polys hast (z.b. eben fussboden, waende und decke), hast du eventuell ein paar hierarchiestufen garkeinen gewinn durch die hierarchie, weil die ueberlappung bei 100% liegen kann.
http://www.flipcode.com/archives/Raytracing_Topics_Techniques-Part_7_Kd-Trees_and_More_Speed.shtml
-
Der Link ist ne gute Idee, um Geschwindigkeit rauszuholen.
Ich habe die Funktion für die Schnittpunktabfrage zwischen Strahl und Dreieck mal testweise ersetzt.
Renderzeit meine Funktion: 52 Sekunden
Renderzeit Jacco Bikkers Funktion: Schwankt zwischen 48 und 51 SekundenEine kleine Verbesserung ist es ja zumindest. Mal sehen was sein KD-Baum bringt.
@Update:
Es gibt mindestens zwei Varianten wie man ein KD-Baum traversieren kann. Entweder rekursiv oder man hat eine eigene Stack-Variable, wo man dann ohne Rekursion auskommt. Diese Information habe ich von hier http://dcgi.felk.cvut.cz/home/havran/ARTICLES/cgf2011.pdf
Der Link, den du mir geschickt hast, implementiert die Stack-Variante aber er war nicht ganz so leicht zu lesen für mich. Ich habe dafür aber eine Rekursive Variante hier gefunden:
http://www.socher.org/uploads/Main/RenderingCompetitionSocherGranados/src/Kdtree.h
http://www.socher.org/uploads/Main/RenderingCompetitionSocherGranados/src/SAHKdtree.hDiesen Algorithmus habe ich nach C# übersetzt und lediglich die Funktion zum Clippen des Querry-Rays mit der Szenen-Bounding-Box habe ich von den Jacco Bikker genommen.
D.h. ich habe jetzt ein Rekursiven KD-Baum mit SAH-Splitplane-Berechnung, welchen ich vielleicht noch auf Iterativ oder weiteres erweitern kann. Ich werde dazu diesen Link hier: http://www.sci.utah.edu/~wald/PhD/wald_phd.pdf weiter durcharbeiten, da dass anscheinend eine gute Quelle ist, wo viele drauf verweisen.
Mein Bounding Intervall Hierachie mit Median-Splitplanes hat 18 Sekunden zum Rendern (+ Erstellung der BIH)gebraucht
Der Rekursive KD-Baum mit SAH-Splitplane hat 10 Sekunden zum Rendern(+ Erstellung des Baumes) gebraucht.
Es war etwas mühsam, da lauter so kleine fiese Fehler mir beim Übersetzen passiert sind aber es hat sich gelohnt. Der Renderzeit ist jetzt schon mit der 'einfachen' Variante besser.
-
auf 50% bei einfachen szenen ist schonmal nicht schlecht. wobei es gerade bei komplexen szenen ist wo die guten algorithmen glaenzen.
rekursive und iterativ ist logisch recht gleich.
beim rekursiven springst du fuer den "nahen" knoten in die in die funktion, dabei legt der compiler fuer dich den anderen knoten auf den stack.
beim iterativen legst du den anderen knoten auf deinen stack und verwendest den "nahen" knoten als hauptknoten weiter.wenn du nun einen schnittpunkt findest, musst du bei der rekursiven variante zig hiearchystufen den stack hoch und jedesmal pruefst du "schon fertig? ach, na dann return".
beim iterativen springst du einfach einmal raus (return) und bist fertig. das kann sehr viel zeit sparen.zudem ist der stack ein wenig reddundant, denn das program weiss nicht 100% was du brauchst, sichert also eventuell sehr viel mehr daten und uebergibt gleichzeitig deine schon z.b. in registern existierenden daten an die aufzurufende funktion. das ist uU viel hin und her ohne wirklich nuetzliches zu machen.
iterativ koennte dir weitere 10%-20% bringen, denke ich. und an sich ist was sehr einfach.
stattbool Trace(Ray,Node) { RayLeft,RayRight; if(Node->IntersectTriangles(Ray)) return true; if(Node->NoChildren()) return false; Node->split(Ray,RayLeft,RayRight); NodeLeft = Node->Left(); NodeRight= Node->Right(); if(RayLeft.Distance(Ray)>RayRight.Distance(Ray)) { swap(RayLeft,RayRight); swap(NodeLeft,NodeRight); } if(Trace(RayLeft,NodeLeft)) return true; return Trace(RayRight,NodeRight); }
machst du
bool Trace(Ray,Node) { //das kann man anders machen, aber erstmal den ray auf den stack zu schieben ist verstaendlicher NodeStack; RayStack; NodeStack.push(Node); RaySTack.push(Ray); while(NodeStack.size()>0) { Node = NodeStack.pop();//hier holen wir immer was noch abzuarbeiten ist Ray = RayStack.pop(); while(true) { if(Node->IntersectTriangles(Ray)) return true; //beim treffer ist uns der stack egal, wir springen sofort ganz raus if(Node->NoChildren()) break; // statt return false;, weil wir uns jetzt was vom stack holen muessen //bleibt gleich RayLeft,RayRight; Node->split(Ray,RayLeft,RayRight); NodeLeft = Node->Left(); NodeRight= Node->Right(); if(RayLeft.Distance(Ray)>RayRight.Distance(Ray)) { swap(RayLeft,RayRight); swap(NodeLeft,NodeRight); } //der unterschied NodeStack.push(NodeRight); // <- die seite die wir spaeter rekursiv abarbeiten wuerden RaySTack.push(RayRight); // schieben wir auf unseren stack Ray = RayLeft; // die die wir zuerst abarbeiten wuerden Node = NodeLeft; // bestimmen wir als die aktuelle }//endlos while }//stack loop return false; }
-
Ich versuche gerade den iterativen Ansatz umzusetzen und ich komme mit einer Abbruchbedingung nicht klar.
http://www.sci.utah.edu/~wald/PhD/wald_phd.pdf Seite 121 (107 Oben Rechts)
Rekursiver Ansatz:
// case three: traverse both sides in turn t_hit = RecTraverse(FrontSideSon(node),t_near,d); if (t_hit <= d) return t_hit; // early ray termination return RecTraverse(BackSideSon(node),d,t_far);
Gleiche Seite Iterativer Ansatz:
// have a leaf now IntersectAllTrianglesInLeaf(node); if (t_far <= ray.t_closesthit) return; // early ray termination
Beim rekursiven Ansatz steht das hier
if (t_hit <= d) return t_hit;
was ich noch verstehe. Wenn ich im vorderen Blattknoten was finde, dann brauche ich den hinteren Blattknoten nicht auch noch kontrollieren, da dort der Schnittpunkt ja weiter weg sein muss.
Beim iterativen Ansatz steht aber das hier:
if (t_far <= ray.t_closesthit)
Wenn ich mich in ein Blattknoten befinde und t_far zeigt auf die hintere Ebene von diesen Knoten, warum gilt es dann Abbruch, wenn mein Schnittpunkt hinter der t_far-Linie liegt?
Wenn man
if (t_far <= ray.t_closesthit)
durch
if (ray.t_closesthit < t_far)
ersetzten würde, dann macht es für mich mehr Sinn. t_far ist die Splitplane vom Elternknoten.
Ich habe diese Ersetzung auch gemacht und komme mit dem iterativen Ansatz jetzt auf 12 Sekunden. Wenn ich diese Ersetzung nicht mache, dann brauche ich nur 2 Sekunden und das Bild ist fehlerhaft^^
Ich kann mir so schwer vorstellen, dass Ingo Wald da ein Fehler gemacht haben kann. Der Fehler muss bei mir liegen. Kann mir jemand sagen, warum iterativ langsamer ist, wenn ich es mit meiner Modifikation mache und warum fast alle Dreiecke im Bild fehlen, wenn ich es mit Ingo Walds Weg mache?
Hier ist meine Funktion nochmal im Ganzen:
ISchnittpunkt GetSchnittpunktStrahlKDBaumIterativ(Strahl ray, IIntersecableObject excludedObjekt, float time) { IntersectionPoint point = new IntersectionPoint(); float t_min, t_max; if (ClipRayWithBoundingBox(ray, this.topBox, out t_min, out t_max) == false) return null; Node node = this.root; KdStack stack = new KdStack(); while (true) { // traverse ’til next leaf while (node is InnerNode) { SplitPlane p = (node as InnerNode).p; float t_split = (p.pe - ray.Start[p.pk]) * (ray.Richtung[p.pk] == 0 ? float.MaxValue : 1.0f / ray.Richtung[p.pk]); // near is the side containing the origin of the ray Node near, far; if (ray.Start[p.pk] < p.pe) { near = (node as InnerNode).leftChild; far = (node as InnerNode).rightChild; } else { near = (node as InnerNode).rightChild; far = (node as InnerNode).leftChild; } if (t_split > t_max || t_split < 0) { node = near; } else if (t_split < t_min) { node = far; } else { stack.Push(far, t_split, t_max); node = near; t_max = t_split; } } // have a leaf now (node as LeafNode).traverse(ray, time, excludedObjekt, t_min, t_max, point); //if (t_max <= point.Distance) return point.Point; //early ray termination (Ingo Wald) if (point.Distance < t_max) return point.Point; //early ray termination (XMAMan) if (stack.IsEmpty()) return point.Point; // noting else to traverse any more... node = stack.Pop(out t_min, out t_max); } }
-
ja, das
if (t_far <= ray.t_closesthit)
schaut wie ein bug aus. zudem fehlt
t_far=d;
am ende vom inner loop. (kannst ja mal profilen wieviel boost das bringt
an sich sollte dein code erstmal laufen.