Cesium’s Tileset are laying behind pointcloud of Potree - three.js

I render cesium and potree on same canvas but when I added tileset from cesium It always laying behind pointcloud.
Tell me how to fix this issue.
I initialize both cesium and potree
cviewer = new Cesium.Viewer('cesiumContainer', {
useDefaultRenderLoop: false,
animation: false,
baseLayerPicker : false,
fullscreenButton: false,
geocoder: false,
homeButton: false,
infoBox: false,
sceneModePicker: false,
selectionIndicator: false,
timeline: false,
navigationHelpButton: false,
imageryProvider : Cesium.createOpenStreetMapImageryProvider({url : 'http://a.tile.openstreetmap.org/'}),
terrainShadows: Cesium.ShadowMode.DISABLED,
});
cviewer.snippet = new ThreeDTile();
viewer = new Potree.Viewer(document.getElementById("potree_render_area"),"");
viewer.setEDLEnabled(false);
viewer.setMaterial("RGB");
viewer.setFOV(60);
if(isMobile()==true)
viewer.setPointSize(2);
viewer.setPointSizing("Fixed");
viewer.setPointBudget(1*1000*1000); //medLow
else
viewer.setPointSize(1);
viewer.setPointSizing("Adaptive");
viewer.setPointBudget(2*1000*1000);
viewer.setQuality("Squares");
viewer.setEDLEnabled(false);
viewer.setShowSkybox(false);
viewer.setMaterialID(Potree.PointColorType.RGB);
viewer.setDescription('');
viewer.loadSettingsFromURL();
viewer.loadGUI();
let cp = new Cesium.Cartesian3(4303414.154026048, 552161.235598733, 4660771.704035539);
cviewer.camera.setView({
destination : cp,
orientation: {
heading : 10,
pitch : -Cesium.Math.PI_OVER_TWO * 0.5,
roll : 0.0
});
Call Cesium render function inside Potree render function
render(){
cviewer.render();
…..
}
I modified initThree() function remove line that add potree’s canvas in potree render area
// this.renderArea.appendChild(this.renderer.domElement);
and add cesium canvas in Three.WebGl Render
this.renderer = new THREE.WebGLRenderer({alpha : true,preserveDrawingBuffer: true , canvas : cviewer.canvas});
And also added the following lines as I see this on https://github.com/AnalyticalGraphicsInc/cesium/issues/648
var resetFunc = this.renderer.state.reset;
this.renderer.state.reset = function(){};
this.renderer.resetGLState();
this.renderer.state.reset = resetFunc;
I remove all events of Cesium except rotate because I want potree’s events only. And to stop collusion of some of the same events from both sides.
cviewer.scene.screenSpaceCameraController.enableTranslate = false;
cviewer.scene.screenSpaceCameraController.enableZoom = false;
cviewer.scene.screenSpaceCameraController.enableTilt = false;
cviewer.scene.screenSpaceCameraController.enableLook = false;
Identified pointdown event handler of Cesium from debugger and remove it. Because it stops orbit controls in potree.
function p(e, t, i, r) {
// function n(t) {
// r(e, t)
// }
i.addEventListener(t, n, !1),
e._removalFunctions.push(function() {
i.removeEventListener(t, n, !1)
})
}

Related

Applying two different fragment shaders to two different materials (of the same type) using onBeforeCompile?

I've imported a GLTF file with two different meshes. My goal is to give each mesh a material with a unique custom fragment shader using onBeforeCompile. Each mesh has the same type of material (MeshNormalMaterial).
When I try to apply one fragment shader to one material and the other fragment shader to the other material, both materials wind up with the same fragment shader. The fragment shader each material has depends on which material I setup first.
Here's a few pictures showing what I'm talking about:
Below is all the relevant code.
Main code: This is the general structure of my code. I've enclosed the important part between "PRIMARY AREA OF INTEREST" comments. For simplicity, I've replaced my shader code with "..." or a comment describing what it does. They do work as shown in the pictures above.
// Three.JS Canvas
const threeDisplay = document.getElementById("threeDisplay");
// Globals
var displayDimensions = getElemDimensions(threeDisplay); // Uniform
var currentTime = 0; // Uniform
var helix = null; // Mesh
var innerHelix = null; // Mesh
var horseshoe = null; // Mesh
// Set the scene and camera up
const scene = new THREE.Scene();
const camera = initCamera();
// Setup a directional light
const light = new THREE.DirectionalLight( 0xffffff, 1.0 );
light.position.set(-0.2, 1, -0.6);
scene.add(light);
// Setup WebGL renderer
const renderer = initRenderer();
threeDisplay.appendChild( renderer.domElement );
// Load the gltf model
new GLTFLoader().load( "./spiral_pillar_hq_horseshoe.glb", function (object) {
const helixFragmentShaderReplacements = [
{
from: ' ... ',
to: ' // rainbow '
}
];
const horseshoeFragmentShaderReplacements = [
{
from: ' ... ',
to: ' // white '
}
];
//////////////////////////////////////
// PRIMARY AREA OF INTEREST - START //
//////////////////////////////////////
// Turn the horseshoe into a shader.
horseshoe = object.scene.children[1];
var horseshoeGeometry = horseshoe.geometry;
var horseshoeMaterial = shaderMeshMaterial(new THREE.MeshNormalMaterial(), horseshoeGeometry, horseshoeFragmentShaderReplacements);
var horseshoeMesh = new THREE.Mesh(horseshoeGeometry, horseshoeMaterial);
horseshoe = horseshoeMesh;
horseshoe.rotation.z = deg2rad(180); // Re-orient the horseshoe to the correct position and rotation.
horseshoe.position.y = 13;
scene.add(horseshoe);
// Turn the inner helix into a colorful, wiggly shader.
helix = object.scene.children[0];
var helixGeometry = helix.geometry;
var helixMaterial = shaderMeshMaterial(new THREE.MeshNormalMaterial(), helixGeometry, helixFragmentShaderReplacements);
var helixMesh = new THREE.Mesh(helixGeometry, helixMaterial);
helix = helixMesh;
scene.add(innerHelix);
animate();
////////////////////////////////////
// PRIMARY AREA OF INTEREST - END //
////////////////////////////////////
}, undefined, function (error) {
console.error(error);
});
Below are functions which are relevant.
shaderMeshMaterial: Constructs a new material based on the supplied materialType that supports editing the default shader. If it's not initProcessing, then the problem may stem from this function.
// Globals used: displayDimensions
function shaderMeshMaterial(materialType, geometry, fragmentShaderReplacements) {
var material = materialType;
material.onBeforeCompile = function ( shader ) {
// Uniforms
shader.uniforms.time = { value: 0 };
shader.uniforms.resolution = { value: new THREE.Vector2(displayDimensions.width, displayDimensions.height) };
shader.uniforms.bboxMin = { value: geometry.boundingBox.min };
shader.uniforms.bboxMax = { value: geometry.boundingBox.max };
fragmentShaderReplacements.forEach((rep) => {
shader.fragmentShader = shader.fragmentShader.replace(rep.from, rep.to);
});
console.log(shader);
material.userData.shader = shader;
}
return material;
}
initRenderer: Sets up the renderer. Just showing you guys the renderer setup I have in case that's important.
// Globals used: displayDimensions
function initRenderer() {
var renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true,
precision: "mediump"
});
renderer.setClearColor( 0x000000, 0);
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( displayDimensions.width, displayDimensions.height );
renderer.shadowMap.enabled = true;
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.25;
return renderer;
}
animate: Handles the animation frames.
// Globals used: renderer, currentTime, postprocessing
function animate (timestamp = 0) {
requestAnimationFrame(animate);
resizeRendererToDisplaySize(renderer);
currentTime = timestamp/1000; // Current time in seconds.
scene.traverse( function ( child ) {
if ( child.isMesh ) {
const shader = child.material.userData.shader;
if ( shader ) {
shader.uniforms.time.value = currentTime;
}
}
} );
renderer.render( scene, camera );
postprocessing.composer.render( 0.1 );
};
One last thing to note is that when I inspected the console log of shader from the shaderMeshMaterial function, I can see that the fragment shaders are indeed different as they should be for each material. Also not sure why there are 4 console logs when there should only be 2.
Sorry for all the code, but I did condense it to where all irrelevant code was stripped out. I'm fairly new to Three.JS, so any possible explanations as to why this is happening are much appreciated!
EDIT: Removed vertex shader parameter from shaderMeshMaterial function to keep this question focused on just the fragment shaders. Though this problem does apply to both the vertex and fragment shaders, I figure if you fix one then you'll fix the other.
EDIT 2: Added language identifiers to code snippets. Also I removed the postprocessing function and the problem still persists, so I know the problem isn't caused by that. I've updated the code above to reflect this change. As a happy side effect of removing the postprocessing function, the console.log of the shader variable from shaderMeshMaterial new appears twice in the log (as it should).
EDIT 3: (Implementing WestLangley's suggestion) I tweaked the shaderMeshMaterial function by adding the customProgramCacheKey function. I had to condense the four parameters of shaderMeshMaterial into one for the sake of the customProgramCacheKey function. I believe I implemented the function correctly, but I'm still getting the same result as before where both materials display the same fragment shader.
New "PRIMARY AREA OF INTEREST" code:
horseshoe = object.scene.children[1];
var horseshoeGeometry = horseshoe.geometry;
var meshData = {
materialType: new THREE.MeshNormalMaterial(),
geometry: horseshoeGeometry,
fragmentShaderReplacements: horseshoeFragmentShaderReplacements
}
var horseshoeMaterial = shaderMeshMaterial(meshData);
var horseshoeMesh = new THREE.Mesh(horseshoeGeometry, horseshoeMaterial);
horseshoe = horseshoeMesh;
horseshoe.rotation.z = deg2rad(180); // Re-orient the horseshoe to the correct position and rotation.
horseshoe.position.y = 13;
scene.add(horseshoe);
// Turn the inner helix into a colorful, wiggly shader.
helix = object.scene.children[0];
var helixGeometry = helix.geometry;
var meshData2 = {
materialType: new THREE.MeshNormalMaterial(),
geometry: helixGeometry,
fragmentShaderReplacements: helixFragmentShaderReplacements
}
var helixMaterial = shaderMeshMaterial(meshData2);
var helixMesh = new THREE.Mesh(helixGeometry, helixMaterial);
helix = helixMesh;
scene.add(innerHelix);
animate();
New shaderMeshMaterial code:
// Globals used: displayDimensions
function shaderMeshMaterial(meshData) {
var material = meshData.materialType;
material.onBeforeCompile = function ( shader ) {
// Uniforms
shader.uniforms.time = { value: 0 };
shader.uniforms.resolution = { value: new THREE.Vector2(displayDimensions.width, displayDimensions.height) };
shader.uniforms.bboxMin = { value: meshData.geometry.boundingBox.min };
shader.uniforms.bboxMax = { value: meshData.geometry.boundingBox.max };
meshData.fragmentShaderReplacements.forEach((rep) => {
shader.fragmentShader = shader.fragmentShader.replace(rep.from, rep.to);
});
material.customProgramCacheKey = function () {
return meshData;
};
console.log(shader);
material.userData.shader = shader;
}
return material;
}
WestLangley suggestion worked for me!
material.onBeforeCompile = ...
// Make sure WebGLRenderer doesnt reuse a single program
material.customProgramCacheKey = function () {
return UNIQUE_PER_MATERIAL_ID;
};
I believe your mistake is returning meshData from customProgramCacheKey.
I think customProgramCacheKey need concrete identifier like a number or string.
It would be nice to understand what exactly happening and why do we need to specify customProgramCacheKey.
EDIT: I discover that default value for customProgramCacheKey calculated as follow in Threejs source.
customProgramCacheKey() {
return this.onBeforeCompile.toString();
}
Perhaps this is explains this default caching behavior because calling toString on function returns that function body literally as string.
For example consider function const myFunc = () => { return 1 }. Calling myFunc.toString() returns "() => { return 1 }"
So if your calling onBeforeCompile in a for loop you function body as string never change.

Cesium issue positioning labels for GeoJSON markers

I'm displaying some GeoJSON as markers in Cesium; they do not have altitude, so I'm using clampToGround: true. This all works.
When I try to label the markers, the labels only display when I'm zoomed far out. When I zoom in the labels disappear "underground", which is under the Terrain "layer".
How to solve this? I've looked at this, but it didn't help in my case. This neither.
The code:
const viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: Cesium.createWorldTerrain(),
timeline: false, // Hide clock thing
animation: false, // ditto
});
// Add Cesium OSM Buildings, a global 3D buildings layer.
const buildingTileset = viewer.scene.primitives.add(Cesium.createOsmBuildings());
// Fly the camera to the given longitude, latitude, and height.
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(11.952996, 57.671910, 400),
orientation : {
heading : Cesium.Math.toRadians(0.0),
pitch : Cesium.Math.toRadians(-15.0),
}
});
var promise = Cesium.GeoJsonDataSource.load('data/botaniska_play.geojson', {
clampToGround: true,
markerColor: Cesium.Color.DARKGREEN,
});
promise.then (function (dataSource) {
viewer.dataSources.add (dataSource);
var entities = dataSource["_entityCollection"]["_entities"]["_array"];
entities.forEach (entity => {
// Add (and poistion?) label
entity.label = new Cesium.LabelGraphics ({
text: entity['_properties']['art']._value,
//eyeOffset: new Cesium.Cartesian3 (0, 10, 0),
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0.0, -40.0), // Show above ground?
});
});
});
Snippet of the geojson:
{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[11.9531552705143,57.6814657645123]},"properties":{"ogc_fid":6,"geom":"{\"type\":\"Point\",\"coordinates\":[11.9531552705143,57.6814657645123]}","plats":"Test Plats","besikt_datum":"2013-06-07","trad_id":"6","art":"Magnolia stellata","status":null,"aldersfas":"Gammalt","hojd":9,"krondiameter":9,"stamdiameter":56,"skador":"Lindriga","vitalitet":"God","anmarkningar":"Vackert","rekommendationer":null,"risk":"Låg","resterande_risk":"-","atgards_datum":null,"atgard_gjort":null,"nasta_besiktning":"2014-06-07","stabiliserings_datum":null}},{"type":"Feature","geometry":{"type":"Point","coordinates":[11.9500765558289,57.6815427053798]},"properties":{"ogc_fid":21,"geom":"{\"type\":\"Point\",\"coordinates\":[11.9500765558289,57.6815427053798]}","plats":null,"besikt_datum":"2017-06-09","trad_id":null,"art":"Acer griseum","status":null,"aldersfas":"Vuxet","hojd":9,"krondiameter":8,"stamdiameter":50,"skador":null,"vitalitet":"God","anmarkningar":"Alléträd högt naturvärde\n","rekommendationer":null,"risk":"Låg","resterande_risk":null,"atgards_datum":null,"atgard_gjort":null,"nasta_besiktning":null,"stabiliserings_datum":null}},{"type":"Feature","geometry":{"type":"Point","coordinates":[11.9514171990732,57.6828566964894]},"properties":{"ogc_fid":3,"geom":"{\"type\":\"Point\",\"coordinates\":[11.9514171990732,57.6828566964894]}","plats":"Test Plats","besikt_datum":"2013-06-07","trad_id":"3","art":"Populus siemonii","status":null,"aldersfas":"Gammalt","hojd":15,"krondiameter":14,"stamdiameter":60,"skador":"Inga","vitalitet":"God","anmarkningar":"Fin","rekommendationer":null,"risk":"Låg","resterande_risk":"-","atgards_datum":null,"atgard_gjort":null,"nasta_besiktning":"2014-06-07","stabiliserings_datum":null}} ...
Try add disableDepthTestDistance: Number.POSITIVE_INFINITY to LabelGraphics options

