Set a PlaneGeometry in the same place of view plane to fill the viewport

Hi.

I am trying to set a white planeGeometry to fill the viewport but the result is a plane geometry with a good rotation but it is far to the camera.

This is a small code that I coded to try it.

<html>
<title></title>
<script type="importmap">
    {
      "imports": {
        "three": "https://unpkg.com/three@0.170/build/three.module.js",
        "three/addons/": "https://unpkg.com/three@0.157/examples/jsm/",
  "dat.gui": "https://unpkg.com/dat.gui@0.7.9/build/dat.gui.module.js"
      }
    }
</script>

</head>
<body>
</body>
<script type="module">
    import * as THREE from "three";
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

    const scene = new THREE.Scene();

    const size = 10;
    const divisions = 10;
    const gridHelper = new THREE.GridHelper( size, divisions );
    scene.add( gridHelper );
    gridHelper.position.y = -1;

    const cube = new THREE.Mesh(
        new THREE.BoxGeometry(1, 1, 1),
        new THREE.MeshBasicMaterial({ color: 0x00ff00 })
    );
    scene.add(cube);

    const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 20);
    camera.position.z = 5;
    camera.position.y = 10;
    camera.position.x = 10;
    const helper = new THREE.CameraHelper( camera ); scene.add( helper );

    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    function createFullScreenPlane(camera) {
        const aspect = camera.aspect;
        const frustumHeight = 2 * Math.tan(THREE.MathUtils.degToRad(camera.fov) / 2) * camera.position.z;
        const frustumWidth = frustumHeight * aspect;

        const planeGeometry = new THREE.PlaneGeometry(frustumWidth, frustumHeight);
        const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
        const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);

        planeMesh.lookAt(camera.position);

        scene.add(planeMesh);
    }

    createFullScreenPlane(camera);

    const debugCamera = new THREE.OrthographicCamera(-20, 20, 20, -20, 1, 100);
    debugCamera.position.set(10, 10, 10);

    const controls = new OrbitControls(camera, renderer.domElement);
    controls.update();
    controls.listenToKeyEvents(window);

    const animate = () => {
        requestAnimationFrame(animate);

        renderer.setViewport(0, 0, window.innerWidth, window.innerHeight);
        helper.update();
        helper.visible = false;
        renderer.render(scene, camera);

        const debugViewportWidth = window.innerWidth * 0.5;
        const debugViewportHeight = window.innerHeight * 0.5;
        renderer.setViewport(10, 10, debugViewportWidth, debugViewportHeight);
        renderer.setScissor(10, 10, debugViewportWidth, debugViewportHeight);
        helper.visible = true;
        renderer.setScissorTest(true);
        renderer.render(scene, debugCamera);
        renderer.setScissorTest(false);

        controls.update();
    };

    animate();
</script>
</html>

And the screenshot with the result:

I am trying with other code, but it is fails too:

    function createFullScreenPlane() {

        const topLeft = new THREE.Vector3(-1, 1, -1);
        const bottomRight = new THREE.Vector3(1, -1, -1);

        topLeft.unproject(camera);
        bottomRight.unproject(camera);

        const width = bottomRight.x - topLeft.x;
        const height = topLeft.y - bottomRight.y;

        const planeGeometry = new THREE.PlaneGeometry(width, height);
        const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide });
        const plane = new THREE.Mesh(planeGeometry, planeMaterial);

        plane.position.copy(camera.position).add(camera.getWorldDirection(new THREE.Vector3()).multiplyScalar(1));

        const posCamera = new THREE.Vector3(0, 0, 0);
        posCamera.unproject(camera);
        console.log(topLeft, bottomRight, posCamera, camera.position);

        plane.lookAt(camera.position);

        scene.add(plane);
    }
    createFullScreenPlane();

Creating a simple plane to fill the screen should be a simple task:

  1. Create a big plane - see, e.g. this example. (You don’t need double-sided texture.)
  2. Put it in front of the camera close enough that it covers the whole screen.

For step 1, the plane should be positioned properly by default - no need to adjust rotation or position, except as noted below.
For step 2, you can either put the camera at 0,0,0 and the object at 0,0,-d (object.position.z = -d) or put the object at 0,0,0 and the camera at 0,0,d (camera.position.z = d).

But you seem to have other things on-screen, like a grid and a green box. Do you want to display those too?

I found the solution.

The code then defines the corners of a plane in view space and unprojects them to world space using the camera. To correctly center the plane, the geometric center of the plane is calculated and subtracted from each vertex. This centers the geometry at the origin. A BufferGeometry is created from these adjusted points, and a Mesh is created and positioned at the calculated center. The mesh is then added to the scene. I added a testing to check the center: a red sphere is created and added as a child to the plane mesh, positioned at the origin of the plane’s local coordinates.

import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
import {
    BoxGeometry,
    BufferGeometry,
    DoubleSide,
    GridHelper,
    Mesh,
    MeshBasicMaterial,
    PerspectiveCamera,
    Scene,
    SphereGeometry,
    Vector3,
    WebGLRenderer
} from "three";

const scene = new Scene();


const camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
scene.add(camera);


camera.position.set(7, 3, 15);
camera.lookAt(scene.position);

camera.updateMatrixWorld(true);


const renderer = new WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);


const size = 50;
const divisions = 10;
const gridHelper = new GridHelper(size, divisions);
scene.add(gridHelper);


const cube = new Mesh(
    new BoxGeometry(1, 1, 1),
    new MeshBasicMaterial({color: 0x00ff00})
);
scene.add(cube);


const topLeft = new Vector3(-1, 1, 0);
const topRight = new Vector3(1, 1, 0);
const bottomRight = new Vector3(1, -1, 0);
const bottomLeft = new Vector3(-1, -1, 0);


topLeft.unproject(camera);
topRight.unproject(camera);
bottomRight.unproject(camera);
bottomLeft.unproject(camera);
console.log("vertex", topLeft, topRight, bottomRight, bottomLeft);


const geometry = new BufferGeometry()
const points = [topLeft, topRight, bottomRight, bottomLeft];


const center = new Vector3();
points.forEach(point => center.add(point));
center.divideScalar(points.length);


const adjustedPoints = points.map(point => point.clone().sub(center));
geometry.setFromPoints(adjustedPoints);
const indices = [
    0, 1, 2,
    0, 2, 3
];
geometry.setIndex(indices);
geometry.computeVertexNormals()

const material = new MeshBasicMaterial({color: 0xffffff, side: DoubleSide});
const mesh = new Mesh(geometry, material);
mesh.position.copy(center);
scene.add(mesh)


mesh.updateMatrixWorld(true);
const worldPosition = new Vector3();
mesh.getWorldPosition(worldPosition);
console.log("World Position of mesh:", worldPosition);


const sphereGeometry = new SphereGeometry(0.1, 32, 32);
const sphereMaterial = new MeshBasicMaterial({color: 0xff0000});
const sphere = new Mesh(sphereGeometry, sphereMaterial);
mesh.add(sphere);
sphere.position.set(0, 0, 0);


const controls = new OrbitControls(camera, renderer.domElement);
controls.listenToKeyEvents(window);

const animate = () => {
    requestAnimationFrame(animate);
    controls.update();
    renderer.render(scene, camera);
};
animate();
1 Like