Three.js Object3d child lookAt camera position - three.js

I am struggling with an Object3D which child meshes should look at the camera position.
It works fine, if the camera is "far" away, but not if the camera moves towards the object.
Than, if the camera position is near to the object position, the second added plane rotates, until the camera look at the edge of the plan.
And I have know idea why this behavior appears just on the second added plane and just if the camera is near the object position.
Here is what i have so far.
Create the Object:
var obj = new THREE.Object3D();
obj.position.set( x, y, z );
var Uniforms = {
texturePrimary: { type: "t", value: Texture },
textureColorGraph: { type: "t", value: ColorGraph },
time: { type: "f", value: 0 },
color: { type: "f", value: 0 }
};
obj.Uniforms = Uniforms;
obj.add( makeplane1( 3.2, Uniforms ) );
obj.add( makeplane2( 25, Uniforms ) );
obj.update = function( pos ){
this.Uniforms.time.value = shaderTiming;
$.each(this.children, function(i,mesh){
if( mesh.name === "plane1" || mesh.name === "plane2"){
var vec = mesh.parent.worldToLocal( pos );
mesh.lookAt( vec );
}
});
};
function makePlane1( radius, uniforms ){
var Geo = new THREE.PlaneGeometry( radius, radius );
var Material = new THREE.ShaderMaterial(
{
uniforms: uniforms,
vertexShader: shaders[1].vertex,
fragmentShader: shaders[1].fragment,
blending: THREE.AdditiveBlending,
transparent: true
};
var plane = new THREE.Mesh( Geo, Material );
plane.name = "plane1";
return plane;
);
function makePlane2( radius, uniforms ){
var Geo = new THREE.PlaneGeometry( radius, radius );
var Material = new THREE.ShaderMaterial(
{
uniforms: uniforms,
vertexShader: shaders[2].vertex,
fragmentShader: shaders[2].fragment,
blending: THREE.AdditiveBlending,
transparent: true
};
);
var plane = new THREE.Mesh( Geo, Material );
plane.name = "plane2";
return plane;
}
I could call this.lookAt( pos ) in obj.update( pos ) to rotate the whole object, but other meshes should not rotate that way, so that is sadly no option.
And a simple vertex shader for both planes:
varying vec2 vUv;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
vUv = uv;
}
And then i call in the animationloop:
$.each(scene.children, function(i, obj){
if( obj.update !== undefined ) {
shaderTiming = (time - startTime )/ 1000;
obj.update( camera.getWorldPosition() );
}
});
EDIT: I Just noticed that this behavior just occur, if the object's position is not (0,0,0). If so it works just like it should at any camera position.
Also a simple distance calculation, object to camera, is not working properly.
vec1 = this.position;
vec2 = camera.position;
var dist = Math.sqrt(Math.pow(vec1.x - vec2.x, 2) + Math.pow(vec1.y - vec2.y, 2) + Math.pow(vec1.z - vec2.z, 2));
Thanks for any hints.

Object3D.lookAt() does not support objects with rotated and/or translated parent(s).
three.js r.85

Related

Shader material glow bug when pan,zoom in three.js

