hi, ive been banging my head against this one for a while and hoping someone can help.
firstly my assumption is that using the full screen quad shader technique i lifted from here > three.js webgl - shader [Monjori], im able to draw into a WebGLRenderTarget of any resolution and this will fill the render target texture. the simple shader below just draws the UVs as color to confirm the UVs are coming through as expected, as values between 0 and 1.
const fsGeom = new THREE.PlaneGeometry( 2, 2 );
const fsMat = new THREE.ShaderMaterial({
uniforms: {
//
},
vertexShader: `
varying vec2 vUv;
void main(){
vUv = uv;
gl_Position = vec4( position, 1.0 );
}
`,
fragmentShader: `
varying vec2 vUv;
void main(){
gl_FragColor = vec4(vUv.xy, 0.0, 1.0);
}
`,
});
const fsMesh = new THREE.Mesh(fsGeom, fsMat);
fsMesh.frustumCulled = false;
const fsCamera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
the above works for me across all browsers, mobile etc.
but it gets weird when switching to WebXR (AR using Google Pixel)
the UVs are no longer between 0 and 1… (the aforementioned weirdness)
my test WebGLRenderTarget resolution is (512 x 512) and the UVs are now coming in as (512 / window.innerWidth, 512 / window.innerHeight)
seems to suggest some issue with the viewport always assuming fullscreen resolution instead of custom resolutions of the WebGLRenderTarget?
full test code below:
const w = window.innerWidth;
const h = window.innerHeight;
const r = window.devicePixelRatio;
const container = document.createElement( 'div' );
document.body.appendChild( container );
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 50, w / h, 0.1, 10 );
camera.position.set( 0, 0, 3 );
const controls = new OrbitControls( camera, container );
controls.minDistance = 0;
controls.maxDistance = 8;
const renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( r );
renderer.setSize( w, h );
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.xr.enabled = true;
renderer.xr.addEventListener( 'sessionstart', onXRSessionStart );
renderer.xr.addEventListener( 'sessionend', onXRSessionEnd );
container.appendChild( renderer.domElement );
const renderTarget = new THREE.WebGLRenderTarget(512, 512, {
wrapS: THREE.ClampToEdgeWrapping,
wrapT: THREE.ClampToEdgeWrapping,
magFilter: THREE.LinearFilter,
minFilter: THREE.LinearFilter,
generateMipmaps: false,
format: THREE.RGBAFormat,
type: THREE.UnsignedByteType,
anisotropy: THREE.Texture.anisotropy,
encoding: THREE.LinearEncoding,
depthBuffer: true,
stencilBuffer: true,
samples: 0,
});
// fullscreen quad - draws a quad full screen to the render target viewport.
// based on this threejs example - https://threejs.org/examples/webgl_shader.html
const fsGeom = new THREE.PlaneGeometry( 2, 2 );
const fsMat = new THREE.ShaderMaterial({
uniforms: {
//
},
vertexShader: `
varying vec2 vUv;
void main(){
vUv = uv;
gl_Position = vec4( position, 1.0 );
}
`,
fragmentShader: `
varying vec2 vUv;
void main(){
gl_FragColor = vec4(vUv.xy, 0.0, 1.0);
}
`,
});
const fsMesh = new THREE.Mesh(fsGeom, fsMat);
fsMesh.frustumCulled = false;
const fsCamera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
// 1x1 plane which uses the render target as a texture to show the UV values.
const planeGeom = new THREE.PlaneGeometry(1, 1);
const planeMat = new THREE.MeshBasicMaterial({
color: 0xffffff,
map: renderTarget.texture,
});
const planeMesh = new THREE.Mesh(planeGeom, planeMat);
scene.add( planeMesh );
document.body.appendChild( ARButton.createButton( renderer, {
requiredFeatures: ['hit-test'],
}));
window.addEventListener( 'resize', onWindowResize );
renderer.setAnimationLoop( render );
function onXRSessionStart() {
planeMesh.position.z = -1.5;
}
function onXRSessionEnd() {
camera.position.set(0, 0, 5);
camera.lookAt(0, 0, 0);
camera.updateMatrixWorld();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function render() {
const renderTargetSaved = renderer.getRenderTarget();
renderer.setRenderTarget(renderTarget);
renderer.clear();
renderer.render(fsMesh, fsCamera);
renderer.setRenderTarget(renderTargetSaved);
renderer.render( scene, camera );
}