Three.js, Camera rotation around a point - camera

I'm trying to make a camera rotation and zoom around a point.
I don't want to use THREE.TrackBallControls because as i see, it only modify the fov when zooming, it is not what i want.
I'm using this simple equation : http://en.wikipedia.org/wiki/Sphere#Equations_in. To compute a point on the sphere, this point will be the position of the camera.
In three.js
var point = new THREE.Vector3(
radius * Math.cos(theta) * Math.sin(phi),
radius * Math.sin(theta) * Math.sin(phi),
radius * Math.cos(phi)
);
point.add(center);
To manipulate it, i use :
left mouse, for rotation : x coord === theta and y coord === phi
middle mouse, for zooming : zoom in/out === radius
It doesn't work well, especially for 'phi' rotation, maybe because it's define between 0 and PI.
Maybe there is a better solution using Quaternion.Slerp ?
edit :
My camera :
aspectRatio = container.offsetWidth / container.offsetHeight;
camera = new THREE.OrthographicCamera( -aspectRatio * viewSize / 2, aspectRatio * viewSize / 2, viewSize / 2, -viewSize / 2, -10, 10 );
camera.position.x = 0.004;
camera.position.y = 0.004;
camera.position.z = 0.004;
camera.lookAt(new THREE.Vector3(0, 0, 0))
scene.add(camera);
My OrbitControls :
controls = new THREE.OrbitControls ( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.keyPanSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
when using left mouse (rotation) the camera.position change.
when using middle mouse (zoom) the camera.position is still the same.
check made in the render function :
function render() {
// update camera controls
controls.update();
console.log(camera.position);
// actually render the scene
renderer.render( scene, camera );
}

Related

Align UI lements correctly in SkiaSharp (UWP)

I am trying to align a few buttons in a circular manner using SkiaSharp. My code looks like this
<skia:SKXamlCanvas x:Name="test" PaintSurface="Test_OnPaintSurface" />
Code behind
private void Test_OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
// the canvas and properties
var canvas = e.Surface.Canvas;
// get the screen density for scaling
var display = DisplayInformation.GetForCurrentView();
var scale = display.LogicalDpi / 96.0f;
var scaledSize = new SKSize(e.Info.Width / scale, e.Info.Height / scale);
// handle the device screen density
canvas.Scale(scale);
// make sure the canvas is blank
canvas.Clear(SKColors.Transparent);
// draw some text
var paintSmallCircle = new SKPaint
{
Color = SKColors.CornflowerBlue,
IsAntialias = true,
Style = SKPaintStyle.Fill,
TextAlign = SKTextAlign.Center,
TextSize = 24
};
var paintCircle = new SKPaint
{
Color = SKColors.LightGray,
IsAntialias = true,
Style = SKPaintStyle.Fill,
TextAlign = SKTextAlign.Center,
TextSize = 24
};
var coord = new SKPoint(
scaledSize.Width / 2,
(scaledSize.Height) / 2);
canvas.DrawCircle(coord, 120, paintCircle);
int r = 100;
int angle = 90;
for (int i = 0; i < 12; i++)
{
double x1 = scaledSize.Width / 2 + r * Math.Cos(Math.PI * angle / 180.0) ;
double y1 = scaledSize.Height / 2 - r * Math.Sin(Math.PI * angle / 180.0) ;
var coord1 = new SKPoint((float) x1, (float)y1);
canvas.DrawCircle(coord1, 10, paintSmallCircle);
Button btn = new Button { Content = i, Height = 25, Width = 25, };
btn.SetValue(SKXamlCanvas.LeftProperty, coord1.X);
btn.SetValue(SKXamlCanvas.TopProperty, coord1.Y);
test.Children.Add(btn);
angle = angle - 30;
}
}
With this code, I am able to draw Blue circles correctly, but the button alignment comes wrong. How can I solve this issue?
right now my output looks like this
As you can see blue small circles are aligned correctly, but not buttons.
The expected behavior is that buttons come in the same place where blue circles are rendered
The point is you place the Button's Left & Top property.
When you use canvas.DrawCircle(coord1, 10, paintSmallCircle); to draw a Circle, the center point is coord1.
And you draw the Buttons Left & Top at the center point of the Circle.
So you can draw Button using
btn.SetValue(SKXamlCanvas.LeftProperty, coord1.X - 25 /2);
btn.SetValue(SKXamlCanvas.TopProperty, coord1.Y - 25 / 2);
25 is the Height and Width of your Button.
See the result.

How to move camera around object to look at object from all sides?

