Orbitcontrols three js как подключить
Перейти к содержимому

Orbitcontrols three js как подключить

  • автор:

Orbit controls for threejs from the project itself

It would not be that hard to implement some camera controls for a three.js project from scratch, it would just involve some event handlers that would be used to set the position and rotation of the camera using some Object3D class methods. However there is some additional resources in the three.js project repository itself that can be used to quickly set some orbit controls in a flash which can be found in the examples folder of the repository. In this post I will be covering how to quickly set up these orbit controls for the camera, so you do not have to keep changing hard coded values, or spend a great deal of time working on your own solution to just look around a scene this way.

The Orbit Controls solution that can be found in the threejs examples folder in the github repo of the project can be used to quickly set up a solution for panning, zooming, and changing the orientation of a camera with the mouse, keyboard, and touch events. So then these source code examples will need threejs, as well as OrbitControls.js on top of the additional code that I am going over in this post.

Orbit controls in threejs and what to know before hand

This is an advanced post on one of the many useful time saving features that are found in the form of additional javaScript files in the threejs examples folder in the three.js github repository. If you are looking for my take on getting started with three.js I have wrote a post on that topic before, and what I am writing about here is just a few steps beyond that. So in this post I assume you have a basic working knowledge of javaScript, and threejs, and are wondering if there is some kind of official solution for quickly adding some orbit controls to a threejs project. With that said yes there is, and I will be writing about at least a few examples that make use of these orbit controls.

The source code examples here are on github

The source code examples here that make use of threejs, and OrbitControls.js can also be found in my test threejs repository. This is also where I park the source code examples for my many other blog posts on threejs as well.

Where to get the file for orbit controls

In order to quickly add Orbit controls you need to add a *.js file that is in the three.js repository that can be found here. You will want to add this file to your project in a way so that it will append three.js, and add a constructor called THREE.OrbitControls.

Version Numbers Matter, and code breaking changes

Yes version numbers matter when working with three.js. When I first wrote this post I was working with r91, and as of this writing when I took a moment to edit this post last I was using r127. Sense then some code breaking changes have happened so be aware of what version of threejs you are using. The main change that I have noticed sense r91 is that it is now required to give a dom element reference as the second argument when calling the main orbit controls constructor function. I will still keep the old r91 examples in this post just for the hell of it as many of the features still seem to work more or less the same way when the few required changes are made.

1 — Using the OrbitControls constructor

If you all ready know how to create a basic scene in threejs, and you have a proper copy of the orbit controls added to the page along with the corresponding version of threejs, then adding orbit controls is not all that hard. The THREE.OrbitControls constructor just needs to be called passing a camera that the orbit controls will be controlling as the first argument. On top of the camera a dom element reference might also have to be added if you are using a later version of three.js ( at least r125 forward as of this writing )

1.1 — Example of the OrbitControls constructor (r127)

When using the orbit controls constructor in r125 I now need to pass a camera as the first argument like before, but now I also need to pass a dom element reference as the second argument. Once you have an instance of the controls you just need to call the update method of the controls in a render or update loop.

1.2 — Old example of the OrbitControls constructor (r91)

When I first wrote this post I was using r91. With that version of threejs I just need to call the constructor passing the instance of the camera I want to to control which will give me an instance of THREE.OrbitControls. However in late versions of threejs this might cause an error.

2 — Basic example of Orbit controls (r125)

I just need to call the THREE.OrbitControls constructor and pass a camera as the first argument, and then a dom element as the second argument. The dom element can be the dome element that is used in a renderer such as the built in web gl renderer.

So this new example is more or less the same as the old r91 example. It would seem that the only change is that I now have to give that dome element reference.

3 — Full working Example of three.js Orbit Controls (r91)

For a full working example of this I will of course want something to look at such as a cube, and of course I will want the usual scene, camera, and renderer. In my html not only will I want to load three.js like normal, I will want to see to it that the Orbit controls are added to three.js, as or r91 they are not part of three.js itself, so it must be added by some means.

3.1 — The html (r91)

I just need to like to threejs, the orbit controls, and then the main.js file that contains the rest of the javaScript code. I also have a single div element that will serve as a mount point for this.

