26

How to create a static background image?

For default background:

scene = new THREE.Scene();

// ...

renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( scene.fog.color, 1 );

How to set a image for scene.fog, or set opacity for clearcolor?

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
user2969890
  • 381
  • 1
  • 4
  • 11

6 Answers6

26

If you are trying to set a static background image (even if you rotate your main camera, the background doesn't change), you have to create 2 scenes and 2 cameras.
The first scene will be composed of a basic plane on which a texture is applied.
The second scene will have all your objects.
Here is a code that would do it :

<html>
    <body>

    <script src="Three.js"></script>
    <script>
        var color = 0x000000;

        // Create your main scene
        var scene = new THREE.Scene();

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

        // Create lights
        var light = new THREE.PointLight(0xEEEEEE);
        light.position.set(20, 0, 20);
        scene.add(light);

        var lightAmb = new THREE.AmbientLight(0x777777);
        scene.add(lightAmb);

        // Create your renderer
        var renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // Create a cube
        var geometry = new THREE.BoxGeometry(1, 1, 1);
        var material = new THREE.MeshLambertMaterial({
            color: 0xff00ff,
            ambient: 0x121212,
            emissive: 0x121212
         });

        var cube = new THREE.Mesh(geometry, material);
        scene.add(cube);

        // Set up the main camera
        camera.position.z = 5;

        // Load the background texture
        var texture = THREE.ImageUtils.loadTexture( '1.jpg' );
        var backgroundMesh = new THREE.Mesh(
            new THREE.PlaneGeometry(2, 2, 0),
            new THREE.MeshBasicMaterial({
                map: texture
            }));

        backgroundMesh .material.depthTest = false;
        backgroundMesh .material.depthWrite = false;

        // Create your background scene
        var backgroundScene = new THREE.Scene();
        var backgroundCamera = new THREE.Camera();
        backgroundScene .add(backgroundCamera );
        backgroundScene .add(backgroundMesh );

        // Rendering function
        var render = function () {
            requestAnimationFrame(render);

            // Update the color to set
            if (color < 0xdddddd) color += 0x0000ff;

            // Update the cube color
            cube.material.color.setHex(color);

            // Update the cube rotations
            cube.rotation.x += 0.05;
            cube.rotation.y += 0.02;

            renderer.autoClear = false;
            renderer.clear();
            renderer.render(backgroundScene , backgroundCamera );
            renderer.render(scene, camera);
       };

       render();
   </script>
   </body>
</html>

**Demo right here **

Hope this helps.

NOTE (2014/06/28): This code works with the latest release of Three.js: R67

ThisIsSparta
  • 1,307
  • 13
  • 22
  • This example doesn't do anything cos "new THREE.Camera()" is abstract class. You need to use "THREE.PerspectiveCamera" instead. – Borut Tomazin Jan 06 '14 at 08:01
  • but it is not working with the object loaders like OBJLoder,OBJMTLLoder – Jothi Kannan Jun 26 '14 at 09:53
  • 1
    @BorutTomazin : Please check the live demo in the updated answer. It has been tested with the latest release of Three.js : the R67. Thanks for the feedback – ThisIsSparta Jun 28 '14 at 10:51
18

Use Texture loader to load a any image as texture and then apply that to scene like this:

//Load background texture
const loader = new THREE.TextureLoader();
loader.load('https://images.pexels.com/photos/1205301/pexels-photo-1205301.jpeg' , function(texture)
            {
             scene.background = texture;  
            });

Result:

enter image description here

Demo:

See the Pen Flat Earth Three.JS by Hitesh Sahu (@hiteshsahu) on CodePen.
sçuçu
  • 2,960
  • 2
  • 33
  • 60
Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
13

this run:

renderer = new THREE.WebGLRenderer({ antialias: false,alpha:true });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x000000, 0);
tamara
  • 131
  • 1
  • 2
  • 2
    This is a simple way to do it, and then of course css a `{ background-image: url('whatever.jpg') }` onto your canvas element or its container. – Octopus Jun 10 '17 at 19:05
  • This worked for me - while the others did not. Using `renderer = new THREE.CanvasRenderer({alpha:true });` – jlansey Sep 18 '18 at 04:12
  • This is by far the best answer (simple and effective)! – supersan Oct 16 '20 at 11:50
