Browse Source

Merge pull request #1249 from sebavan/FixFullShadowIOS

Fix full float shadow ios
David Catuhe 9 years ago
parent
commit
b90322675e

+ 1 - 1
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -138,7 +138,7 @@
             // Texture type fallback from float to int if not supported.
             var textureType: number;
             var caps = this._scene.getEngine().getCaps();
-            if (caps.textureFloat && caps.textureFloatLinearFiltering) {
+            if (caps.textureFloat && caps.textureFloatLinearFiltering && caps.textureFloatRender) {
                 this._useFullFloat = true;
                 textureType = Engine.TEXTURETYPE_FLOAT;
             }

+ 2 - 1
src/Materials/babylon.materialHelper.ts

@@ -108,7 +108,8 @@
                     break;
             }
 
-            if (needShadows && scene.getEngine().getCaps().textureFloat && scene.getEngine().getCaps().textureFloatLinearFiltering) {
+            let caps = scene.getEngine().getCaps();
+            if (needShadows && caps.textureFloat && caps.textureFloatLinearFiltering && caps.textureFloatRender) {
                 if (defines["SHADOWFULLFLOAT"] === undefined) {
                     needRebuild = true;
                 }

+ 153 - 19
src/babylon.engine.ts

@@ -11,13 +11,18 @@
         return shader;
     };
 
-    var getWebGLTextureType = (gl: WebGLRenderingContext, type: number): number => {
-        var textureType = gl.UNSIGNED_BYTE;
+    var HALF_FLOAT_OES = 0x8D61;
 
-        if (type === Engine.TEXTURETYPE_FLOAT)
-            textureType = gl.FLOAT;
+    var getWebGLTextureType = (gl: WebGLRenderingContext, type: number): number => {
+        if (type === Engine.TEXTURETYPE_FLOAT) {
+            return gl.FLOAT;
+        }
+        else if (type === Engine.TEXTURETYPE_HALF_FLOAT) {
+            // Add Half Float Constant.
+            return HALF_FLOAT_OES;
+        }
 
-        return textureType;
+        return gl.UNSIGNED_BYTE;
     };
 
     var getSamplingParameters = (samplingMode: number, generateMipMaps: boolean, gl: WebGLRenderingContext): { min: number; mag: number } => {
@@ -171,6 +176,10 @@
         public highPrecisionShaderSupported: boolean;
         public fragmentDepthSupported: boolean;
         public textureFloatLinearFiltering: boolean;
+        public textureFloatRender: boolean;
+        public textureHalfFloat: boolean;
+        public textureHalfFloatLinearFiltering: boolean;
+        public textureHalfFloatRender: boolean;
         public textureLOD: boolean;
         public drawBuffersExtension;
     }
@@ -201,6 +210,7 @@
 
         private static _TEXTURETYPE_UNSIGNED_INT = 0;
         private static _TEXTURETYPE_FLOAT = 1;
+        private static _TEXTURETYPE_HALF_FLOAT = 2;
 
         public static get ALPHA_DISABLE(): number {
             return Engine._ALPHA_DISABLE;
@@ -274,6 +284,10 @@
             return Engine._TEXTURETYPE_FLOAT;
         }
 
+        public static get TEXTURETYPE_HALF_FLOAT(): number {
+            return Engine._TEXTURETYPE_HALF_FLOAT;
+        }
+
         public static get Version(): string {
             return "2.5-alpha";
         }
@@ -457,6 +471,11 @@
             this._caps.drawBuffersExtension = this._gl.getExtension('WEBGL_draw_buffers');
             this._caps.textureFloatLinearFiltering = this._gl.getExtension('OES_texture_float_linear');
             this._caps.textureLOD = this._gl.getExtension('EXT_shader_texture_lod');
+            this._caps.textureFloatRender = this._canRenderToFloatTexture();
+            
+            this._caps.textureHalfFloat = (this._gl.getExtension('OES_texture_half_float') !== null);
+            this._caps.textureHalfFloatLinearFiltering = this._gl.getExtension('OES_texture_half_float_linear');
+            this._caps.textureHalfFloatRender = this._canRenderToHalfFloatTexture();
 
             if (this._gl.getShaderPrecisionFormat) {
                 var highp = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT);
@@ -1243,27 +1262,29 @@
                 ["diffuseSampler"].concat(samplers), defines, fallbacks, onCompiled, onError);
         }
 