If all goes well I should now be able to use the OrbitControls constructor just like any other constructor in three.js.

3.2 — js (r91)

Now for the javaScript for this basic orbit controls example.

From here I have my basic example of how to go about using the orbit controls to quickly allow for the basic control of the camera that is typical of many games, and 3d modeling programs. From here it is just a question if you want to further tweak the behavior of the controls, there are plenty of additional properties to do that.

4 — AutoRotate

A common task with any three.js project is to have the camera rotate around a mesh or scene that you have made. With the orbit controls constructor there is an boolean that can be set true to start an automation rotation around a target position.

By default the target position is the origin, but you can use the vector3 to change it to another point. Speed can be set as well by giving a Number to the autoRotateSpeed property where 1 will mean it will take about a minute to make one rotation ad sixty frames per second.

5 — Min max values

There are a bunch of properties that can be used to set man and max values for the two angles of rotation Azimuth, and Polar angle of the Spherical coordinate system.

There are also a bunch of other values that can be used to set limits on distance, and paining. The range of the values for the min, and max values for the angles range from -Math.PI to Math.PI

6 — Set the panning mode

There is an option to set the panning mode for panning. By default the orbit controls use Screen Space Panning, but it can be set to Horizontal Panning if desired.

7 — Enable Damping

There is the Enable Damping Boolean value that can be set true to give the controls some inertia. If you enable this you may also want to play around with the damping factor value as well.

Extend three.js With a Camera Controls Plugin

The three.js core is a powerful, lightweight, and focused rendering framework, with intentionally limited capabilities. It has everything you need to create and render physically correct scenes, however, it does not have everything you need to create, say, a game, or a product configurator. Even when building relatively simple apps, you will often find yourself needing functionality that’s not in the core. When this happens, before you write any code yourself, check to see whether there’s a plugin available. The three.js repo contains hundreds of extensions, in the examples/jsm folder. These are also included in the NPM package, for those of you using a package manager.

There are also a huge number of plugins to be found scattered around the web. However, these are sometimes poorly maintained and may not work with the latest three.js version, so in this book, we’ll restrict ourselves to using the official plugins from the repo. There, we’ll find all kinds of plugins, and most of them are showcased in one of the examples. These add all kinds of functionality, such as mirrored surfaces:

Or, how about a loader for the Lego LDraw format:

Here are a few more:

  • One of the many post-processing effects
  • A loader for the Autodesk FBX format
  • An exporter for glTF format
  • Physically accurate ocean and sky

Each extension is stored in a separate module in examples/jsm, and to use them, we simply import them into our app, much like any other three.js class.

Our First Plugin: OrbitControls

One of the most popular extensions is OrbitControls , a camera controls plugin which allows you to orbit, pan, and zoom the camera using touch, mouse, or keyboard. With these controls, we can view a scene from all angles, zoom in to check tiny details, or zoom out to get a birds-eye overview. Orbit controls allow us to control the camera in three ways:

  1. Orbit around a fixed point, using the left mouse button or a single finger swipe.
  2. Pan the camera using the right mouse button, the arrow keys, or a two-finger swipe.
  3. Zoom the camera using the scroll wheel or a pinch gesture.

You can find the module containing OrbitControls on the three.js repo, in the examples/jsm/controls/ folder, in a file called OrbitControls.js. There’s also an official example showcasing OrbitControls . For a quick reference of all the control’s settings and features, head over to the OrbitControls doc page.

Importing Plugins

Since the plugins are part of the three.js repo and included in the NPM package, importing them works in much the same way as importing classes from the three.js core, except that each plugin is in a separate module. Refer back to the intro for a reminder of how to include the three.js files in your app, or head over to the appendix for a deeper exploration of how JavaScript modules work.

In the editor, we’ve placed the OrbitControls.js file in the equivalent directory to the repo, under vendor/. Go ahead and locate the file now. Since the editor uses NPM style imports, we can import OrbitControls like this, from anywhere in our code like this:

Importing the OrbitControls extension using NPM style imports

