Qt's QPainter draws on a 2D plane and is able to modify its coordinate system by .setTransform() in combination with QTransform.The latter has a built-in kind of projection (using a constant distance to plane of 1024), for example used when rotating around the x-axis.
Is it possible to set the transforms to different z-Values in order to achieve a 3D projection (for example rotating a cube around its center x-axis, viewed from (0, 0,0))?In QTransform doc it says: "Perspective transformation is achieved by setting both the projection factors and the scaling factors" [of the transform matrix]. https://doc.qt.io/qt-6/qtransform.html#basic-matrix-operations
But what would be the math behind that?
Another solution could be using QMatrix4x4 transformation and the perspective() and toTransform() functions to get a QTransform object. But thats seems even more complicated.
Related
I'm building a scene, which I want to view through the orthographic camera, from an angle. I do the following:
Build the scene.
Move the OrbitControl's (camera's) target to the center of the scene.
Move the camera by a certain (unit) vector using spherical coordinates.
Try to adjust the camera's left/right/top/bottom params to keep the object in the view, centered. Also considered adjusting the zoom.
My simplified, ideally positioned scene looks like that:
So I guess it is a problem of a calculation of positions of object extremities after (spherical) transformation and projecting them back into cartesian coordinates. I tried to use Euler transform helper, but it depends on the order of transformation for each of the axis. Quaternions are also non-commutive, and I'm lost. Perhaps I need to calculate how would the widths/heights of the diagonals change after transformation and use these?
I am trying to do / understand all the basic mathematical computations needed in the graphics pipeline to render a simple 2D image from a 3D scene description like VRML. Is there a good example of the steps needed, like model transformation (object coordinates to world coordinates), view transformation (from world coordinate to view coordinate), calculation of vertex normals for lighting, clipping, calculating the screen coordinates of objects inside the view frustum and creating the 2D projection to calculate the individual pixels with colors.
I am used to OpenGL style render math so I stick to it (all the renders use almost the same math)
First some therms to explain:
Transform matrix
Represents a coordinate system in 3D space
double m[16]; // it is 4x4 matrix stored as 1 dimensional array for speed
m[0]=xx; m[4]=yx; m[ 8]=zx; m[12]=x0;
m[1]=xy; m[5]=yy; m[ 9]=zy; m[13]=y0;
m[2]=xz; m[6]=yz; m[10]=zz; m[14]=z0;
m[3]= 0; m[7]= 0; m[11]= 0; m[15]= 1;
where:
X(xx,xy,xz) is unit vector of X axis in GCS (global coordinate system)
Y(yx,yy,yz) is unit vector of Y axis in GCS
Z(zx,zy,zz) is unit vector of Z axis in GCS
P(x0,y0,z0) is origin of represented coordinate system in GCS
Transformation matrix is used to transform coordinates between GCS and LCS (local coordinate system)
GCS -> LCS: Al = Ag * m;
GCS <- LCS: Ag = Al * (m^-1);
Al (x,y,z,w=1) is 3D point in LCS ... in homogenous coordinates
Ag (x,y,z,w=1) is 3D point in GCS ... in homogenous coordinates
homogenous coordinate w=1 is added so we can multiply 3D vector by 4x4 matrix
m transformation matrix
m^-1 inverse transformation matrix
In most cases is m orthonormal which means X,Y,Z vectors are perpendicular to each other and with unit size this can be used for restoration of matrix accuracy after rotations,translations,etc ...
For more info see Understanding 4x4 homogenous transform matrices
Render matrices
There are usually used these matrices:
model - represents actual rendered object coordinate system
view - represents camera coordinate system (Z axis is the view direction)
modelview - model and view multiplied together
normal - the same as modelview but x0,y0,z0 = 0 for normal vector computations
texture - manipulate texture coordinates for easy texture animation and effect usually an unit matrix
projection - represent projections of camera view ( perspective ,ortho,...) it should not include any rotations or translations its more like Camera sensor calibration instead (otherwise fog and other effects will fail ...)
The rendering math
To render 3D scene you need 2D rendering routines like draw 2D textured triangle ... The render converts 3D scene data to 2D and renders it. There are more techniques out there but the most usual is use of boundary model representation + boundary rendering (surface only) The 3D -> 2D conversion is done by projection (orthogonal or perspective) and Z-buffer or Z-sorting.
Z-buffer is easy and native to now-days gfx HW
Z-sorting is done by CPU instead so its slower and need additional memory but it is necessary for correct transparent surfaces rendering.
So the pipeline is as this:
obtain actual rendered data from model
Vertex v
Normal n
Texture coord t
Color,Fog coord, etc...
convert it to appropriate space
v=projection*view*model*v ... camera space + projection
n=normal*n ... global space
t=texture*t ... texture space
clip data to screen
This step is not necessary but prevent to render of screen stuff for speed and also face culling is usually done here. If normal vector of rendered 'triangle' is opposite then the polygon winding rule set then ignore 'triangle'
render the 3D/2D data
use only v.x,v.y coordinates for screen rendering and v.z for z-buffer test/value also here goes the perspective division for perspective projections
v.x/=v.z,vy/=v.z
Z-buffer works like this: Z-buffer (zed) is 2D array with the same size (resolution) as screen (scr). Any pixel scr[y][x] is rendered only if (zed[y][x]>=z) in that case scr[y][x]=color; zed[y][x]=z; The if condition can be different (it is changeable)
In case of using triangles or higher primitives for rendering The resulting 2D primitives are converted to pixels in process called rasterization for example like this:
Algorithm to fill triangle
For more clarity here is how it looks like:
[Notes]
Transformation matrices are multiplicative so if you need transform N points by M matrices you can create single matrix = m1*m2*...mM and convert N points by this resulting matrix only (for speed). Sometimes are used 3x3 transform matrix + shift vector instead of 4x4 matrix. it is faster in some cases but you cannot multiply more transformations together so easy. For transformation matrix manipulation look for basic operations like Rotate or Translate there are also matrices for rotations inside LCS which are more suitable for human control input but these are not native to renders like OpenGL or DirectX. (because they use inverse matrix)
Now all the above stuff was for standard polygonal rendering (surface boundary representation of objects). There are also other renderers out there like Volumetric rendering or (Back)Ray-tracers and hybrid methods. Also the scene can have any dimensionality not just 3D. Here some related QAs covering these topics:
GLSL 3D Volumetric back raytracer
GLSL 3D Mesh back raytracer
2D Doom/Wolfenstein techniques
4D Rendering techniques
Comanche Voxel space ray casting
You can have a look Chapter 15 from the book Computer Graphics: Principles and Practice - Third Edition by Hughes et al. That chapter
Derives the ray-casting and rasterization algorithms and then builds
the complete source code for a software ray-tracer, software
rasterizer, and hardware-accelerated rasterization renderer.
How would one go about retrieving scan lines for all the lines in a 2D triangle?
I'm attempting to implement the most basic feature of a 2D software renderer, that of texture mapping triangles. I've done this more times than i can count using OpenGL, but i find myself limping when trying to do it myself.
I see a number of articles saying that in order to fill a triangle (whose three vertices each have texture coordinates clamped to [0, 1]), i need to linearly interpolate between the three points. What? I thought interpolation was between two n-dimensional values.
NOTE; This is not for 3D, it's strictly 2D, all the triangles are arbitrary (not axis-aligned in any way). I just need to fill the screen with their textures the way OpenGL would. I cannot use OpenGL as a solution.
An excellent answer and description can be found here: http://sol.gfxile.net/tri/index.html
You can use the Bresenham algorithm to draw/find the sides.
One way to handle it is to interpolate in two steps if you use scanline algorithm. First you interpolate the value on the edges of the triangle and when you start drawing the scanline you interpolate between the start and end value of that scanline.
Since you are working in 2d you can also use a matrix transformation to obtain the screen coordinate to texture coordinate. Yesterday I answered a similar question here. The technique is called change of basis in mathematics.
I'm writing a software renderer which is currently working well, but I'm trying to get perspective correction of texture coordinates and that doesn't seem to be correct. I am using all the same matrix math as opengl for my renderer. To rasterise a triangle I do the following:
transform the vertices using the modelview and projection matrixes, and transform into clip coordinates.
for each pixel in each triangle, calculate barycentric coordinates to interpolate properties (color, texture coordinates, normals etc.)
to correct for perspective I use perspective correct interpolation:
(w is depth coordinate of vertex, c is texture coordinate of vertex, b is the barycentric weight of a vertex)
1/w = b0*(1/w0) + b1*(1/w1) + b2*(1/w2)
c/w = b0*(c0/w0) + b1*(c1/w1) + b2*(c2/w2)
c = (c/w)/(1/w)
This should correct for perspective, and it helps a little, but there is still an obvious perspective problem. Am I missing something here, perhaps some rounding issues (I'm using floats for all math)?
See in this image the error in the texture coordinates evident along the diagonal, this is the result having done the division by depth coordinates.
Also, this is usually done for texture coordinates... is it necessary for other properties (e.g. normals etc.) as well?
I cracked the code on this issue recently. You can use a homography if you plan on modifying the texture in memory prior to assigning it to the surface. That's computationally expensive and adds an additional dependency to your program. There's a nice hack that'll fix the problem for you.
OpenGL automatically applies perspective correction to the texture you are rendering. All you need to do is multiply your texture coordinates (UV - 0.0f-1.0f) by the Z component (world space depth of an XYZ position vector) of each corner of the plane and it'll "throw off" OpenGL's perspective correction.
I asked and solved this problem recently. Give this link a shot:
texture mapping a trapezoid with a square texture in OpenGL
The paper I read that fixed this issue is called, "Navigating Static Environments Using Image-Space Simplification and Morphing" - page 9 appendix A.
Hope this helps!
ct
The only correct transformation from UV coordinates to a 3D plane is an homographic transformation.
http://en.wikipedia.org/wiki/Homography
You must have it at some point in your computations.
To find it yourself, you can write the projection of any pixel of the texture (the same as for the vertex) and invert them to get texture coordinates from screen coordinates.
It will come in the form of an homographic transform.
Yeah, that looks like your traditional broken-perspective dent. Your algorithm looks right though, so I'm really not sure what could be wrong. I would check that you're actually using the newly calculated value later on when you render it? This really looks like you went to the trouble of calculating the perspective-correct value, and then used the basic non-corrected value for rendering.
You need to inform OpenGL that you need perspective correction on pixels with
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST)
What you are observing is the typical distortion of linear texture mapping. On hardware that is not capable of per-pixel perspective correction (like for example the PS1) the standard solution is just subdividing in smaller polygons to make the defect less noticeable.
I need to project a 3D object onto a sphere's surface (uhm.. like casting a shadow).
AFAIR this should be possible with a projection matrix.
If the "shadow receiver" was a plane, then my projection matrix would be a 3D to 2D-plane projection, but my receiver in this case is a 3D spherical surface.
So given sphere1(centerpoint,radius),sphere2(othercenter,otherradius) and an eyepoint how can I compute a matrix that projects all points from sphere2 onto sphere1 (like casting a shadow).
Do you mean that given a vertex v you want the following projection:
v'= centerpoint + (v - centerpoint) * (radius / |v - centerpoint|)
This is not possible with a projection matrix. You could easily do it in a shader though.
Matrixes are commonly used to represent linear operations, like projection onto a plane.
In your case, the resulting vertices aren't deduced from input using a linear function, so this projection is not possible using a matrix.
If the sphere1 is sphere((0,0,0),1), that is, the sphere of radius 1 centered at the origin, then you're in effect asking for a way to convert any location (x,y,z) in 3D to a corresponding location (x', y', z') on the unit sphere. This is equivalent to vector renormalization: (x',y',z') = (x,y,z)/sqrt(x^2+y^2+z^2).
If sphere1 is not the unit sphere, but is say sphere((a,b,c),R) you can do mostly the same thing:
(x',y',z') = R*(x-a,y-b,z-c) / sqrt((x-a)^2+(y-b)^2+(z-c)^2) + (a,b,c). This is equivalent to changing coordinates so the first sphere is the unit sphere, solving the problem, then changing coordinates back.
As people have pointed out, these functions are nonlinear, so the projection cannot be called a "matrix." But if you prefer for some reason to start with a projection matrix, you could project first from 3D to a plane, then from a plane to the sphere. I'm not sure if that would be any better though.
Finally, let me point out that linear maps don't produce division-by-zero errors, but if you look closely at the formulas above, you'll see that this map can. Geometrically, that's because it's hard to project the center point of a sphere to its boundary.