Simples View Frustum Culling

Einstiegsfragen, Mathematik, Physik, künstliche Intelligenz, Engine Design
Antworten
Slin
Beiträge: 44
Registriert: 15.05.2010, 01:21
Echter Name: Nils Daumann
Wohnort: Lübeck
Kontaktdaten:

Simples View Frustum Culling

Beitrag von Slin »

Hallo,
ich versuche gerade einfaches View Frustum Culling zu realisieren. Irgendeine Kleinigkeit scheine ich aber zu übersehen.
Die Idee hinter meinem Ansatz ist, dass jedes Objekt eine Kugel zugewiesen bekommt, dessen Zentrum der Mittelwert der Vertexpositionen (lokal zum Objekt Mittelpunkt) des Objektes ist und dessen Radius, der Abstand zum am weitesten entfernten Vertex ist.
Das sieht bei mir dann so aus:

Code: Alles auswählen

void sgObject::initCullSphere()
{
	sgVector3 center;
	float radius = 0;
	float temp;
	sgVector3 diff;
	for(int m = 0; m < meshs.size(); m++)
	{
		//Calculate the center
		for(int i = 0; i < meshs[m]->vertexnum; i++)
		{
			center += meshs[m]->vertices[i].position;
		}
		center /= meshs[m]->vertexnum;
		
		//Find the radius
		radius = 0;
		for(int i = 0; i < meshs[m]->vertexnum; i++)
		{
			diff = meshs[m]->vertices[i].position-center;
			temp = diff.length();
			if(radius < temp)
				radius = temp;
			
		}
		meshs[m]->cullsphere = sgVector4(center.x, center.y, center.z, radius);
	}
}
Nun transformiere ich diese Kugel mit der Objekttransformationsmatrix in mein globales Koordinatensystem. (Der Radius wird dabei im Moment noch ignoriert, was aber irrelevant sein sollte da ich auch nichts skaliere.)
Die nächste Transformation bringt den Kugelmittelpunkt in den Raum der Kamera und anschließend multiplitziere ich noch mit meiner Projektionsmatrix. Nach meinem Verständnis, ist der Mittelpunkt der Kugel nun immer dann sichtbar, wenn w positiv (und < clipfar) ist und x und y zwischen -1 und 1 sind. Soweit scheint auch alles zu klappen. Nun muss aber natürlich auch der Radius noch mit einfließen und dazu muss ich auch diesen noch irgendwie projizieren, so dass ich die 1 - -1 Grenzen um diesen erweitern kann. Dazu habe ich mir überlegt, dass ich zwei Positionen aus dem radius relativ zur Kamera konstruiere, die ich dann projiziere. Diese sind (radius|0|mittelpunkt.w|1) und (0|radius|mittelpunkt.w|1). Hier pflücke ich mir dann x, bzw y heraus und erweitere meine Grenzen damit. Das Ergebniss ist dann aber dass alles was den Rand schneidet geculled wird, wenn ich den Einfluss vom Radius bei den Punkten invertiere passt es dann besser, was ich aber auch irgendwie komisch finde.
Im Code sieht dies derzeit so aus:

Code: Alles auswählen

void sgRenderer::culling(sgCamera *cam)
{
	sgMatrix4x4 projview = cam->matproj*cam->matview;
	
	sgMatrix4x4 projviewmodel;
	sgVector4 pos;
	sgVector4 temp;
	sgVector2 radius;
	for(sgObject *obj = first_solid->next; obj != NULL; obj = obj->next)
	{
		obj->culled = false;
		for(int i = 0; i < obj->meshs.size(); i++)
		{
			//Transformation and projection
			pos = obj->meshs[i]->cullsphere;
			projviewmodel = projview*obj->matmodel;
			pos.w = 1.0;
			pos = projviewmodel*pos;
			pos.x /= pos.w;
			pos.y /= pos.w;
			
			//Projection of the radius
			temp = sgVector4(-obj->meshs[i]->cullsphere.w, 0.0, pos.w, 1.0);
			temp = cam->matproj*temp;
			radius.x = temp.x/temp.w;
			temp = sgVector4(0.0, -obj->meshs[i]->cullsphere.w, pos.w, 1.0);
			temp = cam->matproj*temp;
			radius.y = temp.y/temp.w;
			
			//Check for visibility
			if((pos.x < 1.0+radius.x && pos.x > -1.0-radius.x && pos.y < 1.0+radius.y && pos.y > -1.0-radius.y && pos.w > 0) || (abs(pos.w) <= obj->meshs[i]->cullsphere.w))
				obj->meshs[i]->culled = false;
			else
				obj->meshs[i]->culled = true;
		}
	}
}
Das Problem ist nun, dass es zwar im prinzip super klappt, nur blöderweise wird zu viel geculled. Und mir will einfach nicht klar werden, warum.

