mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.77k stars 35.38k forks source link

Enhancement: Game SpriteHUD..Avoiding 2x-Render #4795

Closed erichlof closed 8 years ago

erichlof commented 10 years ago

Hi all, I am a lone-wolf 3D game programmer working with three.js. My most recent game project is a space shooter where some kind of HUD is needed. I was recently looking at this thread for possible solutions: https://github.com/mrdoob/three.js/issues/1959 and https://github.com/mrdoob/three.js/pull/4121

However, @mrdoob and @WestLangley , your updated sprite example requires an orthographic camera (in addition to the perspective one) and a second rendering pass.

I was working on my game when I 'stumbled' upon a nice way to circumvent having to render the scene a second time with a second camera.

The solution is instead of adding the sprites to the scene object as we normally do for everything else, we make the sprite a 'child' of the camera object. Like so: camera.add( crosshairSprite );

Now, whatever image you have loaded for 'crosshairSprite' will stick to the center of the screen no matter what. No orthographic camera, no second rendering pass.

I threw together a basic demo showing off the new capabilities. I positioned the 'redball' Sprites in the four corners of the webpage (a la your Sprite example) and I manipulate them in various ways. Notice there should not be any performance hit while using this method.

Press 'M' for MouseLook, Space to thrust, LMButton to Fire http://erichlof.github.io/AsteroidPatrol3D/SpriteHUD.html

You can drag the browser-window's dimensions around, shrink, squash, stretch, etc.. - and the sprites and HUD objects positions will remain relative to their desired initial position on the webpage.

To further this method's usefulness, I created a simple formula to make the X and Y HUD coordinates available to the threejs user by specifying a percentage of the webpage (kind of like one of the various HTML-element positioning options):

var crosshairPercentX = 50; //middle horizontally var crosshairPercentY = 50; //middle vertically var crosshairPositionX = (crosshairPercentX / 100) * 2 - 1; var crosshairPositionY = (crosshairPercentY / 100) * 2 - 1; crosshairSprite.position.x = crosshairPositionX * camera.aspect; crosshairSprite.position.y = crosshairPositionY; crosshairSprite.position.z = -1.5;

This way, you can specify the X percentage (0 being leftmost, 50 being middle, 100 percent being rightmost) of the webpage. Same for the Y percentage (0 being bottom, 50 being middle, and 100 being top of the webpage).

This trick also works with 3D objects. I put a slowly rotating Tetrahedron at the bottom of the HUD and it stays right there even when you change your player's ship rotation. Also, since they are rendered with the same camera as the rest of the scene/world, you can interact with them. (Although raycasting a HUD/menu item with a mouseclick might require adding it to the 'scene' as well, as is traditionally done).

Hope this helps some of you game/threejs programmers out there looking to do quick HUDs! -Erich

mrdoob commented 10 years ago

Nice! I'll try to add this in the pointerlock example :)

WestLangley commented 10 years ago

@erichlof Thank you for your demo and explanation.

It depends, of course, on the effect you want. In the three.js demo http://threejs.org/examples/webgl_sprites.html, the HUD sprites remain a constant size, and a constant offset from the edge of the screen when the window is resized. You could, perhaps, achieve the same effect with some clever mathematics.

Also, I am not sure there is a significant performance hit from a second rendering pass with an orthographic camera. Do you have evidence otherwise?

To me, a HUD overlay with an orthographic camera is more natural, especially when the overlay is rendered in a separate coordinate system.

erichlof commented 10 years ago

@WestLangley Hi, yes traditionally a HUD is done with a separate rendering pass and orthographic camera. This was the way I learned how to do it when I was starting out in the late 90's. But as we game programmers typically like to do, we try to save any calculations that we can and cut corners where we can get away with it, for the sake of framerate. Of course my little HUD example might not be affected by a second orthographic camera pass, but I have a hunch that a real intensive HUD in a FPS or space-themed game would start to show some degradation in performance.

Unfortunately I don't have raw numbers to back my hunch up but if someone were willing to test a heavy sample HUD using both methods I would gladly go back to orthographic and 2x render if impact was found to be negligible.

Yes I did some rough math to get the sprites to stay relatively positioned in the window, even with resizing and on mobile and tablets , etc.. it should work consistently across all devices. If it feels like a hack, that's because it is - ha ha. :-)

But hopefully this will allow someone to just drop in sprites and HUD-geometry quickly and easily. Thanks for three.js - I really appreciate all your work! -Erich

WestLangley commented 10 years ago

@erichlof Since both approaches result in the same number of draw calls, my personal opinion is I like your idea for cross-hairs, but with all due respect, for a HUD in general, with its own coordinate system -- not so much.

erichlof commented 10 years ago

Hi, that's cool. Yes it works quite well for crosshairs. All that is needed is one line of code ( to set the sprite as a child of the camera) and you've got instant crosshairs centered in the screen - almost no effort required. Again thanks @mrdoob and @WestLangley for your work on the awesome three.js framework. What used to be a nightmare getting cross-platform games to work , is now much less painful and actually possible with your library. Keep up the great work guys! :-)

mrdoob commented 10 years ago

It's our pleasure! :D

IceCreamYou commented 10 years ago

For anyone who comes across this issue in the future, I just want to comment that another (probably faster) way to do a HUD for a WebGL game is to make the HUD in HTML and just set the CSS pointer-events: none on the HUD element(s). That way updating them stays out of the Three.js rendering loop. Just because we can do anything in WebGL doesn't mean everything has to be in WebGL. :smile:

erichlof commented 10 years ago

Hi d3nd3, Yes it is necessary to add the camera to the scene. I just tried leaving it out, and the world still renders, but the sprites disappear. So you need somewhere in your init() function, the following: scene.add(camera); Sorry for my late reply, the message got buried in my email inbox. Good luck! :) -Erich


From: d3nd3 notifications@github.com To: mrdoob/three.js three.js@noreply.github.com Cc: erichlof erichlof@swbell.net Sent: Friday, September 12, 2014 11:35 AM Subject: Re: [three.js] Enhancement: Game SpriteHUD..Avoiding 2x-Render (#4795)

@ erichlof is it necesary to add the camera to the scene? — Reply to this email directly or view it on GitHub.

trinketmage commented 8 years ago

Hello, I am quite new to threejs, don't be too harsh if i'm telling any wrong stuff :) I was working on a crosshair for VR in three.js and it worked fine since the moment i have to move the location of my camera cause of the zBuffer rendering when i got closed to some objects. I tried to set renderer.sortObjects to false, but with lots of objects in the scene loaded from files.obj I got a lot of troubles reordering elements and children elements. Like in this example where objects are messed up : http://jsfiddle.net/QHssJ/20/. I believe that it might be a solution with reordoring with a "vector" from the camera but that this will cost pretty much ? Can anyone tell me tips to measure difference within two codes out of fps parameter please ?

To avoid too much struggle, i opt for the two scenes rendering. For VR, html rendering is not possible as its Orthographic (am i wrong ?) and VR need Perspective, so i came out with this solution : https://github.com/trinketmage/three.js-vr-hud. I would appreciate much any feedback, to level up my code.

mrdoob commented 8 years ago

@trinketmage please use StackOverflow for help.