7

An background image can be set by setting the scene.background attribute of the THREE.Scene:

scene = new THREE.Scene();
bgTexture = loader.load("https://i.stack.imgur.com/vDUZz.jpg",
    function ( texture ) {
        var img = texture.image;
        bgWidth= img.width;
        bgHeight = img.height;
        resize();
    } );
scene.background = bgTexture;
bgTexture.wrapS = THREE.MirroredRepeatWrapping;
bgTexture.wrapT = THREE.MirroredRepeatWrapping;

The aspect ratio of the image can be adjusted to the aspect ratio of the canvas like this:

var aspect = window.innerWidth / window.innerHeight;
var texAspect = bgWidth / bgHeight;
var relAspect = aspect / texAspect;

bgTexture.repeat = new THREE.Vector2( 
    Math.max(relAspect, 1),
    Math.max(1/relAspect,1) ); 
bgTexture.offset = new THREE.Vector2(
    -Math.max(relAspect-1, 0)/2,
    -Math.max(1/relAspect-1, 0)/2 ); 

See the code snippet:

(function onLoad() {
  var container, loader, camera, scene, renderer, controls, bgTexture, bgWidth, bgHeight;
  
  init();
  animate();

  function init() {
    container = document.getElementById('container');
    
    renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    container.appendChild(renderer.domElement);

    camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 100);
    camera.position.set(0, -4, -1.5);

    loader = new THREE.TextureLoader();
    loader.setCrossOrigin("");

    scene = new THREE.Scene();
    bgTexture = loader.load("https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/background.jpg",
        function ( texture ) {
            var img = texture.image;
            bgWidth= img.width;
            bgHeight = img.height;
            resize();
        }
    );
    scene.background = bgTexture;
    bgTexture.wrapS = THREE.MirroredRepeatWrapping;
    bgTexture.wrapT = THREE.MirroredRepeatWrapping;
  
    scene.add(camera);
    window.onresize = resize;
    
    var ambientLight = new THREE.AmbientLight(0x404040);
    scene.add(ambientLight);

    var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
    directionalLight.position.x = -0.75;
    directionalLight.position.y = -0.5;
    directionalLight.position.z = -1;
    scene.add( directionalLight );

    controls = new THREE.OrbitControls(camera, renderer.domElement);
  
    createModel();
  }

  function createModel() {

    var material = new THREE.MeshPhongMaterial({color:'#b090b0'});
    var geometry = new THREE.BoxGeometry( 1, 1, 1 );
    var mesh = new THREE.Mesh(geometry, material);

    scene.add(mesh);
  }

  function resize() {
    
    var aspect = window.innerWidth / window.innerHeight;
    var texAspect = bgWidth / bgHeight;
    var relAspect = aspect / texAspect;

    bgTexture.repeat = new THREE.Vector2( Math.max(relAspect, 1), Math.max(1/relAspect,1) ); 
    bgTexture.offset = new THREE.Vector2( -Math.max(relAspect-1, 0)/2, -Math.max(1/relAspect-1, 0)/2 ); 

    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = aspect;
    camera.updateProjectionMatrix();
  }

  function animate() {
    requestAnimationFrame(animate);
    render();
  }

  function render() {
    renderer.render(scene, camera);
  }
})();
<script src="https://threejs.org/build/three.min.js"></script>
<!--script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.min.js"></script-->
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

<div id="container"></div>
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Using scene.background seems to be the easiest way to add a background image. However, your code renders a distorted background image (since it matches the window resolution). Any idea how to fix that? – TheCat Oct 03 '18 at 09:07
2