Meine Projektionsmatrix sieht übrigens so aus:

Code: Alles auswählen

void sgMatrix4x4::makeProjectionPersp(float arc, float aspect, float clipnear, float clipfar)
{
	memset(mat, 0, sizeof(float)*16);
	float xFac, yFac;
	yFac = tanf(arc * 3.14f/360);
	xFac = yFac*aspect;

	mat[0] = 1/xFac;
	mat[5] = 1/yFac;
	mat[10] = -(clipfar+clipnear)/(clipfar-clipnear);
	mat[11] = -1;
	mat[14] = -(2*clipfar*clipnear)/(clipfar-clipnear);
}
Ich würde mich über jede Hilfe sehr freuen :-).
Nils
Despotist
Establishment
Beiträge: 394
Registriert: 19.02.2008, 16:33

Re: Simples View Frustum Culling

Beitrag von Despotist »

Ich denke du machst das ganze viel zu kompliziert. Warum nicht einfach dem View Frustum auch eine Sphere verpassen und dann einen einfachen Sphere-Sphere Test machen? So kannst du in großen Welten mit sehr geringem Rechenaufwand einen Großteil der Objekte ausschließen. Alle in der Kugel renderts du einfach wobei da natürlich ein paar Objekte zu viel renderst die zwischen Kugel und Frustum-Pyramide sind aber dafür geht der Rest schnell.
Es gab da glaub ich mal im Forum eine Grafik dazu dem Frustum eine minimale Sphere zu verpassen.
Slin
Beiträge: 44
Registriert: 15.05.2010, 01:21
Echter Name: Nils Daumann
Wohnort: Lübeck
Kontaktdaten:

Re: Simples View Frustum Culling

Beitrag von Slin »

Danke für deine Antwort, allerdings erscheint mir eine Kugel für das Viewfrustum zu ungünstig. Zumal bei mir derzeit deutlich die hohe Polygonzahl das Problem ist, wärend die CPU noch ein paar Reserven hat. Abgesehen davon lässt sich mein, deiner meinung nach umständlicher Ansatz ja auch durchaus noch etwas optimieren, wobei ich dazu ersteinmal zu faul bin.
Ich hab es jetzt ersteinmal einfach mit einem recht willkürlich gewählten Faktor von 1.1 vorm Radius gelöst. Ich finds nicht schön, aber das kann ich später auch immernoch ändern.
Despotist
Establishment
Beiträge: 394
Registriert: 19.02.2008, 16:33

Re: Simples View Frustum Culling

Beitrag von Despotist »

Slin hat geschrieben: allerdings erscheint mir eine Kugel für das Viewfrustum zu ungünstig.
Du kannst doch erst mal grob testen Kugel gegen Kugel und wenn das Objekt in der Nähe der Kugel liegt lässt du deinen Detailtest laufen. Eine Abstandsberechnung und ein Vergleich mit dem quadrierten Durchmesser der Frustum-Kugel (damit du nicht ständig Wurzel ziehen musst) ist nunmal viel schneller als deine ganzen Transformationen und Test gegen 6 Seiten.
Es geht auch nicht darum ob die CPU noch Reserven hat. Vielleicht willst du ja noch andere Features einbauen. Und es kann auch gut sein dass eine Entlastung da einen kleinen Gewinn bei deinen Polygonen bringt weil die Gesamtlast sinkt. Davon abgesehen ist dieser Test viel leichter zu implementieren als deiner. Ich sehe also nichts was dagegen spricht ihn zu verwenden.
Und wie gesagt würde ich mal benchmarken ob der detaillierte Test innerhalb der Frustumkugel noch was bringt oder ob der Rechenaufwand dafür höher ist als die paar Modelle mit zu zeichnen und von der Graka aussortieren zu lassen.
Benutzeravatar
B.G.Michi
Establishment
Beiträge: 163
Registriert: 07.03.2006, 20:38
Alter Benutzername: B.G.Michi
Kontaktdaten:

Re: Simples View Frustum Culling

Beitrag von B.G.Michi »

Wenn ich das jetzt auf die schnelle richtig verstanden habe, berechnest du das aritmetische (das war doch das Wort?) Mittel der Positionen...
Stell dir vor du hast z.b. ne Schaufel mit 1000 Vertices für die Kelle und 10 am obenen Stielende: dann liegt mit deiner Berechnung der Mittelwert zu weit bei der Kelle
versuch mal sowas:

