New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
VREffect: Use skewed frusta to simulate focal length #7036
Conversation
Yes a focal point is important. |
Definitely interesting! Please go on, and thanks for sharing the link. The amount to skew the frusta at the clipping planes, as required for the projection matrices, can be determined as follows: Parallel frusta (shown dotted blue) have the eye distance at any point. Moving them toward each other by half of the eye distance makes the images converge.
|
I forgot to mention the guidelines for the forum are bugs and feature requests only. This kind of thing should be asked on stack overflow. |
No. Doesn't look that way. @MasterJames OP was making a feature request or maybe even proposing a contribution... |
Thanks for the explanations and links! |
@njam Yes, you can rephrase your title if you want so it sounds more like a suggestion. |
@njam How about you just implement skewed frusta in `VREffect, send a pull request and we close the ticket? ;-) As said, it shouldn't really be too difficult... |
...I have some concerns that changing the left and right angles of the FOV makes this a "toe in" projection. I.e. that the frusta are in fact not asymmetric. |
It appears to me you actually got it right. Look at The discrepancy we see is because we use slopes (as produced by I wonder whether it makes sense to also nullify the translation along the Y-axis, this way making sure the two images really match in the focal plane. |
Initially I wondered if alpha prime was there for the device's lens specs as depicted in the later half of this video, (which was more deeply hidden within the link I posted above relating to Oculus Rift). |
The WebVR spec does not provide lens information and tells us to render to a rectangular region. I guess lens distortion is dealt with at a lower level. However, we should probably call the method Some devices may already give us focused frusta, and in this case the focal distance set by the proposed code will be off (sorry, I didn't look close enough). @njam Could you provide the contents of the It seems quite useful to allow the client to get / set the focal distance, for rendering 2D HUDs / scene-dependent enhancement of the 3D experience. However, a focus at infinity clearly does not make any sense and just seems a recipe for a poor experience. So what we should do is:
The code structure could be something like: var userFocalDistance = 0; // means "not set"
var focalDistance = calculateFocus( device, userFocalDistance );
this.setFocalDistance = function( value ) {
userFocalDistance = value;
focalDistance = calculateFocus( device, userFocalDistance );
};
this.getFocalDistance = function() {
return focalDistance;
};
// If there are misbehaved devices:
if ( focalDistance === Infinity ) {
var RelativeFallbackFocus = 0.25; // 0 (near) ... 1 (far)
this.setFocalDistance( near + ( far - near ) * RelativeFallbackFocus );
} |
Very interesting! I didn't know that WebVR provides a way to set the FOV. I'm using the experimental Chrome WebVR builds, so there's no "real" FOV-data available. This Chrome build provides an HMD device called "Mockulus Rift" with this FOV:
This means the focal distance is 1.90 meters, assuming my math is correct. Calculating the focal distance from the provided frustum angles might be a bit tricky. I'm wondering whether and how we should handle the case that they are not symmetric (i.e. right eye's On a side note the webvr-polyfill uses
You mean the y-translation of the eyes? I've never seen it not being zero, what does it mean? |
To make things worse: We should use world space distances when getting / setting the focal distance.
I simply wouldn't bother, guessing the probability to meet a device that recommends asymmetric eyes equals zero.
Setting clipping planes to arbitrary distances is (sadly common but) very bad practise, and likely to cause depth buffering artifacts. There's more precision for nearby depth values because of perspective division, but some platforms have lower depth buffers precision. The default of two meters could have been the reason why you perceived a focus at infinite distance. Are you sure your scene was at least two meters deep? ;-)
Seems so. I didn't try to make sense of it, but I got the same result taking a different route (pretty much the direct inversion of your code):
|
Very interesting. I think we should also consider moving all this into a new |
Not so sure for all of this stuff: I think it makes sense to keep the "lobotomic trigonometry tango" in Interestingly, OK, let's build a When rendering to a 2D display, we usually don't care too much about realistic viewing: In fact, since the viewer focuses on the screen, we often crank up the viewing angle far beyond what would be realistic in case the screen was used as a window into another world. In that case, the viewing angle would depend on the size and the distance of the display and we'd also be concerned about the scale. Now, with stereo displays we obviously have to care about scale: Our eyes happen to have a physical distance. How does it translate to the units of our virtual world? More precisely, how can we calculate the amount by which to translate the cameras? Let's assume the kind-of tunnel vision mentioned before can still work to our advantage when using some kind of 3D monitor. Also, the (good, old, "non-lobotomic") viewing angle is known to be intuitive enough to be provided by the client. For HMDs, WebVR provides enough information to derive it automatically. From the viewing angle we can get the slope of the frustum width, IOW the frustum width at any distance along the optical axis. With a stereo display, the screen is perceived at the focal distance. Given this distance in virtual units and the physical size of the display, we can determine the scale and thus the eye distance to skew our projection matrices. The physical size of the display is easy to come by for monitors and projectors, and we can set a guesstimated default (e.g. unreliable DPI * resolution * 0.0254 "/m) for the monitor case. Although the physical size of the screen inside an HMD can be determined using a screw driver, the lens system can transform the optical size & position of the screen, which is the information we're really interested in, to pretty much anywhere. However, it can again be derived from the information provided by WebVR, given that we get a focused
Summing up the properties of
Makes sense? |
Yep! So |
That's how I would start. The idea was to finally allow all of the classes to use a central |
Sounds good! |
Very clever with using What will BTW here's the data Google is collecting about their Cardboard viewers: Sorry for my sporadic replies, I'm currently travelling. I will be happy to work a bit on this next week or the week after! |
PS: #6609 creates the skew that is needed for VR effects and is compatible with the VR skew in 3DS Max, Maya, Mental Ray, etc. |
Thanks for filing that PR, @njam .
Interesting! What was the motivation for that compatibility? Given the parameters usually depend on device / installation, does it make much sense to import them? I don't think that above use cases can easily be tackled based on these parameters. Although they could be used as the low-level interface for setting up the matrices. |
It is mostly the above. The above formulation is the official formulation for stereoscopic skew based on projection plane offset. A renderer I wrote for stereoscopic effects (used on Mad Max, Jurassic World, Avengers 2...) used the above matrix formulation for skew for pixel perfect matching with other renderer and tool results (sub-pixel perfect matching is required for compositing different partial results together into the final result you see on screen.) We can add more options on top, but this is the base formulation I would recommend. BTW Clara.io already supports these parameters via Film Size and Film Offset (the same parameters as Maya exposes.) |
To clarify, I was talking about the cases for interactive rendering discussed throughout this thread, such as
These are anything but straightforward having to mess with "Film Size" and "Film Offset" directly. BTW I very much prefer these names over anything that contains "projectionPlane", which in fact is a synonym for "near clipping plane" with hardware z-buffering and thus rather confusing. Note that I've been avoiding the term "focal length" for a similar reason; it's defined as the distance of the projection plane (= near clipping plane) from the focal point (of the projection) thus focalLength Also note that when writing a software renderer, the projection plane can be decoupled from clipping considerations, IOW the projection plane can be any slice of the frustum orthogonal to the optical axis, so the naming is less confusing in this context: The projection plane just defines a mathematical frame of reference for the projection: |
It isn't that big of an issue, but technically I've never actually seen "focal length" be synonymous with near clipping plane, but I guess they could be. BTW Clara.io implements properly focal length and DOF derived from focal length, F-stop etc.: https://clara.io/view/d328427e-70de-491c-bc1f-8c6bc661b74f ) I guess you can have projectionPlane be the same thing as nearClippingPlane although I haven't seen this, but remember that the formulation in the PR I proposed actually just used projectionPlane so it could calcuation a ratio of projectionOffset to projectionPlane, a result that ends up unit less anyhow (thus it actually doesn't matter where those two are located, whether in front or behind the focal point because the divergence ratio will be the same.) |
Anyone planning on implementing the Also, should I merge this PR? |
Once the shading system stuff is pushed & merged. That is, unless someone else picks it up first.
No - would be breaking. It doesn't consider the device info and puts the adjustments on top (details somewhere above). |
@tschw, just remember as you do this I will want to ensure that it is still compatible with how cameras are represented in Maya, 3DS Max, and all of the production ray tracers (ARnold, V-Ray, etc.) I can share well tested code in that area if you want as I have achieved absolutely perfect camera matching with Maya/V-Ray/Arnold with a full camera parameter set for stereoscopic rendering. I am okay if your camera setup is a superset of 3DS Max/Maya, etc,... I do not want to limit you. :) Just we can not invent something new that isn't capable of doing what is already the industry standard for stereoscopic camera setups. |
Yay! |
Moving slowly but moving... http://tschw.github.io/angler.js/app Right-click on Shadertoy content to control the camera (W,A,S,D,Z,C,Q,E & mouse to position the camera, Y to lock the Y axis to the global coordinate system). |
This seems neat @tschw , but none of the keys do not seem to do anything for me, and right click just brings up a context menu. I'm on Chrome beta on Windows 10 x64. |
Thanks for reporting. Can you confirm you are viewing a I'm asking because there's no right click handler for images and videos and the context menu is quite normal in those cases (there's nothing that can possibly be changed about the camera that was used for shooting the content, except for image level disparity adjustment and aspect correction - but those properties should probably be considered external to the camera). I get proper pointer lock with Chromium 48.0.2564.23 (64-bit) which seems fairly close version-wise. |
/ping @bhouston I would also be super curious to know whether the stereo parameters behave similar to the tools you are familiar with. Ended up with two decorrelated values to control the stereoscopic effect. |
I did confirm I was viewing a *.frag. Nothing could make it navigate on my machine, maybe it isn't supposed to? BTW here is battle tested code for creating a stereoscopic matrix that is pixel-perfect compatible with Maya, Softimage, V-Ray and Mental Ray and was used on a ton of major films for their 3D smoke effects:
More details here, see H/V Film Aperture, and H/V Film Offset: https://knowledge.autodesk.com/support/maya-lt/learn-explore/caas/CloudHelp/cloudhelp/2015/ENU/MayaLT/files/GUID-35B872B1-840E-4DEE-B80F-F2715B1E8BF0-htm.html Now a very important caveat, the above is for stereoscopic film, where one uses the same projector (size and position) for both eyes. This actually is different than the VR case were the projector for each eye is distinctly positioned in front of the eye and the FOV could vary to the left and to the right for each eye (because of the positioning of the image in front of the eye.) Thus the VR specifications give out distinct FOV values for top, left, bottom right separately for each eye as well as the eye offset from center -- just follow those directly for best results: https://developers.google.com/cardboard/android/latest/reference/com/google/vrtoolkit/cardboard/FieldOfView#public-constructors |
No, certainly isn't. Thanks. Hopefully this commit fixes it.
Ah cool! So that's how the standard renderer interface looks like - low level, per-cam info and the displacement of the cams is in their positions. Actually the quotient This information must of course come from somewhere. In their low level form, these parameters are hard to control, because all values are codependent. I found this page on the Autodesk site you cited. After Effects seems to have similar stereo settings. This is the kind of stuff I'm after in this thread; to allow the properties of a stereo system to change individually - without an influence on each other. Interaxial distance and FOV must be chosen appropriate for the display setup (load your favorite FPS -stereo not required- and play it on a projector if you want to know how simulator sickness feels like :). The interesting value to change is the convergence distance, in order to force the focus to a specific depth. FOV can be changed for a zoom effect, but relative to what is sound according to the display setup. It also makes sense to keep the interaxial distance relative to the tangent of the FOV: This way it remains constant in respect to the screen size and zooming in won't yield insane parallax. Interesting facts to note about FOVx are
Good point. Calibration is described in the MDN article "Using the WebVR API". After calibration, except for the possible relative scale you mention, it's not so much different: The optical system engineered into the device makes you perceive the screens somewhere else. For the eyes it's still a single image, there's still a common (virtual) screen of some (optical) size at some (optical) distance that both eyes look at. It really couldn't work any other way. The distortion of the lenses is dealt with at driver level and is non-linear, so doesn't affect the matrices.
These angles are by no means fixed (see link above). They are a more human readable representation of a projection matrix minus clipping planes. Their tangents can be plucked in there pretty much directly - that's why the API is like it is. However, one really shouldn't mess with the FOV too badly. The API provides min/max pairs for all the angles. Also, it seems advisable to use the setters to communicate the changes back to the driver level. Luckily the changes of these angles are fairly minimal when changing the convergence distance. Unless doing it wrong and accidentally changing absolutely everything, that is :).
This one is actually very subtle. A quality device will report not only a translation along x but also along z. Why? Because it allows you to safely scale the stereo system to your scene without messing up all its properties. Just see below. Note that the cameras have to be moved back to resemble the same frustum. BTW it's currently implemented incorrectly in Three.js here - only |
Here are some other docs of high level setup parameters: http://docs.chaosgroup.com/display/VRAY3MAX/Stereo+3D+Camera+%7C+VRayStereoscopic
I would strongly advocate a design that leaves projection plane size, projection plane offset around in the camera so that it is compatible with existing workflows - this is what people get from Maya, Softimage, etc. The stereo camera rig that controls the individual cameras can use zeroParallexDistance and interocularDistance and all those other high level parameters to then control the low level eye Camera settings. That generally is how one does it. The reason why we should not simplify projection plane size and plane offset into a ratio, as you are suggesting, at least at the user parameter level, is that we need to preserve the film size (projection plane size) if we want physical camera DOF effects, rather than magic number DOF effects. This example here from a year ago does true physical camera DOF based on film size, focal length: https://clara.io/view/d328427e-70de-491c-bc1f-8c6bc661b74f
I think it would be great. I had tried to get projectionplane offset/size in as a PR exactly a year ago:
The standard name for that in the industry is "Zero Parallax Plane" which is usually a distance (maybe zeroParallaxDistance) one can set and is distinct again from the projection plane to which the projection plane offset is relative. I would love to see this in ThreeJS proper. :) But I would keep the StereoCamera doing the interocular stuff and the parallax plane while the individual camera just has filmSize/filmOffset or projectionPlaneSize/projectionPlaneOffset if possible to avoid over simplification that moves away from physical cameras. Not sure how to relate this to the WebVR standard so that there were minimal special cases, but it would be great to do that. |
I thought they gave out the different FOVs for left and right because our eyes have different FOVs for left and right -- our eyes regularly handle only partially overlapping views. I was hoping the VR standard was anticipating more immersive displays coming that would have non-perfectly overlapping views: But you are saying I was incorrect on that? Okay. I didn't look into it too far. :) |
Here is the Maya stereo camera rig that sets up the individual camera settings: http://download.autodesk.com/us/maya/2011help/Nodes/stereoRigCamera.html It seems to have three stereo modes: converted, off-axis and parallel. The standard one I've seen used is off-axis. For off-axis it seems that these are the relevant parameters:
I am a little confuses as to why it lefts you also set the filmOffsets manually, I think that they are actually computed in the rig automatically in the "off-axis" mode and this is a secondary offset. |
But I guess I do not want to be doing the gauntlet thing to you on this issue, and my preferred design isn't that important to me anyhow. So I likely will support your PR if it is an improvement over what we have right now. |
Convinces me. I guess this existing code would also better change then - and I guess I could use your help with it: https://github.com/mrdoob/three.js/blob/dev/src/cameras/PerspectiveCamera.js#L29-L42 Odin's bloody hammer... Viking likes :).
I'm not!!! Probably should have used "off-axis zoom" instead of "scale" for it to read more clearly. I'm just pointing out that API designers thought about it. Once we have measured reference points that work during calibration, there's a way to deal with it.
Only considering off-axis projections, here.
plus FOV, which is not limited to stereo, it's what it all boils down to. Units should be pixels or (EDIT: Probably a BAD idea. Pixels are way better when the window is being resized.) This way, once configured the values are constant and touching one won't require changing the others because the change has resulted in a new scale of their units. |
I thought they would be world units? Everyone else uses world units for this separation as far as I know. |
Yes, I was similarly confused by the Motionbuilder settings. Maybe values below update when changing the ones above (?!)... |
This way it's codependent with the FOV angle. When zooming in by narrowing it, we'd experience extreme sudden parallax, for instance (unless of course adjusting it to the new resulting world scale). When decorrelated from the other values, |
|
Sounds good. Currently, EDIT: Been talking about the base revision of my branch. Obviously has a property on current |
It sort of is, just not really used consistently: https://github.com/mrdoob/three.js/blob/dev/src/cameras/PerspectiveCamera.js#L13
Technically filmSize is independent of aspect ratio, it is the size of the digital sensor or actual film, see: https://en.wikipedia.org/wiki/Anamorphic_format And even with full digital pipelines, animorphic is still supported: http://www.red.com/learn/red-101/anamorphic-lenses RED advocates not using animorphic lenses for capturing aspect ratios that do not fit the film size, but rather keeping the incoming aspect ratio as one projects onto the filmSize just not utilizing the full filmSize for capture, leaving some pixels unused (e.g. the notorious black bars.) This means that while one has a filmSize, there is also an effective filmSize defined by best fitting the aspect ratio. That gets a bit technical though, and not sure how relevant this is. |
The main complication that ThreeJS has is that the render window aspect ratio constantly varies based on window size or phone orientation. I have problems figuring out how to map that to a traditional camera setting where the aspect ratio and film size is fixed. |
Maybe the solution to ThreeJS's every changing aspect ratio is to introduce an effectiveFilmSize getter that does a best fit of the aspect ratio into the specified filmSize -- thus taking the RED camera suggested approach. Then one will not get unwanted animorphic distortion but one can still calculate physically correct DOF and set the camera up using physical parameters without worrying about the user's display aspect ratio. |
YAGNI maybe?
I can't really say I like the extra complexity it adds. I guess we're effectively talking about the aspect of the DOF blur? Would we consider it, even? If so, how about Feels right? |
Well, if we have filmWidth or filmHeight (or a filmSize as a scalar, which defines the max width/height) we do not need filmAspect, we'd just use "aspect" as the aspect ratio. |
I think the technical term for a single dimension film parameter is filmGauge: |
I think this works because filmOffset becomes singular as well and it is only the width / horizontal dimension that generally matters anyhow. One can use filmOffset / filmGauge in a single dimension to determine stereoscopic camera skew. I'll be honest am not confident as we invent new things here that they are the right solutions. |
I guess the root of our problem might be, that for windowed rendering we have film !== projection plane, after all. The projection plane ends up on the screen, where the film size (or its aspect) OTOH defines the distortion of an anamorphic projection lens. Both seem very different pairs of shoes. When specifying FOV in a photographic fashion, however, both do refer to the same length in different units, if you like. In this case we only consider one axis, so it's essentially scalar. Seems we primarily need
. This also allows us to turn the half-assed
(...and if it's the max we'd have to check aspect <> 1 every time we apply it, so it's kinda impractical)
Depends. As long as we're fine with isotropic DOF blur, yes. If we want the COC to become an ellipsis of confusion due to an anamorphic projection lens, we do need it (note: Different magnification along X and Y - I know you actually know, but could be someone tries to follow our discussion). Maybe something like We could either leave it out and add it once we actually implement anisotropic DOF, or add it to the camera interface for completeness and also to the code interfacing with Maya and friends. You brought up the DOF case, so please say something :).
Right. So let's look at our requirements, again. Since stereo seems to be the only use case at this point, I still think it might be just fine to go with the bare shear. But since we already defined I think it all makes sense and the import should be cake enough. |
|
This is more of a question, as I don't fully understand the topic yet.
This article recommends to use two parallel frustums for stereoscopic rendering, and cutting off a bit on the outer side of each image. This would be done so that the two rendered images overlap completely at the focal plane:
I understand the current
VREffect
doesn't do it, so the focal length is infinity.Is this correct? Would it make sense to allow configuration of the focal length?
Does anyone have experience how it affects the experience?
cc @dmarcos