About Monkey 2 › Forums › Monkey 2 Programming Help › Frustum Culling – Is this ok?
This topic contains 1 reply, has 2 voices, and was last updated by
cocon 9 months, 1 week ago.
-
AuthorPosts
-
July 9, 2018 at 2:20 am #15011
I was not really able to understand how frustum culling works with the way I keep finding it described or coded online. They use a AABB system and I really do not understand this yet.
My own take on it right now is to use a point in triangle approach and find visible meshes using that way. I coded this test to see if it would work and it seems to do.
Am I right that this is usable also?
Monkey12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697#Import "<std>"#Import "<mojo>"' Frustum Culling' Every tile would be a mesh. The frustum culling would' find what is visible in the camera and what not would not' be drawn.'Using std..Using mojo..Class MyWindow Extends Window' Our camera angleField angle:FloatMethod New()End methodMethod OnRender( canvas:Canvas ) OverrideApp.RequestRender() ' Activate this method' Our base coordinates' and left side and right side coordinates' of triangleLocal b:Vec2iLocal l:Vec2iLocal r:Vec2i' base is the center of the screenb.X = 320b.Y = 240' left and right side coordinatesl.X=b.X+(Cos(angle-Pi/4)*250)l.Y=b.Y+(Sin(angle-Pi/4)*250)r.X=b.X+(Cos(angle+Pi/4)*250)r.Y=b.Y+(Sin(angle+Pi/4)*250)' Draw our frustrum linescanvas.DrawLine(b.X,b.Y,l.X,l.Y)canvas.DrawLine(b.X,b.Y,r.X,r.Y)' Here we draw our map. The green tiles' would be visible and the red ones would' not be visible'For Local y:Int=0 Until 20For Local x:Int=0 Until 20If pointintriangle2d(x*32,y*32,b.X,b.Y,l.X,l.Y,r.X,r.Y)canvas.Color = Color.GreenElsecanvas.Color = Color.RedEnd ifcanvas.DrawRect(x*32,y*32,32,32)NextNext' Move the camera angleIf Keyboard.KeyDown(Key.Left) Then angle-=.1If Keyboard.KeyDown(Key.Right) Then angle+=.1canvas.Color = Color.Whitecanvas.DrawText("Use cursor left and right to move camera angle.",0,0)' if key escape then quitIf Keyboard.KeyReleased(Key.Escape) Then App.Terminate()End MethodEnd Class' Here is the point in triangle collision function' by RaGR (Ralph G. Roeske). from blitzbasic.com' px,py = pixel x and y to test collision with' x1,y1,x2,y2,x3,y3 = The triangle pointsFunction pointintriangle2d:bool(px:Float,pz:Float, x1:Float,y1:Float,x2:Float,y2:Float,x3:Float,y3:Float)Local bc:Float,ca:Float,ab:Float,ap:Float,bp:Float,cp:Float,abc:Floatbc = x2*y3 - y2*x3ca = x3*y1 - y3*x1ab = x1*y2 - y1*x2ap = x1*pz - y1*pxbp = x2*pz - y2*pxcp = x3*pz - y3*pxabc = Sgn(bc + ca + ab)If (abc*(bc-bp+cp)>=0) And (abc*(ca-cp+ap)>=0) And (abc*(ab-ap+bp)>=0) Return TrueReturn FalseEnd FunctionFunction Main()New AppInstanceNew MyWindowApp.Run()End FunctionJuly 9, 2018 at 1:07 pm #15014Frustum culling is based on simple collision detection. The math in it are only the means to calculate the shape’s volume and then perform collision detection tests and figure if a point is inside this volume.
Your implementation in terms of frustum culling is right, also you take it to the next level which is about hierarchical organization of the scene. Here you use spaces of fixed size which looks like a grid.
One idea for using space partitioning:
12345678910111213141516171819202122232425262728Without space partitioning:your scene would look like this:sceneobjects = [1,2,3,4,5,6,7,8,9,10]every object is placed in a linear fashionand there's no degree of scene organization.if you want to find which objects are inside the frustumyou would have to waste 10 iterationsWith space partitioning:your scene would look like this:sceneobjects =["A" : [1,2] ,"B" : [3,4,5] ,"C" : [6,7,8] ,"D" : [9,10] ,]now if you imagine that the frustum hits spaces A + Byou would simply iterate these two spaces and collectthe objects found in them.This way of splitting up the scene would work fine in simple terms, I would not see something wrong about it. Most of the gaming industry has settled to nested space partions like binary/quadtree/octree. It means basically that a space contains many other spaces (in terms of hierarchical organization) – resulting to an actual tree structure. Each one is supposed to be an alternative way of managing the data structures. For example imagine that you would travel in a desert (which the environment is in the majority much empty) and then suddenly you would enter a small cave where it has lots of stuff in it. This would be a good case of using nested space partition.
Here is an example of space partitioning:
Java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345/* OpenProcessing Tweak of *@*http://www.openprocessing.org/sketch/10796*@* *//* !do not delete the line above, required for linking your tweak if you upload again */// Quadtree containers demo// Click to make a point. As soon as more than binSize points are// inside of a quad, it subdivides.// Right-click to resetfinal static float dotSize = 2;final static int binSize = 5;final static color[] colors = new color[4];Box b;FuzzyBin q;DLA dla1;void setup() {size(600, 600);frameRate(30);//smooth();colors[0] = color(127,0,0);colors[1] = color(0,127,0);colors[2] = color(0,0,127);colors[3] = color(127,127,0);b = new Box(1, 1, width, height);q = new FuzzyBin(b, dotSize);dla1 = new DLA(q, binSize);dla1.addPoint(width/2, height/2);}void draw() {q.drawMe();fill(0,0,0,0);if (mousePressed) {if (mouseButton == LEFT) {dla1.addPoint(mouseX, mouseY);ellipse(mouseX, mouseY, 4*dotSize, 4*dotSize);} else if (mouseButton == RIGHT) {q = new FuzzyBin(b, dotSize);dla1 = new DLA(q, binSize);background(255);}}stroke(0);}class Box {float Xl, Yl;float Xh, Yh;final static float zero = 1e-20;Box(float X_1, float Y_1, float X_2, float Y_2) {// Orient so (Xl,Yl) and (Xh,Yh) are min/max extent, respectivelyXl = min(X_1, X_2);Xh = max(X_1, X_2);Yl = min(Y_1, Y_2);Yh = max(Y_1, Y_2);}boolean pointInside(float x, float y) {return x > Xl && x < Xh && y > Yl && y < Yh;}// Compute the intersection of the given ray (where x=R0x+Rdx*t and// y=R0y+Rdy*t) with this box; return two t values, Tnear and Tfar,// which give the entry and exit values of t (> 0)float[] intersectRay(float R0x, float R0y, float Rdx, float Rdy) {float Tnear = -1e30;float Tfar = 1e30;// First, check slab in X.if (abs(Rdx) < zero) {// Ray is parallel to X, but starts outside. Fail.if (R0x < Xl || R0x > Xh) {return null;}} else {float Ta = (Xl-R0x)/Rdx, Tb = (Xh-R0x)/Rdx;float T1 = min(Ta,Tb);float T2 = max(Ta,Tb);if (T1 > Tnear) Tnear = T1;if (T2 < Tfar) Tfar = T2;if (Tnear > Tfar) return null;if (Tfar < 0) return null;}// Then check slab in Y.if (abs(Rdy) < zero) {// Ray is parallel to X, but starts outside. Fail.if (R0y < Yl || R0y > Yh) {return null;}} else {float Ta = (Yl-R0y)/Rdy, Tb = (Yh-R0y)/Rdy;float T1 = min(Ta,Tb);float T2 = max(Ta,Tb);if (T1 > Tnear) Tnear = T1;if (T2 < Tfar) Tfar = T2;if (Tnear > Tfar) return null;if (Tfar < 0) return null;}// If we have survived this far, the test passed.return new float[] {Tnear, Tfar};}// Divide this box down into 4 quadrantsBox[] quarter() {return quarter(0);}// d = boundary "fuzziness" sizeBox[] quarter(float d) {float Xm = 0.5*(Xl+Xh);float Ym = 0.5*(Yl+Yh);Box[] quads = new Box[4];quads[0] = new Box(Xm-d, Yh, Xh, Ym-d);quads[1] = new Box(Xl, Yh, Xm+d, Ym-d);quads[2] = new Box(Xl, Yl, Xm+d, Ym+d);quads[3] = new Box(Xm-d, Ym+d, Xh, Yl);return quads;}void drawMe() {rect(Xl, Yl, Xh-Xl, Yh-Yl);}}// QuadTree class. Subdivide it at will.class QuadTree {Box b;QuadTree[] children;int level;static final int maxLevel = 8;QuadTree(Box b) {this.b = b;children = null;level = 0;}QuadTree(Box b, int level) {this.b = b;children = null;this.level = level;}// Divide into 4 smaller quadtrees.// If this exceeds the recursion limit, it will just return null.QuadTree[] divide() {if (children == null && level < maxLevel) {children = new QuadTree[4];Box[] b2 = b.quarter();for(int i = 0; i < 4; ++i) {children[i] = new QuadTree(b2[i], level + 1);}}return children;}// Draw - recursively - the entire quadtree.void drawMe() {b.drawMe();if (children == null) return;for(int i = 0; i < 4; ++i) {stroke(colors[i]);children[i].drawMe();}}boolean isDivided() {return children != null;}// Find the smallest of the child quadtrees which contains the given point.// If none are found, return null. This won't subdivide any further.QuadTree getSmallestIntersect(float x, float y) {if (children == null) {if (b.pointInside(x,y)) {return this;}else {return null;}}for(int i = 0; i < 4; ++i) {QuadTree q = children[i].getSmallestIntersect(x, y);if (q != null) {return q;}}return null;}}// Basically QuadTree class, but with fuzzy boundaries.class FuzzyQuadTree extends QuadTree {float delta;FuzzyQuadTree(Box b, float delta) {super(b);this.delta = delta;}FuzzyQuadTree(Box b, int level, float delta) {super(b, level);this.delta = delta;}// Divide into 4 smaller quadtrees.// If this exceeds the recursion limit, it will just return null.QuadTree[] divide() {if (children == null && level < maxLevel) {children = new QuadTree[4];Box[] b2 = b.quarter(delta);for(int i = 0; i < 4; ++i) {children[i] = (QuadTree) new FuzzyQuadTree(b2[i], level + 1, delta);}}return children;}}// A FuzzyQuadTree that can contain points in each quad.class FuzzyBin extends FuzzyQuadTree {ArrayList x_points = new ArrayList();ArrayList y_points = new ArrayList();FuzzyBin(Box b, float delta) {super(b, delta);}FuzzyBin(Box b, int level, float delta) {super(b, level, delta);}// Add a point to this quad if it's inside, _and_ to any other child// quads that contain it. Return the undivided child quads which// contains it.// If this point is outside of the quad, return null.ArrayList addPoint(float x, float y) {ArrayList tips = new ArrayList();if (b.pointInside(x, y)) {x_points.add(x);y_points.add(y);// We've hit an undivided quad; we're done.if (children == null) {tips.add(this);return tips;}for(int i = 0; i < children.length; ++i) {FuzzyBin c = (FuzzyBin) children[i];ArrayList tips_new = c.addPoint(x, y);tips.addAll(tips_new);}}return tips;}int countPoints() {return x_points.size();}// Divide into 4 smaller quadtrees.// If this exceeds the recursion limit, it will just return null.// Any points contained are passed on.QuadTree[] divide() {if (children == null && level < maxLevel) {children = new QuadTree[4];Box[] b2 = b.quarter(delta);for(int i = 0; i < 4; ++i) {children[i] = (QuadTree) new FuzzyBin(b2[i], level + 1, delta);for(int j = 0; j < x_points.size(); ++j) {FuzzyBin f = (FuzzyBin) children[i];// Note that addPoint will only add it if it actually belongs.f.addPoint((Float)x_points.get(j), (Float)y_points.get(j));}}}return children;}// Return an ArrayList of undivided quads (they'll be FuzzyBin objects)// which the given ray passes through and which are not empty (that is,// they contain some points inside that this ray could conceivably hit).ArrayList intersectRay(float r0x, float r0y, float rdx, float rdy) {ArrayList bins = new ArrayList();// If this quad is empty, we're done.if (countPoints() == 0) return bins;// This quad has stuff in it. Do we intersect it?float[] hit = b.intersectRay(r0x, r0y, rdx, rdy);// If we don't hit it, we're done.if (hit == null) return bins;// Does it have children we can hit?// (That doesn't sound right, does it.)if (children == null) {// If not, we're done _and_ we have a result.bins.add(this);}else {// Otherwise, recursively see which children this ray hits.for(int i = 0; i < children.length; ++i) {FuzzyBin f = (FuzzyBin) children[i];ArrayList bins_new = f.intersectRay(r0x, r0y, rdx, rdy);bins.addAll(bins_new);}}return bins;}// This function could be made much more clever be looking at the actual// entry and exit points (which Box.intersectRay returns) since there are// a lot of cases where it's obvious that it is impossible for an intersection// to occur, but I don't feel like applying this.}class DLA {FuzzyBin bin;int binSize;DLA(FuzzyBin bin) {this.bin = bin;binSize = 10;}DLA(FuzzyBin bin, int binSize) {this.bin = bin;this.binSize = binSize;}void addPoint(float x, float y) {// Two things happen here:// (1) We add the point to the bin.// (2) We make sure that every undivided quad contains at most// binSize points inside (i.e. we divide some if needed).ArrayList f = bin.addPoint(x, y);for(int i = 0; i < f.size(); ++i) {FuzzyBin bin = (FuzzyBin) f.get(i);if (bin.countPoints() > binSize) {bin.divide();// Uncomment this to see them as they are added.//bin.drawMe();}}}}You can look at the OGRE source to see the Octree implementation. In Doom/Quake you can see the BSP implementation.
-
AuthorPosts
You must be logged in to reply to this topic.