Once again, if you’re working locally and not using a bundler, you’ll have to change the import path. For example, you can import from skypack.dev instead.

Importing the OrbitControls extension using relative imports

Important note: Make sure you import plugins from examples/jsm/ and not legacy plugins from examples/js/!

The controls.js Module

As usual, we’ll create a new module in our app to handle setting up the controls. Since the controls operate on the camera, they will go in the systems category. Open or create the module systems/controls.js to handle setting up the camera controls. This new module has the same structure as most of our other modules. First, import the OrbitControls class, then make a createControls function, and finally, export the function:

systems/controls.js: initial setup

Back over in World, add the new function to the list of imports:

World.js: import the controls module

Next, call the function and store the result in a variable called controls . While you’re here, comment out the line adding cube to the updatables array. This will stop the cube from rotating and make the effect of the controls easier to see:

World.js: stop the cube’s animation

Initialize the Controls

If you check out the OrbitControls docs page, you’ll see that the constructor takes two parameters: a Camera , and a HTMLDOMElement . We’ll use our camera for the first parameter and the canvas, stored in renderer.domElement , for the second.

Internally, OrbitControls uses addEventListener to listen for user input. The controls will listen for events such as click , wheel , touchmove , and keydown , amongst others, and use these to move the camera. We previously used this method to listen for the resize event when we set up automatic resizing. There, we listened for the resize event on the entire window . Here, the controls will listen for user input on whatever element we pass in as the second parameter. The rest of the page will be unaffected. In other words, when we pass in the canvas, the controls will work when the mouse/touch is over the canvas, but the rest of the page will continue to work as normal.

Pass the camera and canvas into the createControls function, then create the controls:

controls.js: create the controls

Back over in the world module, pass in the camera and renderer.domElement :

World.js: initialize the controls

With that, the controls should start to work. Take them for a spin!

You’ll immediately notice the cube is not illuminated from the back. We’ll explain why and how to fix this in the next chapter.

Working with the Controls

Manually Set the Target

By default, the controls orbit around the center of the scene, point $(0,0,0)$. This is stored in the controls.target property, which is a Vector3 . We can move this target to a new position:

Set the control’s target

We can also point the controls at an object by copying the object’s position.

World.js: target an object

Whenever you pan the controls (using the right mouse button), the target will pan too. If you need a fixed target, you can disable panning using controls.enablePan = false .

Enable Damping for Added Realism

As soon as the user stops interacting with the scene, the camera will come to an abrupt stop. Objects in the real world have inertia and never stop abruptly like this, so we can make the controls feel more realistic by enabling damping.

controls.js: enable damping

With damping enabled, the controls will slow to a stop over several frames which gives them a feeling of weight. You can adjust the .dampingFactor to control how fast the camera comes to a stop. However, for damping to work, we must call controls.update every frame in the animation loop. If we’re rendering frames on demand instead of using the loop, we cannot use damping.

Update the Controls in the Animation Loop

Whenever we need to update an object in the loop, we’ll use the technique we devised when creating the cube’s animation. In other words, we’ll give the controls a .tick method and then add them to the loop.updatables array. First, the .tick method:

controls.js: add controls.tick

Here, .tick simply calls controls.update . Next, add the controls to the updatables array:

World.js: add the controls to the updatables array

Now, controls.tick will be called once per frame in the update loop, and damping will work. Test it out. Can you see the difference?

Working With the Camera While Using OrbitControls

With the controls in place, we have relinquished control of the camera to them. However, sometimes you need to take back control to manually position the camera. There are two ways to go about this:

  1. Cut/jump to a new camera position
  2. Smoothly animate to a new camera position

We’ll take a brief look at how you would go about both of these, but we won’t add the code to our app.

Cut to a New Camera Position

To perform a camera cut, update the camera’s transform as usual, and then call controls.update :

Manually adjust the camera transform while using OrbitControls

If you’re calling .update in the loop, you don’t need to do it manually and you can simply move the camera. If you move the camera without calling .update , weird things will happen, so watch out!

