Ver Fonte

WebGL Depth Texture Extension (#8577)

* working on depth texture

* working on depth texture

* support depth texture in webgl1 and webgl2

* fix comments

* reset build to avoid diff noise

* add check for webgl2 context

* fix depth texture example

* animation for example

* add file

* add depthTexture to WebGLRenderTarget

* ensure depth format returns correct GL parmaeter

* use null for initial depthTexture value

* add some commented out code
Matt DesLauriers há 9 anos atrás
pai
commit
e77697f4aa

+ 1 - 0
examples/files.js

@@ -10,6 +10,7 @@ var files = {
 		"webgl_clipping",
 		"webgl_clipping_advanced",
 		"webgl_decals",
+		"webgl_depth_texture",
 		"webgl_effects_anaglyph",
 		"webgl_effects_parallaxbarrier",
 		"webgl_effects_peppersghost",

+ 257 - 0
examples/webgl_depth_texture.html

@@ -0,0 +1,257 @@
+
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <title>three.js webgl - Depth Texture</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: #fff;
+        font-family:Monospace;
+        font-size:13px;
+        text-align:center;
+        background-color: #000;
+        margin: 0px;
+        overflow: hidden;
+      }
+      a {
+        color: #B2E9FF;
+        font-weight: bold;
+        pointer-events: auto;
+      }
+      
+      canvas {
+        position: absolute;
+        top: 0;
+        left: 0;
+      }
+
+      #info {
+        pointer-events: none;
+        position: absolute;
+        left: 0;
+        top: 0px; width: 100%;
+        padding: 5px;
+        display: inline-block;
+      }
+      
+      #error {
+        margin: auto;
+        margin-top: 40px;
+        display: block;
+        max-width: 400px;
+        padding: 20px;
+        background: #CE0808;
+      }
+    </style>
+    
+    <script id="post-vert" type="x-shader/x-vertex">
+      varying vec2 vUv;
+
+      void main() {
+        vUv = uv;
+        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
+      }
+    </script>
+    <script id="post-frag" type="x-shader/x-fragment">
+      #include <packing>
+    
+      varying vec2 vUv;
+      uniform sampler2D tDiffuse;
+      uniform sampler2D tDepth;
+      uniform float cameraNear;
+      uniform float cameraFar;
+
+      float readDepth (sampler2D depthSampler, vec2 coord) {
+        float cameraFarPlusNear = cameraFar + cameraNear;
+        float cameraFarMinusNear = cameraFar - cameraNear;
+        float cameraCoef = 2.0 * cameraNear;
+        return cameraCoef / (cameraFarPlusNear - texture2D(depthSampler, coord).x * cameraFarMinusNear);
+      }
+
+      // float depthTexel = texture2D(tDepth, vUv).x;
+      // float depth = invClipZToViewZ(depthTexel, cameraNear, cameraFar);
+      // float linearClipZ = viewZToLinearClipZ(depth, cameraNear, cameraFar);
+
+      void main() {
+        vec3 diffuse = texture2D(tDiffuse, vUv).rgb;
+        float depth = readDepth(tDepth, vUv);
+
+        gl_FragColor.rgb = vec3(depth);
+        gl_FragColor.a = 1.0;
+      }
+    </script>
+
+  </head>
+  <body>
+    <canvas></canvas>
+    <div id="info">
+      <a href="http://threejs.org" target="_blank">threejs</a> - WebGL - Depth Texture<br/>
+      Stores render target depth in a texture attachment.<br/>
+      Created by <a href="http://twitter.com/mattdesl" target="_blank">@mattdesl</a>.
+      
+      <div id="error" style="display: none;">
+      Your browser does not support <strong>WEBGL_depth_texture</strong>.<br/><br/>
+      This demo will not work.
+      </div>  
+    </div>
+
+    <script src="../build/three.js"></script>
+    <script src="js/shaders/SSAOShader.js"></script>
+    <script src="js/controls/OrbitControls.js"></script>
+
+    <script>
+
+      var camera, scene, renderer, controls;
+      var target;
+      var postScene, postCamera;
+      var supportsExtension = true;
+
+      init();
+      animate();
+
+      function init() {
+
+        var canvas = document.querySelector('canvas');
+        var gl; 
+        try {
+
+          gl = canvas.getContext('webgl2');
+
+        } catch (err) {
+
+          console.error(err);
+
+        }
+        var isWebGL2 = Boolean(gl);
+        
+        renderer = new THREE.WebGLRenderer( {
+          canvas: canvas,
+          context: gl
+        } );
+
+        if ( !renderer.extensions.get('WEBGL_depth_texture') ) {
+          supportsExtension = false;
+          document.querySelector('#error').style.display = 'block';
+          return;
+        }
+
+        renderer.setPixelRatio( window.devicePixelRatio );
+        renderer.setSize( window.innerWidth, window.innerHeight );
+
+        //
+
+        camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 50 );
+        camera.position.z = -4;
+
+        controls = new THREE.OrbitControls( camera, renderer.domElement );
+        controls.enableDamping = true;
+        controls.dampingFactor = 0.25;
+        controls.rotateSpeed = 0.35;
+
+        // Create a multi render target with Float buffers
+        target = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight );
+        target.texture.format = THREE.RGBFormat;
+        target.texture.minFilter = THREE.NearestFilter;
+        target.texture.magFilter = THREE.NearestFilter;
+        target.texture.generateMipmaps = false;
+        target.stencilBuffer = false;
+        target.depthBuffer = true;
+        target.depthTexture = new THREE.DepthTexture();
+        target.depthTexture.type = isWebGL2 ? THREE.FloatType : THREE.UnsignedShortType;
+
+        // Our scene
+        scene = new THREE.Scene();
+        setupScene();
+
+        // Setup post-processing step
+        setupPost();
+
+        onWindowResize();
+        window.addEventListener( 'resize', onWindowResize, false );
+
+      }
+
+      function setupPost () {
+
+        // Setup post processing stage
+        postCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
+        var postMaterial = new THREE.ShaderMaterial({
+          vertexShader: document.querySelector('#post-vert').textContent.trim(),
+          fragmentShader: document.querySelector('#post-frag').textContent.trim(),
+          uniforms: {
+            cameraNear: { type: 'f', value: camera.near },
+            cameraFar: { type: 'f', value: camera.far },
+            tDiffuse: { type: 't', value: target },
+            tDepth: { type: 't', value: target.depthTexture }
+          }
+        });
+        var postPlane = new THREE.PlaneGeometry(2, 2);
+        var postQuad = new THREE.Mesh(postPlane, postMaterial);
+        postScene = new THREE.Scene();
+        postScene.add(postQuad);
+
+      }
+
+      function setupScene () {
+        var diffuse = new THREE.TextureLoader().load('textures/brick_diffuse.jpg');
+        diffuse.wrapS = diffuse.wrapT = THREE.RepeatWrapping;
+
+        // Setup some geometries
+        var geometry = new THREE.TorusKnotGeometry(1, 0.3, 128, 64);
+        var material = new THREE.MeshBasicMaterial({ color: 'blue' })
+
+        var count = 50;
+        var scale = 5;
+
+        for ( var i = 0; i < count; i ++ ) {
+
+          var r = Math.random() * 2.0 * Math.PI;
+          var z = (Math.random() * 2.0) - 1.0;
+          var zScale = Math.sqrt(1.0 - z * z) * scale;
+
+          var mesh = new THREE.Mesh(geometry, material);
+          mesh.position.set(
+            Math.cos(r) * zScale,
+            Math.sin(r) * zScale,
+            z * scale
+          );
+          mesh.rotation.set(Math.random(), Math.random(), Math.random());
+          scene.add(mesh);
+
+        }
+        
+      }
+
+      function onWindowResize() {
+
+        var aspect = window.innerWidth / window.innerHeight;
+        camera.aspect = aspect;
+        camera.updateProjectionMatrix();
+
+        var dpr = renderer.getPixelRatio();
+        target.setSize( window.innerWidth * dpr, window.innerHeight * dpr );
+        renderer.setSize( window.innerWidth, window.innerHeight );
+
+      }
+
+      function animate() {
+
+        if ( !supportsExtension ) return;
+
+        requestAnimationFrame( animate );
+        controls.update();
+
+        // render scene into target
+        renderer.render( scene, camera, target );
+
+        // render post FX
+        renderer.render( postScene, postCamera );
+
+      }
+
+    </script>
+
+  </body>
+</html>