how to Combe three.js page into A-frame page

Here is a Three.js Example from stemkoski, now I want to use this Texture-Animation plane or box in A-frame page, how can I Combine it.
A-frame Version: 0.9.0
I couldn't find any examples.
When integrating three.js pieces into aframe, it's recommended to use custom components. Here's a simple example:
js
AFRAME.registerComponent('foo', {
// this is called upon initialization
init: function() {
// we'll need this later on for updating the animation
this.animator = null
// wait until the component is loaded
this.el.addEventListener('loaded', e => {
// copied straight from stemkoski's code:
var runnerTexture = new THREE.ImageUtils.loadTexture( 'images/run.png' );
this.animator = new TextureAnimator( runnerTexture, 10, 1, 10, 75 );
// apply the texture to our element
let mesh = this.el.getObject3D('mesh')
mesh.material.map = runnerTexture
mesh.material.needsUpdate = true
})
},
// this is called before each render loop
tick: function(time, delta) {
// update only if animator was created
if (!this.animator) return
this.animator.update(1000 * delta);
}
})
HTML:
<a-plane foo></a-plane>
glitch here. To make it work with a glitch i had to preload the image with a-assets due to cors issues.

Jcrop Image Intervention Laravel 5

i am using Image Intervention and jcrop to crop and resize image in laravel, but having problems. The issue which i think is that , when i save the file width and height is correct according to the selection, but the x & y is not correct, I am completely lost here, dont know what to do , Please help.
I have made but cropping area is wrong.
here is the code example.
// convert bytes into friendly format
function bytesToSize(bytes) {
var sizes = ['Bytes', 'KB', 'MB'];
if (bytes == 0) return 'n/a';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
}
// check for selected crop region
function checkForm() {
if (parseInt($('#w').val())) return true;
$('.setting-image-error').html('Select area').show();
return false;
}
// update info by cropping (onChange and onSelect events handler)
function updateInfo(e) {
$('#x1').val(e.x);
$('#y1').val(e.y);
$('#x2').val(e.x2);
$('#y2').val(e.y2);
$('#w').val(e.w);
$('#h').val(e.h);
}
// clear info by cropping (onRelease event handler)
function clearInfo() {
$('#w').val('');
$('#h').val('');
}
// Create variables (in this scope) to hold the Jcrop API and image size
var jcrop_api, boundx, boundy;
function fileSelectHandler() {
// get selected file
var oFile = $('#picture')[0].files[0];
// hide all errors
$('.setting-image-error').hide();
// check for image type (jpg and png are allowed)
var rFilter = /^(image\/jpeg|image\/png)$/i;
if (!rFilter.test(oFile.type)) {
$('.setting-image-error').html('Select only jpg, png').show();
return;
}
// check for file size
if (oFile.size > 10000000) {
$('.setting-image-error').html('Too Big file ').show();
return;
}
// preview element
var oImage = document.getElementById('preview');
// prepare HTML5 FileReader
var oReader = new FileReader();
oReader.onload = function (e) {
// e.target.result contains the DataURL which we can use as a source of the image
oImage.src = e.target.result;
oImage.onload = function () { // onload event handler
// display step 2
$('.setting-image-cropping-stage').fadeIn(500);
// display some basic image info
var sResultFileSize = bytesToSize(oFile.size);
$('#filesize').val(sResultFileSize);
$('#filetype').val(oFile.type);
$('#filedim').val(oImage.naturalWidth + ' x ' + oImage.naturalHeight);
// destroy Jcrop api if already initialized
if (typeof jcrop_api !== 'undefined') {
jcrop_api.destroy();
jcrop_api = null;
$('#preview').width(oImage.naturalWidth);
$('#preview').height(oImage.naturalHeight);
}
//Scroll the page to the cropping image div
$("html, body").animate({scrollTop: $(document).height()}, "slow");
// initialize Jcrop
$('#preview').Jcrop({
minSize: [32, 32], // min crop size
aspectRatio: 1, // keep aspect ratio 1:1
bgFade: true, // use fade effect
bgOpacity: .3, // fade opacity
onChange: updateInfo,
onSelect: updateInfo,
onRelease: clearInfo
}, function () {
// use the Jcrop API to get the real image size
var bounds = this.getBounds();
boundx = bounds[0];
boundy = bounds[1];
// Store the Jcrop API in the jcrop_api variable
jcrop_api = this;
});
}
}
// read selected file as DataURL
oReader.readAsDataURL(oFile);
}
and Controller code is below.
public function image_crop_resize_and_upload($file, $user_id,$width,$height,$x1,$y1)
{
$filename = $user_id . '.jpg';// image file name
$target_path = User::PICTURE_PATH . $filename;//path where to create picture with new dimensions
$img = \Image::make($file->getRealPath());// create the instance of image with the real path of the image
$filetype = $img->mime();//get file mime type
$filetypes = ['image/jpg', 'image/jpeg', 'image/png']; //allowed files types
//if file exists in the target folder, system will delete the file and next step will create new one.
if (File::exists($target_path)) {
File::delete($target_path);
}
if (in_array($filetype, $filetypes, true)) {
$img->crop($width, $height,$x1,$y1);
$img->encode('jpg', 85);
$img->resize($width,$height);
$img->save('uploads/' . $user_id . '.jpg');
return true;
} else {
return false;
}
}
When i have the file the file width and height is correct, but the selection area, x & y is not correct.
Yes , I have got the answer. The problem is very simple the x and y position of image are wrong because it is inside a bootstrap responsive class. the proper solution is just to remove the class . So the image actually dimension will be shown. and than select the are. Thats it.
<img id="preview" name="preview" class="img-responsive"/>
this should be
<img id="preview" name="preview"/>

