Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
341 changes: 341 additions & 0 deletions examples/webgl_camera_logarithmicdepthbuffer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,341 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - cameras - logarithmic depth buffer</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
color: #808080;
font-family:Monospace;
font-size:13px;
text-align:center;

background-color: #000;
margin: 0px;
overflow: hidden;
}

#info {
position: absolute;
top: 0px; width: 100%;
padding: 5px;
z-index: 100;
color: #ddd;
text-shadow: 0 0 1px rgba(0,0,0,1);
}

a {

color: #0080ff;
}

b { color: lightgreen }

#stats { position: absolute; top:0; left: 0 }

.renderer_label {
position: absolute;
bottom: 1em;
width: 100%;
color: white;
z-index: 10;
display: block;
text-align: center;
}
.renderer_label.renderer_label_normal {
}
.renderer_label.renderer_label_logzbuf {
}
#container {
white-space: nowrap;
}
#container_normal {
width: 50%;
display: inline-block;
position: relative;
overflow: hidden;
}
#container_logzbuf {
width: 50%;
display: inline-block;
position: relative;
overflow: hidden;
}
#renderer_border {
position: absolute;
top: 0;
bottom: 0;
width: 2px;
z-index: 10;
opacity: .8;
background: #ccc;
border: 1px inset #ccc;
cursor: col-resize;
}
</style>
</head>
<body>

<div id="container">
<div id="container_normal"><h2 class="renderer_label renderer_label_normal">normal z-buffer</h2></div><div id="container_logzbuf"><h2 class="renderer_label renderer_label_logzbuf">logarithmic z-buffer</h2></div>
<div id="renderer_border"></div>
</div>

<div id="info">
<a href="http://threejs.org" target="_blank">three.js</a> - cameras - logarithmic depth buffer<br/>
Zoom through scene with objects ranging in size from 1µm to 100,000,000 light years using the mousewheel<br/>
Linear z-buffer handles close-up objects well, but fails spectacularly at distant objects<br/>
Logarithmic handles all but the smallest objects with ease
</div>

<script src="../build/three.min.js"></script>
<script src="js/libs/stats.min.js"></script>
<script src="fonts/helvetiker_regular.typeface.js"></script>

<script>

// 1 micrometer to 100 billion light years in one scene, with 1 unit = 1 meter? preposterous! and yet...
var NEAR = 1e-6, FAR = 1e27;
var SCREEN_WIDTH = window.innerWidth;
var SCREEN_HEIGHT = window.innerHeight;
var screensplit = .25, screensplit_right = 0;
var mouse = [.5, .5];
var zoompos = -100, minzoomspeed = .015;
var zoomspeed = minzoomspeed;

var container, stats;
var objects = {};

// Generate a number of text labels, from 1µm in size up to 100,000,000 light years
// Try to use some descriptive real-world examples of objects at each scale

var labeldata = [
{ size: .01, scale: .001, label: "microscopic (1µm)", scale: .0001 }, // FIXME - triangulating text fails at this size, so we scale instead
{ size: .01, scale: 0.1, label: "minuscule (1mm)", scale: .1},
{ size: .01, scale: 1.0, label: "tiny (1cm)", scale: 1 },
{ size: 1, scale: 1.0, label: "child-sized (1m)", scale: 1 },
{ size: 10, scale: 1.0, label: "tree-sized (10m)", scale: 1 },
{ size: 100, scale: 1.0, label: "building-sized (100m)", scale: 1 },
{ size: 1000, scale: 1.0, label: "medium (1km)", scale: 1 },
{ size: 10000, scale: 1.0, label: "city-sized (10km)", scale: 1 },
{ size: 3400000, scale: 1.0, label: "moon-sized (3,400 Km)", scale: 1 },
{ size: 12000000, scale: 1.0, label: "planet-sized (12,000 km)", scale: 1 },
{ size: 1400000000, scale: 1.0, label: "sun-sized (1,400,000 km)", scale: 1 },
{ size: 7.47e12, scale: 1.0, label: "solar system-sized (50Au)", scale: 1 },
{ size: 9.4605284e15, scale: 1.0, label: "gargantuan (1 light year)", scale: 1 },
{ size: 3.08567758e16, scale: 1.0, label: "ludicrous (1 parsec)", scale: 1 },
{ size: 1e19, scale: 1.0, label: "mind boggling (1000 light years)", scale: 1 },
{ size: 1.135e21, scale: 1.0, label: "galaxy-sized (120,000 light years)", scale: 1 },
{ size: 9.46e23, scale: 1.0, label: "... (100,000,000 light years)", scale: 1 }
];

init();
animate();

