9

Scenario:
In my scene I implemented a vertex shader that positions a plane mesh on the xz-axis at the position of the camera. So if the camera moves, the plane mesh moves with it. This leads to the visual effect that, while moving the camera, the plane mesh seems to stay fixed in place. This seems to work correctly.

Problem:
If I move the camera (and therefore the plane mesh) to a certain extend, the mesh suddenly disappears.
I realized that there seems to be a relationship between the disappearance and the size of the plane, i.e. the larger the plane, the more I can move the camera before the plane mesh disappears.

Also, in my test scene the plane mesh only disappears when moving on the negative x-axis, positive x-axis or negative z-axis. It does NOT disappear when moving on the positive z-axis.

I assume it has something to do with some kind of clipping, but may be wrong. Recomputing the bounding box of the plane mesh had no effect.

Any ideas?

Cheers

Fiddle:
I created a fiddle that shows the problem: http://jsfiddle.net/p8wZ6/10/

In the fiddle I added an additional box mesh to better visualize that the camera actually moves.
- To change the axis the camera moves on (negative z-axis by default) (un-)comment the appropriate code line in the tick method.
- To change the size of the plane change the size value in the createPlane method.

Sourcecode Shader:

<script id="vertexShader" type="x-shader/x-vertex">
    void main() {
        vec4 pos = vec4( position, 1.0 );

        vec4 wPos = modelMatrix * pos;

        wPos.x += cameraPosition.x;
        wPos.z += cameraPosition.z;

        // standard
        // vec4 pPos = projectionMatrix * modelViewMatrix * pos;
        // keep fixed
        vec4 pPos = projectionMatrix * viewMatrix * wPos;

        gl_Position = pPos;
    }
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
    void main() {
        gl_FragColor.rgb = vec3(0.7, 0.7, 0.7);
        gl_FragColor.a = 1.0;
}
</script>


Sourcecode JS:

var scene;
var camera;
var light;
var renderer;
var controls;
var onTick;
var planeMesh;
var boxMesh;
var heightmap;
var clock;


function createPlane(){
    // disappearance seems related to size of geometry.
    // the larger the longer it takes until disappearance.
    var size = 20;
    var geom = new THREE.PlaneGeometry(size, size, 20, 20);

    return geom;
}


function createBox(){
    var geom = new THREE.CubeGeometry(2, 2, 4);

    return geom;
}

function createMesh(){
    // plane
    var geom = createPlane();

    var shaderMaterial = new THREE.ShaderMaterial({
        vertexShader: document.getElementById( 'vertexShader' ).textContent,
        fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
        side: THREE.DoubleSide,
        wireframe: true
    });

    planeMesh = new THREE.Mesh(geom, shaderMaterial);
    var axis = new THREE.AxisHelper(4);
    planeMesh.rotation.x = -90 * (Math.PI / 180);
    planeMesh.add(axis);
    scene.add(planeMesh);

    // box
    geom = createBox();

    var material = new THREE.MeshBasicMaterial( {
        color: 0xff00ff,
    });

    boxMesh = new THREE.Mesh(geom, material);
    boxMesh.position.x = 5;
    boxMesh.position.z = -15;
    axis = new THREE.AxisHelper(4);
    boxMesh.add(axis);
    scene.add(boxMesh);
}

function startRendering(){
    onTick();
};

function onTick(){
    // move camera

    // causes disappearance
    // neg. z
     camera.position.z -= .1;
    // pos. x
    // camera.position.x += .1;
    // neg. x
    // camera.position.x -= .1;

    // causes no disappearance
    // pos. z
    // camera.position.z += .1;

    requestAnimationFrame(onTick);
    //controls.update(clock.getDelta());    
    renderer.render(scene, camera);
}

function init(){
    renderer = new THREE.WebGLRenderer();
    renderer.setClearColor( 0xffffff, 1 );
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    scene = new THREE.Scene();
    scene.add(new THREE.AxisHelper(4));

    camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.set(0, 1, 0);

    light = new THREE.DirectionalLight(0xffffff, 1);
    light.shadowCameraVisible = true;
    light.position.set(0, 0, 100);
    scene.add(light);

    //clock = new THREE.Clock();
    //controls = new THREE.FirstPersonControls(camera);
    //controls.movementSpeed = 20;
    //controls.lookSpeed = .1;
}

init();
createMesh();
startRendering();
padde
  • 93
  • 1
  • 1
  • 4
  • 1
    The plane is not being moved, but the camera is. Therefore, eventually the plane falls outside the camera's frustum and is clipped -- as it should be. – WestLangley Jan 17 '14 at 15:50
  • Hmm, actually i move the camera AND the plane. The camera is moved by setting its position property, the plane is moved solely in the vertex shader.So it seems to me like the camera does not take the repositiong in the vertex shader into account. – padde Jan 18 '14 at 12:42
  • So basically I wonder if there is a way to avoid this (incorrect?) clipping when moving geometry in the vertex shader. Or am i doing something completely wrong here? – padde Jan 18 '14 at 12:52
  • Not directly relevant to the accepted answer: If anyone is wrestling with disappearing things when changing camera position, and happen to be using `OrbitControls`, try to run `myOrbitControls.update()` after changing camera position. – Kalnode Apr 10 '23 at 16:29

2 Answers2

20

You have a fundamental misunderstanding.

You are moving the camera in the CPU. You are moving the vertices of the plane in the GPU.

The camera's frustum calculation knows nothing about the vertex displacements in the vertex shader.

As a work-around, you can set

 planeMesh.frustumCulled = false;

A better solution is to just add the plane as a child of the camera, and omit vertex displacements.

planeMesh.position.set( 0, -1, 0 );
camera.add( planeMesh );
scene.add( camera );

You must add the camera to the scene graph it you use the second approach.

three.js r.65

WestLangley
  • 102,557
  • 10
  • 276
  • 276
  • Thanks for clearing this up. I wasn't sure about where/when the clipping actually is done: on the CPU or GPU. Somehow I assumed it would be on the GPU. As it's done on the CPU, for the camera the plane is indeed still unmoved and therefore gets clipped. Totally makes sense. I'm aware of your second suggestion, just wanted to check if i can do it on the GPU side. Didn't know about the frustumCulled property though. Cheers. – padde Jan 19 '14 at 10:24
3

When you define your camera in r73 the last two parameters allow you to specify your camera's near and far z clipping distance.

Taken from this link: http://threejs.org/docs/#Manual/Introduction/Creating_a_scene

var camera = 
new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

The third parameter of Three.PerspectiveCamera defines the camera's near clipping distance and the fourth parameter defines the camera's far clipping distance.

Tim
  • 31
  • 4