thank you ))

I found yet another solution:

<!DOCTYPE html>
<head>
    <title>three.js webgl - orbit controls</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            color: #000;
            font-family:Monospace;
            font-size:13px;
            text-align:center;
            font-weight: bold;
            background-image:url(./foto.jpg);

            margin: 0px;
            overflow: hidden;
        }

        #info {
            color:#000;
            position: absolute;
            top: 0px; width: 100%;
            padding: 5px;

        }

        a {
            color: red;
        }
    </style>
</head>

<body>
    <div id="container"></div>
    <div id="info">

        <a href="http://threejs.org" target="_blank">three.js</a> 
    </div>

    <script src="./three.min.js"></script>
    <script src="js/loaders/OBJLoader.js"></script>
    <script src="js/controls/OrbitControls.js"></script>

    <script src="js/Detector.js"></script>
    <script src="js/libs/stats.min.js"></script>
    <!--используем для вывода информации fps-->

    <script>
        if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
        var container, stats;
        var camera, controls, scene, renderer;
        var cross;

        init();
        animate();

        function init() {       

            camera = new THREE.PerspectiveCamera( 90, window.innerWidth / window.innerHeight, 0.1, 2000 );
            camera.position.z = 100;
            controls = new THREE.OrbitControls( camera );
            controls.addEventListener( 'change', render );
            scene = new THREE.Scene();

            var manager = new THREE.LoadingManager();
            manager.onProgress = function ( item, loaded, total ) {

                console.log( item, loaded, total );

            };

            var texture = new THREE.Texture();
            var loader = new THREE.ImageLoader( manager );
            loader.load( './kos.jpg', function ( image ) {

                texture.image = image;
                texture.needsUpdate = true;

            } );

            // model

            var loader = new THREE.OBJLoader( manager );
            loader.load( './skull.obj', function ( object ) {
                object.traverse( function ( child ) {
                    if ( child instanceof THREE.Mesh ) {
                        child.material.map = texture;
                    }
                } );
                object.position.y = 10;
                scene.add( object );
            } );


            // lights

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

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

            light = new THREE.AmbientLight( 0xffffff );
            scene.add( light );


            renderer = new THREE.WebGLRenderer( { antialias: false } );
             renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            container = document.getElementById( 'container' );
            container.appendChild( renderer.domElement );
            stats = new Stats();
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.top = '0px';
            stats.domElement.style.zIndex = 100;
            container.appendChild( stats.domElement );
            window.addEventListener( 'resize', onWindowResize, false );
        }


        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize( window.innerWidth, window.innerHeight );
            render();

        }

        function animate() {
            requestAnimationFrame( animate );
            controls.update();

        }

        function render() {

            renderer.render( scene, camera );
            stats.update();
        }


    </script>

</body>

user2969890
  • 381
  • 1
  • 4
  • 11
  • unfortunately, your code is not working with three.js r78, you might forget to set alpha to true when constructing the webgl render, which is the key point for seeing the body's background image. – iefreer Sep 15 '16 at 10:23
  • 1
    @user2969890 I like your solution (+1) because you're simply clearing the buffer with the texture. I've tried the same approach, but either I get a warning about texture being non-power of two and downscaling or incorrect aspect ratio. How did you get around the issue of maintaining the aspect ratio while using a texture as scene background ? – George Profenza Jul 09 '18 at 17:27
0

It seems rather late in the day to be adding any contribution to this thread but here is my 'hap'orth':

    <head>
        <meta charset=utf-8>
        <title>Temple7</title>
        <style>
            body { margin: 0;
                 }
            canvas { width: 100%; height: 100%;
                  background-image:url(Clouds.jpg);
                }
        </style>
    </head>

This rather simplistic approach has its limitations. The .jpg image retains its pixel dimensions so that, for different sizes of the browser window one sees different amounts of the image. If the canvas size exceeds the size of the .jpg, then tiling occurs.