瀏覽代碼

Merge pull request #13196 from node-3d/dev

Added GLBufferAttribute
Mr.doob 5 年之前
父節點
當前提交
ba9f8597d4

+ 117 - 0
docs/api/en/core/GLBufferAttribute.html

@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<p class="desc">
+			This buffer attribute class does not construct a VBO. Instead, it uses
+			whatever VBO is passed in constructor and can later be altered via the
+			*buffer* property.<br /><br />
+			It is required to pass additional params alongside the VBO. Those are:
+			the GL context, the GL data type, the number of components per vertex,
+			the number of bytes per component, and the number of vertices.<br /><br />
+			The most common use case for this class is when some kind of GPGPU
+			calculation interferes or even produces the VBOs in question.
+		</p>
+
+		<h2>Constructor</h2>
+		<h3>[name]( [param:WebGLBuffer buffer], [param:GLenum type], [param:Integer itemSize], [param:Integer elementSize], [param:Integer count] )</h3>
+		<p>
+		*buffer* — Must be a <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGLBuffer" target="_blank">WebGLBuffer</a>.
+		<br />
+		*type* — One of <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Constants#Data_types" target="_blank">WebGL Data Types</a>.
+		<br />
+		*itemSize* — The number of values of the array that should be associated with
+		a particular vertex. For instance, if this
+		attribute is storing a 3-component vector (such as a position, normal, or color), then itemSize should be 3.
+		<br />
+		*elementSize* — 1, 2 or 4. The corresponding size (in bytes) for the given "type" param.
+		<ul>
+			<li>gl.FLOAT: 4</li>
+			<li>gl.UNSIGNED_SHORT: 2</li>
+			<li>gl.SHORT: 2</li>
+			<li>gl.UNSIGNED_INT: 4</li>
+			<li>gl.INT: 4</li>
+			<li>gl.BYTE: 1</li>
+			<li>gl.UNSIGNED_BYTE: 1</li>
+		</ul>
+		*count* — The expected number of vertices in VBO.
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:WebGLBuffer buffer]</h3>
+		<p>
+			The current <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGLBuffer" target="_blank">WebGLBuffer</a> instance.
+		</p>
+
+		<h3>[property:Integer count]</h3>
+		<p>
+			The expected number of vertices in VBO.
+		</p>
+
+		<h3>[property:Integer itemSize]</h3>
+		<p>
+			How many values make up each item (vertex).
+		</p>
+
+		<h3>[property:Integer elementSize]</h3>
+		<p>
+			Stores the corresponding size in bytes for the current *type* property value.
+		</p>
+		<p>
+			See above (constructor) for a list of known type sizes.
+		</p>
+
+		<h3>[property:GLenum type]</h3>
+		<p>
+			A <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Constants#Data_types" target="_blank">WebGL Data Type</a>
+			describing the underlying VBO contents.
+		</p>
+		<p>
+			Set this property together with *elementSize*. The recommended way is
+			using the *setType* method.
+		</p>
+
+		<h3>[property:Boolean isGLBufferAttribute]</h3>
+		<p>
+			Read-only. Always *true*.
+		</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:null setBuffer]( buffer ) </h3>
+		<p>Sets the *buffer* property.</p>
+
+		<h3>[method:null setType]( type, elementSize ) </h3>
+		<p>Sets the both *type* and *elementSize* properties.</p>
+
+		<h3>[method:null setItemSize]( itemSize ) </h3>
+		<p>Sets the *itemSize* property.</p>
+
+		<h3>[method:null setCount]( count ) </h3>
+		<p>Sets the *count* property.</p>
+
+		<h3>[property:Integer version]</h3>
+		<p>
+		A version number, incremented every time the needsUpdate property is set to true.
+		</p>
+
+		<h3>[property:Boolean needsUpdate]</h3>
+		<p>
+		Default is *false*. Setting this to true increments [page:GLBufferAttribute.version version].
+		</p>
+
+		<h2>Source</h2>
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+		</p>
+	</body>
+</html>

+ 117 - 0
docs/api/zh/core/GLBufferAttribute.html