-        public createShaderProgram(vertexCode: string, fragmentCode: string, defines: string): WebGLProgram {
-            var vertexShader = compileShader(this._gl, vertexCode, "vertex", defines);
-            var fragmentShader = compileShader(this._gl, fragmentCode, "fragment", defines);
+        public createShaderProgram(vertexCode: string, fragmentCode: string, defines: string, context?:WebGLRenderingContext): WebGLProgram {
+            context = context || this._gl;
+
+            var vertexShader = compileShader(context, vertexCode, "vertex", defines);
+            var fragmentShader = compileShader(context, fragmentCode, "fragment", defines);
 
-            var shaderProgram = this._gl.createProgram();
-            this._gl.attachShader(shaderProgram, vertexShader);
-            this._gl.attachShader(shaderProgram, fragmentShader);
+            var shaderProgram = context.createProgram();
+            context.attachShader(shaderProgram, vertexShader);
+            context.attachShader(shaderProgram, fragmentShader);
 
-            this._gl.linkProgram(shaderProgram);
+            context.linkProgram(shaderProgram);
 
-            var linked = this._gl.getProgramParameter(shaderProgram, this._gl.LINK_STATUS);
+            var linked = context.getProgramParameter(shaderProgram, context.LINK_STATUS);
 
             if (!linked) {
-                var error = this._gl.getProgramInfoLog(shaderProgram);
+                var error = context.getProgramInfoLog(shaderProgram);
                 if (error) {
                     throw new Error(error);
                 }
             }
 
-            this._gl.deleteShader(vertexShader);
-            this._gl.deleteShader(fragmentShader);
+            context.deleteShader(vertexShader);
+            context.deleteShader(fragmentShader);
 
             return shaderProgram;
         }
@@ -1884,9 +1905,13 @@
                 if (options.samplingMode !== undefined) {
                     samplingMode = options.samplingMode;
                 }
-                if (type === Engine.TEXTURETYPE_FLOAT) {
-                    // if floating point (gl.FLOAT) then force to NEAREST_SAMPLINGMODE
-                    samplingMode = Texture.NEAREST_SAMPLINGMODE;
+                if (type === BABYLON.Engine.TEXTURETYPE_FLOAT && !this._caps.textureFloatLinearFiltering) {
+                    // if floating point linear (gl.FLOAT) then force to NEAREST_SAMPLINGMODE
+                    samplingMode = BABYLON.Texture.NEAREST_SAMPLINGMODE;
+                }
+                else if (type === BABYLON.Engine.TEXTURETYPE_HALF_FLOAT && !this._caps.textureHalfFloatLinearFiltering) {
+                    // if floating point linear (HALF_FLOAT) then force to NEAREST_SAMPLINGMODE
+                    samplingMode = BABYLON.Texture.NEAREST_SAMPLINGMODE;
                 }
             }
             var gl = this._gl;
@@ -2233,6 +2258,10 @@
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
                 }
+                else if (textureType === HALF_FLOAT_OES && !this._caps.textureHalfFloatLinearFiltering) {
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+                }
                 else {
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, noMipmap ? gl.LINEAR : gl.LINEAR_MIPMAP_LINEAR);
@@ -2622,6 +2651,111 @@
             }
         }
 