+ 1 - 0
src/Three.js

@@ -272,6 +272,7 @@ THREE.LuminanceFormat = 1022;
 THREE.LuminanceAlphaFormat = 1023;
 // THREE.RGBEFormat handled as THREE.RGBAFormat in shaders
 THREE.RGBEFormat = THREE.RGBAFormat; //1024;
+THREE.DepthFormat = 1026;
 
 // DDS / ST3C Compressed texture formats
 

+ 2 - 0
src/renderers/WebGLRenderTarget.js

@@ -29,6 +29,7 @@ THREE.WebGLRenderTarget = function ( width, height, options ) {
 
 	this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
 	this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;
+	this.depthTexture = null;
 
 };
 
@@ -69,6 +70,7 @@ THREE.WebGLRenderTarget.prototype = {
 
 		this.depthBuffer = source.depthBuffer;
 		this.stencilBuffer = source.stencilBuffer;
+		this.depthTexture = source.depthTexture;
 
 		return this;
 

+ 89 - 16
src/renderers/WebGLRenderer.js

@@ -245,8 +245,10 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	}
 
+	var _isWebGL2 = (typeof WebGL2RenderingContext !== 'undefined' && _gl instanceof WebGL2RenderingContext);
 	var extensions = new THREE.WebGLExtensions( _gl );
 