@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<p class="desc">
+			This buffer attribute class does not construct a VBO. Instead, it uses
+			whatever VBO is passed in constructor and can later be altered via the
+			*buffer* property.<br /><br />
+			It is required to pass additional params alongside the VBO. Those are:
+			the GL context, the GL data type, the number of components per vertex,
+			the number of bytes per component, and the number of vertices.<br /><br />
+			The most common use case for this class is when some kind of GPGPU
+			calculation interferes or even produces the VBOs in question.
+		</p>
+
+		<h2>Constructor</h2>
+		<h3>[name]( [param:WebGLBuffer buffer], [param:GLenum type], [param:Integer itemSize], [param:Integer elementSize], [param:Integer count] )</h3>
+		<p>
+		*buffer* — Must be a <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGLBuffer" target="_blank">WebGLBuffer</a>.
+		<br />
+		*type* — One of <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Constants#Data_types" target="_blank">WebGL Data Types</a>.
+		<br />
+		*itemSize* — The number of values of the array that should be associated with
+		a particular vertex. For instance, if this
+		attribute is storing a 3-component vector (such as a position, normal, or color), then itemSize should be 3.
+		<br />
+		*elementSize* — 1, 2 or 4. The corresponding size (in bytes) for the given "type" param.
+		<ul>
+			<li>gl.FLOAT: 4</li>
+			<li>gl.UNSIGNED_SHORT: 2</li>
+			<li>gl.SHORT: 2</li>
+			<li>gl.UNSIGNED_INT: 4</li>
+			<li>gl.INT: 4</li>
+			<li>gl.BYTE: 1</li>
+			<li>gl.UNSIGNED_BYTE: 1</li>
+		</ul>
+		*count* — The expected number of vertices in VBO.
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:WebGLBuffer buffer]</h3>
+		<p>
+			The current <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGLBuffer" target="_blank">WebGLBuffer</a> instance.
+		</p>
+
+		<h3>[property:Integer count]</h3>
+		<p>
+			The expected number of vertices in VBO.
+		</p>
+
+		<h3>[property:Integer itemSize]</h3>
+		<p>
+			How many values make up each item (vertex).
+		</p>
+
+		<h3>[property:Integer elementSize]</h3>
+		<p>
+			Stores the corresponding size in bytes for the current *type* property value.
+		</p>
+		<p>
+			See above (constructor) for a list of known type sizes.
+		</p>
+
+		<h3>[property:GLenum type]</h3>
+		<p>
+			A <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Constants#Data_types" target="_blank">WebGL Data Type</a>
+			describing the underlying VBO contents.
+		</p>
+		<p>
+			Set this property together with *elementSize*. The recommended way is
+			using the *setType* method.
+		</p>
+
+		<h3>[property:Boolean isGLBufferAttribute]</h3>
+		<p>
+			Read-only. Always *true*.
+		</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:null setBuffer]( buffer ) </h3>
+		<p>Sets the *buffer* property.</p>
+
+		<h3>[method:null setType]( type, elementSize ) </h3>
+		<p>Sets the both *type* and *elementSize* properties.</p>
+
+		<h3>[method:null setItemSize]( itemSize ) </h3>
+		<p>Sets the *itemSize* property.</p>
+
+		<h3>[method:null setCount]( count ) </h3>
+		<p>Sets the *count* property.</p>
+
+		<h3>[property:Integer version]</h3>
+		<p>
+		A version number, incremented every time the needsUpdate property is set to true.
+		</p>
+
+		<h3>[property:Boolean needsUpdate]</h3>
+		<p>
+		Default is *false*. Setting this to true increments [page:GLBufferAttribute.version version].
+		</p>
+
+		<h2>Source</h2>
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+		</p>
+	</body>
+</html>

+ 2 - 0
docs/list.js