How can a cesium camera be moved around an object in circular path?
Assuming you want something similar to helicopter circles... It can be done by 'lookAt' at every clock tick.
let heading = 0; //or any starting angle in radians
let rotation = -1; //counter-clockwise; +1 would be clockwise
let centre = new Cesium.Cartesian3.fromDegrees(longitude, latitude);
let elevation = 100; // 100 meters
let pitch = -0.7854; //looking down at 45 degrees
const SMOOTHNESS = 600; //it would make one full circle in roughly 600 frames
viewer.clock.onTick.addEventListener(() => {
heading += rotation * Math.PI / SMOOTHNESS;
viewer.camera.lookAt(centre, new Cesium.HeadingPitchRange(heading, pitch, elevation));
});

DirectXMath Unproject on the wrong axis

EDIT: Solved the issue, see my own answer
Recently I've been working on a 3D world editor that will be using picking to raise or lower terrain. I'm using camera unprojection and ray casting to find the world position of the mouse screen coordinates.
However, it seems like the ray is on the wrong axis. As I remember, the ray that is supposed to come out of unproject should be coming straight from the camera.
Here's an example of what it currently looks like.
My question is, why is the ray over the Y-axis when it's supposed to be over the Z-axis?
XMFLOAT3 D3D11Camera::Unproject(const float& px, const float& py, const float& pz)
{
const XMFLOAT2& res = D3D11RenderSettings::Instance()->resolution();
XMVECTOR coords = XMVector3Unproject(XMVectorSet(px, res.y - py, pz, 0.0f), 0.0f, 0.0f, res.x, res.y, near_plane_, far_plane_, projection_, view_, XMMatrixIdentity());
XMFLOAT3 to_ret;
XMStoreFloat3(&to_ret, coords);
return to_ret;
}
This is the unprojection code..
And this is how I'm using it
projectRay: function()
{
var p = Mouse.position(MousePosition.Relative);
p.x = (p.x + RenderSettings.resolution().w / 2);
p.y = (p.y + RenderSettings.resolution().h / 2);
var unprojA = this._camera.unproject(p.x, p.y, 0);
var unprojB = this._camera.unproject(p.x, p.y, 1);
var dir = Vector3D.normalise(Vector3D.sub(unprojB, unprojA));
var ray = Ray.construct(unprojA, dir);
var p1 = ray.origin;
var p2 = Vector3D.add(ray.origin, Vector3D.mul(ray.direction, 1000));
RenderTargets.ui.drawLine(p1.x, p1.y, p1.z, 1, 0, 0, p2.x, p2.y, p2.z, 1, 0, 0);
return ray;
}
Cheers!
Stupidity aside, I'm working with both an inverted Y-axis coordinate system and a default -- non inverted -- one. It seemed like it was a ray oriented vertically, but in fact it was a ray that was oriented in the direction of '/' while it was supposed to be oriented like '\'. Multiplying the Y component of the result by -1 solved the issue.

Three.js - Fisheye effect

