Utilizing three.js, I have developed an inset trackball camera controller for a 3D scene. The current setup involves a tiny cube, a circle, and an orthographic camera positioned at the world's origin. However, these elements remain visible in the scene when viewed through the main camera. In the demo code provided below, you can see that the cube has been intentionally enlarged to make it conspicuous, but it could be scaled down significantly.
There is also an issue where certain objects from the main scene passing through the origin are visible in the inset view. For instance, the AxisHelper linked to the primary scene can also be observed in the inset.
My query is whether it is feasible in three.js/webgl to render specific objects visible only to particular cameras?
If not supported by the framework, one workaround would involve relocating the objects essential for the trackball functionality far away into deep space where they are out of sight of the main camera. Nonetheless, I am inclined towards finding a more straightforward solution if there exists one.
Demo: http://codepen.io/anon/pen/MKWrOr
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="WebGL-output"></div>
<script>
function init() {
var scene = new THREE.Scene()
var renderer = new THREE.WebGLRenderer()
var camera
var cameras = []
var WIDTH = window.innerWidth
var HEIGHT = window.innerHeight
;(function createPerspectiveCamera(){
var FOV = 45
var ASPECT = WIDTH / HEIGHT
var NEAR = 1
var FAR = 360
camera = new THREE.PerspectiveCamera(FOV, ASPECT, NEAR, FAR)
camera.position.x = 100
camera.position.y = 100
camera.position.z = 100
camera.viewport = { x: 0, y: 0, width: WIDTH, height: HEIGHT }
camera.lookAt(scene.position)
cameras.push(camera)
})()
;(function initializeRenderer(){
renderer.setClearColor(new THREE.Color(0xEEEEFF))
renderer.setSize(WIDTH, HEIGHT)
renderer.autoClear = false;
document.getElementById("WebGL-output").appendChild(renderer.domElement)
;(function render() {
var viewport
renderer.setViewport( 0, 0, WIDTH, HEIGHT );
renderer.clear();
cameras.forEach(function (camera) {
viewport = camera.viewport // custom property
renderer.setViewport(
viewport.x
, viewport.y
, viewport.width
, viewport.height
)
renderer.render(scene, camera)
})
requestAnimationFrame(render)
})()
})()
;(function createCameraController(){
var viewport = {
x: WIDTH - 100
, y: HEIGHT - 100
, width: 100
, height: 100
}
var circle = {
x: WIDTH - 50
, y: 50
, radius: 50
}
var settings = {
viewport: viewport
, circle: circle
}
addCameraController(scene, camera, cameras, settings)
})()
// Something to look at
scene.add(new THREE.AxisHelper(70))
}
function addCameraController(scene, camera, cameras, settings) {
var controlCamera
var viewport = settings.viewport
// For mouse interactions
var centreX = settings.circle.x
var centreY = settings.circle.y
var radius = settings.circle.radius
var radius2 = radius * radius
var rotationMatrix = new THREE.Matrix4()
var pivotMatrix = new THREE.Matrix4()
var startMatrix = new THREE.Matrix4()
var start = new THREE.Vector3()
var end = new THREE.Vector3()
var angle
camera.matrixAutoUpdate = false /** takes control of main camera **/
;(function createControlCameraCubeAndCircle(){
var side = 10
var radius = Math.sqrt(side/2 * side/2 * 3)
;(function createCamera(){
controlCamera = new THREE.OrthographicCamera(
-radius, radius
, radius, -radius
, -radius, radius
);
controlCamera.viewport = viewport
controlCamera.rotation.copy(camera.rotation)
// If matrixAutoUpdate is set immediately, the camera rotation is
// not applied
setTimeout(function () {
controlCamera.matrixAutoUpdate = false
}, 1)
scene.add(controlCamera)
cameras.push( controlCamera )
})()
;(function createCompanionCube(){
var cube = new THREE.Object3D()
var cubeGeometry = new THREE.BoxGeometry( side, side, side )
var lineMaterial = new THREE.LineBasicMaterial({
color: 0xffffff
, transparent: true
, opacity: 0.5
})
var faceMaterial = new THREE.MeshPhongMaterial({
color: 0x006699
, emissive: 0x006699
, shading: THREE.FlatShading
, transparent: true
, opacity: 0.2
})
cube.add(
new THREE.LineSegments(
new THREE.WireframeGeometry( cubeGeometry )
, lineMaterial
)
)
cube.add(
new THREE.Mesh(
cubeGeometry
, faceMaterial
)
)
// cube.add(new THREE.AxisHelper(radius))
scene.add(cube);
})()
;(function createCircle(){
var circleGeometry = new THREE.CircleGeometry( radius, 36 );
var material = new THREE.MeshBasicMaterial( {
color: 0xccccff
} );
var circle = new THREE.Mesh( circleGeometry, material );
controlCamera.add( circle );
circle.translateZ(-radius)
})()
})()
window.addEventListener("mousedown", startDrag, false)
function startDrag(event) {
var x = event.clientX - centreX
var y = centreY - event.clientY
var delta2 = x * x + y * y
if (delta2 > radius2) {
return
}
var z = Math.sqrt(radius2 - delta2)
start.set(x, y, z)
window.addEventListener("mousemove", drag, false)
window.addEventListener("mouseup", stopDrag, false)
function drag(event) {
var delta
x = event.clientX - centreX
y = centreY - event.clientY
delta2 = x * x + y * y
if (delta2 > radius2) {
// constrain to adge of sphere
delta = Math.sqrt(delta2)
x = x / delta * radius
y = y / delta * radius
z = 0
} else {
z = Math.sqrt(radius2 - delta2)
}
end.set(x, y, z)
angle = start.angleTo(end)
start.cross(end).normalize()
rotationMatrix.makeRotationAxis(start, -angle)
controlCamera.matrix.multiply(rotationMatrix)
controlCamera.matrixWorldNeedsUpdate = true
rotationMatrix.extractRotation(camera.matrixWorld)
start.applyMatrix4(rotationMatrix).normalize()
rotationMatrix.makeRotationAxis(start, -angle)
camera.applyMatrix(rotationMatrix)
camera.matrixWorldNeedsUpdate = true
start.copy(end)
}
function stopDrag(event) {
window.removeEventListener("mousemove", drag, false)
window.removeEventListener("mouseup", stopDrag, false)
}
}
}
window.onload = init
</script>
</body>
</html>