I create a virtual earth like this with this code:
function earthView(){
if (!scene){
main(); // create three js basic code(camera, renderer etc.)
}
// create the geometry sphere stars
var geometry = new THREE.SphereBufferGeometry(6371000000, 36, 36)
// create the material, using a texture of startfield
var material = new THREE.MeshBasicMaterial()
material.map = THREE.ImageUtils.loadTexture('images/earthView/ESO_-_Milky_Way.jpg')
material.side = THREE.BackSide
// create the mesh based on geometry and material
var mesh = new THREE.Mesh(geometry, material)
mesh.position.set(0,0,-6371000)
scene.add(mesh)
/*
var geometry = new THREE.SphereGeometry(5000,10,10);
var material = new THREE.MeshBasicMaterial({color:"0xff0000"});
var mesh_test = new THREE.Mesh(geometry,material);
scene.add(mesh_test);*/
//earth
var geometry = new THREE.SphereBufferGeometry(6371000, 36, 36)
var material = new THREE.MeshPhongMaterial()
var earthMesh = new THREE.Mesh(geometry, material)
earthMesh.position.set(0,0,-6371000)
earthMesh.rotation.set(Math.PI/2,Math.PI/2,0)
earthMesh.rotation.y -=22.87*Math.PI/180//rightturn ^
earthMesh.rotation.x +=49.02*Math.PI/180//rightturn ^
helper = new THREE.EdgesHelper( earthMesh );
helper.material.color.set( 0xffffff );
material.map = THREE.ImageUtils.loadTexture('images/earthView/earthmap1k.jpg')
material.bumpMap = THREE.ImageUtils.loadTexture('images/earthView/earthbump1k.jpg')
material.bumpScale = 100
material.specularMap = THREE.ImageUtils.loadTexture('images/earthView/earthspec1k.jpg')
scene.add(earthMesh);
scene.add( helper );
//atmosphere
var geometry = new THREE.SphereBufferGeometry(9371000, 36, 36)
var material = new createAtmosphereMaterial()
material.uniforms.glowColor.value.set(0x00b3ff)
material.uniforms.coeficient.value = 0.02
material.uniforms.power.value = 2.5
material.side = THREE.DoubleSide
var earthAtmo = new THREE.Mesh(geometry, material)
earthAtmo.position.set(0,0,-6371000)
scene.add(earthAtmo);
/**
* from http://stemkoski.blogspot.fr/2013/07/shaders-in-threejs-glow-and- halo.html
* #return {[type]} [description]
*/
function createAtmosphereMaterial(){
var vertexShader = [
'varying vec3 vNormal;',
'void main(){',
' // compute intensity',
' vNormal = normalize( normalMatrix * normal );',
' // set gl_Position',
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
'}',
].join('\n')
var fragmentShader = [
'uniform float coeficient;',
'uniform float power;',
'uniform vec3 glowColor;',
'varying vec3 vNormal;',
'void main(){',
' float intensity = pow( coeficient - dot(vNormal, vec3(0.0, 0.0, 1.0)), power );',
' gl_FragColor = vec4( glowColor * intensity, 1.0 );',
'}',
].join('\n')
// create custom material from the shader code above
// that is within specially labeled script tags
var material = new THREE.ShaderMaterial({
uniforms: {
coeficient : {
type : "f",
value : 1.0
},
power : {
type : "f",
value : 2
},
glowColor : {
type : "c",
value : new THREE.Color('blue')
},
},
vertexShader : vertexShader,
fragmentShader : fragmentShader,
side : THREE.BackSide,
blending : THREE.AdditiveBlending,
transparent : true,
depthWrite : false,
});
return material
}
}
in previus question, I had problem with the renderer because i create the virtual earth in real scale (1px = 1m). I overcame this error by setting the logarithmicDepthBuffer: true when defining the renderer.
Now the problem is that the atmosphere (glow shader material) has a bug when panning or zooming in the webgl - container which is already been stated and here is an example to solve this problem.
The question is: how can i change my code to overcome this bug?(I suppose there is something to add in the render function but i just can't get it to work).
Hint1: this bug is only happening when setting the logarithmicDepthBuffer: true. Else i get a false rendering because of the large scale object i am using.
Image1:render option logarithmicDepthBuffer: false,no bug, only false rendering.
Image2:render option logarithmicDepthBuffer: true,no bug, if not zoom or pan.
Image3:render option logarithmicDepthBuffer: true, when zoom in the area of the applied shader material seams to became smaller.
Image4:render option logarithmicDepthBuffer: true,when pan the area of the applied shader material seams not to follow or understand the pan.
Hint2: the area that shader material is renderable seams to became bigger when zooming out and smaller when zooming in.
Update: As a see now the problem is taking place when i add the star sphere. If i dont add the star sphere then everything works correct.. Any thoughts why this is happening??

Display wireframe and solid color

Is it possible to display the wireframe of the object and also the solid color of its faces on the same object? I found a way using a clone of the object and assign different materials e.g
var geometry = new THREE.PlaneGeometry(plane.width, plane.height,width - 1, height - 1);
var materialWireframe = new THREE.MeshPhongMaterial({color:"red",wireframe:true});
var materialSolid = new THREE.MeshPhongMaterial({color:"green",wireframe:false});
var plane = new THREE.Mesh(geometry, materialWireframe );
var plane1 = plane.clone();
plane1.material = materialSolid ;
plane1.material.needsUpdate = true;
any thoughts?
To render both a model and its wireframe, you can use a pattern like this one:
// mesh
var material = new THREE.MeshPhongMaterial( {
color: 0xff0000,
polygonOffset: true,
polygonOffsetFactor: 1, // positive value pushes polygon further away
polygonOffsetUnits: 1
} );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh )
// wireframe
var geo = new THREE.EdgesGeometry( mesh.geometry ); // or WireframeGeometry
var mat = new THREE.LineBasicMaterial( { color: 0xffffff } );
var wireframe = new THREE.LineSegments( geo, mat );
mesh.add( wireframe );
The use of polygonOffset will help prevent z-fighting between the mesh material and the wireframe line. Consequently, the wireframe will look a lot better.
three.js r.126
This can also be achieved with WireframeGeometry:
https://threejs.org/docs/#api/en/geometries/WireframeGeometry.
(and give plane and line the same position, you can also play with opacity see the docs).
let geometryWithFillAndWireFrame = () => {
let geometry = new THREE.PlaneGeometry(250, 250, 10, 10);
let material = new THREE.MeshBasicMaterial( { color: 0xd3d3d3} );
let plane = new THREE.Mesh(geometry, material);
scene.add(plane);
let wireframe = new THREE.WireframeGeometry( geometry );
let line = new THREE.LineSegments( wireframe );
line.material.color.setHex(0x000000);
scene.add(line);
};
To do that, a possibility is to use a GLSL fragment shader that changes the fragment color when the fragment is near one edge of the triangle. Here is the GLSL shader that I am using. As input, it takes the barycentric coordinates of the fragment in the triangle, and an edge mask that selects for each edge whether it should be drawn or not. (rem: I had to use it with the compatibility profile for backward compatibility reasons, if you do not want to do that, it can easily be adapted):
(fragment source)
#version 150 compatibility
flat in float diffuse;
flat in float specular;
flat in vec3 edge_mask;
in vec2 bary;
uniform float mesh_width = 1.0;
uniform vec3 mesh_color = vec3(0.0, 0.0, 0.0);
uniform bool lighting = true;
out vec4 frag_color;
float edge_factor(){
vec3 bary3 = vec3(bary.x, bary.y, 1.0-bary.x-bary.y);
vec3 d = fwidth(bary3);
vec3 a3 = smoothstep(vec3(0.0,0.0,0.0), d*mesh_width, bary3);
a3 = vec3(1.0, 1.0, 1.0) - edge_mask + edge_mask*a3;
return min(min(a3.x, a3.y), a3.z);
}
void main() {
float s = (lighting && gl_FrontFacing) ? 1.0 : -1.0;
vec4 Kdiff = gl_FrontFacing ?
gl_FrontMaterial.diffuse : gl_BackMaterial.diffuse;
float sdiffuse = s * diffuse;
vec4 result = vec4(0.1, 0.1, 0.1, 1.0);
if(sdiffuse > 0.0) {
result += sdiffuse*Kdiff +
specular*gl_FrontMaterial.specular;
}
frag_color = (mesh_width != 0.0) ?
mix(vec4(mesh_color,1.0),result,edge_factor()) :
result;
}
To avoid cloning my object I used a pattern like that :
var mat_wireframe = new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true});
var mat_lambert = new THREE.MeshLambertMaterial({color: 0xffffff, shading: THREE.FlatShading});
var meshmaterials = [ mat_wireframe, mat_lambert ];
and then applied it to my mesh like that :
var myMesh = THREE.SceneUtils.createMultiMaterialObject( mesh_geometry, meshmaterials );
scene.add( myMesh ) ;
I hope it could help...

