THREE.DataTexture is not sampled (or rendered) through ShaderMaterial

Hi there! I’m trying to write my first GPGPU particle simulation following the amazing tutorial by Yuri.

I am getting stuck right after declaring my THREE.DataTexture and passing it into my THREE.ShaderMaterial as a texture argument. Nothing gets displayed. When I feed it a regular .jpg image it gets displayed without a problem.

I am using an M3 MacBook Pro (I wonder if it might be a hardware limitation about floatTextures). I’ve tried firefox and safari and the problem persists. I’ve read elsewhere that you need to activate floatTexture capability on the GL context. I’ve tried this too but I wonder if I’m doing it incorrectly

Here’s the code. Any help would be incredible. Cheers!

import * as THREE from 'three';
// import{ OrbitControls } from "three/addons/controls/OrbitControls.js";

// Load Shaders
const defaultVert = await (await fetch('./shaders/defaultVert.glsl')).text();
const defaultFrag = await (await fetch('./shaders/defaultFrag.glsl')).text();
const simVert = await (await fetch('./shaders/simVert.glsl')).text();
const simFrag = await (await fetch('./shaders/simFrag.glsl')).text();

const tex = new THREE.TextureLoader().load('./tex/img0.jpg' ); 

// Init Funcs

setupFBO();

// Basic Scene Setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const clock = new THREE.Clock();

const renderer = new THREE.WebGLRenderer();
var gl = renderer.getContext('webgl');
gl.getExtension("OES_texture_float");

renderer.setClearColor(0x000000f,1)
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

const geometry = new THREE.PlaneGeometry( 5, 5);
const mat = new THREE.ShaderMaterial({
    uniforms:{
        u_time:{value:0},
    },
    vertexShader:defaultVert,
    fragmentShader:defaultFrag
});

const plane = new THREE.Mesh( geometry, mat );
scene.add(plane);
camera.position.z = 5;

// Animation Loop

function animate() {

	// renderer.render( scene, camera );
    renderer.setRenderTarget(null);
    renderer.render(fboScene,fboCamera);

    plane.rotation.x = Math.sin(clock.getElapsedTime()*1.)*.3;
    plane.rotation.y = Math.sin(clock.getElapsedTime()*1.)*.3;
}
renderer.setAnimationLoop( animate );

// Functions

function getRenderTarget() {
    const renderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight,{
        minFilter: THREE.NearestFilter,
        magFilter: THREE.NearestFilter,
        format: THREE.RGBAFormat,
        type: THREE.FloatType
    });
    return renderTarget;
}

function setupFBO(){
    globalThis.size = 128;
    globalThis.fbo = getRenderTarget();
    globalThis.fbo1 = getRenderTarget();

    globalThis.fboScene = new THREE.Scene();
    globalThis.fboCamera = new THREE.OrthographicCamera(-1,1,1,-1,-1,1);
    fboCamera.position.set(0,0,0.5);
    fboCamera.lookAt(0,0,0);
    let geo = new THREE.PlaneGeometry(2,2);

    globalThis.data = new Float32Array(size*size*4);

    for(let i = 0; i<size;i++){
        for(let j =0; j<size; j++){
            let index = (i + j * size) * 4;
            let theta = Math.random() * Math.PI * 2;
            let r = 0.5 + 0.5 * Math.random();
            data[index + 0] = r*Math.cos(theta);
            data[index + 1] = r*Math.sin(theta);
            data[index + 2] = 1;
            data[index + 3] = 0;
        }
    } 

    globalThis.fboTexture = new THREE.DataTexture(data,size,size,THREE.RGBAFormat,THREE.FloatType);
    fboTexture.magFilter = THREE.NearestFilter;
    fboTexture.minFilter = THREE.NearestFilter;
    fboTexture.needsUpdate = true;

    globalThis.fboMaterial = new THREE.ShaderMaterial({
        uniforms:{
            uPositions: {value: fboTexture},
            u_time: {value:0},
        },
        vertexShader:simVert,
        fragmentShader:simFrag
    });

    // fboMaterial.uniforms.uPositions.value = tex;
    // globalThis.fboMaterial = new THREE.MeshBasicMaterial();
    // fboMaterial.map = fboTexture;

    globalThis.fboMesh = new THREE.Mesh(geo,fboMaterial)
    fboScene.add(fboMesh)
}

Code looks a bit sus… whats globalThis? :
bc you assign to globalThis.fboTexture
but then refer to just fboTexture… seems… odd


    globalThis.fboTexture = new THREE.DataTexture(data,size,size,THREE.RGBAFormat,THREE.FloatType);
    fboTexture.magFilter = THREE.NearestFilter;
    fboTexture.minFilter = THREE.NearestFilter;

I’d try adding:

    fboTexture.wrapS=fboTexture.wrapT= THREE.RepeatWrapping; 

as a sanity check…

Thanks for the response. The weird thing is that if I replace the ShaderMaterial for a BasicMaterial and feed it the exact same texture variable fboTexture, it displays it no problem.

Maybe your shaders are the problem. I work a lot with FloatType textures. There is usually something wrong in the shader when the image goes black.

Try this for a test:

const simVert =`
	
	varying vec2 vUv;
	
	void main() {
	
		vUv = uv;
		
		gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
		
	}
`;


const simFrag =`

	uniform sampler2D uPositions;
	uniform float u_time;
	
	varying vec2 vUv;

	void main() {
	
		vec3 color = texture2D(colormap, vUv).rgb;


		//for test you should see red
		gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );

		//after you see red you know that the shader work. Then use this instead and you should see your texture
		//gl_FragColor = vec4( color, 1.0 );
	}
`;

I recommend that you invest your enthusiasm in the current technology right from the start, because webgl1 is already very old and is no longer maintained by threejs.

Thank you Attila! I explained in the post that when I feed the shader an image (const tex) from my hard-drive it renders it no problem but when I feed it my custom DataTexture (fboTexture) it doesn’t render anything. On top of that when I replace my material for a basicMaterial and feed it my same DataTexture (fboTexture) it renders it no problem so I don’t believe my shader code is the problem…