Search code examples
three.jscullingskybox

Three.js Unculled SkyBox


I am attempting to have my skybox not be affected by the camera.far parameter. I would like to cull all other scene objects with this just not the skybox.

When I set skyBox.frustumCulled = false; it makes no difference. skyBox being the mesh of course.

Is this done by adding another render pass? If so I would need 2 different cameras one with a really high far value to allow viewing of the skybox right? How can this be done efficiently?

For clarity here is the snippet from my terrain object code used for drawing the skybox:

    shader = THREE.ShaderLib["cube"];
    shader.uniforms["tCube"].value = this.cubetexture;

    mat = new THREE.ShaderMaterial({
        uniforms: shader.uniforms,
        fragmentShader: shader.fragmentShader,
        vertexShader: shader.vertexShader,
        depthWrite: false,
        side: THREE.BackSide
    });

    geo = new THREE.BufferGeometry().fromGeometry(new THREE.BoxGeometry(1024, 1024, 1024));

    mesh = new THREE.Mesh(geo, mat);

    mesh.rotation.y += 90;

    mesh.scale.x = mesh.scale.y = mesh.scale.z = 50;

    mesh.frustumCulled = false;
    mesh.matrixAutoUpdate = false;
    mesh.rotationAutoUpdate = false;
    mesh.updateMatrix();

    this.skybox = mesh;

    scene.add(this.skybox);

Solution

  • You're adding a skybox in the 'main' scene. A better way to accomplish a skydome would be to create a new scene. this will be the 'background' to your 'main' scene. There's also a discussion about skydomes v.s. skyboxes, simply put, a box saves polys, a dome looks better. in this example i'll be using a dome/sphere.

    var renderer = new THREE.WebGLRenderer( {alpha: true, antialias: true} );
    var mainScene = new THREE.Scene();
    var mainCamera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20000 );
    
    var skydome = {
        scene: new THREE.Scene(),
        camera: new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20000 );
    };
    
    skydome.material = new THREE.MeshBasicMaterial({color: 0x0F0F0F}) //the material for the skydome, for sake of lazyness i took a MeshBasicMaterial.
    skydome.mesh = new THREE.Mesh(new THREE.SphereGeometry(100, 20, 20), skydome.material);
    skydome.scene.add(skydome.mesh);
    

    now, during the render function you adjust only the rotation of the skydome camera, not the position.

    var render = function(){
        requestAnimationFrame( render );
        skydome.camera.quaternion = mainCamera.quaternion;
        renderer.render(skydome.scene, skydome.camera); //first render the skydome
        renderer.render(mainScene, mainCamera);//then render the rest over the skydome
    };
    renderer.autoclear = false; //otherwise only the main scene will be rendered.