Browse Source

Texture 3D support for colorGrading

Sebastien Vandenberghe 7 years ago
parent
commit
aef6fe3afb

+ 2 - 2
localDev/index.html

@@ -65,8 +65,8 @@
 			.require(indexjs)
 			.load(function() {
 				if (BABYLON.Engine.isSupported()) {
-					engine = new BABYLON.Engine(canvas, true, { stencil: true });					
-					BABYLONDEVTOOLS.Loader.debugShortcut(engine);			
+					engine = new BABYLON.Engine(canvas, true, { stencil: true, disableWebGL2Support: false });
+					BABYLONDEVTOOLS.Loader.debugShortcut(engine);
 
 					// call the scene creation from the js.
 					var scene = createScene();

+ 152 - 3
src/Engine/babylon.engine.ts

@@ -726,6 +726,7 @@
 
         private _emptyTexture: Nullable<InternalTexture>;
         private _emptyCubeTexture: Nullable<InternalTexture>;
+        private _emptyTexture3D: Nullable<InternalTexture>;
 
         private _frameHandler: number;
 
@@ -753,6 +754,13 @@
 
             return this._emptyTexture;
         }
+        public get emptyTexture3D(): InternalTexture {
+            if (!this._emptyTexture3D) {
+                this._emptyTexture3D = this.createRawTexture3D(new Uint8Array(4), 1, 1, 1, BABYLON.Engine.TEXTUREFORMAT_RGBA, false, false, BABYLON.Texture.NEAREST_SAMPLINGMODE);
+            }
+
+            return this._emptyTexture3D;
+        }
         public get emptyCubeTexture(): InternalTexture {
             if (!this._emptyCubeTexture) {
                 var faceData = new Uint8Array(4);
@@ -3196,6 +3204,12 @@
                 this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_MAG_FILTER, filters.mag);
                 this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_MIN_FILTER, filters.min);
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
+            } else if (texture.is3D) {
+                this._bindTextureDirectly(this._gl.TEXTURE_3D, texture);
+
+                this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MAG_FILTER, filters.mag);
+                this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MIN_FILTER, filters.min);
+                this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
             } else {
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
 
@@ -4121,6 +4135,73 @@
             return texture;
         };
 
+        public updateRawTexture3D(texture: InternalTexture, data: ArrayBufferView, format: number, invertY: boolean, compression: Nullable<string> = null): void {
+            var internalFormat = this._getInternalFormat(format);
+            this._bindTextureDirectly(this._gl.TEXTURE_3D, texture);
+            this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
+
+            if (!this._doNotHandleContextLost) {
+                texture._bufferView = data;
+                texture.format = format;
+                texture.invertY = invertY;
+                texture._compression = compression;
+            }
+
+            if (texture.width % 4 !== 0) {
+                this._gl.pixelStorei(this._gl.UNPACK_ALIGNMENT, 1);
+            }
+
+            if (compression) {
+                this._gl.compressedTexImage3D(this._gl.TEXTURE_3D, 0, (<any>this.getCaps().s3tc)[compression], texture.width, texture.height, texture.depth, 0, data);
+            } else {
+                this._gl.texImage3D(this._gl.TEXTURE_3D, 0, internalFormat, texture.width, texture.height, texture.depth, 0, internalFormat, this._gl.UNSIGNED_BYTE, data);
+            }
+
+            if (texture.generateMipMaps) {
+                this._gl.generateMipmap(this._gl.TEXTURE_3D);
+            }
+            this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
+            this.resetTextureCache();
+            texture.isReady = true;
+        }
+
+        public createRawTexture3D(data: ArrayBufferView, width: number, height: number, depth: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number, compression: Nullable<string> = null): InternalTexture {
+            var texture = new InternalTexture(this, InternalTexture.DATASOURCE_RAW3D);
+            texture.baseWidth = width;
+            texture.baseHeight = height;
+            texture.baseDepth = depth;
+            texture.width = width;
+            texture.height = height;
+            texture.depth = depth;
+            texture.format = format;
+            texture.generateMipMaps = generateMipMaps;
+            texture.samplingMode = samplingMode;
+            texture.is3D = true;
+
+            if (!this._doNotHandleContextLost) {
+                texture._bufferView = data;
+            }
+
+            this.updateRawTexture3D(texture, data, format, invertY, compression);
+            this._bindTextureDirectly(this._gl.TEXTURE_3D, texture);
+
+            // Filters
+            var filters = getSamplingParameters(samplingMode, generateMipMaps, this._gl);
+
+            this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MAG_FILTER, filters.mag);
+            this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MIN_FILTER, filters.min);
+
+            if (generateMipMaps) {
+                this._gl.generateMipmap(this._gl.TEXTURE_3D);
+            }
+
+            this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
+
+            this._internalTexturesCache.push(texture);
+
+            return texture;
+        }
+
         private _prepareWebGLTextureContinuation(texture: InternalTexture, scene: Nullable<Scene>, noMipmap: boolean, isCompressed: boolean, samplingMode: number): void {
             var gl = this._gl;
             if (!gl) {
@@ -4306,6 +4387,9 @@
                 this.activateTexture((<any>this._gl)["TEXTURE" + channel]);
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
+                if (this.webGLVersion > 1) {
+                    this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
+                }
             }
         }
 
