Inverting the normals of a sphere threejs example

As of late I have been editing some of my older threejs content and have got around to my post on cube textures which in turn put me in the direction of exploring this topic and related subjects and alternatives. The process of just adding a cube texture to a scene is one thing, but the process of creating textures to use with a cube texture is a whole other matter. Thus far I have not found a sound way to go about creating these kinds of textures from a resource image because doing so is a little involved, and I have a lot of pots boiling on top of this that makes me want to look for a kind of simple place holder solution for now. There are a lot of issues that come up when trying to make one of these cube textures, for one thing I need to start with a texture that is seamless in all directions, and on top of that I need to have a way to mutate the state of the texture so that it does not look like I am in inside a cube.

While I look into the subject of making these kinds of textures the thought occurred that there should be more than one way to go about doing this sort of thing, such as just having a sphere and inverting the normals attribute so that the front side of the sphere is the inside of the sphere. Then there is making a texture to use with the inner surface of this sphere, and making all of this part of a DAE file that I can then just load, scale up as needed, and have fixed at the center of the scene object. I can then have a situation in which the camera and all additional objects of interest are inside this kind of sky sphere, and I can then just draw on the surface of this sphere as a way to create a kind of background for one or more over all projects. This is then a post on a threejs example in which I am doing this as an alternative way of making a kind of cube texture.

Inverting Sphere normals, and what to know before hand

This is an advanced post on the subject of using the javaScript library known as threejs as well as a number of additional assets to create a kind of sky sphere to serve as a kind of background for a threejs project other than that of a solid color or fixed single texture. I am then taking a lot of liberties here and assuming that you have at least a fair amount of experience with threejs, javaScript, and also using blender as a way to create and export DAE files as a preferred way of creating external assets to load into a scene object.

This example makes use of the DAE loader that must be added on top of threejs

This example makes use of the DAE file, or Collada File loader that can be found in the threejs repository. I have all ready wrote a blog post on this DAE loader a while back and thus assume that you know a thing or two about these various additional assets to work with in the threejs repository. Also I am making use of an additional module that I made a while back where I am building on top of this DAE loader, but this is mainly just some abstractions and additional methods that I have found that I like to have when working with DAE assets thus far.

Source code and DAE assets are up on Guthub

On top of using threejs and additional javaScript files such as the DAE loader, I am also loading an extremal DAE file that I exported from a blender file. It would be best to learn how to do reproduce this kind of thing in blender, but I also have a copy of the dae file that I am loading in my test threejs repository. In the for post folder of this repo I also have copies of the source code examples I am writing about here, and this is also where I am parking the source code for my many other posts on threejs that I write about on this website.

Version numbers matter

When I first wrote this blog post I was using r135 of threejs.

1 - First version of this inverted normals sphere DAE file example

In this section I will be going over what I have together thus far when it comes to the source code of this example. In this section I am just going over the source code as the state of the sphere as well as the state with textures is all something that I have worked out in an external dae file that I am loading with my DAE tools module that I worked out in another example.

1.1 - The main javaScript file

In the main javaScript file of this example I am creating my scene object, camera, renderer, and light source just like that of any other typical threejs example. I am then also working out an animation loop in which I will want to have a camera just look around as a way to just make sure that the end result of what I am making looks okay at least.

The main thing that stands out as far as this example goes is that I am calling the load one method of my DAE tools module that I made in a previous example. I will be going over a copy of the source code of this module in the next sub section, but for now the main thing here is that this is just the way that I thus far like to load not just a dae file, but also any additional textures that the dae file needs to load also. This load one method will return a promise that should resolve when the dae file as well as all textures used in the dae file are finished loading.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
(function () {
// SCENE
var scene = new THREE.Scene();
scene.add(new THREE.GridHelper(20, 20));
scene.background = new THREE.Color('cyan');
var camera = new THREE.PerspectiveCamera(50, 4 / 3, 0.5, 1000);
camera.position.set(0, 10, 0);
camera.lookAt(0, 5, 10);
scene.add(camera);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(640, 480);
document.getElementById('demo').appendChild(renderer.domElement);
// LIGHT
var dl = new THREE.DirectionalLight(0xffffff, 1);
dl.position.set(2, 1, 3)
scene.add(dl);
// CONTROL
//var controls = new THREE.OrbitControls(camera, renderer.domElement);
var frame = 0, frameMax = 300;
// app loop
var loop = function () {
requestAnimationFrame(loop);
renderer.render(scene, camera);
var per = frame / frameMax,
bias = 1 - Math.abs( per - 0.5) / 0.5;
var r = Math.PI * 2 * per;
var x = Math.cos(r) * 10;
var z = Math.sin(r) * 10;
camera.lookAt(x, 15 - 15 * bias, z);
frame += 1;
frame %= frameMax;
//controls.update();
};
// USING DAE TOOLS TO LOAD THE *.dae file
var daeObjects = DAE.create();
DAE.loadOne(daeObjects, "/dae/sphere-normal-invert/sphere-normal-invert.dae")
.then(function(daeObjects){
var group = DAE.createGroup(daeObjects, 0);
scene.add(group);
var mesh = group.children[0];
// OPTION1 - REPLACE MATERIAL WITH BASIC MATERIAL?
// A crude fix for this can be just be replacing the material
// with the basic material, using the THREE.DoubleSide valu for the
// side value
//mesh.material = new THREE.MeshBasicMaterial({
// side: THREE.DoubleSide,
// map: null
//});
// OPTION2 - MUTATE PHONG MATERIAL
//var material = mesh.material;
//material.side = THREE.DoubleSide;
// OPTION3 - REPLACE WITH BASIC MATERIAL, USING MAP VALUE
// FROM DAE FILE IMPORT
var sourceMaterial = mesh.material;
var newMaterial = new THREE.MeshBasicMaterial({
map: sourceMaterial.map
});
mesh.material = newMaterial;
// BASE GROUND MESH
var baseGround = new THREE.Mesh( new THREE.BoxGeometry(1, 0.1, 1), new THREE.MeshPhongMaterial({
color: 0x00ff00
}));
baseGround.position.y = -0.125;
baseGround.scale.set(60, 1, 60);
scene.add(baseGround);
// VERTEX NORMALS HELPER
//var helper = new THREE.VertexNormalsHelper(mesh, 5, 0x00ff00, 3);
//scene.add(helper);
loop();
})
.catch(function(e){
console.log(e);
loop();
});
}
());