@@ -89,6 +89,7 @@ var list = {
 				"EventDispatcher": "api/en/core/EventDispatcher",
 				"Face3": "api/en/core/Face3",
 				"Geometry": "api/en/core/Geometry",
+				"GLBufferAttribute": "api/en/core/GLBufferAttribute",
 				"InstancedBufferAttribute": "api/en/core/InstancedBufferAttribute",
 				"InstancedBufferGeometry": "api/en/core/InstancedBufferGeometry",
 				"InstancedInterleavedBuffer": "api/en/core/InstancedInterleavedBuffer",
@@ -570,6 +571,7 @@ var list = {
 				"EventDispatcher": "api/zh/core/EventDispatcher",
 				"Face3": "api/zh/core/Face3",
 				"Geometry": "api/zh/core/Geometry",
+				"GLBufferAttribute": "api/zh/core/GLBufferAttribute",
 				"InstancedBufferAttribute": "api/zh/core/InstancedBufferAttribute",
 				"InstancedBufferGeometry": "api/zh/core/InstancedBufferGeometry",
 				"InstancedInterleavedBuffer": "api/zh/core/InstancedInterleavedBuffer",

+ 1 - 0
examples/files.js

@@ -287,6 +287,7 @@ var files = {
 		"webgl_buffergeometry_lines_indexed",
 		"webgl_buffergeometry_morphtargets",
 		"webgl_buffergeometry_points",
+		"webgl_buffergeometry_points_glbufferattribute",
 		"webgl_buffergeometry_points_interleaved",
 		"webgl_buffergeometry_rawshader",
 		"webgl_buffergeometry_selective_draw",

二進制
examples/screenshots/webgl_buffergeometry_points_glbufferattribute.jpg


二進制
examples/screenshots/webgl_buffergeometry_points_glbufferattribute.png


+ 174 - 0
examples/webgl_buffergeometry_points_glbufferattribute.html

@@ -0,0 +1,174 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - buffergeometry - custom VBOs</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+
+		<div id="container"></div>
+		<div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - buffergeometry - custom VBOs</div>
+
+		<script type="module">
+
+			import * as THREE from '../build/three.module.js';
+
+			import Stats from './jsm/libs/stats.module.js';
+			
+			var container, stats;
+
+			var camera, scene, renderer;
+
+			var points;
+
+			var particles = 300000;
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.getElementById( 'container' );
+
+				//
+
+				renderer = new THREE.WebGLRenderer( { antialias: false } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				container.appendChild( renderer.domElement );
+
+				//
+
+				camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 5, 3500 );
+				camera.position.z = 2750;
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0x050505 );
+				scene.fog = new THREE.Fog( 0x050505, 2000, 3500 );
+
+				//
+
+				var geometry = new THREE.BufferGeometry();
+
+				var positions = [];
+				var positions2 = [];
+				var colors = [];
+
+				var color = new THREE.Color();
+
+				var n = 1000, n2 = n / 2; // particles spread in the cube
+
+				for ( var i = 0; i < particles; i ++ ) {
+
+					// positions
+
+					var x = Math.random() * n - n2;
+					var y = Math.random() * n - n2;
+					var z = Math.random() * n - n2;
+
+					positions.push( x, y, z );
+					positions2.push( z * 0.3, x * 0.3, y * 0.3 );
+
+					// colors
+
+					var vx = ( x / n ) + 0.5;
+					var vy = ( y / n ) + 0.5;
+					var vz = ( z / n ) + 0.5;
+
+					color.setRGB( vx, vy, vz );
+
+					colors.push( color.r, color.g, color.b );
+
+				}
+
+				var gl = renderer.getContext();
+
+				var pos = gl.createBuffer();
+				gl.bindBuffer( gl.ARRAY_BUFFER, pos );
+				gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( positions ), gl.STATIC_DRAW );
+
+				var pos2 = gl.createBuffer();
+				gl.bindBuffer( gl.ARRAY_BUFFER, pos2 );
+				gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( positions2 ), gl.STATIC_DRAW );
+
+				var rgb = gl.createBuffer();
+				gl.bindBuffer( gl.ARRAY_BUFFER, rgb );
+				gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( colors ), gl.STATIC_DRAW );
+
+				var posAttr = new THREE.GLBufferAttribute( pos, gl.FLOAT, 3, 4, particles );
+				geometry.setAttribute( 'position', posAttr );
+
+				setInterval( function () {
+
+					posAttr.buffer = ( posAttr.buffer === pos ) ? pos2 : pos;
+					posAttr.needsUpdate = true;
+
+				}, 2000 );
+
+				geometry.setAttribute( 'color', new THREE.GLBufferAttribute( rgb, gl.FLOAT, 3, 4, particles ) );
+
+				//
+
+				var material = new THREE.PointsMaterial( { size: 15, vertexColors: THREE.VertexColors } );
+
+				points = new THREE.Points( geometry, material );
+
+				// Choose one:
+				// geometry.boundingSphere = ( new THREE.Sphere() ).set( new THREE.Vector3(), Infinity );
+				points.frustumCulled = false;
+
+				scene.add( points );
+
+				//
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			var drawCount = 10000;
+			function render() {
+
+				drawCount = ( Math.max( 5000, drawCount ) + Math.floor( 500 * Math.random() ) ) % particles;
+				points.geometry.setDrawRange( 0, drawCount );
+
+				var time = Date.now() * 0.001;
+
+				points.rotation.x = time * 0.1;
+				points.rotation.y = time * 0.2;
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 1 - 0
src/Three.js

@@ -98,6 +98,7 @@ export { InterleavedBufferAttribute } from './core/InterleavedBufferAttribute.js
 export { InstancedInterleavedBuffer } from './core/InstancedInterleavedBuffer.js';
 export { InterleavedBuffer } from './core/InterleavedBuffer.js';
 export { InstancedBufferAttribute } from './core/InstancedBufferAttribute.js';
+export { GLBufferAttribute } from './core/GLBufferAttribute.js';
 export * from './core/BufferAttribute.js';
 export { Face3 } from './core/Face3.js';
 export { Object3D } from './core/Object3D.js';

+ 24 - 1
src/core/BufferGeometry.js

@@ -554,6 +554,19 @@ BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototy
 		const position = this.attributes.position;
 		const morphAttributesPosition = this.morphAttributes.position;
 
+		if ( position && position.isGLBufferAttribute ) {
+
+			console.error( 'THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".', this );
+
+			this.boundingBox.set(
+				new Vector3( - Infinity, - Infinity, - Infinity ),
+				new Vector3( + Infinity, + Infinity, + Infinity )
+			);
+
+			return;
+
+		}
+
 		if ( position !== undefined ) {
 
 			this.boundingBox.setFromBufferAttribute( position );
@@ -594,7 +607,7 @@ BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototy
 
 		if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {
 
-			console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this );
+			console.error( 'THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this );
 
 		}
 
@@ -611,6 +624,16 @@ BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototy
 		const position = this.attributes.position;
 		const morphAttributesPosition = this.morphAttributes.position;
 
+		if ( position && position.isGLBufferAttribute ) {
+
+			console.error( 'THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".', this );
+
+			this.boundingSphere.set( new Vector3(), Infinity );
+
+			return;
+
+		}
+
 		if ( position ) {
 
 			// first, find the center of the bounding sphere

+ 31 - 0
src/core/GLBufferAttribute.d.ts

@@ -0,0 +1,31 @@
+/**
+ * @see <a href="https://github.com/mrdoob/three.js/blob/master/src/core/GLBufferAttribute.js">src/core/GLBufferAttribute.js</a>
+ */
+
+export class GLBufferAttribute {
+
+	constructor(
+		buffer: WebGLBuffer,
+		type: number,
+		itemSize: number,
+		elementSize: 1 | 2 | 4,
+		count: number
+	);
+
+	buffer: WebGLBuffer;
+	type: number;
+	itemSize: number;
+	elementSize: 1 | 2 | 4;
+	count: number;
+	version: number;
+
+	readonly isGLBufferAttribute: true;
+
+	set needsUpdate( value: boolean );
+
+	setBuffer( buffer: WebGLBuffer ): this;
+	setType( type: number, elementSize: 1 | 2 | 4 ): this;
+	setItemSize( itemSize: number ): this;
+	setCount( count: number ): this;
+
+}

+ 77 - 0
src/core/GLBufferAttribute.js

@@ -0,0 +1,77 @@
+/**
+ * @author raub / https://github.com/raub
+ */
+
+/**
+ * Element size is one of:
+ * gl.FLOAT: 4
+ * gl.UNSIGNED_SHORT: 2
+ * gl.SHORT: 2
+ * gl.UNSIGNED_INT: 4
+ * gl.INT: 4
+ * gl.BYTE: 1
+ * gl.UNSIGNED_BYTE: 1
+ */
+function GLBufferAttribute( buffer, type, itemSize, elementSize, count ) {
+
+	this.buffer = buffer;
+	this.type = type;
+	this.itemSize = itemSize;
+	this.elementSize = elementSize;
+	this.count = count;
+
+	this.version = 0;
+
+}
+
+Object.defineProperty( GLBufferAttribute.prototype, 'needsUpdate', {
+
+	set: function ( value ) {
+
+		if ( value === true ) this.version ++;
+
+	}
+
+} );
+
+Object.assign( GLBufferAttribute.prototype, {
+
+	isGLBufferAttribute: true,
+
+	setBuffer: function ( buffer ) {
+
+		this.buffer = buffer;
+
+		return this;
+
+	},
+
+	setType: function ( type, elementSize ) {
+
+		this.type = type;
+		this.elementSize = elementSize;
+
+		return this;
+
+	},
+
+	setItemSize: function ( itemSize ) {
+
+		this.itemSize = itemSize;
+
+		return this;
+
+	},
+
+	setCount: function ( count ) {
+
+		this.count = count;
+
+		return this;
+
+	},
+
+} );
+
+
+export { GLBufferAttribute };

+ 19 - 0
src/renderers/webgl/WebGLAttributes.js

@@ -122,6 +122,25 @@ function WebGLAttributes( gl, capabilities ) {
 
 	function update( attribute, bufferType ) {
 
+		if ( attribute.isGLBufferAttribute ) {
+
+			var cached = buffers.get( attribute );
+
+			if ( ! cached || cached.version < attribute.version ) {
+
+				buffers.set( attribute, {
+					buffer: attribute.buffer,
+					type: attribute.type,
+					bytesPerElement: attribute.elementSize,
+					version: attribute.version
+				} );
+
+			}
+
+			return;
+
+		}
+
 		if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
 
 		const data = buffers.get( attribute );

+ 2 - 0
src/renderers/webgl/WebGLBindingStates.js

@@ -176,6 +176,8 @@
 			const cachedAttribute = cachedAttributes[ key ];
 			const geometryAttribute = geometryAttributes[ key ];
 
+			if ( geometryAttribute.isGLBufferAttribute ) return true;
+
 			if ( cachedAttribute === undefined ) return true;
 
 			if ( cachedAttribute.attribute !== geometryAttribute ) return true;