One important thing to note here: when you move the camera, the controls.target does not move. If you have not moved it, it will remain at the center of the scene. When you move the camera to a new position but leave the target unchanged, the camera will not only move but also rotate so that it continues to point at the target. This means that camera movements may not work as you expect when using the controls. Often, you will need to move the camera and the target at the same time to get your desired outcome.

Smoothly Transition to a New Camera Position

If you want to smoothly animate the camera to a new position, you will probably need to transition the camera and the target at the same time, and the best place to do this is in the controls.tick method. However, you will need to disable the controls for the duration of the animation, otherwise, if the user attempts to move the camera before the animation has completed, you’ll end up with the controls fighting against your animation, often with disastrous results.

Disable the controls while animating the camera or target

Save and Restore a View State

You can save the current view using .saveState , and later restore it using .reset :

controls.js: save and restore state

If we call .reset without first calling .saveState , the camera will jump back to the position it was in when we created the controls.

Disposing of the Controls

If you no longer need the controls, you can clean them up using .dispose, which will remove all event listeners created by the controls from the canvas.

controls.js: remove all event listeners from the canvas

Rendering on Demand with OrbitControls

A couple of chapters ago we set up the animation loop, a powerful tool that allows us to create beautiful animations with ease. On the other hand, as we discussed at the end of that chapter, the loop does have some downsides, such as increased battery use on mobile devices. As a result, sometime we’ll choose to render frames on demand instead of generating a constant stream of frames using the loop.

Now that our app has orbit controls, whenever the user interacts with your scene, the controls will move the camera to a new position, and when this occurs you must draw a new frame, otherwise, you won’t be able to see that the camera has moved. If you’re using the animation loop, that’s not a problem. However, if we’re rendering on demand we’ll have to figure something else out.

Fortunately, OrbitControls provides an easy way to generate new frames whenever the camera moves. The controls have a custom event called change which we can listen for using addEventListener . This event will fire whenever a user interaction causes the controls to move the camera.

To use rendering on demand with the orbit control, you must render a frame whenever this event fires:

Rendering on demand with OrbitControls

To set this up inside World.js, you’ll use this.render :

World.js: Rendering on demand with OrbitControls

Next, over in main.js, make sure we’re no longer starting the loop. Instead, render the initial frame:

main.js: render a single frame instead of starting the loop

If you make these changes in your app, you’ll see that this results in a slight problem. When we render the initial frame in main.js, the texture has not yet loaded, so the cube will look black. If we were running the loop, this frame would almost instantly be replaced with a new one after the texture loads, so it might not even be noticeable that the cube was black for a few milliseconds. However, with rendering on demand, we are now only generating new frames when the user interacts with the scene and moves the camera. As soon as you move the controls, sure enough, a new frame will be created and the texture will show up.

As a result, you also need to generate a new frame after the texture has loaded. We won’t cover how to do that here, but hopefully, it highlights why rendering on demand is trickier than using the loop. You have to consider all situations where you need a new frame (for example, don’t forget that you’ll also need to render a frame on resize).

OrbitControls Configuration

The controls have lots of options that allow us to adjust them to our needs. Most of these are well explained in the docs, so we won’t cover them exhaustively here. The following are some of the most important.

Enable or Disable the Controls

controls.js: enable or disable

Or, we can disable any of the three modes of control individually:

controls.js: disable individual modes

You can optionally listen for key events and use the arrow keys to pan the camera:

controls.js: enable arrow keys

Auto Rotate

.autoRotate will make the camera automatically rotate around the .target , and .autoRotateSpeed controls how fast:

controls.js: enable auto-rotation

As with .enableDamping , you must call controls.update every frame for this to work. Note that .autoRotate will still work if the controls are disabled.

Limiting Zoom

We can limit how far the controls will zoom in or out:

controls.js: limit zoom

Make sure minDistance is not smaller than the camera’s near clipping plane and maxDistance is not greater than the camera’s far clipping plane. Also, minDistance must be smaller than maxDistance .

Limiting Rotation

We can limit the control’s rotation, both horizontally (azimuth angle):

controls.js: limit horizontal rotation

… and vertically (polar angle)

controls.js: limit vertical rotation