function init() {

container = document.getElementById( 'container' );

// Initialize two copies of the same scene, one with normal z-buffer and one with logarithmic z-buffer
objects.normal = initScene('normal', false);
objects.logzbuf = initScene('logzbuf', true);

stats = new Stats();
container.appendChild(stats.domElement);

// Resize border allows the user to easily compare effects of logarithmic depth buffer over the whole scene
border = document.getElementById( 'renderer_border' );
border.addEventListener("mousedown", onBorderMouseDown);

window.addEventListener( 'resize', onWindowResize, false );
window.addEventListener( 'mousewheel', onMouseWheel, false );
window.addEventListener( 'DOMMouseScroll', onMouseWheel, false );
window.addEventListener( 'mousemove', onMouseMove, false );

render();
}

function initScene(name, logDepthBuf) {

var scene = new THREE.Scene();
var framecontainer = document.getElementById('container_' + name);

var camera = new THREE.PerspectiveCamera( 50, screensplit * SCREEN_WIDTH / SCREEN_HEIGHT, NEAR, FAR );
scene.add(camera);

var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(100,100,100);
scene.add(light);

var pointlight = new THREE.PointLight(0xffccaa, .1);
pointlight.position = camera.position;
scene.add(pointlight);

var materialargs = {
color: 0xffffff,
specular: 0xffaa00,
shininess: 50,
shading: THREE.SmoothShading,
emissive: 0x000000
};

var geomtransform = new THREE.Matrix4();
var tmpvec = new THREE.Vector3();
var meshes = [];
var coloroffset = 0;
var colorskip = ['black', 'antiquewhite', 'bisque', 'beige', 'blanchedalmond', 'darkblue', 'darkcyan'];
var colorkeys = Object.keys( THREE.ColorKeywords );

for (var i = 0; i < labeldata.length; i++) {
var scale = labeldata[i].scale || 1;
var labelgeo = new THREE.TextGeometry( labeldata[i].label, {
size: labeldata[i].size,
height: labeldata[i].size / 2,
font: 'helvetiker',
});
labelgeo.computeBoundingSphere();

// center text
geomtransform.setPosition( tmpvec.set( -labelgeo.boundingSphere.radius, 0, 0 ) );
labelgeo.applyMatrix( geomtransform );

// Pick a color at "random". Exclude black, because it looks bad.
while ( colorskip.indexOf( colorkeys[ i + coloroffset ] ) != -1 ) {
coloroffset++;
}
materialargs.color = THREE.ColorKeywords[ colorkeys[ i + coloroffset ] ];

var material = new THREE.MeshPhongMaterial( materialargs );
var textmesh = new THREE.Mesh( labelgeo, material );
textmesh.scale.set(scale, scale, scale);
textmesh.position.z = -labeldata[i].size * scale;
textmesh.position.y = labeldata[i].size / 4 * scale;
var dotmesh = new THREE.Mesh(new THREE.SphereGeometry(labeldata[i].size * scale / 2, 24, 12), material);
dotmesh.position.y = -labeldata[i].size / 4 * scale;

var merged = new THREE.Geometry();
THREE.GeometryUtils.merge(merged, textmesh);
THREE.GeometryUtils.merge(merged, dotmesh);

var mesh = new THREE.Mesh(merged, material);
mesh.position.z = -labeldata[i].size * 1 * scale;

scene.add(mesh);
}

var renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: logDepthBuf });
renderer.setSize(SCREEN_WIDTH/2, SCREEN_HEIGHT);
renderer.domElement.style.position = "relative";
renderer.domElement.id = 'renderer_' + name;
framecontainer.appendChild(renderer.domElement);

return { container: framecontainer, renderer: renderer, scene: scene, camera: camera }
}

function updateRendererSizes() {

// Recalculate size for both renderers when screen size or split location changes

SCREEN_WIDTH = window.innerWidth;
SCREEN_HEIGHT = window.innerHeight;

screensplit_right = 1 - screensplit;

objects.normal.renderer.setSize( screensplit * SCREEN_WIDTH, SCREEN_HEIGHT );
objects.normal.camera.aspect = screensplit * SCREEN_WIDTH / SCREEN_HEIGHT;
objects.normal.camera.updateProjectionMatrix();
objects.normal.camera.setViewOffset( SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH * screensplit, SCREEN_HEIGHT );
objects.normal.container.style.width = (screensplit * 100) + '%';

objects.logzbuf.renderer.setSize( screensplit_right * SCREEN_WIDTH, SCREEN_HEIGHT );
objects.logzbuf.camera.aspect = screensplit_right * SCREEN_WIDTH / SCREEN_HEIGHT;
objects.logzbuf.camera.updateProjectionMatrix();
objects.logzbuf.camera.setViewOffset( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH * screensplit, 0, SCREEN_WIDTH * screensplit_right, SCREEN_HEIGHT );
objects.logzbuf.container.style.width = (screensplit_right * 100) + '%';

border.style.left = (screensplit * 100) + "%";

}

