2

I'm trying to render to a single component 8 bit render target in three.js but I'm getting a number of errors I'm not sure how to solve and searching doesn't seem to come up with a lot of other hits. The WebGLRenderTarget is created with the following parameters:

format: THREE.RedIntegerFormat,
type: THREE.UnsignedByteType,
internalFormat: 'R8UI',
minFilter: THREE.NearestFilter,
magFilter: THREE.NearestFilter,

When rendering to the render target and then trying to copy it to the screen I get the following WebGL errors:

GL_INVALID_OPERATION: No defined conversion between clear value and attachment format.
GL_INVALID_OPERATION: Fragment shader output type does not match the bound framebuffer attachment type.
GL_INVALID_OPERATION: Mismatch between texture format and sampler type (signed/unsigned/float/shadow).

Here's a repro case for the issue I'm seeing:

<script type="module">
import * as THREE from 'https://cdn.skypack.dev/three'
import { FullScreenQuad } from 'https://cdn.skypack.dev/three/examples/jsm/postprocessing/Pass.js'

let camera, scene, renderer;
let geometry, material, mesh;
let renderTarget, fsQuad;

class FinalMaterial extends THREE.ShaderMaterial {

  constructor() {
  
    super( {

      uniforms: {

        map: { value: null }

      },

      vertexShader: /* glsl */`

        varying vec2 vUv;

        void main() {

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

        }`,

      fragmentShader: /* glsl */`

        uniform sampler2D map;

        varying vec2 vUv;

        void main() {

          vec4 texel = texture2D( map, vUv );
          gl_FragColor = texel;

        }`

    } );

  }

}

init();

function init() {

    camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
    camera.position.z = 1;

    scene = new THREE.Scene();

    geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
    material = new THREE.MeshNormalMaterial();

    mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );

    renderer = new THREE.WebGLRenderer( { antialias: true } );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.setAnimationLoop( animation );
    document.body.appendChild( renderer.domElement );
    
    renderTarget = new THREE.WebGLRenderTarget(
        window.innerWidth,
      window.innerHeight,
      {
        format: THREE.RedIntegerFormat,
        type: THREE.UnsignedByteType,
        internalFormat: 'R8UI',
        minFilter: THREE.NearestFilter,
        magFilter: THREE.NearestFilter,
        }
    );
    
    renderTarget.texture.internalFormat = 'R8UI';
    
    fsQuad = new FullScreenQuad( new FinalMaterial() );
    fsQuad.material.uniforms.map.value = renderTarget.texture;

}

function animation( time ) {

    mesh.rotation.x = time / 2000;
    mesh.rotation.y = time / 1000;

    renderer.setRenderTarget( renderTarget );
    renderer.render( scene, camera );
    
    renderer.setRenderTarget( null );
    fsQuad.render( renderer );

}
</script>
Garrett Johnson
  • 2,413
  • 9
  • 17
  • 1
    If it's any help, I ran your snippet in Firefox, and [I got more descriptive errors](https://i.stack.imgur.com/JtcXC.jpg). It looks like the source is UINT and the destination is FLOAT, and vice-versa. – M - Aug 17 '21 at 22:17
  • 1
    @Marquizzo Thanks! I think I understand a bit more about what's going on now but the short of it is that basically three.js doesn't support clearing or rendering to integer render targets. It's not using the correct clear functions and the shader system isn't set up to write out integer values. I've made a couple PRs to improve three.js' support for it, though, so it may be possible at some point. – Garrett Johnson Aug 17 '21 at 22:48

0 Answers0