Write in front
In the previous series of light painting, the effect of solid geometry, reflection, refraction, etc. is realized, but the biggest flaw is that the complexity is too high. When the sample is 1024, the rendering time goes straight up (with 4 threads), so that a couple of hours can complete a piece of work, which is too slow. However, when I saw this article in C + + painting, I had some ideas.
I think of the "Game frame series" Simple Graphics (a) series of ideas in the article, ah, why use SDF to slowly approaching it? Wouldn't it be faster to use a ready-made analytic geometry algorithm?
After some groping, finally have a map.
The place to pay attention to
The algorithm for detecting the intersection of circles and lines, I use JavaScript to play computer graphics (a) Ray tracing primer-Milo Yip-Blog Park copy:
Intersect:function(Ray) {varv = ray.origin.subtract ( This. Center);vara0 = V.sqrlength ()- This. Sqrradius;varDdotv = Ray.direction.dot (v);if(Ddotv <= 0) {varDISCR = Ddotv * DDOTV-A0;if(DISCR >= 0) {varresult =NewIntersectresult (); Result.geometry = This; Result.distance =-ddotv-Math. sqrt (DISCR); Result.position = Ray.getpoint (result.distance); Result.normal = Result.position.subtract ( This. Center). normalize ();returnResult } }returnIntersectresult.nohit; }
But here's the difference: Ray tracing in 3D has a visual angle, that is, the light comes from the same point, the location of the camera, so the starting point of the light is not inside the geometry!! in 2D, we render differently, light comes from every point in the 2D area, so the light start may be inside the graph .
So the modified code is this:
Geo2dresult geo2dcircle::sample (vector2 ori, Vector2 dir)Const{//Circle on Dot x meet: | | dot x-center Center | | = Circle RADIUS RADIUS //Ray equation R (t) = O + t.d (t>=0) //Generation | | O + t.d-c | | = R //Order v = o-c, then | | v + T.D | | = R //Simplify t =-d.v-sqrt ((D.V) ^2 + (v^2-r^2)) (for nearest point) //Make V = origin-center Autov = ori-center;//a0 = (v^2-r^2) Autoa0 = squaremagnitude (v)-rsq;//Ddotv = D.V AutoDdotv = DotProduct (dir, v);//if (Ddotv <= 0){//Point multiply test intersect, negative in same direction AutoDISCR = (DDOTV * ddotv)-A0;calculation in//square root if(DISCR >= 0) {//non-negative equations have solutions, intersect established //R (t) = O + t.d AutoDistance =-ddotv-sqrtf (DISCR);//Derive t, that is, the distance from the camera's light to its intersection with the Circle AutoDistance2 =-ddotv + sqrtf (DISCR);AutoPosition = ori + dir * distance;//Enter the linear equation to obtain the intersection position Autonormal = Normalize (Position-center);//Normal vector = end of Ray (ball to spot)-Globe coordinates if(A0 <= 0 | | distance >= 0)//Here is not the same!! returnGeo2dresult ( This, A0 <= 0, Distance, distance2, position, normal); } }returnGeo2dresult ();//fail, disjoint}
In essence, the intersection algorithm is solved by substituting the parametric equation, and the parameter T is actually the distance. One case is invalid, and it is important to note that such a solution is not valid when the distance is negative and the beginning of the light is not inside the graph, because our lines are rays .
Why the rendering speed is getting faster
The original method is SDF, which is iterative and eventually approximates the intersection point. The problem with this approach is that the number of iterations is too many, and the same calculation is done for each iteration.
After optimization, we calculate the intersection point, we use analytic method directly, one equation can be done, even better, we can find the closest point and farthest point of intersection (this is very important!). )。
How much of the rendering time is affected by the different methods.
How to arrange the code
Bajdcc/gameframework
Let's take a look at the top-level call (HTTPS://GITHUB.COM/BAJDCC/GAMEFRAMEWORK/BLOB/MASTER/CCGAMEFRAMEWORK/BASE/PE2D/RENDER2DSCENE2.CPP#L72):
or and (Geo2dfactory::new_circle (1.3f, 0.5f, 0.4f, Color (2.0f, 1.0f, 1.0f)), Geo2dfactory::new_circle (1.7f, 0.5f, 0.4f, Color ( 2.0f, 1.0f, 1.0f)), Geo2dfactory:: Sub (geo2dfactory::new_circle (0.5f, 0.5f, 0.4f, Color (1.0f, 1.0f, 2.0f)), Geo2dfactory::new_circle (0.9f, 0.5f, 0.4f, Color (1.0f, 1.0f, 2.0f)));
The result is a graph, the code defines two graphs, one is two circles intersect and, the other is two round sub, two graphs are concatenated with or.
Of course, you can use overloading later to make your code more concise.
The intersection of line and circle algorithm in https://github.com/bajdcc/GameFramework/blob/master/CCGameFramework/base/pe2d/Geometries2D.cpp#L163, It has been posted above.
How to achieve two graphs of intersection, and, poor?
This is the focus of this article, and it takes a lot of time to implement this feature.
And:
if (op = = t_union) { constauto r1 = obj1->sample (ori, DST); Const Auto r2 = obj2->sample (ori, DST); return r1.distance < r2.distance? R1:R2;}
Good explanation, the line sweep to two graphics, if there is no intersection, then distance is infinite, if there is an intersection, take the distance closer to the graph.
Make:
if(op = = t_intersect) {Const AutoR1 = Obj1->sample (ori, DST);if(r1.body) {Const Autor2 = Obj2->sample (ori, DST);if(r2.body) {Const AutoRD = ((r1.inside 1:0) << 1) | (r2.inside? 1:0);Switch(RD) { Case0://Not (A or B) if(R1.distance < R2.distance) {if(R2.distance2 > R1.distance && r2.distance > R1.distance2) Break;returnR2; }if(R2.distance < R1.distance) {if(R1.distance2 > R2.distance && r1.distance > R2.distance2) Break;returnR1; } Break; Case1://B if(R1.distance < R2.distance2)returnR1; Break; Case2://A if(R2.distance < R1.distance2)returnR2; Break; Case3://A and B returnR1.distance > R2.distance? R1:R2;default: Break; } } }}
The distance in the code is the nearest intersection (the smaller root), and the Distance2 is the farthest intersection (larger root).
The intersection is much more complicated, first of all, the light must have an intersection with two graphs, and secondly, in four cases (I like to write the classification discussion of two bool). ) to discuss the position relationship between the start of light and the two graphs.
first , the discussion of the beginning of light is not in two circles.
The case of intersection
There are six cases (c{2,4}=6), two of which are disjoint, and the remaining four are intersecting, which is the idea in the code.
second , the discussion of light starting point in B, it is obvious that the intersection is the boundary of a
Thirdly , the beginning of light is discussed in a, it is obvious that the intersection is the boundary of B
Fourth , discuss the beginning of light in a cross B, here must intersect
Poor:
if(op = = t_subtract) {Const AutoR1 = Obj1->sample (ori, DST);Const Autor2 = Obj2->sample (ori, DST);Const AutoRD = ((r1.body 1:0) << 1) | (r2.body? 1:0);Switch(RD) { Case0://Not (A or B) Break; Case1://B Break; Case2://A returnR1; Case3://A and B if(r2.inside) {if(R1.distance2 > R2.distance2) {AutoR (R2); R.body = R1.body; R.inside =false; R.distance = R.distance2;returnR } Break; }if(r1.inside) {returnR1; }if(R2.distance < R1.distance) {if(R1.distance2 < R2.distance2) { Break; }AutoR (R2); R.body = R1.body; R.inside =false; R.distance = R.distance2;returnR }returnR1;default: Break; }}
The implementation of the difference is also not simple, discussing the intersection of the Ray and the two circles:
First : The ray does not intersect the two circles, and the ray does not intersect with a-B.
Second : The Rays intersect with B, do not intersect with a, and the ray does not intersect with a-a
Third : The ray intersects with a, does not intersect with B, and the ray must intersect a-a
Fourth : The Rays intersect with a intersection, and then the situation is complicated.
The situation of the difference set
Only two types of disjoint situations are considered. Reflected in the code is two break.
Summary
The result of rendering is different, the color of the luminous figure itself is white, this is because the color of the definition is RGB (2.0f,1.0f,1.0f), the final sampling after the average after rendering or RGB (2.0f,1.0f,1.0f), do a truncation is RGB (1.0f, 1.0f,1.0f) is white.
This paper focuses on the intersection between two circles, the analytic method of difference set, and the intersection algorithm of line and circle.
Program Download: bajdcc/gameframework
Backed up by https://zhuanlan.zhihu.com/p/32150887.
Draw light in C + + (I.)--Optimize