So, I've messed around with three.js, works out great. The only thing I can't figure out, is how to make a camera with a real fisheye effect.
How is that possible? camera.setLens()?
The fish eye effect can be achieved using Giliam de Carpentier's shader for lens distortion.
Shader code:
function getDistortionShaderDefinition()
{
return {
uniforms: {
"tDiffuse": { type: "t", value: null },
"strength": { type: "f", value: 0 },
"height": { type: "f", value: 1 },
"aspectRatio": { type: "f", value: 1 },
"cylindricalRatio": { type: "f", value: 1 }
},
vertexShader: [
"uniform float strength;", // s: 0 = perspective, 1 = stereographic
"uniform float height;", // h: tan(verticalFOVInRadians / 2)
"uniform float aspectRatio;", // a: screenWidth / screenHeight
"uniform float cylindricalRatio;", // c: cylindrical distortion ratio. 1 = spherical
"varying vec3 vUV;", // output to interpolate over screen
"varying vec2 vUVDot;", // output to interpolate over screen
"void main() {",
"gl_Position = projectionMatrix * (modelViewMatrix * vec4(position, 1.0));",
"float scaledHeight = strength * height;",
"float cylAspectRatio = aspectRatio * cylindricalRatio;",
"float aspectDiagSq = aspectRatio * aspectRatio + 1.0;",
"float diagSq = scaledHeight * scaledHeight * aspectDiagSq;",
"vec2 signedUV = (2.0 * uv + vec2(-1.0, -1.0));",
"float z = 0.5 * sqrt(diagSq + 1.0) + 0.5;",
"float ny = (z - 1.0) / (cylAspectRatio * cylAspectRatio + 1.0);",
"vUVDot = sqrt(ny) * vec2(cylAspectRatio, 1.0) * signedUV;",
"vUV = vec3(0.5, 0.5, 1.0) * z + vec3(-0.5, -0.5, 0.0);",
"vUV.xy += uv;",
"}"
].join("\n"),
fragmentShader: [
"uniform sampler2D tDiffuse;", // sampler of rendered scene?s render target
"varying vec3 vUV;", // interpolated vertex output data
"varying vec2 vUVDot;", // interpolated vertex output data
"void main() {",
"vec3 uv = dot(vUVDot, vUVDot) * vec3(-0.5, -0.5, -1.0) + vUV;",
"gl_FragColor = texture2DProj(tDiffuse, uv);",
"}"
].join("\n")
};
}
One way to setup the effect using effect composer (assuming scene and renderer have been been created):
// Create camera
camera = new THREE.PerspectiveCamera( 100, window.innerWidth / window.innerHeight, 1, 1000000 );
camera.position.z = 800;
// Create effect composer
composer = new THREE.EffectComposer( renderer );
composer.addPass( new THREE.RenderPass( scene, camera ) );
// Add distortion effect to effect composer
var effect = new THREE.ShaderPass( getDistortionShaderDefinition() );
composer.addPass( effect );
effect.renderToScreen = true;
// Setup distortion effect
var horizontalFOV = 140;
var strength = 0.5;
var cylindricalRatio = 2;
var height = Math.tan(THREE.Math.degToRad(horizontalFOV) / 2) / camera.aspect;
camera.fov = Math.atan(height) * 2 * 180 / 3.1415926535;
camera.updateProjectionMatrix();
effect.uniforms[ "strength" ].value = strength;
effect.uniforms[ "height" ].value = height;
effect.uniforms[ "aspectRatio" ].value = camera.aspect;
effect.uniforms[ "cylindricalRatio" ].value = cylindricalRatio;
Following script are needed and they can be found for example from three.js GitHub page:
<script src="examples/js/postprocessing/EffectComposer.js"></script>
<script src="examples/js/postprocessing/RenderPass.js"></script>
<script src="examples/js/postprocessing/MaskPass.js"></script>
<script src="examples/js/postprocessing/ShaderPass.js"></script>
<script src="examples/js/shaders/CopyShader.js"></script>
Link to Giliam's example: http://www.decarpentier.nl/downloads/lensdistortion-webgl/lensdistortion-webgl.html
Link to Giliam's article about lens distortion: http://www.decarpentier.nl/lens-distortion
Image of my test where lens distortion effect is used:
Put a camera inside a reflective sphere. Make sure the sphere is double sided. Parent the camera and sphere together if you want to move it around your scene. Works like a charm:
http://tileableart.com/code/NOCosmos/test.html
borrowed from:
http://mrdoob.github.io/three.js/examples/webgl_materials_cubemap_dynamic2.html
cubeCamera = new THREE.CubeCamera( 1, 3000, 1024);
cubeCamera.renderTarget.minFilter = THREE.LinearMipMapLinearFilter;
scene.add( cubeCamera );
camParent.add(cubeCamera);
var material = new THREE.MeshBasicMaterial( { envMap: cubeCamera.renderTarget } );
material.side = THREE.DoubleSide;
sphere = new THREE.Mesh( new THREE.SphereGeometry( 2, 60, 30 ), material );
It's possible to get the fisheye effect with a high Field of View.
var fishCamera = new THREE.PerspectiveCamera( 110, window.innerWidth / window.innerHeight, 1, 1100 );
var normalCamera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1100 );
or set
camera.fov = 110
camera.updateProjectionMatrix();
Live Example here:
http://mrdoob.github.com/three.js/examples/canvas_geometry_panorama_fisheye.html
One way is to set a large field of view on the camera:
new THREE.PerspectiveCamera(140, ... )
This will not technically be a fisheye effect, but it may be the effect you're looking for.
In a real camera lens, getting a large field of view without distorsion would likely make the lens pretty expensive, but in computer graphics, it's the easy way.
A real fisheye lens distorts the image so that straight line become curved, like in this image:
If you want to create an actual fisheye effect with this kind of distorsion, you would have to modify the geometry, as in Three.js's fisheye example. In that example, the geometry is actually modified beforehand, but for a more advanced scene, you'd want to use a vertex shader to update the vertices on the fly.
A wide angle lens generally have a very low focus length.
To achieve an extreme wide angle we need to reduce focus length.
Note that fish eye lens is an extreme wide angle lens.
To reduce focus length(or to achieve extreme wide angles), one can just increase FOV (field of view), as FOV is inversely proportional to focus length.
example:
var camera_1 = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );
var camera_2 = new THREE.PerspectiveCamera( 80, width / height, 1, 1000 );
Here camera_2 is a wider angle setup.
Note
To achieve desired effect, one may have to adjust camera position.