+        private _canRenderToFloatTexture(): boolean {
+            if (!this.getCaps().textureFloat) {
+                return false;
+            }
+
+            return this._canRenderToTextureOfType(BABYLON.Engine.TEXTURETYPE_FLOAT, 'OES_texture_float');
+        }
+
+        private _canRenderToHalfFloatTexture(): boolean {
+            if (!this.getCaps().textureHalfFloat) {
+                return false;
+            }
+
+            return this._canRenderToTextureOfType(BABYLON.Engine.TEXTURETYPE_HALF_FLOAT, 'OES_texture_half_float');
+        }
+
+        // Thank you : http://stackoverflow.com/questions/28827511/webgl-ios-render-to-floating-point-texture
+        private _canRenderToTextureOfType(format: number, extension: string): boolean {
+            var tempcanvas = document.createElement("canvas");
+            tempcanvas.height = 16;
+            tempcanvas.width = 16;
+            var gl = <WebGLRenderingContext>(tempcanvas.getContext("webgl") || tempcanvas.getContext("experimental-webgl"));
+
+            // extension.
+            var ext = gl.getExtension(extension);
+
+            // setup GLSL program
+            var vertexCode = `attribute vec4 a_position;
+                void main() {
+                    gl_Position = a_position;
+                }`;
+            var fragmentCode = `precision mediump float;
+                uniform vec4 u_color;
+                uniform sampler2D u_texture;
+
+                void main() {
+                    gl_FragColor = texture2D(u_texture, vec2(0.5, 0.5)) * u_color;
+                }`;
+            var program = this.createShaderProgram(vertexCode, fragmentCode, null, gl);
+            gl.useProgram(program);
+
+            // look up where the vertex data needs to go.
+            var positionLocation = gl.getAttribLocation(program, "a_position"); 
+            var colorLoc = gl.getUniformLocation(program, "u_color");
+
+            // provide texture coordinates for the rectangle.
+            var positionBuffer = gl.createBuffer();
+            gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
+                -1.0, -1.0,
+                1.0, -1.0,
+                -1.0,  1.0,
+                -1.0,  1.0,
+                1.0, -1.0,
+                1.0,  1.0]), gl.STATIC_DRAW);
+            gl.enableVertexAttribArray(positionLocation);
+            gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
+            
+            var whiteTex = gl.createTexture();
+            gl.bindTexture(gl.TEXTURE_2D, whiteTex);
+            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 255, 255, 255]));
+
+            var tex = gl.createTexture();
+            gl.bindTexture(gl.TEXTURE_2D, tex);
+            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, getWebGLTextureType(gl, format), null);
+            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+            
+            var fb = gl.createFramebuffer();
+            gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+            var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+            if (status !== gl.FRAMEBUFFER_COMPLETE) {
+                Tools.Log("GL Support: can **NOT** render to " + format + " texture");
+                return false;
+            }
+            
+            // Draw the rectangle.
+            gl.bindTexture(gl.TEXTURE_2D, whiteTex);
+            gl.uniform4fv(colorLoc, <any>[0, 10, 20, 1]);
+            gl.drawArrays(gl.TRIANGLES, 0, 6);
+            
+            gl.bindTexture(gl.TEXTURE_2D, tex);
+            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+            
+            gl.clearColor(1, 0, 0, 1);
+            gl.clear(gl.COLOR_BUFFER_BIT);
+            
+            gl.uniform4fv(colorLoc, <any>[0, 1/10, 1/20, 1]);
+            gl.drawArrays(gl.TRIANGLES, 0, 6);
+            
+            var pixel = new Uint8Array(4);
+            gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
+            if (pixel[0] !== 0 ||
+                pixel[1] < 248 ||
+                pixel[2] < 248 ||
+                pixel[3] < 254) {
+                BABYLON.Tools.Log("GL Support: Was not able to actually render to " + format + " texture");
+                return false;
+            }
+            
+            // Succesfully rendered to "format" texture.
+            return true;
+        }
+
         // Statics
         public static isSupported(): boolean {
             try {