I am writing a mesh editor where I have manipulators with the help of which I change the vertices of the mesh. The task is to render the manipulators with constant dimensions, which would not change when changing the camera and viewport parameters. The projection matrix is perspective. I will be grateful for ideas how to implement the invariant scale geometry.
1 Answers
If I got it right you want to render some markers (for example vertex drag editation area) with the same visual size for any depth they are rendered to.
There are 2 approaches for this:
scale with depth
compute perpendicular distance to camera view (simple dot product) and scale the marker size so it has the same visual size invariant on the depth.
So if
P0
is your camera position andZ
is your camera view direction unit vector (usually Z axis). Then for any positionP
compute the scale like this:depth = dot(P-P0,Z)
Now the scale depends on wanted visual
size0
at some specifieddepth0
. Now using triangle similarity we want:size/dept = size0/depth0 size = size0*depth/depth0
so render your marker with
size
or scaledepth/depth0
. In case of using scaling you need to scale around your target positionP
otherwise your marker would shift to the sides (so translate, scale, translate back).compute screen position and use non perspective rendering
so you transform target coordinates the same way as the graphic pipeline does until you got the screen
x,y
position. Remember it and in pass that will render your markers just use that instead of real position. For this rendering pass either use some constant depth (distance from camera) or use non perspective view matrix.
For more info see Understanding 4x4 homogenous transform matrices
[Edit1] pixel size
you need to use FOVx,FOVy
projection angles and view/screen resolution (xs,ys) for that. That means if depth is znear
and coordinate is at half of the angle then the projected coordinate will go to edge of screen:
tan(FOVx/2) = (xs/2)*pixelx/znear
tan(FOVy/2) = (ys/2)*pixely/znear
---------------------------------
pixelx = 2*znear*tan(FOVx/2)/xs
pixely = 2*znear*tan(FOVy/2)/ys
Where pixelx,pixely
is size (per axis) representing single pixel visually at depth znear
. In case booth sizes are the same (so pixel is square) you have all you need. In case they are not equal (pixel is not square) then you need to render markers in screen axis aligned coordinates so approach #2 is more suitable for such case.
So if you chose depth0=znear
then you can set size0
as n*pixelx
and/or n*pixely
to get the visual size of n
pixels. Or use any dept0
and rewrite the computation to:
pixelx = 2*depth0*tan(FOVx/2)/xs
pixely = 2*depth0*tan(FOVy/2)/ys
Just to be complete:
size0x = size_in_pixels*(2*depth0*tan(FOVx/2)/xs)
size0y = size_in_pixels*(2*depth0*tan(FOVy/2)/ys)
-------------------------------------------------
sizex = size_in_pixels*(2*depth0*tan(FOVx/2)/xs)*(depth/depth0)
sizey = size_in_pixels*(2*depth0*tan(FOVy/2)/ys)*(depth/depth0)
---------------------------------------------------------------
sizex = size_in_pixels*(2*tan(FOVx/2)/xs)*(depth)
sizey = size_in_pixels*(2*tan(FOVy/2)/ys)*(depth)
---------------------------------------------------------------
sizex = size_in_pixels*2*depth*tan(FOVx/2)/xs
sizey = size_in_pixels*2*depth*tan(FOVy/2)/ys

- 49,595
- 11
- 110
- 380
-
thank you! works like a charm. what do you think is the best way to name the topic? – innochenti Jun 06 '17 at 10:39
-
what do you think is the best way of automatic calculation of depth0? – innochenti Jun 06 '17 at 12:43
-
@innochenti it does not really matter what `depth0` you chose as it is just numerical scaling constant ... I usually select something between `znear` and `zfar` of perspective projection frustrum – Spektre Jun 06 '17 at 17:51
-
agree. but it would be better somehow to connect this constant with "pixel size" of manipulator – innochenti Jun 07 '17 at 05:59
-
is it possible to calculate depth0 based on pixelSize? I want something like depth0 = f(pixelSize); and use depth/depth0 for scaling – innochenti Jun 08 '17 at 12:19
-
@innochenti yes it is possible but I see no point of doing it ... what is wrong with seting `size` directly in pixels like in [edit1] ? I added the full equation there. As you can see depth0 is not there anymore as it is just numerical constant integrated in the FOV angles ... – Spektre Jun 09 '17 at 07:45
-
currently I do sceneNode->setTransform(dot(center(bbox)-eye,forward)/depth0); – innochenti Jun 09 '17 at 13:44
-
I want to set depth0 based on pixelSize value – innochenti Jun 09 '17 at 13:44
-
and don't want to change transform above – innochenti Jun 09 '17 at 13:44