Rotating camera around the X-axis (three.js)

I am trying to rotate the camera around to X-axis of the scene.
At this point my code is like this:
rotation += 0.05;
camera.position.y = Math.sin(rotation) * 500;
camera.position.z = Math.cos(rotation) * 500;
This makes the camera move around but during the rotation something weird happens and either the camera flips, or it skips some part of the imaginary circle it's following.
You have only provided a snippet of code, so I have to make some assumptions about what you are doing.
This code:
rotation += 0.05;
camera.position.x = 0;
camera.position.y = Math.sin(rotation) * 500;
camera.position.z = Math.cos(rotation) * 500;
camera.lookAt( scene.position ); // the origin
will cause the "flipping" you refer to because the camera is trying to remain "right side up", and it will quickly change orientation as it passes over the "north pole."
If you offset the camera's x-coordinate like so,
camera.position.x = 200;
the camera behavior will appear more natural to you.
Three.js tries to keep the camera facing up. When you pass 0 along the z-axis, it'll "fix" the camera's rotation. You can just check and reset the camera's angle manually.
camera.lookAt( scene.position ); // the origin
if (camera.position.z < 0) {
camera.rotation.z = 0;
}
I'm sure this is not the best solution, but if anyone else runs across this question while playing with three.js (like I just did), it'll give one step further.
This works for me, I hope it helps.
Rotating around X-Axis:
var x_axis = new THREE.Vector3( 1, 0, 0 );
var quaternion = new THREE.Quaternion;
camera.position.applyQuaternion(quaternion.setFromAxisAngle(x_axis, rotation_speed));
camera.up.applyQuaternion(quaternion.setFromAxisAngle(x_axis, rotation_speed));
Rotating around Y-Axis:
var y_axis = new THREE.Vector3( 0, 1, 0 );
camera.position.applyQuaternion(quaternion.setFromAxisAngle(y_axis, angle));
Rotating around Z-Axis:
var z_axis = new THREE.Vector3( 0, 0, 1 );
camera.up.applyQuaternion(quaternion.setFromAxisAngle(z_axis, angle));
I wanted to move my camera to a new location while having the camera look at a particular object, and this is what I came up with [make sure to load tween.js]:
/**
* Helper to move camera
* #param loc Vec3 - where to move the camera; has x, y, z attrs
* #param lookAt Vec3 - where the camera should look; has x, y, z attrs
* #param duration int - duration of transition in ms
**/
function flyTo(loc, lookAt, duration) {
// Use initial camera quaternion as the slerp starting point
var startQuaternion = camera.quaternion.clone();
// Use dummy camera focused on target as the slerp ending point
var dummyCamera = camera.clone();
dummyCamera.position.set(loc.x, loc.y, loc.z);
// set the dummy camera quaternion
var rotObjectMatrix = new THREE.Matrix4();
rotObjectMatrix.makeRotationFromQuaternion(startQuaternion);
dummyCamera.quaternion.setFromRotationMatrix(rotObjectMatrix);
dummyCamera.up.set(camera)
console.log(camera.quaternion, dummyCamera.quaternion);
// create dummy controls to avoid mutating main controls
var dummyControls = new THREE.TrackballControls(dummyCamera);
dummyControls.target.set(loc.x, loc.y, loc.z);
dummyControls.update();
// Animate between the start and end quaternions
new TWEEN.Tween(camera.position)
.to(loc, duration)
.onUpdate(function(timestamp) {
// Slerp the camera quaternion for smooth transition.
// `timestamp` is the eased time value from the tween.
THREE.Quaternion.slerp(startQuaternion, dummyCamera.quaternion, camera.quaternion, timestamp);
camera.lookAt(lookAt);
})
.onComplete(function() {
controls.target = new THREE.Vector3(scene.children[1].position-0.001);
camera.lookAt(lookAt);
}).start();
}
Example usage:
var pos = {
x: -4.3,
y: 1.7,
z: 7.3,
};
var lookAt = scene.children[1].position;
flyTo(pos, lookAt, 60000);
Then in your update()/render() function, call TWEEN.update();
Full example