Cory Evans
Cory Evans

Reputation: 193

three.js multiple renderings

I currently have a function that is supposed to create a 3d planet sphere when ran, but I seem to be unable to get my function to render more than one time, if I run it more than once on a page it just completely ignores all but the last call to the function, I know I did a horrible job coding this, it is meant to be used as a proof of concept, but I don't want to go and optimize anything if I cannot figure out why I cannot get 3 different renderings on the same page.

function makePlanet(planetContainer, texture){
        $this = this;

    $this.camera; 
    $this.scene;
    $this.sceneAtmosphere;
    $this.renderer;
    $this.primitive; 
    $this.material;
    $this.stats;

    $this.loop = function() {
        var angle = (Math.PI/180*0.1);

        cosRY = Math.cos(angle);
        sinRY = Math.sin(angle);

        var tempz = $this.camera.position.z;
        var tempx = $this.camera.position.x; 

        $this.camera.position.x = (tempx*cosRY)+(tempz*sinRY);
        $this.camera.position.z = (tempx*-sinRY)+(tempz*cosRY);

        $this.renderer.clear();
        $this.renderer.render( $this.scene, $this.camera );
        $this.renderer.render( $this.sceneAtmosphere, $this.camera );

        setTimeout(function(){
            $this.loop();
        }, 1000 / 60);
    }

        $this.Shaders = {

        'earth' : {

            uniforms: {

                "texture": { type: "t", value: 0, texture: null }

            },

            vertex_shader: [

                "varying vec3 vNormal;",
                "varying vec2 vUv;",

                "void main() {",

                    "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",

                    "vNormal = normalize( normalMatrix * normal );",
                    "vUv = uv;",

                "}"

            ].join("\n"),

            fragment_shader: [

                "uniform sampler2D texture;",

                "varying vec3 vNormal;",
                "varying vec2 vUv;",

                "void main() {",

                    "vec3 diffuse = texture2D( texture, vUv ).xyz;",
                    "float intensity = 1.10 - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) );",
                    "vec3 atmosphere = vec3( 0.75, 0.75, 2.0 ) * pow( intensity, 3.0 );",

                    "gl_FragColor = vec4( diffuse + atmosphere, 1.0 );",

                "}"

            ].join("\n")

        },

        'atmosphere' : {

            uniforms: {},

            vertex_shader: [

                "varying vec3 vNormal;",

                "void main() {",

                    "vNormal = normalize( normalMatrix * normal );",
                    "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",

                "}"

            ].join("\n"),

            fragment_shader: [

                "varying vec3 vNormal;",

                "void main() {",
                    // First float seems to affect opacity, and the last float affects size/strenth of the gradient
                    "float intensity = pow( 0.4 - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) ), 5.0 );",
                    // This seems to just be a simple colour value
                    "gl_FragColor = vec4( 0.5, 1.0, 1.0, 0.8 ) * intensity;",

                "}"

            ].join("\n")

        }

    };

    $this.camera = new THREE.Camera(30, $(planetContainer).width() / $(planetContainer).height(), 1, 100000);
    $this.camera.position.z = 1000;

    $this.scene = new THREE.Scene();
    $this.sceneAtmosphere = new THREE.Scene();

    $this.texture = ImageUtils.loadTexture('img/planets/textures/'+texture, {}, function() {
        $this.renderer.render($this.scene);
    });
    $this.texture.needsUpdate = true;

    $this.material = new THREE.MeshBasicMaterial( { map: $this.texture });
    $this.geom = new Sphere( 200, 50, 50);

    $this.primitive = new THREE.Mesh($this.geom, $this.material );

    $this.scene.addObject( $this.primitive );

// Atmosphere
    $this.shader = $this.Shaders[ 'atmosphere' ];
    $this.uniforms = Uniforms.clone( $this.shader.uniforms );

    $this.material = new THREE.MeshShaderMaterial( {

        uniforms: $this.uniforms,
        vertex_shader: $this.shader.vertex_shader,
        fragment_shader: $this.shader.fragment_shader

    } );

    $this.primitive = new THREE.Mesh( $this.geom, $this.material );
    $this.primitive.scale.x = $this.primitive.scale.y = $this.primitive.scale.z = 1.12;
    $this.primitive.flipSided = true;
    $this.sceneAtmosphere.addObject( $this.primitive );


    $this.renderer = new THREE.WebGLRenderer();
    $this.renderer.autoClear = false;
    $this.renderer.setClearColorHex( 0x000000, 1.0 );
    $this.renderer.setSize($(planetContainer).width(), $(planetContainer).height());

    $(planetContainer).append($this.renderer.domElement);

    setTimeout(function(){
        $this.loop();
    }, 1000 / 60);
}

Can anyone explain why I would not be able to use this function more than once on a page?

When I run the function more than once on any page, I only see 1 3D object, the other places that were supposed to render an object do not appear, think I have 3 sections, each meant to render a different planet, if I run this function 3 times with different arguments, the last time I run the function is the only one that displays, the other two places do not display anything, just an empty spot.

Also, I have checked and if I remove the 3rd one, the second one displays but not the first, so it is not an issue with the parameters not being correct, I believe it is an issue with three.js not wanting to create more than one instance of a renderer.

Example of Use

$(document).ready(function(){
    var goldilocks = new makePlanet('.goldilocksplanet .planetRender','planet_Jinx1200.png');
    var ocean = new makePlanet('.oceanplanet .planetRender','planet_serendip_1600.jpg');
    var sulfur = new makePlanet('.sulfurplanet .planetRender','planet_Telos1200.png');
});

Below is a sample of a webpage that will contain the 3 different renderings, they are not in the same scene, or I would have made them all in one scene, they are considered different blocks in the website, this is just an example and is by no means the only way that the function will need to work, but the function does need to treat each planet as its own scene, we cannot put them all in one scene together.

https://i.sstatic.net/tooOA.jpg

Upvotes: 0

Views: 2258

Answers (2)

Aycox
Aycox

Reputation: 136

You might want to consider replacing the:

    setTimeout(function(){
            $this.loop();
        }, 1000 / 60);

part for:

    function animate() {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
    }

The main reason is that requestAnimationFrame doesn't waste rendering power when the browser window isn't visible.

Upvotes: 1

Cory Evans
Cory Evans

Reputation: 193

So I figured out my own problem, apparently it was because I was using global variables and not local variables within the function. All I had to do was change

$this = this;

to

var $this = this;

and that fixed all of my problems... sometimes I feel like such an idiot.

Upvotes: 2

Related Questions