+	extensions.get( 'WEBGL_depth_texture' );
 	extensions.get( 'OES_texture_float' );
 	extensions.get( 'OES_texture_float_linear' );
 	extensions.get( 'OES_texture_half_float' );
@@ -609,23 +611,33 @@ THREE.WebGLRenderer = function ( parameters ) {
 		var renderTargetProperties = properties.get( renderTarget );
 		var textureProperties = properties.get( renderTarget.texture );
 
-		if ( ! renderTarget || textureProperties.__webglTexture === undefined ) return;
+		if ( ! renderTarget ) return;
 
-		_gl.deleteTexture( textureProperties.__webglTexture );
+		if ( textureProperties.__webglTexture !== undefined ) {
+
+			_gl.deleteTexture( textureProperties.__webglTexture );
+
+		}
+
+		if ( renderTarget.depthTexture ) {
+
+			renderTarget.depthTexture.dispose();
+
+		}
 
 		if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
 
 			for ( var i = 0; i < 6; i ++ ) {
 
 				_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] );
-				_gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] );
+				if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] );
 
 			}
 
 		} else {
 
 			_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer );
-			_gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer );
+			if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer );
 
 		}
 
@@ -3141,7 +3153,27 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		var mipmap, mipmaps = texture.mipmaps;
 
-		if ( texture instanceof THREE.DataTexture ) {
+		if ( texture instanceof THREE.DepthTexture ) {
+
+			// populate depth texture with dummy data
+
+			var internalFormat = _gl.DEPTH_COMPONENT;
+
+			if ( texture.type === THREE.FloatType ) {
+
+				if ( !_isWebGL2 ) throw new Error('Float Depth Texture only supported in WebGL2.0');
+				internalFormat = _gl.DEPTH_COMPONENT32F;
+
+			} else if ( _isWebGL2 ) {
+
+				// WebGL 2.0 requires signed internalformat for glTexImage2D
+				internalFormat = _gl.DEPTH_COMPONENT16;
+
+			}
+
+			state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null );
+
+		} else if ( texture instanceof THREE.DataTexture ) {
 
 			// use manually created mipmaps if available
 			// if there are no manual mipmaps
@@ -3483,6 +3515,36 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	}
 
