diff --git a/docs/api/en/renderers/WebGLRenderer.html b/docs/api/en/renderers/WebGLRenderer.html
index ce1ba15f2502bc..640b3694ae13db 100644
--- a/docs/api/en/renderers/WebGLRenderer.html
+++ b/docs/api/en/renderers/WebGLRenderer.html
@@ -310,6 +310,9 @@
[method:null copyFramebufferToTexture]( [param:Vector2 position], [param:Tex
[method:null copyTextureToTexture]( [param:Vector2 position], [param:Texture srcTexture], [param:Texture dstTexture], [param:Number level] )
Copies all pixels of a texture to an existing texture starting from the given position. Enables access to [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texSubImage2D WebGLRenderingContext.texSubImage2D].
+ [method:null copyTextureToTexture3D]( [param:Box3 sourceBox], [param:Vector2 position], [param:Texture srcTexture], [param:Texture dstTexture], [param:Number level] )
+ Copies the pixels of a texture in the bounds '[page:Box3 sourceBox]' in the desination texture starting from the given position. Enables access to [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/texSubImage3D WebGL2RenderingContext.texSubImage3D].
+
[method:null dispose]( )
Dispose of the current rendering context.
diff --git a/examples/files.json b/examples/files.json
index 6c0a0db3c02dd0..e6928ef0a17e5d 100644
--- a/examples/files.json
+++ b/examples/files.json
@@ -319,6 +319,7 @@
"webgl2_materials_texture3d",
"webgl2_multisampled_renderbuffers",
"webgl2_rendertarget_texture2darray",
+ "webgl2_materials_texture3d_partialupdate.html",
"webgl2_volume_cloud",
"webgl2_volume_instancing",
"webgl2_volume_perlin"
diff --git a/examples/webgl2_materials_texture3d_partialupdate.html b/examples/webgl2_materials_texture3d_partialupdate.html
new file mode 100644
index 00000000000000..db841419f16290
--- /dev/null
+++ b/examples/webgl2_materials_texture3d_partialupdate.html
@@ -0,0 +1,358 @@
+
+
+
+ three.js webgl2 - volume - cloud
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js
index 5c50a0c11b923f..451552aca98644 100644
--- a/src/renderers/WebGLRenderer.js
+++ b/src/renderers/WebGLRenderer.js
@@ -1987,6 +1987,80 @@ function WebGLRenderer( parameters ) {
};
+ this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) {
+
+ if ( _this.isWebGL1Renderer ) {
+
+ console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' );
+ return;
+
+ }
+
+ const { width, height, data } = srcTexture.image;
+ const glFormat = utils.convert( dstTexture.format );
+ const glType = utils.convert( dstTexture.type );
+ let glTarget;
+
+ if ( dstTexture.isDataTexture3D ) {
+
+ textures.setTexture3D( dstTexture, 0 );
+ glTarget = _gl.TEXTURE_3D;
+
+ } else if ( dstTexture.isDataTexture2DArray ) {
+
+ textures.setTexture2DArray( dstTexture, 0 );
+ glTarget = _gl.TEXTURE_2D_ARRAY;
+
+ } else {
+
+ console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' );
+ return;
+
+ }
+
+ _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );
+ _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );
+ _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );
+
+ const unpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH );
+ const unpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT );
+ const unpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS );
+ const unpackSkipRows = _gl.getParameter( _gl.UNPACK_SKIP_ROWS );
+ const unpackSkipImages = _gl.getParameter( _gl.UNPACK_SKIP_IMAGES );
+
+ _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, width );
+ _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, height );
+ _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, sourceBox.min.x );
+ _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, sourceBox.min.y );
+ _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, sourceBox.min.z );
+
+ _gl.texSubImage3D(
+ glTarget,
+ level,
+ position.x,
+ position.y,
+ position.z,
+ sourceBox.max.x - sourceBox.min.x + 1,
+ sourceBox.max.y - sourceBox.min.y + 1,
+ sourceBox.max.z - sourceBox.min.z + 1,
+ glFormat,
+ glType,
+ data
+ );
+
+ _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, unpackRowLen );
+ _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight );
+ _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, unpackSkipPixels );
+ _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, unpackSkipRows );
+ _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, unpackSkipImages );
+
+ // Generate mipmaps only when copying level 0
+ if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget );
+
+ state.unbindTexture();
+
+ };
+
this.initTexture = function ( texture ) {
textures.setTexture2D( texture, 0 );
diff --git a/utils/build/rollup.config.js b/utils/build/rollup.config.js
index 240e78392aa170..912649d8333174 100644
--- a/utils/build/rollup.config.js
+++ b/utils/build/rollup.config.js
@@ -153,6 +153,11 @@ function glconstants() {
MAX_FRAGMENT_UNIFORM_VECTORS: 36349,
UNPACK_FLIP_Y_WEBGL: 37440,
UNPACK_PREMULTIPLY_ALPHA_WEBGL: 37441,
+ UNPACK_ROW_LENGTH: 3314,
+ UNPACK_IMAGE_HEIGHT: 32878,
+ UNPACK_SKIP_PIXELS: 3316,
+ UNPACK_SKIP_ROWS: 3315,
+ UNPACK_SKIP_IMAGES: 32877,
MAX_SAMPLES: 36183,
READ_FRAMEBUFFER: 36008,
DRAW_FRAMEBUFFER: 36009