Remember, rotations are specified using radians, not degrees, and $\pi$ radians is equal to $180^<\circ>$.

A Glaring Problem!

As soon as we rotate the camera using our fancy new orbit controls, we’ll see a glaring problem. The camera rotates, but the light is fixed and shines only from one direction. The rear faces of the cube receive no light at all!

In the real world, light bounces and reflects off every surface, so the rear of the cube would be dimly lit. There’s nothing in this simple scene aside from the cube, so there’s nothing for the light to bounce off. But, even if there was, performing these calculations is much too expensive for us to do in real-time. In the next chapter, we will look at a technique for overcoming this problem known as ambient lighting.

Challenges

Try adjusting the control’s minimum and maximum zoom levels. What happens if you make these two values equal? Or make minDistance greater than maxDistance ?

Enable auto-rotation and then try adjusting the rotation speed.

Try disabling each of the three modes of control, one at a time, and observe the results.

Adjust the damping speed ( .dampingFactor ) to get a feel for how damping works. Values greater than 0 and less than 1 work best.

Medium

Try adjusting the control’s horizontal and vertical rotation limits. Remember, if you are working in degrees you must convert to radians. Look inside cube.js if you need a reminder of how that works.

Add a button (or a click event listener) to the page, and whenever you click the button, move the camera and control’s target to a new, random position. Try and constrain the movement so that the cube is always still somewhere on the screen.

Set up rendering on demand while using the controls, including generating a new frame after the texture has loaded, and whenever the scene is resized.

Can you make the camera and the control’s target animate to a new position over a few seconds? Maybe add a button to the page, and when you click it, play the animation. See what happens when you animate just the camera, or just the target, or what happens when you don’t disable the controls while animating. The best place to set up this animation is in the controls module.

Orbitcontrols three js как подключить

Orbit controls allow the camera to orbit around a target.
To use this, as with all files in the /examples directory, you will have to include the file separately in your HTML.

Import