Code: Alles auswählen

vector3d max(-flt_max, -flt_max, -flt_max), min(flt_max, flt_max, flt_max);
for(int i = 0; i < numvertices; i++)
{
    if(v[i].pos.x < min.x) min.x = v[i].pos.x;
    if(v[i].pos.x > max.x) max.x = v[i].pos.x;
    // das gleiche für y und z
}
vector3d center = (min + max) / 2.0f;
also im Endeffekt den Mittelpunkt der AABB, ob du dann gegen ein ViewFrustum testest oder gegen eine Kugel um dieses bleibt dir überlassen...
hoffe ich konnte helfen
JFF_B.G.Michi

edit: wenn ich mir das grad nochmal durchlese glaube ich nicht, dass das dein Problem ist... so werden die Kugeln kleiner und somit müsste mehr geculled werden und es wird ja anscheinend eh zu viel geculled... vlt hilfts trotzdem
joeydee
Establishment
Beiträge: 1044
Registriert: 23.04.2003, 15:29
Kontaktdaten:

Re: Simples View Frustum Culling

Beitrag von joeydee »

Für mich schwer durchzublicken was du da wirklich machst... z.B. was mittelpunkt.w an der z-Position sucht, wenn du den Punkt nochmal projizierst.
Ich vermute, du versuchst etwa folgendes: du willst 3 Punkte im Screenspace berechnen (also fertig projiziert, und zwar Mittelpunkt sowie zwei auf dem projizierten Kreis entlang Screen-X und Screen-Y) und hoffst, wenn keiner davon sichtbar ist, ist die zugehörige Kugel auch nicht sichtbar? (An dieser Stelle kurz zur .w-Sache: wenn du den Mittelpunkt projiziert hast, musst du eigentlich nur noch radius*mittelpunkt.w rechnen und hast den x- bzw. y-Abstand vom projizierten Mittelpunkt im Screenspace den du suchst, sprich den passend skalierten Radius auf dem Bildschirm.)

Falls ich deine Idee richtig gedeutet habe, da funktionieren 2 Dinge grundsätzlich nicht:
1. Ein Kreis bei -10/-10 mit den Punkten 100/-10 für x und -10/100 für y liegt z.B. mit all diesen Punkten außerhalb des Bildschirms, wäre aber trotzdem sichtbar.
2. Eine projizierte Kugel ist nicht deckungsgleich mit einem Screen-Kreis um den projizierten Mittelpunkt, sondern es handelt sich bei der Kugel um eine gedrehte Ellipse.

Mein Tip für die Fehlersuche: lass dir eine "echte" Kugel an die Stelle projizieren, sowie deine berechneten Punkte und den berechneten Kreis, alles ohne Culling und in einem verkleinerten Frustum, damit du auch ein paar Sachen außerhalb siehst. Dann wirst du eher erkennen was da nicht stimmt.

Mein Tip für eine bessere Variante: wie genannt zuerst Boundingspheres gegen die Frustumkugel, dann Boundingspheres gegen die Frustumebenen. Das sind jeweils sehr einfache Berechnungen. Mache nichts im Screenspace, da wird es zu viele Extremfälle geben.
Despotist
Establishment
Beiträge: 394
Registriert: 19.02.2008, 16:33

Re: Simples View Frustum Culling

Beitrag von Despotist »

Slin
Beiträge: 44
Registriert: 15.05.2010, 01:21
Echter Name: Nils Daumann
Wohnort: Lübeck
Kontaktdaten:

Re: Simples View Frustum Culling

Beitrag von Slin »

Despotist, erst auf Kugel und dann genauer zu überprüfen ist eine Idee die mir irgendwie noch garnicht in den Sinn gekommen ist, die ich aber wohl tatsächlich umsetzen werde.
B.G. Michi, da hast du wohl recht und ich meinen Code angepasst... .
Joeydee, meine Grundüberlegung war, dass ich mit meinen Matrix und Vektorklassen sehr einfach einen Punkt auf Sichtbarkeit überprüfen kann in dem ich ihn auf 2D projiziere. Und dann musste ich eben noch irgendwie den blöden Radius da reinfummeln. Gegen einen Test gegen die ebenen hatte ich mich eigentlich entschieden, da mir die Konstruktion dieser recht aufwändig vorgekommen ist und ich ersteinmal schnelle Ergebnisse wollte. Aber warscheinlich ist das wirklich der solideste Ansatz.

Danke für all die nützlichen Antworten!
Antworten