What's the different between using modelViewmatrix directly and using normalMatrix instead? [duplicate] - graphics

I am working on some shaders, and I need to transform normals.
I read in few tutorials the way you transform normals is you multiply them with the transpose of the inverse of the modelview matrix. But I can't find explanation of why is that so, and what is the logic behind that?

It flows from the definition of a normal.
Suppose you have the normal, N, and a vector, V, a tangent vector at the same position on the object as the normal. Then by definition N·V = 0.
Tangent vectors run in the same direction as the surface of an object. So if your surface is planar then the tangent is the difference between two identifiable points on the object. So if V = Q - R where Q and R are points on the surface then if you transform the object by B:
V' = BQ - BR
= B(Q - R)
= BV
The same logic applies for non-planar surfaces by considering limits.
In this case suppose you intend to transform the model by the matrix B. So B will be applied to the geometry. Then to figure out what to do to the normals you need to solve for the matrix, A so that:
(AN)·(BV) = 0
Turning that into a row versus column thing to eliminate the explicit dot product:
[tranpose(AN)](BV) = 0
Pull the transpose outside, eliminate the brackets:
transpose(N)*transpose(A)*B*V = 0
So that's "the transpose of the normal" [product with] "the transpose of the known transformation matrix" [product with] "the transformation we're solving for" [product with] "the vector on the surface of the model" = 0
But we started by stating that transpose(N)*V = 0, since that's the same as saying that N·V = 0. So to satisfy our constraints we need the middle part of the expression — transpose(A)*B — to go away.
Hence we can conclude that:
transpose(A)*B = identity
=> transpose(A) = identity*inverse(B)
=> transpose(A) = inverse(B)
=> A = transpose(inverse(B))

My favorite proof is below where N is the normal and V is a tangent vector. Since they are perpendicular their dot product is zero. M is any 3x3 invertible transformation (M-1 * M = I). N' and V' are the vectors transformed by M.
To get some intuition, consider the shear transformation below.
Note that this does not apply to tangent vectors.

Take a look at this tutorial:
https://paroj.github.io/gltut/Illumination/Tut09%20Normal%20Transformation.html
You can imagine that when the surface of a sphere stretches (so the sphere is scaled along one axis or something similar) the normals of that surface will all 'bend' towards each other. It turns out you need to invert the scale applied to the normals to achieve this. This is the same as transforming with the Inverse Transpose Matrix. The link above shows how to derive the inverse transpose matrix from this.
Also note that when the scale is uniform, you can simply pass the original matrix as normal matrix. Imagine the same sphere being scaled uniformly along all axes, the surface will not stretch or bend, nor will the normals.

If the model matrix is made of translation, rotation and scale, you don't need to do inverse transpose to calculate normal matrix. Simply divide the normal by squared scale and multiply by model matrix and we are done. You can extend that to any matrix with perpendicular axes, just calculate squared scale for each axes of the matrix you are using instead.
I wrote the details in my blog: https://lxjk.github.io/2017/10/01/Stop-Using-Normal-Matrix.html

Don't understand why you just don't zero out the 4th element of the direction vector before multiplying with the model matrix. No inverse or transpose needed. Think of the direction vector as the difference between two points. Move the two points with the rest of the model - they are still in the same relative position to the model. Take the difference between the two points to get the new direction, and the 4th element, cancels out to zero. Lot cheaper.

Related

how to calculate anti/clockwise angle in direction of lines?

I need to offset a curve, which by the simplest way is just shifting the points perpendicularly. I can access each point to calculate angle of each line along given path, for now I use atan2. Then I take those two angle and make average of it. It returns the shortest angle, not what I need in this case.
How can I calculate angle of each connection? Concerning that I am not interested in the shortest angle but the one that would create parallel offset curve.
Assuming 2D case...
So do a cross product of direction vectors of 2 neighboring lines the sign of z coordinate of the result will tell you if the lines are CW/CCW
So if you got 3 consequent control points on the polyline: p0,p1,p2 then:
d1 = p1-p0
d2 = p2-p1
if you use some 3D vector math then convert them to 3D by setting:
d1.z=0;
d2.z=0;
now compute 3D cross:
n = cross(d1,d2)
which returns vector perpendicular to both vectors of size equals to the area of quad (parallelogram) constructed with d1,d2 as base vectors. The direction (from the 2 possible) is determined by the winding rule of the p0,p1,p2 so inspecting z of the result is enough.
The n.x,n.y are not needed so you can compute directly without doing full cross product:
n.z=(d1.x*d2.y)-(d1.y*d2.x)
if (n.z>0) case1
if (n.z<0) case2
if the case1 is CW or CCW depends on your coordinate system properties (left/right handness). This approach is very commonly used in CG fur back face culling of polygons ...
if n.z is zero it means that your vectors/lines are either parallel or at lest one of them is zero.
I think these might interest you:
draw outline for some connected lines
How can I create an internal spiral for a polygon?
Also in 2D you do not need atan2 to get perpendicular vector... You can do instead this:
u = (x,y)
v = (-y,x)
w = (x,-y)
so u is any 2D vector and v,w are the 2 possible perpendicular vectors to u in 2D. they are the result of:
cross((x,y,0),(0,0,1))
cross((0,0,1),(x,y,0))

