I noticed that I get a different color output, depending on whether I render my scene to a WebGLRenderTarget or directly to the canvas frame buffer. Rendering into a WebGLRenderTarget looks somehow dark&oversaturated compared to rendering it to the canvas directly. Maybe you can help me out what I’m doing wrong. I modified the gltfloader example to illustrate the issue. You can change the render output by switching between “compositingScene” and “scene” in “renderer.render(compositingScene /* scene */, camera);”.
“scene”: expected normal rendering:
“compositingScene”: rendering via WebGLRenderTarget:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - GLTFloader</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - GLTFLoader<br />
Battle Damaged Sci-fi Helmet by
<a href="https://sketchfab.com/theblueturtle_" target="_blank" rel="noopener">theblueturtle_</a><br />
<a href="https://hdrihaven.com/hdri/?h=royal_esplanade" target="_blank" rel="noopener">Royal Esplanade</a> from <a href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a>
</div>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
let camera, scene, renderer, surfaceBuffer, compositingScene;
init();
function init() {
surfaceBuffer = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, {
});
const compositingShader = createCompositingShader(surfaceBuffer);
compositingScene = createCompositingScene(compositingShader);
const container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20000 );
const fff = 1;
camera.position.set( - 1.8 * fff, 3 * fff, 2.7 * fff );
scene = new THREE.Scene();
const ambientLight = new THREE.AmbientLight(0xffffff, 1 * Math.PI);
scene.add(ambientLight);
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
const controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 0, 0 );
controls.update();
window.addEventListener( 'resize', onWindowResize );
const loader = new GLTFLoader().setPath( 'models/gltf/DamagedHelmet/glTF/' );
loader.load( 'DamagedHelmet.gltf', async function ( gltf ) {
const model = gltf.scene;
await renderer.compileAsync( model, camera, scene );
scene.add( model );
} );
renderLoop();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function renderLoop() {
render();
requestAnimationFrame(renderLoop);
}
function render() {
renderer.setRenderTarget(surfaceBuffer);
renderer.clear();
renderer.render(scene, camera);
renderer.setRenderTarget(null);
renderer.clear();
renderer.render(compositingScene /* scene */, camera);
}
function createCompositingScene(compositingMaterial) {
const screenQuad = new THREE.Mesh(
new THREE.PlaneGeometry(2, 2),
compositingMaterial
);
screenQuad.frustumCulled = false;
const compositingScene = new THREE.Scene();
compositingScene.add(screenQuad);
return compositingScene;
}
function createCompositingShader(surfaceBuffer) {
const compositingVertexShader = `
varying vec2 varUv;
void main() {
varUv = uv;
gl_Position = vec4(position, 1.0);
}
`;
const compositingFragmentShader = `
uniform sampler2D surfaceBuffer;
varying vec2 varUv;
void main(void) {
vec4 surfaceC = texture2D(surfaceBuffer, varUv);
gl_FragColor = surfaceC;
}
`;
const compositingUniforms = {
surfaceBuffer: { value: surfaceBuffer.texture },
};
return new THREE.ShaderMaterial({
side: THREE.FrontSide,
uniforms: compositingUniforms,
vertexShader: compositingVertexShader,
fragmentShader: compositingFragmentShader,
transparent: false,
depthWrite: false,
depthTest: false
});
}
</script>
</body>
</html>