Buffer geometry custom attribute material leaking into other objects

I'm making a visualization and want soft edged particles to represent data points.
I've gathered code from various examples and have something working except for a transparency issue. I tried out the the new Stack Overflow snippet system and made a simple example of what I'm seeing.
As you'll notice, the sphere picks up the transparency from the particle system. Is there a way to turn this off - in my application, this is a required feature. I tried applying attributes to the material I use for the sphere but that didn't appear to help.
Any ideas what I'm doing wrong?
var camera, scene, renderer, controls;
var uniforms;
init();
animate();
function init() {
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
var ambient_light = new THREE.AmbientLight(0x333333);
scene.add(ambient_light);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 10, 1000);
camera.position.z = 80;
var num_particles = 5000;
var geometry = new THREE.BufferGeometry();
var positions = new Float32Array(num_particles * 3);
var colors = new Float32Array(num_particles * 3);
var sizes = new Float32Array(num_particles);
for (var i = 0; i < num_particles; i++) {
positions[3 * i + 0] = Math.random() * 100 - 50;
positions[3 * i + 1] = Math.random() * 100 - 50;
positions[3 * i + 2] = Math.random() * 100 - 50;
colors[3 * i + 0] = Math.random();
colors[3 * i + 1] = Math.random();
colors[3 * i + 2] = Math.random();
sizes[i] = 2.5;
}
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('customColor', new THREE.BufferAttribute(colors, 3));
geometry.addAttribute('size', new THREE.BufferAttribute(sizes, 1));
var attributes = {
size: {
type: 'f',
value: null
},
customColor: {
type: 'c',
value: null
}
};
uniforms = {
color: {
type: "c",
value: new THREE.Color(0xffffff)
},
texture: {
type: "t",
value: THREE.ImageUtils.loadTexture("data:image/png;base64,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")
}
};
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
attributes: attributes,
vertexShader: document.getElementById('vertexshader').textContent,
fragmentShader: document.getElementById('fragmentshader').textContent,
blending: THREE.AdditiveBlending,
depthTest: true,
depthWrite: false,
transparent: true
});
var pointcloud = new THREE.PointCloud(geometry, shaderMaterial);
scene.add(pointcloud);
scene.add(new THREE.Mesh(
new THREE.SphereGeometry(50.0, 16, 16),
new THREE.MeshNormalMaterial({ })
))
controls = new THREE.TrackballControls(camera);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
controls.update();
}
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script type="x-shader/x-vertex" id="vertexshader">
attribute float size;
attribute vec3 customColor;
varying vec3 vColor;
void main() {
vColor = customColor;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( 300.0 / length( mvPosition.xyz ) );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
uniform vec3 color;
uniform sampler2D texture;
varying vec3 vColor;
void main() {
gl_FragColor = vec4( color * vColor, 1.0 );
gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );
}
</script>
Edit: WestLangley's suggestion in comments of turning on depth test results in this: (Don't know how to add an image to a comment so adding here)
The point cloud material attribute I was missing was depthWrite:false and as #WestLangley correctly said, depthTest: true;
I don't know how to version the embedded code so I updated it to work with these values.