@@ -4325,6 +4409,9 @@
                     this.activateTexture((<any>this._gl)["TEXTURE" + channel]);
                     this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
                     this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
+                    if (this.webGLVersion > 1) {
+                        this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
+                    }
                 }
                 return;
             }
@@ -4340,8 +4427,19 @@
                 return;
             }
 
-            var internalTexture = texture.isReady() ? texture.getInternalTexture() :
-                (texture.isCube ? this.emptyCubeTexture : this.emptyTexture);
+            let internalTexture: InternalTexture;
+            if (texture.isReady()) {
+                internalTexture = texture.getInternalTexture();
+            }
+            else if (texture.isCube) {
+                internalTexture = this.emptyCubeTexture;
+            }
+            else if (texture.is3D) {
+                internalTexture = this.emptyTexture3D;
+            }
+            else {
+                internalTexture = this.emptyTexture;
+            }
 
             if (this._activeTexturesCache[channel] === internalTexture) {
                 return;
@@ -4351,7 +4449,58 @@
                 this.activateTexture((<any>this._gl)["TEXTURE" + channel]);
             }
 
-            if (internalTexture && internalTexture.isCube) {
+            if (internalTexture && internalTexture.is3D) {
+                this._bindTextureDirectly(this._gl.TEXTURE_3D, internalTexture);
+
+                if (internalTexture && internalTexture._cachedWrapU !== texture.wrapU) {
+                    internalTexture._cachedWrapU = texture.wrapU;
+
+                    switch (texture.wrapU) {
+                        case Texture.WRAP_ADDRESSMODE:
+                            this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_S, this._gl.REPEAT);
+                            break;
+                        case Texture.CLAMP_ADDRESSMODE:
+                            this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_S, this._gl.CLAMP_TO_EDGE);
+                            break;
+                        case Texture.MIRROR_ADDRESSMODE:
+                            this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_S, this._gl.MIRRORED_REPEAT);
+                            break;
+                    }
+                }
+
+                if (internalTexture && internalTexture._cachedWrapV !== texture.wrapV) {
+                    internalTexture._cachedWrapV = texture.wrapV;
+                    switch (texture.wrapV) {
+                        case Texture.WRAP_ADDRESSMODE:
+                            this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_T, this._gl.REPEAT);
+                            break;
+                        case Texture.CLAMP_ADDRESSMODE:
+                            this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_T, this._gl.CLAMP_TO_EDGE);
+                            break;
+                        case Texture.MIRROR_ADDRESSMODE:
+                            this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_T, this._gl.MIRRORED_REPEAT);
+                            break;
+                    }
+                }
+
+                if (internalTexture && internalTexture._cachedWrapR !== texture.wrapR) {
+                    internalTexture._cachedWrapR = texture.wrapR;
+                    switch (texture.wrapV) {
+                        case Texture.WRAP_ADDRESSMODE:
+                            this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_R, this._gl.REPEAT);
+                            break;
+                        case Texture.CLAMP_ADDRESSMODE:
+                            this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_R, this._gl.CLAMP_TO_EDGE);
+                            break;
+                        case Texture.MIRROR_ADDRESSMODE:
+                            this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_R, this._gl.MIRRORED_REPEAT);
+                            break;
+                    }
+                }
+
+                this._setAnisotropicLevel(this._gl.TEXTURE_3D, texture);
+            }
+            else if (internalTexture && internalTexture.isCube) {
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, internalTexture);
 
                 if (internalTexture._cachedCoordinatesMode !== texture.coordinatesMode) {

+ 12 - 0
src/Engine/babylon.webgl2.ts

@@ -3,6 +3,18 @@
 // Definitions by: Nico Kemnitz <https://github.com/nkemnitz/>
 // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
 
+interface WebGLRenderingContext {
+    readonly TEXTURE_3D: number;
+    readonly TEXTURE_2D_ARRAY: number;
+    readonly TEXTURE_WRAP_R: number;
+
+    texImage3D(target: number, level: number, internalformat: number, width: number, height: number, depth: number, border: number, format: number, type: number, pixels: ArrayBufferView | null): void;
+    texImage3D(target: number, level: number, internalformat: number, width: number, height: number, depth: number, border: number, format: number, type: number, pixels: ArrayBufferView, offset: number): void;
+    texImage3D(target: number, level: number, internalformat: number, width: number, height: number, depth: number, border: number, format: number, type: number, pixels: ImageBitmap | ImageData | HTMLVideoElement | HTMLImageElement | HTMLCanvasElement): void;
+    
+    compressedTexImage3D(target: number, level: number, internalformat: number, width: number, height: number, depth: number, border: number, data: ArrayBufferView, offset?: number, length?: number): void;
+}
+
 interface ImageBitmap {
     readonly width: number;
     readonly height: number;

+ 1 - 0
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -106,6 +106,7 @@
         public CONTRAST = false;
         public COLORCURVES = false;
         public COLORGRADING = false;
+        public COLORGRADING3D = false;
         public SAMPLER3DGREENDEPTH = false;
         public SAMPLER3DBGRMAP = false;
         public IMAGEPROCESSINGPOSTPROCESS = false;

+ 6 - 0
src/Materials/Textures/babylon.baseTexture.ts

@@ -51,12 +51,18 @@
         public wrapV = Texture.WRAP_ADDRESSMODE;
 
         @serialize()
+        public wrapR = Texture.WRAP_ADDRESSMODE;
+
+        @serialize()
         public anisotropicFilteringLevel = BaseTexture.DEFAULT_ANISOTROPIC_FILTERING_LEVEL;
 
         @serialize()
         public isCube = false;
 
         @serialize()
+        public is3D = false;
+
+        @serialize()
         public gammaSpace = true;
 
         @serialize()

+ 20 - 3
src/Materials/Textures/babylon.colorGradingTexture.ts

@@ -42,8 +42,11 @@ module BABYLON {
             this.url = url;
             this.hasAlpha = false;
             this.isCube = false;
+            this.is3D = scene.getEngine().webGLVersion > 1;
             this.wrapU = Texture.CLAMP_ADDRESSMODE;
             this.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this.wrapR = Texture.CLAMP_ADDRESSMODE;
+
             this.anisotropicFilteringLevel = 1;
 
             this._texture = this._getFromCache(url, true);
@@ -69,7 +72,15 @@ module BABYLON {
          * Occurs when the file being loaded is a .3dl LUT file.
          */
         private load3dlTexture() {
-            var texture = this.getScene().getEngine().createRawTexture(null, 1, 1, BABYLON.Engine.TEXTUREFORMAT_RGBA, false, false, Texture.BILINEAR_SAMPLINGMODE);
+            var engine = this.getScene().getEngine();
+            var texture: InternalTexture;
+            if (engine.webGLVersion === 1) {
+                texture = engine.createRawTexture(null, 1, 1, BABYLON.Engine.TEXTUREFORMAT_RGBA, false, false, Texture.BILINEAR_SAMPLINGMODE);
+            } 
+            else {
+                texture = engine.createRawTexture3D(null, 1, 1, 1, BABYLON.Engine.TEXTUREFORMAT_RGBA, false, false, Texture.BILINEAR_SAMPLINGMODE);
+            }
+
             this._texture = texture;
 
             var callback = (text: string) => {
@@ -136,8 +147,14 @@ module BABYLON {
                     }
                 }
 
-                texture.updateSize(size * size, size);
-                this.getScene().getEngine().updateRawTexture(texture, data, BABYLON.Engine.TEXTUREFORMAT_RGBA, false);
+                if (texture.is3D) {
+                    texture.updateSize(size, size, size);
+                    engine.updateRawTexture3D(texture, data, BABYLON.Engine.TEXTUREFORMAT_RGBA, false);
+                }
+                else {
+                    texture.updateSize(size * size, size);
+                    engine.updateRawTexture(texture, data, BABYLON.Engine.TEXTUREFORMAT_RGBA, false);
+                }
             }
 
             Tools.LoadFile(this.url, callback);

+ 20 - 3
src/Materials/Textures/babylon.internalTexture.ts

@@ -11,9 +11,11 @@ module BABYLON {
         public static DATASOURCE_CUBE = 7;
         public static DATASOURCE_CUBERAW = 8;
         public static DATASOURCE_CUBEPREFILTERED = 9;
+        public static DATASOURCE_RAW3D = 10;
 
         public isReady: boolean;
         public isCube: boolean;
+        public is3D: boolean;
         public url: string;
         public samplingMode: number;
         public generateMipMaps: boolean;
@@ -23,8 +25,10 @@ module BABYLON {
         public onLoadedObservable = new Observable<InternalTexture>();
         public width: number;
         public height: number;
+        public depth: number;
         public baseWidth: number;
         public baseHeight: number;
+        public baseDepth: number;
         public invertY: boolean;
 
         // Private
@@ -44,6 +48,7 @@ module BABYLON {
         public _cachedCoordinatesMode: Nullable<number>;
         public _cachedWrapU: Nullable<number>;
         public _cachedWrapV: Nullable<number>;
+        public _cachedWrapR: Nullable<number>;
         public _cachedAnisotropicFilteringLevel: Nullable<number>;
         public _isDisabled: boolean;
         public _compression: Nullable<string>;
@@ -78,12 +83,16 @@ module BABYLON {
             this._references++;
         }
 
-        public updateSize(width: number, height: number): void {
+        public updateSize(width: int, height: int, depth: int = 1): void {
             this.width = width;
             this.height = height;
-            this._size = width * height;
+            this.depth = depth;
+
             this.baseWidth = width;
             this.baseHeight = height;
+            this.baseDepth = depth;
+
+            this._size = width * height * depth;
         }
 
         public _rebuild(): void {
@@ -111,7 +120,15 @@ module BABYLON {
                     proxy._swapAndDie(this);
 
                     this.isReady = true;
-                return;                    
+                return;
+
+                case InternalTexture.DATASOURCE_RAW3D:
+                    proxy = this._engine.createRawTexture3D(this._bufferView, this.baseWidth, this.baseHeight, this.baseDepth, this.format, this.generateMipMaps, 
+                        this.invertY, this.samplingMode, this._compression); 
+                    proxy._swapAndDie(this);
+
+                    this.isReady = true;
+                return;
                 
                 case InternalTexture.DATASOURCE_DYNAMIC:
                     proxy = this._engine.createDynamicTexture(this.baseWidth, this.baseHeight, this.generateMipMaps, this.samplingMode); 

+ 7 - 4
src/Materials/babylon.imageProcessingConfiguration.ts

@@ -13,6 +13,7 @@ module BABYLON {
         EXPOSURE: boolean;
         COLORCURVES: boolean;
         COLORGRADING: boolean;
+        COLORGRADING3D: boolean;
         SAMPLER3DGREENDEPTH: boolean;
         SAMPLER3DBGRMAP: boolean;
         IMAGEPROCESSINGPOSTPROCESS: boolean;
@@ -78,7 +79,7 @@ module BABYLON {
         }
 
         @serialize()
-        private _colorGradingWithGreenDepth = false;
+        private _colorGradingWithGreenDepth = true;
         /**
          * Gets wether the color grading effect is using a green depth for the 3d Texture.
          */
@@ -98,7 +99,7 @@ module BABYLON {
         }
 
         @serialize()
-        private _colorGradingBGR = false;
+        private _colorGradingBGR = true;
         /**
          * Gets wether the color grading texture contains BGR values.
          */
@@ -338,8 +339,9 @@ module BABYLON {
                 defines.CONTRAST = false;
                 defines.EXPOSURE = false;
                 defines.COLORCURVES = false;
-                defines.COLORGRADING = false;  
-                defines.IMAGEPROCESSING = false;              
+                defines.COLORGRADING = false;
+                defines.COLORGRADING3D = false;
+                defines.IMAGEPROCESSING = false;
                 defines.IMAGEPROCESSINGPOSTPROCESS = this.applyByPostProcess;
                 return;
             }
@@ -351,6 +353,7 @@ module BABYLON {
             defines.EXPOSURE = (this.exposure !== 1.0);
             defines.COLORCURVES = (this.colorCurvesEnabled && !!this.colorCurves);
             defines.COLORGRADING = (this.colorGradingEnabled && !!this.colorGradingTexture);
+            defines.COLORGRADING3D = defines.COLORGRADING && (this.colorGradingTexture.getScene().getEngine().webGLVersion > 1);
             defines.SAMPLER3DGREENDEPTH = this.colorGradingWithGreenDepth;
             defines.SAMPLER3DBGRMAP = this.colorGradingBGR;
             defines.IMAGEPROCESSINGPOSTPROCESS = this.applyByPostProcess;

+ 1 - 0
src/Materials/babylon.standardMaterial.ts

@@ -80,6 +80,7 @@ module BABYLON {
         public CONTRAST = false;
         public COLORCURVES = false;
         public COLORGRADING = false;
+        public COLORGRADING3D = false;
         public SAMPLER3DGREENDEPTH = false;
         public SAMPLER3DBGRMAP = false;
         public IMAGEPROCESSINGPOSTPROCESS = false;

+ 1 - 0
src/PostProcess/babylon.imageProcessingPostProcess.ts

@@ -304,6 +304,7 @@
             CONTRAST: false,
             COLORCURVES: false,
             COLORGRADING: false,
+            COLORGRADING3D: false,
             FROMLINEARSPACE: false,
             SAMPLER3DGREENDEPTH: false,
             SAMPLER3DBGRMAP: false,

+ 5 - 1
src/Shaders/ShadersInclude/imageProcessingDeclaration.fx

@@ -19,6 +19,10 @@
 #endif
 
 #ifdef COLORGRADING
-	uniform sampler2D txColorTransform;
+	#ifdef COLORGRADING3D
+		uniform highp sampler3D txColorTransform;
+	#else
+		uniform sampler2D txColorTransform;
+	#endif
 	uniform vec4 colorTransformSettings;
 #endif

+ 46 - 42
src/Shaders/ShadersInclude/imageProcessingFunctions.fx

@@ -1,52 +1,52 @@
-#ifdef COLORGRADING
-/** 
- * Polyfill for SAMPLE_TEXTURE_3D, which is unsupported in WebGL.
- * sampler3dSetting.x = textureOffset (0.5 / textureSize).
- * sampler3dSetting.y = textureSize.
- */
-vec3 sampleTexture3D(sampler2D colorTransform, vec3 color, vec2 sampler3dSetting)
-{
-	float sliceSize = 2.0 * sampler3dSetting.x; // Size of 1 slice relative to the texture, for example 1/8
-
-#ifdef SAMPLER3DGREENDEPTH
-	float sliceContinuous = (color.g - sampler3dSetting.x) * sampler3dSetting.y;
-#else
-	float sliceContinuous = (color.b - sampler3dSetting.x) * sampler3dSetting.y;
-#endif
-	float sliceInteger = floor(sliceContinuous);
+#if defined(COLORGRADING) && !defined(COLORGRADING3D)
+	/** 
+	* Polyfill for SAMPLE_TEXTURE_3D, which is unsupported in WebGL.
+	* sampler3dSetting.x = textureOffset (0.5 / textureSize).
+	* sampler3dSetting.y = textureSize.
+	*/
+	vec3 sampleTexture3D(sampler2D colorTransform, vec3 color, vec2 sampler3dSetting)
+	{
+		float sliceSize = 2.0 * sampler3dSetting.x; // Size of 1 slice relative to the texture, for example 1/8
+
+	#ifdef SAMPLER3DGREENDEPTH
+		float sliceContinuous = (color.g - sampler3dSetting.x) * sampler3dSetting.y;
+	#else
+		float sliceContinuous = (color.b - sampler3dSetting.x) * sampler3dSetting.y;
+	#endif
+		float sliceInteger = floor(sliceContinuous);
 
-	// Note: this is mathematically equivalent to fract(sliceContinuous); but we use explicit subtract
-	// rather than separate fract() for correct results near slice boundaries (matching sliceInteger choice)
-	float sliceFraction = sliceContinuous - sliceInteger;
+		// Note: this is mathematically equivalent to fract(sliceContinuous); but we use explicit subtract
+		// rather than separate fract() for correct results near slice boundaries (matching sliceInteger choice)
+		float sliceFraction = sliceContinuous - sliceInteger;
 
-#ifdef SAMPLER3DGREENDEPTH
-	vec2 sliceUV = color.rb;
-#else
-	vec2 sliceUV = color.rg;
-#endif
-	
-	sliceUV.x *= sliceSize;
-	sliceUV.x += sliceInteger * sliceSize;
+	#ifdef SAMPLER3DGREENDEPTH
+		vec2 sliceUV = color.rb;
+	#else
+		vec2 sliceUV = color.rg;
+	#endif
+		
+		sliceUV.x *= sliceSize;
+		sliceUV.x += sliceInteger * sliceSize;
 
-	sliceUV = clamp(sliceUV, 0., 1.);
+		sliceUV = clamp(sliceUV, 0., 1.);
 
-	vec4 slice0Color = texture2D(colorTransform, sliceUV);
+		vec4 slice0Color = texture2D(colorTransform, sliceUV);
 
-	sliceUV.x += sliceSize;
-	
-	sliceUV = clamp(sliceUV, 0., 1.);
-	vec4 slice1Color = texture2D(colorTransform, sliceUV);
+		sliceUV.x += sliceSize;
+		
+		sliceUV = clamp(sliceUV, 0., 1.);
+		vec4 slice1Color = texture2D(colorTransform, sliceUV);
 
-	vec3 result = mix(slice0Color.rgb, slice1Color.rgb, sliceFraction);
+		vec3 result = mix(slice0Color.rgb, slice1Color.rgb, sliceFraction);
 
-#ifdef SAMPLER3DBGRMAP
-	color.rgb = result.rgb;
-#else
-	color.rgb = result.bgr;
-#endif
+	#ifdef SAMPLER3DBGRMAP
+		color.rgb = result.rgb;
+	#else
+		color.rgb = result.bgr;
+	#endif
 
-	return color;
-}
+		return color;
+	}
 #endif
 
 vec4 applyImageProcessing(vec4 result) {
@@ -101,7 +101,11 @@ vec4 applyImageProcessing(vec4 result) {
 	// Apply Color Transform
 #ifdef COLORGRADING
 	vec3 colorTransformInput = result.rgb * colorTransformSettings.xxx + colorTransformSettings.yyy;
-	vec3 colorTransformOutput = sampleTexture3D(txColorTransform, colorTransformInput, colorTransformSettings.yz).rgb;
+	#ifdef COLORGRADING3D
+		vec3 colorTransformOutput = texture(txColorTransform, colorTransformInput).rgb;
+	#else
+		vec3 colorTransformOutput = sampleTexture3D(txColorTransform, colorTransformInput, colorTransformSettings.yz).rgb;
+	#endif
 
 	result.rgb = mix(result.rgb, colorTransformOutput, colorTransformSettings.www);
 #endif