+	// Setup resources for a Depth Texture for a FBO (needs an extension)
+	function setupDepthTexture ( framebuffer, renderTarget ) {
+
+		var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube );
+		if ( isCube ) throw new Error('Depth Texture with cube render targets is not supported!');
+
+		_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
+
+		if ( !( renderTarget.depthTexture instanceof THREE.DepthTexture ) ) {
+
+			throw new Error('renderTarget.depthTexture must be an instance of THREE.DepthTexture');
+
+		}
+
+		// upload an empty depth texture with framebuffer size
+		if ( !properties.get( renderTarget.depthTexture ).__webglTexture ||
+				renderTarget.depthTexture.image.width !== renderTarget.width ||
+				renderTarget.depthTexture.image.height !== renderTarget.height ) {
+			renderTarget.depthTexture.image.width = renderTarget.width;
+			renderTarget.depthTexture.image.height = renderTarget.height;
+			renderTarget.depthTexture.needsUpdate = true;
+		}
+
+		_this.setTexture( renderTarget.depthTexture, 0 );
+
+		var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture;
+		_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );
+
+	}
+
 	// Setup GL resources for a non-texture depth buffer
 	function setupDepthRenderbuffer( renderTarget ) {
 
@@ -3490,23 +3552,33 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube );
 
-		if ( isCube ) {
+		if ( renderTarget.depthTexture ) {
 
-			renderTargetProperties.__webglDepthbuffer = [];
+			if ( isCube ) throw new Error('target.depthTexture not supported in Cube render targets');
 
-			for ( var i = 0; i < 6; i ++ ) {
+			setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget );
+
+		} else {
 
-				_gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] );
-				renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer();
-				setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget );
+			if ( isCube ) {
 
-			}
+				renderTargetProperties.__webglDepthbuffer = [];
 
-		} else {
+				for ( var i = 0; i < 6; i ++ ) {
+
+					_gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] );
+					renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer();
+					setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget );
 
-			_gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );
-			renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer();
-			setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget );
+				}
+
+			} else {
+
+				_gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );
+				renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer();
+				setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget );
+
+			}
 
 		}
 
@@ -3791,6 +3863,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		if ( p === THREE.RGBAFormat ) return _gl.RGBA;
 		if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE;
 		if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA;
+		if ( p === THREE.DepthFormat ) return _gl.DEPTH_COMPONENT;
 
 		if ( p === THREE.AddEquation ) return _gl.FUNC_ADD;
 		if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT;

+ 3 - 0
src/renderers/webgl/WebGLExtensions.js

@@ -18,6 +18,9 @@ THREE.WebGLExtensions = function ( gl ) {
 
 		switch ( name ) {
 
+			case 'WEBGL_depth_texture':
+				extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' );
+
 			case 'EXT_texture_filter_anisotropic':
 				extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
 				break;

+ 22 - 0
src/textures/DepthTexture.js

@@ -0,0 +1,22 @@
+/**
+ * @author Matt DesLauriers / @mattdesl
+ */
+
+THREE.DepthTexture = function ( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
+
+  THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, THREE.DepthFormat, type, anisotropy );
+
+  this.image = { width: width, height: height };
+
+  this.type = type !== undefined ? type : THREE.UnsignedShortType;
+
+  this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter;
+  this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter;
+
+  this.flipY = false;
+  this.generateMipmaps  = false;
+
+};
+
+THREE.DepthTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.DepthTexture.prototype.constructor = THREE.DepthTexture;

+ 1 - 0
utils/build/includes/common.json

@@ -103,6 +103,7 @@
 	"src/materials/RawShaderMaterial.js",
 	"src/materials/SpriteMaterial.js",
 	"src/textures/Texture.js",
+	"src/textures/DepthTexture.js",
 	"src/textures/CanvasTexture.js",
 	"src/textures/CubeTexture.js",
 	"src/textures/CompressedTexture.js",