Texture atlas offset/repeat works for meshes but is ignored for point system particles

I am using a texture atlas to hold a sequence of images. When mapping to a mesh with MeshLambertMaterial, using Texture.offset and Texture.repeat works beautifully to cut the subtexture out of the entire image.
However, using the exact same texture instance for a PointCloudMaterial renders the particles with the entire atlas, not just the selected subimage.
I tried to follow the three.js source code, but the documentation is scarce.
Is there a workaround for this better than using canvases to chop up the image?
Edit: As requested, a work-in-progress is available at http://jnm2.com/minesweeper/.
THREE.PointCloudMaterial has been renamed THREE.PointsMaterial.
THREE.PointCloud has been renamed THREE.Points.
You want to use a sprite sheet with your point cloud.
Instead of using PointsMaterial with your Points, you can create a custom ShaderMaterial instead.
The custom ShaderMaterial can access your sprite sheet and use a different sub-image for each particle.
To do so, use a shader like this one:
<script type="x-shader/x-vertex" id="vertexshader">
attribute vec2 offset;
varying vec2 vOffset;
void main() {
vOffset = offset;
gl_PointSize = 25.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
uniform sampler2D spriteSheet;
uniform vec2 repeat;
varying vec2 vOffset;
void main() {
vec2 uv = vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y );
vec4 tex = texture2D( spriteSheet, uv * repeat + vOffset );
if ( tex.a < 0.5 ) discard;
gl_FragColor = tex;
}
</script>
Then
// geometry
geometry = new THREE.BufferGeometry();
// attributes
var numVertices = 20;
var positions = new Float32Array( numVertices * 3 ); // 3 coordinates per point
var offsets = new Float32Array( numVertices * 2 ); // 2 coordinates per point
geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geometry.setAttribute( 'offset', new THREE.BufferAttribute( offsets, 2 ) );
// populate offsets
var offset = new THREE.Vector2();
for ( var i = 0, index = 0, l = numVertices; i < l; i ++, index += 3 ) {
positions[ index ] = 100 * Math.random() - 50;
positions[ index + 1 ] = 100 * Math.random() - 50;
positions[ index + 2 ] = 100 * Math.random() - 50;
}
for ( var i = 0, index = 0, l = numVertices; i < l; i ++, index += 2 ) {
offset.set( THREE.Math.randInt( 1, 3 ), THREE.Math.randInt( 2, 3 ) ).multiplyScalar( 0.25 ); // sprite sheet: 4 rows x 4 cols
offsets[ index ] = offset.x;
offsets[ index + 1 ] = offset.y;
}
// image
image = document.createElement( 'img' );
image.addEventListener( 'load', function ( event ) { texture.needsUpdate = true; } );
// texture
var texture = new THREE.Texture( image );
// uniforms
uniforms = {
spriteSheet: { value: texture },
repeat: { value: new THREE.Vector2( 0.25, 0.25 ) }
};
// material
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexshader' ).textContent,
fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
transparent: true
} );
// point cloud
pointCloud = new THREE.Points( geometry, material );
scene.add( pointCloud );
fiddle:https://jsfiddle.net/nL0b6hco/
three.js r.137.4