1.2 - The dae tools file that I am using for this

This is the source code for the DAE tools module that I made a while back that I am using for this project. Sense this is a project in which I am just loading a single DAE file I am using just the load one method, but I also worked out a load all method for this module as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// dae tools r2
(function (api) {
// create aa daeObjects state object
api.create = function (opt) {
opt = opt || {};
var state = {
results: [],
onItemProgress : opt.onItemProgress || function(){},
onFileLoad : opt.onFileLoad || function(){},
onLoad : opt.onLoad || function(){}
};
return state;
};
// load one dae file
api.loadOne = function(daeObjects, url, onFileLoad){
// I will want a manager for this
var manager = new THREE.LoadingManager();
// the collada loader instance
var loader = new THREE.ColladaLoader(manager);
// result value to pass to onFileLoad
var resultValue = {};
onFileLoad = onFileLoad || function(){};
// return a promise
return new Promise(function(resolve, reject){
// call on done, and resolve the promise only when the dae file AND all textures load
var len = daeObjects.results.length;
manager.onLoad = function(){
onFileLoad(resultValue, daeObjects.results, daeObjects);
resolve(daeObjects);
};
// load the dae file and any textures
loader.load(url,
// done
function (result) {
resultValue = result;
daeObjects.results.push(result);
},
// progress
function(xhr){
//console.log(xhr);
},
// error
function(e){
reject(e);
}
);
});
};
// load a collection of dea files
api.loadAll = function(daeObjects, opt){
opt = opt || {};
opt.baseUrl = opt.baseUrl === undefined ? '/' : opt.baseUrl;
opt.relUrls = opt.relUrls === undefined ? [] : opt.relUrls;
opt.origin = opt.origin === undefined ? document.location.origin : opt.origin;
// resolve urls
var url_obj_base = new URL(opt.baseUrl, document.location.origin);
var urls = opt.relUrls.map(function(relUrl){
var url_obj_file = new URL(relUrl, url_obj_base.href + '/');
return url_obj_file.href;
});
// create and return Promise.all of load one method called for each file
var n = 0,
d = urls.length;
return Promise.all(urls.map(function(url, i){
return api.loadOne(daeObjects, url, daeObjects.onFileLoad).then(function(){
n += 1;
daeObjects.onItemProgress(n / d, n , d);
});
})).then(function(){
daeObjects.onLoad(daeObjects, daeObjects.results);
});
};
// create a group from a dae result object
api.createGroup = function(daeObjects, what){
var result = typeof what === 'object' ? what : daeObjects.results[what];
var group = new THREE.Group();
// copy mesh objects only
result.scene.children.forEach(function(obj){
if(obj.type === 'Mesh'){
group.add(obj.clone());
}
});
// copy result.scene rotation to group
group.rotation.copy(result.scene.rotation);
return group;
};
}
(this['DAE'] = {}));

Conclusion

As of late the direction I seem to be going in with threejs is using the library to make Videos, and when it comes to this kind of application the bottom line with whatever I am doing is how the frames turn out. With that said this seems to be a kind of solution that I would say works out okay when it comes to that, and the kind of style that I have in mind. However I would not say that this is an end when it comes to looking into this sort of thing more.

Creating a background for scenes in threejs strikes me as just one of many things that can end up being a total time consuming rabbit hole if I do not first find a half way decent place holder solution for it which is what this is. If I am happy with the idea of manly creating textures that I then use with the uv map of a sphere that has inverted normals then I can go with this and move on to the next thing. With that said I think I can go with something like this when it comes to just making some that works okay and moving on, at least when it comes to making my videos at least. However I am sure that this is yet another topic that I will have to come back to again at some point when it comes to slowly moving forward with every little thing that comes up when doing this kind of work.