Three.js make the text created with THREE.ShapeGeometry face the camera

I created a shapegeometry with the text. How can I keep the text face the camera on move the camera?
...
this.textGeometry = new THREE.ShapeGeometry(THREE.FontUtils.generateShapes(value, parameters));
this.textValue = new THREE.Mesh(this.textGeometry, new THREE.MeshBasicMaterial({ color: color, side: THREE.DoubleSide }));
this.textValue.matrixAutoUpdate = true;
this.add(this.textValue)
...
I think my problem is that I modified the parent quaternion 3D object:
this.quaternion.setFromAxisAngle (axis, radians);
then the only operation:
textValue.quaternion.copy (camera.quaternion);
is not sufficient
how can I fix the rotation considering the state of the quaternion?
If you don't care about calling the base updateMatrix function,
this can be a solution
yourShapeGeometry.prototype.updateMatrix = function(){
// THREE.Object3D.prototype.updateMatrix.call(this);
fixOrientation(this.textValue);
}
function fixOrientation(mesh){
mesh.setRotationFromQuaternion(camera.quaternion);
mesh.updateMatrix();
}
or simply edit the updateMatrix of your text mesh like
textMesh.updateMatrixWorld = updateSpriteWorld;
function updateSpriteWorld(){
if ( this.matrixWorldNeedsUpdate === true || force === true ) {
this.setRotationFromQuaternion(camera.quaternion);
this.updateMatrix();
this.matrixWorld.copy( this.matrix );
this.matrixWorldNeedsUpdate = false;
force = true;
}
// update children
for ( var i = 0, l = this.children.length; i < l; i ++ ) {
this.children[ i ].updateSpriteWorld( force );
}
}
I think this should do the trick:
this.textValue.lookAt( camera.position );

Resources