How to map texture on a custom non square quad in THREE JS

playing around with ThreeJS, i encoutered a classic problem of non square quad texturing :
http://www.xyzw.us/~cass/qcoord/
Problem is ThreeJS only let you set texture coordinates with a vec2 (for what i know ..)
And after spending hours on this, and finally found a working solution, i wanted to share it with the community, and maybe get some better ideas ?
So here is the code:
First, the javascript to make my Quad using three JS:
var planeGeom = new THREE.Geometry();
planeGeom.vertices.push(new THREE.Vector3(0, 0, 10));
planeGeom.vertices.push(new THREE.Vector3(10, 0, 10));
planeGeom.vertices.push(new THREE.Vector3(20, 0,0));
planeGeom.vertices.push(new THREE.Vector3(-10, 0, 0));
//create the 2 faces , maybe i should play with CW or CCW order... ;)
planeGeom.faces.push(new THREE.Face3(0,1,3));
planeGeom.faces.push(new THREE.Face3(1,2,3));
//Compute widths ratio
var topWidth = Math.abs(Plane.TR.x - Plane.TL.x);
var bottomWidth = Math.abs(Plane.BR.x - Plane.BL.x);
var ratio = topWidth / bottomWidth;
//create UV's as barely explained in the link above (www.xyzw.us)
var UVS = [
new THREE.Vector2(0, ratio),
new THREE.Vector2(0, 0),
new THREE.Vector2(1.0, 0),
new THREE.Vector2(ratio, ratio)
];
//faceVertexUvs[materialID] [face index] [vertex index among face]
planeGeom.faceVertexUvs[0][0] = [UVS[0],UVS[1],UVS[3]];
planeGeom.faceVertexUvs[0][1] = [UVS[1],UVS[2],UVS[3]];
//load the image
var checkerTexture = THREE.ImageUtils.loadTexture('./resources/images/checker_largeColor.gif');
//Now create custom shader parts
customUniforms =
{
uSampler: { type: "t", value: checkerTexture },
};
var customMaterial = new THREE.ShaderMaterial(
{
uniforms: customUniforms,
vertexShader: document.getElementById( 'vertexShader').textContent,
fragmentShader: document.getElementById( 'fragmentShader').textContent,
side: THREE.DoubleSide
} );
//create the mesh with the custom geometry and material
var planeMesh = new THREE.Mesh(planeGeom, customMaterial);
//add the object to the threeJS scene
this.m_Scene.add(planeMesh);
and now the custom shader code:
Vertex shader:
varying vec4 textureCoord;
void main()
{
//here i reCreate the Vec4 i would have liked to have in threeJS
textureCoord = vec4(uv,0.0, 1.0);
if(uv.y != 0.0)
{
textureCoord.w *= (uv.y);
}
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
and the fragment shader:
uniform sampler2D uSampler;
varying vec4 textureCoord;
void main()
{
gl_FragColor = texture2D(uSampler, vec2(textureCoord.x/textureCoord.w, textureCoord.y/textureCoord.w));
}
voilaaa. I hope it could help some, or maybe myself in a few years... ;)

Resources