How can I detect and remove unneeded points in cubic bezier

Here is example image of what I want to do:
I want to calculate Path 1 from Path 2.
Screenshot made from Inkscape, where I'm, at first, create Path 1, then add p3 to the original path. This is didn't change the original path at all, because new point actually unneeded. So, how can I detect this point(p3) using Path 2 SVG path representation and calculate Path 1 from Path 2?
Basically, I search for the math formulas, which can help me to convert(also checking that whether it is possible):
C 200,300 300,250 400,250 C 500,250 600,300 600,400
to
C 200,200 600,200 600,400
You're solving a constraint problem. Taking your first compound curve, and using four explicit coordinates for each subcurve, we have:
points1 = point[8];
points2 = point[4];
with the following correspondences:
points1[0] == points2[0];
points1[7] == points2[3];
direction(points1[0],points1[1]) == direction(points2[0], points2[1]);
direction(points1[6],points1[7]) == direction(points2[2], points2[3]);
we also have a constraint on the relative placement for points2[1] and points2[2] due to the tangent of the center point in your compound curve:
direction(points1[2],points[4]) == direction(points2[1],points2[2]);
and lastly, we have a general constraint on where on- and off-curve points can be for cubic curves if we want the curve to pass through a point, which is described over at http://pomax.github.io/bezierinfo/#moulding
Taking the "abc" ratio from that section, we can check whether your compound curve parameters fit a cubic curve: if we construct a new cubic curve with points
A = points1[0];
B = points1[3];
C = points1[7];
with B at t=0.5 (in this case), then we can verify whether the resulting curve fits the constraints that must hold for this to be a legal simplification.
The main problem here is that we, in general, don't know whether the "in between start and end" point should fall on t=0.5, or whether it's a different t value. The easiest solution is to see how far that point is along the total curve (using arc length: distance = arclength(c1) / arclength(c1)+arclength(c2) will tell us) and use that as initial guess for t, iterating outward on either side for a few values.
The second option is to solve a generic cubic equation for the tangent vector at your "in between" point. We form a cubic curve with points
points3 = [ points1[0], points1[1], points1[6], points1[7] ];
and then solve its derivative equations to find one or more t values that have the same tangent direction (but not magnitude!) as our in-between point. Once we have those (and we might have more than 2), we evaluate whether we can create a curve through our three points of interest with the middle point set to each of those found t values. Either one or zero of the found t values will yield a legal curve. If we have one: perfect, we found a simplification. If we find none, then the compound curve cannot be simplified into a single cubic curve.

Generate random points inside a rectangle (uniformly)?

