THREE.DataTexture works on mobile only when I keep the type THREE.FloatType but not as THREE.HalfFloatType

I have been trying to figure out how to get my project to work on mobile devices, but I came across a very weird problem, and the fix I do not fully understand. I change my float type when a mobile device is detected for all of my WebGlRenderTargets, and my simulation that pings pongs my texture to THREE.HalfFloatType. When I run on mobile and keep the DataTexture as THREE.FloatType, and have everything else change to THREE.HalfFloatType it works, but if my DataTexture uses THREE.HalfFloatType it breaks.

I will give my basics of code for an example.

Creating my data Texture:
// Each object has an x, y, z, and w
let positions = new Float32Array(this.size * this.size * 4);
let r = 100;

    for (var j = 0; j < positions.length; j += 4) {
        positions[j + 0] = -r + Math.random() * (r - (-r));
        positions[j + 1] = -r + Math.random() * (r - (-r));
        positions[j + 2] = -r + Math.random() * (r - (-r));
        positions[j + 3] = Math.random() * 100;
    }

    this.posTexture = new THREE.DataTexture(positions, this.size, this.size, THREE.RGBAFormat, THREE.FloatType);
    this.posTexture.needsUpdate = true;

If I switch my THREE.FloatType in the DataTexture to THREE.HalfFloatType it will give me this warning, and I tried changing to a Uint16Array and could not get it to work.

WebGL: INVALID_OPERATION: texImage2D: type HALF_FLOAT_OES but ArrayBufferView is not NULL and not Uint16Array

RENDER WARNING: texture bound to texture unit 1 is not renderable. It maybe non-power-of-2 and have incompatible texture filtering.

Then in all of my shaders I use precision highp float, and when I was testing how well mediump works on mobile it just messes everything up, but I do not think this is apart of the issue. I want to know why the DataTexture with THREE.FloatType will still work on mobile, but if I switch to THREE.HalfFloatType it breaks. All the data in the positions looks like this for values in the DataTexture: 0: -99.54922485351562, 1:-66.3533935546875, 2:75.77011108398438 and so on.

Here are my shaders if you want to see those. My simulation uses the sim shaders, and my mesh uses the material shaders.

public materialVertexShader = `
precision highp float;

attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;
attribute vec2 lookup;

uniform mat4 modelMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat3 normalMatrix;

uniform sampler2D curPos;
uniform sampler2D prevPos;


varying vec2 vUv;

void main() {
	        vUv = uv;

	        vec2 luv = lookup;
	        vec4 i = texture2D( curPos, luv );
	        vec4 p = texture2D( prevPos, luv );
	        vec3 vPosition = position;
	        vPosition += mix(p.xyz, i.xyz, .5);



	        gl_Position = projectionMatrix * modelViewMatrix * vec4( vPosition, 1.0 );;

}

`;

public materialFragmentShader = `
precision highp float;

varying vec2 vUv;

void main() {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

`;

public simVertexShader = `
precision highp float;

attribute vec3 position;
attribute vec2 uv;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

varying vec2 vUv;

void main() {

        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1. );

}

`;

public simFragmentShader = `
precision highp float;

uniform sampler2D source;
uniform sampler2D seed;
uniform vec2 resolution;
uniform float time;
uniform float persistence;
uniform float speed;
uniform float decay;
uniform float spread;
uniform float init;

varying vec2 vUv;

void main() {
        vec4 s = texture2D(source,vUv);
        if( s.w <= 0. ) {
	        s = texture2D(seed,vUv);
	        //s.xyz *= spread;
	        if( init == 0. ) s.w = 100.;
        }else{
	        s.xyz += .1;
	        s.w -= decay;
        }
        gl_FragColor = s;
}

`;

If you have any ideas let me know.

1 Like

Can you please check if your device passes the corresponding conformance test?

https://www.khronos.org/registry/webgl/sdk/tests/conformance/extensions/oes-texture-half-float.html?webglVersion=1&quiet=0

1 Like

This works on my computer: https://jsfiddle.net/2Lmt8rsq/

You have to use an instance of Uint16Array and convert your data into a corresponding half-float representation.

Related: https://esdiscuss.org/topic/float16array

@Mugen87 could you please explain how you converted 10000 to a half float (0x70E2)

I’ve used the following code snippet from RGBELoader:

Live example: https://jsfiddle.net/vq270kjc/

2 Likes