function animate() {

requestAnimationFrame( animate );
render();

}

function render() {

// Put some limits on zooming
var minzoom = labeldata[0].size * labeldata[0].scale*1;
var maxzoom = labeldata[labeldata.length-1].size * labeldata[labeldata.length-1].scale * 100;
var damping = (Math.abs(zoomspeed) > minzoomspeed ? .95 : 1.0);

// Zoom out faster the further out you go
var zoom = THREE.Math.clamp(Math.pow(Math.E, zoompos), minzoom, maxzoom);
zoompos = Math.log(zoom);

// Slow down quickly at the zoom limits
if ((zoom == minzoom && zoomspeed < 0) || (zoom == maxzoom && zoomspeed > 0)) {
damping = .85;
}

zoompos += zoomspeed;
zoomspeed *= damping;

objects.normal.camera.position.x = Math.sin(.5 * Math.PI * (mouse[0] - .5)) * zoom;
objects.normal.camera.position.y = Math.sin(.25 * Math.PI * (mouse[1] - .5)) * zoom;
objects.normal.camera.position.z = Math.cos(.5 * Math.PI * (mouse[0] - .5)) * zoom;
objects.normal.camera.lookAt(objects.normal.scene.position);

// Clone camera settings across both scenes
objects.logzbuf.camera.position.copy(objects.normal.camera.position);
objects.logzbuf.camera.quaternion.copy(objects.normal.camera.quaternion);

// Update renderer sizes if the split has changed
if (screensplit_right != 1 - screensplit) {
updateRendererSizes();
}

objects.normal.renderer.render(objects.normal.scene, objects.normal.camera);
objects.logzbuf.renderer.render(objects.logzbuf.scene, objects.logzbuf.camera);

stats.update();

}

function onWindowResize(event) {
updateRendererSizes();
}

function onBorderMouseDown(ev) {
// activate draggable window resizing bar
window.addEventListener("mousemove", onBorderMouseMove);
window.addEventListener("mouseup", onBorderMouseUp);
ev.stopPropagation();
ev.preventDefault();
}
function onBorderMouseMove(ev) {
screensplit = Math.max(0, Math.min(1, ev.clientX / window.innerWidth));
ev.stopPropagation();
}
function onBorderMouseUp(ev) {
window.removeEventListener("mousemove", onBorderMouseMove);
window.removeEventListener("mouseup", onBorderMouseUp);
}
function onMouseMove(ev) {
mouse[0] = ev.clientX / window.innerWidth;
mouse[1] = ev.clientY / window.innerHeight;
}
function onMouseWheel(ev) {
var amount = -ev.wheelDeltaY || ev.detail;
var dir = amount / Math.abs(amount);
zoomspeed = dir/10;

// Slow down default zoom speed after user starts zooming, to give them more control
minzoomspeed = 0.001;
}
</script>
</body>
</html>
16 changes: 16 additions & 0 deletions src/renderers/WebGLRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ THREE.WebGLRenderer = function ( parameters ) {
_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
_logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false,

_clearColor = new THREE.Color( 0x000000 ),
_clearAlpha = 0;
Expand Down Expand Up @@ -184,6 +185,7 @@ THREE.WebGLRenderer = function ( parameters ) {
var _glExtensionTextureFilterAnisotropic;
var _glExtensionCompressedTextureS3TC;
var _glExtensionElementIndexUint;
var _glExtensionFragDepth;


initGL();
Expand Down Expand Up @@ -4100,6 +4102,7 @@ THREE.WebGLRenderer = function ( parameters ) {
fogExp: fog instanceof THREE.FogExp2,

sizeAttenuation: material.sizeAttenuation,
logarithmicDepthBuffer: _logarithmicDepthBuffer,

skinning: material.skinning,
maxBones: maxBones,
Expand Down Expand Up @@ -4299,6 +4302,13 @@ THREE.WebGLRenderer = function ( parameters ) {

_gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements );

if ( _logarithmicDepthBuffer ) {

_gl.uniform1f(p_uniforms.logDepthBufFC, 2.0 / (Math.log(camera.far + 1.0) / Math.LN2));

}


if ( camera !== _currentCamera ) _currentCamera = camera;

}
Expand Down Expand Up @@ -6135,6 +6145,12 @@ THREE.WebGLRenderer = function ( parameters ) {
}
}

if ( _logarithmicDepthBuffer ) {

_glExtensionFragDepth = _gl.getExtension( 'EXT_frag_depth' );

}

};

function setDefaultGLState () {
Expand Down
Loading