[name] is an add-on, and must be imported explicitly. See [link:#manual/introduction/Installation Installation / Addons].

import < OrbitControls >from ‘three/addons/controls/OrbitControls.js’;

Code Example

Examples

[example:misc_controls_orbit misc / controls / orbit ]

Constructor

[name]( [param:Camera object], [param:HTMLDOMElement domElement] )

[page:Camera object]: (required) The camera to be controlled. The camera must not be a child of another object, unless that object is the scene itself.

[page:HTMLDOMElement domElement]: The HTML element used for event listeners.

Events

change

Fires when the camera has been transformed by the controls.

start

Fires when an interaction was initiated.

Fires when an interaction has finished.

Properties

[property:Boolean autoRotate]

Set to true to automatically rotate around the target.
Note that if this is enabled, you must call [page:.update] () in your animation loop.

[property:Float autoRotateSpeed]

How fast to rotate around the target if [page:.autoRotate] is true. Default is 2.0, which equates to 30 seconds per orbit at 60fps.
Note that if [page:.autoRotate] is enabled, you must call [page:.update] () in your animation loop.

[property:Float dampingFactor]

The damping inertia used if [page:.enableDamping] is set to `true`. Default is `0.05`.
Note that for this to work, you must call [page:.update] () in your animation loop.

[property:HTMLDOMElement domElement]

The HTMLDOMElement used to listen for mouse / touch events. This must be passed in the constructor; changing it here will not set up new event listeners.

[property:Boolean enabled]

When set to `false`, the controls will not respond to user input. Default is `true`.

[property:Boolean enableDamping]

Set to true to enable damping (inertia), which can be used to give a sense of weight to the controls. Default is false.
Note that if this is enabled, you must call [page:.update] () in your animation loop.

[property:Boolean enablePan]

Enable or disable camera panning. Default is true.

[property:Boolean enableRotate]

Enable or disable horizontal and vertical rotation of the camera. Default is true.
Note that it is possible to disable a single axis by setting the min and max of the [page:.minPolarAngle polar angle] or [page:.minAzimuthAngle azimuth angle] to the same value, which will cause the vertical or horizontal rotation to be fixed at that value.

[property:Boolean enableZoom]

Enable or disable zooming (dollying) of the camera.

[property:Float keyPanSpeed]

How fast to pan the camera when the keyboard is used. Default is 7.0 pixels per keypress.

[property:Object keys]

This object contains references to the keycodes for controlling camera panning. Default is the 4 arrow keys. controls.keys = < LEFT: 'ArrowLeft', //left arrow UP: 'ArrowUp', // up arrow RIGHT: 'ArrowRight', // right arrow BOTTOM: 'ArrowDown' // down arrow >See [link:https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code KeyboardEvent.code] for a full list of keycodes.

[property:Float maxAzimuthAngle]

How far you can orbit horizontally, upper limit. If set, the interval [ min, max ] must be a sub-interval of [ — 2 PI, 2 PI ], with ( max — min controls.mouseButtons =

[property:Camera object]

The camera being controlled.

[property:Float panSpeed]

Speed of panning. Default is 1.

[property:Vector3 position0]

Used internally by the [page:.saveState] and [page:.reset] methods.

[property:Float rotateSpeed]

Speed of rotation. Default is 1.

[property:Boolean screenSpacePanning]

Defines how the camera’s position is translated when panning. If true, the camera pans in screen space. Otherwise, the camera pans in the plane orthogonal to the camera’s up direction. Default is true for OrbitControls; false for MapControls.

[property:Vector3 target0]

Used internally by the [page:.saveState] and [page:.reset] methods.

[property:Vector3 target]

The focus point of the controls, the [page:.object] orbits around this. It can be updated manually at any point to change the focus of the controls.

[property:Object touches]

This object contains references to the touch actions used by the controls. controls.touches =

[property:Float zoom0]

Used internally by the [page:.saveState] and [page:.reset] methods.

[property:Float zoomSpeed]

Speed of zooming / dollying. Default is 1.

Methods

[method:undefined dispose] ()

Remove all the event listeners.

[method:radians getAzimuthalAngle] ()

Get the current horizontal rotation, in radians.

[method:radians getPolarAngle] ()

Get the current vertical rotation, in radians.

[method:Float getDistance] ()

Returns the distance from the camera to the target.

[method:undefined listenToKeyEvents] ( [param:HTMLDOMElement domElement] )

Adds key event listeners to the given DOM element. `window` is a recommended argument for using this method.

[method:undefined reset] ()

Reset the controls to their state from either the last time the [page:.saveState] was called, or the initial state.

[method:undefined saveState] ()

Save the current state of the controls. This can later be recovered with [page:.reset].

[method:undefined stopListenToKeyEvents] ()

Removes the key event listener previously defined with [page:.listenToKeyEvents]().

[method:Boolean update] ()

Update the controls. Must be called after any manual changes to the camera’s transform, or in the update loop if [page:.autoRotate] or [page:.enableDamping] are set.

How to load three-orbitcontrols with import syntax?

Has anyone tried using the OrbitControls function with ReactJS ? Here is the sample code I wrote:

It returns the following error:

./node_modules/three-orbitcontrols/OrbitControls.js 1054:70-89 "export ‘OrbitControls’ (imported as ‘THREE’) was not found in ‘three’

Does anyone know how to resolve this issue?

Anurag Srivastava's user avatar

5 Answers 5

There is also an option to import OrbitControls directly from "three" package like this:

and use it in the app without any issues:

domElement has to be passed as second argument in latest builds of Three.js. React ref can be used for it.

Alexander Solovyev's user avatar

Update 2020:

three.js now exports OrbitControls as a module by default, and as others have pointed out, it can be used as follows:

Original Answer:

There is an npm package that you can use: Orbit Controls ES6

Anurag Srivastava's user avatar

The problem is that when you import THREE, it is not globally scoped which is what the ‘three-orbitcontrols’ module relies on.

and import it like so:

const OrbitControls = require(‘three-orbit-controls’)(THREE);

Usage of orbital controls is the same as you have now but with this, an instance of THREE is being passed to the Orbit Controls module.

Although the above answer was useful when the question was first asked, @Marquizzo rightly pointed out this is no longer the case.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *