xeokit-sdk
xeokit-sdk copied to clipboard
[USAGE TIP] Workaround for low perceived quality on devices with high DPI
On devices where window.devicePixelRatio
returns a value greater than 1 , there is a visual reduction of perceived quality on the 3d rendering.
This happens on most mobile devices (pads and phones), where the canvas size as calculated by the viewer/scene/canvas/Canvas.js
class can have a much lower pixel count than what's the phyisical pixel count of the canvas, due to DPI scaling by the browser.
One simple solution seems to be allowing to externally provide the desired devicePixelRatio
(ideally taken from window.devicePixelRatio
) when creating the instance of the Viewer
class and propagate it all the way down to the Canvas
class (falling back to 1, for example) so that it can take into account that "scaling factor" when setting the canvas width/height and the viewport bounds.
I've did a quick test and that seems to have the effect of now having nice sharp edges on the mobile viewer (both tablet and phone), although I still have some issues with it (e.g. performance picking seems to suffer a clipping efffect (it only works within a region of the viewer canvas)).
If you want, I can create a PR with those changes (propagating the devicePixelRatio
down to the Canvas
) so that we can collaborate further in solving this 😃
Would it be possible to encapsulate this within Viewer
so that it's transparent to the user?
Hello 🙋
I managed to solve the problem with performance picking 😀.
The idea, as you @xeolabs suggest is to provide a new devicePixelRatio
parameter to the configuration options during Viewer
creation, which will fallback to 1.0 if not provided (this is, same behaviour as without this change), and from there automatically propagayted to where it's needed. This will avoid introducing and additional dependency to the window
object inside xeokit's code.
What I will try to do now is to implement it with a setter/getter, as this could then act as a dynamically adjustable "render quality" switch (although only for devices where window.devicePixelRatio
> 1).
I did some quick tests with its performance, and for example on an iPad with retina display where window.devicePixelRatio
is 2 (this means rendering as much as 2*2 times the number of pixels), the freame rate drop is only about 30% but with much finer perceived render detail (the render then does not seem "downsampled").
Just stay tuned, I will try to send a PR for this one at the end of this week 😀.
Hi! I would like to know if this functionality that tmarti describes has been implemented into XeoKit. Or how to implement it if it is not. Could anyone point me in the right direction?
Hey @david-San, I had this code prepared somewhere but within our private fork of xeokit, so I can't really share the code.
BUT the idea was to propagatewindow.devicePixelRatio
all the way down from the scene creation into the canvas.
Things that I remember to take into account:
- all rendering target surfaces must be scaled according to that parameter
- the canvas resize listener also needa to take into account the parameter
- ray picking also needs to take into accpubt that parameter
I think those were all important aspects to take into account to have nice sharp edges :)
Just found a really easy solution to this problem, tested on Chrome, Firefox and Safari:
<style>
body {
position: absolute;
margin: auto;
transform-origin: top left;
}
#xeokit_canvas {
width: 100%;
height: 100%;
}
</style>
<body id="the_body">
<canvas id="xeokit_canvas"></canvas>
</body>
<script>
the_body.style.transform = "scale(" + (1/window.devicePixelRatio) + ")";
the_body.style.width = (window.devicePixelRatio*100) + "%";
the_body.style.height = (window.devicePixelRatio*100) + "%";
</script>
The point is that by default the viewport is downscaled according to DPI scaling (window.devicePixelRatio
).
So if for example that factor is 2.5, the viewport is downscaled by that number... then the trick is to upscale the body by that factor 😉 (adjusting width
and height
). Doing that alone would not improve the pixel density (even worse, would introduce scrollbars), and for this reason we apply a visual downscale with the same factor (transform.scale=...
).
We need a combination of CSS styling and JS code because the devicePixelRatio
value is only known to the Javascript engine (I believe it cannot be accessed and used to style the body from CSS).
Enjoy! 😃
Hi tmarti! Thank you so much for sharing this.
This is excellent! I tried it and it works. I can now get a very high resolution rather than the grainy effect.
The only thing that I have not found a solution when implementing your code is that everything on the screen looks very small since text is following the window.devicePixelRatio change.
Do you have any ideas how to overcome this problem? Changing the font size to huge seem like the evident solution but I am not sure if there is a more elegant solution.
Maybe you could create a container div also with position absolute to that it overlays with the canvas, and apply the inverse transform (i.e. a downscale transform) to it?
Just guessing ;)
El dv., 6 des. 2019, 13:25, david-San [email protected] va escriure:
Hi tmarti! Thank you so much for sharing this.
This is excellent! I tried it and it works. I can now get a very high resolution rather than the grainy effect.
The only thing that I have not found a solution when implementing your code is that everything on the screen looks very small since text is following the window.devicePixelRatio change.
Do you have any ideas how to overcome this problem? Changing the font size to huge seem like the evident solution but I am not sure if there is a more elegant solution.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/xeokit/xeokit-sdk/issues/46?email_source=notifications&email_token=AASLIJSL5NA565ZXWN5WRV3QXJAEZA5CNFSM4HEJZL6KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEGD52SY#issuecomment-562552139, or unsubscribe https://github.com/notifications/unsubscribe-auth/AASLIJUKFPLPESJZCWHODJLQXJAEZANCNFSM4HEJZL6A .
Is there a more recent solution ? The CSS transform scale adds a lot of latency during the manipulation of the 3D model.
I just found this :
this.viewer.scene["canvas"].resolutionScale = window.devicePixelRatio;