I am trying to generate a certain amount of random uniform points inside a rectangle (I know the pair of coordinates for each corner).
Let our rectangle be
ABCD
My idea is:
Divide the rectangle into two triangles by the AC diagonal. Find the slope and the intercept of the diagonal.
Then, generate two random numbers from [0,1] interval, let them be a,b.
Evaluate x = aAB and y = bAD (AB, AD, distances). If A is not (0,0), then we can add to x and y A's coordinates.
Now we have a point (x,y). If it is not in the lower triangle (ABC), skip to the next step.
Else, add the point to our plot and also add the symmetric of (x,y) vs. the AC diagonal so that we can fill the upper triangle (ADC) too.
I have implemented this, but I highly doubt that the points are uniformly generated (judging from the plot). How should I modify my algorithm? I guess that the issue is related to how I pick the triangle and the symmetric thing.
Why not just generate x=random([A.x, B.x]) and y=random([B.y, C.y]) and put them together as (x,y)? A n-dimensional uniform distribution is simply the product of the n uniform distributions of the components.
This is referred to as point picking and other similar terms. You seem to be on the right track in that the points should come from the uniform distribution. Your plot looks reasonably random to me.
What are you doing with upper and lower triangles? They seem unnecessary and would certainly make things less random. Is this some form variance reduction along the lines of antithetic variates? If #Paddy3118 is right an you really just need random-ish points to fill the space, then you should look into low-discrepancy sequences. The Halton sequence generalizes the van der Corput sequence to multiple dimensions. If you have Matlab's Statistics Toolbox check out the sobolset and haltonset functions or qrandstream and qrand.
This approach (from #Xipan Xiao & #bonanova.) should be reproducible in many languages. MATLAB code below.
a = 0; b = 1;
n = 2000;
X = a + (b-a)*rand(n,1);
Y = a + (b-a)*rand(n,1);
Newer versions of MATLAB can make use of the makedist and random commands.
pdX = makedist('Uniform',a,b);
pdY = makedist('Uniform',a,b);
X = random(pdX,n,1);
Y = random(pdY,n,1);
The points (X,Y) will be uniformly in the rectangle with corner points (a,a), (a,b), (b,a), (b,b).
For verification, we can observe the marginal distributions for X and Y and see that those are uniform as well.
scatterhist(X,Y,'Marker','.','Direction','out')
Update: Using haltonset (suggested by #horchler)
p = haltonset(2);
XY = net(p,2000);
scatterhist(XY(:,1),XY(:,2),'Marker','.','Direction','out')
If you are after a more uniform density then you might consider a Van der Corput sequence. The sequence finds use in Monte-Carlo simulations and Wolfram Mathworld calls them a quasi-random sequence.
Generate two random numbers in the interval [0,1] translate and scale them to your rectangle as x and y.
There is just my thought, i haven't test with code yet.
1.Divide the rectangle to grid with N x M cells, depends on variable density.
2.loop through the cell and pick a random point in the cell until it reached your target point quantity.

Transforming a 3D plane using a 4x4 matrix

I have a shape made out of several triangles which is positioned somewhere in world space with scale, rotate, translate. I also have a plane on which I would like to project (orthogonal) the shape.
I could multiply every vertex of every triangle in the shape with the objects transformation matrix to find out where it is located in world coordinates, and then project this point onto the plane.
But I don't need to draw the projection, and instead I would like to transform the plane with the inverse transformation matrix of the shape, and then project all the vertices onto the (inverse transformed) plane. Since it only requires me to transform the plane once and not every vertex.
My plane has a normal (xyz) and a distance (d). How do I multiply it with a 4x4 transformation matrix so that it turns out ok?
Can you create a vec4 as xyzd and multiply that? Or maybe create a vector xyz1 and then what to do with d?
You need to convert your plane to a different representation. One where N is the normal, and O is any point on the plane. The normal you already know, it's your (xyz). A point on the plane is also easy, it's your normal N times your distance d.
Transform O by the 4x4 matrix in the normal way, this becomes your new O. You will need a Vector4 to multiply with a 4x4 matrix, set the W component to 1 (x, y, z, 1).
Also transform N by the 4x4 matrix, but set the W component to 0 (x, y, z, 0). Setting the W component to 0 means that your normals won't get translated. If your matrix is composed of more that just translating and rotating, then this step isn't so simple. Instead of multiplying by your transformation matrix, you have to multiply by the transpose of the inverse of the matrix i.e. Matrix4.Transpose(Matrix4.Invert(Transform)), there's a good explanation on why here.
You now have a new normal vector N and a new position vector O. However I suppose you want it in xyzd form again? No problem. As before, xyz is your normal N all that's left is to calculate d. d is the distance of the plane from the origin, along the normal vector. Hence, it is simply the dot product of O and N.
There you have it! If you tell me what language you're doing this in, I'd happily type it up in code as well.
EDIT, In pseudocode:
The plane is vector3 xyz and number d, the matrix is a matrix4x4 M
vector4 O = (xyz * d, 1)
vector4 N = (xyz, 0)
O = M * O
N = transpose(invert(M)) * N
xyz = N.xyz
d = dot(O.xyz, N.xyz)
xyz and d represent the new plane
This question is a bit old but I would like to correct the accepted answer.
You do not need to convert your plane representation.
Any point lies on the plane if
It can be written as dot product :
You are looking for the plane transformed by your 4x4 matrix .
For the same reason, you must have
So and with some arrangements
TLDR : if p=(a,b,c,d), p' = transpose(inverse(M))*p
Notation:
n is a normal represented as a (1x3) row-vector
n' is the transformed normal of n according to transform matrix T
(n|d) is a plane represented as a (1x4) row-vector (with n the plane's normal and d the plane's distance to the origin)
(n'|d') is the transformed plane of (n|d) according to transform matrix T
T is a (4x4) (affine) column-major transformation matrix (i.e. transforming a column-vector t is defined as t' = T t).
Transforming a normal n:
n' = n adj(T)
Transforming a plane (n|d):
(n'|d') = (n|d) adj(T)
Here, adj is the adjugate of a matrix which is defined as follows in terms of the inverse and determinant of a matrix:
T^-1 = adj(T)/det(T)
Note:
The adjugate is generally not equal to the inverse of a transformation matrix T. If T includes a reflection, det(T) = -1, reversing the winding order!
Re-normalizing n' is mathematically not required (but maybe numerically depending on the implementation) since scaling is taken care off by the determinant. Thanks to Adrian Leonhard.
You can directly transform the plane without first decomposing and recomposing a plane (normal and point).

CGAL: Transformation Matrix for Rotation given two lines/vectors/directions

How do I generate a transformation matrix for rotating points/others by the angle between two lines/vectors/directions in CGAL?
2D is what I need. 3D is what I love.
According to the manual you have these tools to work with:
Aff_transformation_2<Kernel> t ( const Rotation, Direction_2<Kernel> d, Kernel::RT num, Kernel::RT den = RT(1))
approximates the rotation over the angle indicated by direction d, such that the differences between the sines and cosines of the rotation given by d and the approximating rotation are at most num/den each.
Precondition: num/den>0 and d != 0.
Aff_transformation_2<Kernel> t.operator* ( s) composes two affine transformations.
Aff_transformation_2<Kernel> t.inverse () gives the inverse transformation.
With them you should be able to compute the matrices corresponding to the two directions and use an identity along the lines of:
Mat(d1-d2) === Mat(d1)*Inv(Mat(d2))
to get what you want.

Resources