Threejs issue with CSG substract rendering - three.js

I try to substract text geometry from an mesh which has been previously omported (GLB with GLTFLoader).
Unfortunatly object seems not intersect rather than when I add the text geometry in my scene for debugging I see it properly and my mesh should have removed part.
See photo below:
Image 1
But I got this result:
Image 2
I tried to lower up the size of my font, and with x100 ratio I got the result below (here the mesh has removed part but I do not know what is refering to):
Image 3
The code:
let font
function engraving() {
const loaderFont = new FontLoader()
loaderFont.load('fonts/helvetiker_regular.typeface.json', function (f) {
font = f
regenerateGeometry()
})
}
function regenerateGeometry() {
let newGeometry
newGeometry = new TextGeometry("AAAAAAAAA", {
font: font,
size: 0.003,
height: 0.003,
curveSegments: 2,
})
newGeometry.center()
//bender.bend(newGeometry, 'y', Math.PI / 16)
newGeometry.translate(0, 0, 0)
const material = new THREE.MeshStandardMaterial({
envMap: '',
metalness: 1.0,
roughness: 0.0,
color: 0xffd700,
})
const textCSG = CSG.fromGeometry(newGeometry)
var engraved = engravedCSG.subtract(textCSG)
engravedMesh.geometry.dispose()
engravedMesh.geometry = CSG.toMesh(
engraved,
new THREE.Matrix4()
).geometry
}
Looking forward for some advise

Related

How to create a Points (Mesh) of TextGeometry?

I am creating a TextGeometry but not able to convert in Points like a sphere or like a detailed 3d object.
In the below image I have created a sphere using
var dotGeometry = new THREE.SphereBufferGeometry(2, 100, 100);
var dotMaterial = new THREE.PointsMaterial({ size: 1, sizeAttenuation: false });
var dot = new THREE.Points(dotGeometry, dotMaterial);//new THREE.MeshBasicMaterial()
scene.add(dot);
and the for text I use
const fontloader = new THREE.FontLoader();
fontloader.load('./models/font.json', function (font) {
const textgeometry = new THREE.TextGeometry('Hello', {
font: font,
size: 1,
height: 0.5,
curveSegments: 12,
bevelEnabled: false,
bevelThickness: 10,
bevelSize: 8,
bevelOffset: 0,
bevelSegments: 5
});
var dotMaterial = new THREE.PointsMaterial({ size: 1, sizeAttenuation: false });
let textmesh = new THREE.Points(textgeometry, dotMaterial) //new THREE.MeshBasicMaterial({
// color: 0xffffff,
// wireframe: true
// }));
textmesh.geometry.center();
scene.add(textmesh);
// textmesh.position.set(0, 2, 0)
});
So how to create a geometry having many points for text also, Why I am not able to use MeshBasicMaterial for Points in sphere?
What you see is actually the expected result. TextGeometry produces a geometry intended for meshes. If you use the same data for a point cloud, the result is probably not as expected since you just render each vertex as a point.
When doing this with TextGeometry, you will only see points at the front and back side of the text since points in between are not necessary for a mesh.
Consider to author the geometry in a tool like Blender instead and import it via GLTFLoader.

Why is polygonOffset not working in this case?

I have some text rendering over a background quad. Let's call this a 'label'. Both are positioned at the same point which causes z-fighting.
I'd like to promote the text to avoid z-fighting using polygon offset.
This is how I add the polygon offset to the text material:
const material = new THREE.RawShaderMaterial(
CreateMSDFShader({
map: this.glyphs,
opacity: opt.opacity ?? 1,
alphaTest: (opt.opacity ?? 1) < 1 ? 0.001 : 1,
color: opt.colour ?? '#ffffff',
transparent: opt.transparent ?? true,
glslVersion: opt.renderMode === 'webgl' ? THREE.GLSL1 : THREE.GLSL3,
side: opt.side ?? THREE.DoubleSide,
depthFunc: opt.depthFunc ?? THREE.LessEqualDepth,
depthTest: true,
depthWrite: false,
polygonOffset: true,
polygonOffsetUnits: -1.0,
polygonOffsetFactor: -4.0,
})
);
const mesh = new THREE.Mesh(geom, material);
and this is the background material:
if (tableOptions.background) {
const geometry = new THREE.PlaneGeometry(1, 1, 1, 1);
const backgroundMaterial = new ActivatableMaterial(
{
color: new THREE.Color(tableOptions.backgroundColour),
toneMapped: false,
opacity: 1,
alphaTest: 0.001,
transparent: true,
},
{
activationColour: new THREE.Color(tableOptions.activationColour),
}
);
this.background = buildUIObject(
Actions.ExpandLabel as Action,
geometry,
backgroundMaterial
);
setName(this.background, 'Background');
this.tableGroup.add(this.background);
}
The polygon offset just isn't working (using Chrome). The text disappears behind the background quad as I orbit the camera around, and reappears at random. The labels are always facing the camera (using lookAt).
What could stop the polygon offset from working?
The labels are rendering to a renderpass with a render taget set up as follows:
const pars = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat,
stencilBuffer: false,
depthBuffer: false,
};
this.renderTargetBuffer = new THREE.WebGLRenderTarget(
resolution.x,
resolution.y,
pars
);
this.renderTargetBuffer.texture.name = 'RenderTargetBuffer';
this.renderTargetBuffer.texture.generateMipmaps = false;
I'm assuming that because the polygonOffset is a state thing it doesn't matter that this is a RawShaderMaterial. Is that a safe assumption?
Edit: I have added the opposite polygonOffset to the background mesh separately and again it doesn't work.
Polygon offset wasn't the best solution in this case. I switched to maintaining a custom order for the labels based on distance to the camera. That way I could force the text render order to be greater than the background.

How to export the mesh after modified the Vertices in Three.js?

I tried to export the bent text geometry in the example of https://threejs.org/examples/webgl_modifier_curve.html by OBJExporter.js. But the result text geometry exported was not bent as seen in the scene.
The code of "new OBJExporter..." was added after the code line "scene.add( flow.object3D );"
Please advise where I should put the exporter code and what steps I missed to get the text geometry with modified vertices? Great thanks for help!
const loader = new THREE.FontLoader();
loader.load( "fonts/Microsoft_YaHei_Regular.json", function (
font
) {
const geometry = new THREE.TextGeometry( "1 234567890", {
font: font,
size:0.25,
height: 0.02, //0.05 thickness
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 0.02,
bevelSize: 0.01,
bevelOffset: 0,
bevelSegments: 5,
} );
geometry.rotateX( Math.PI );
geometry.rotateY( Math.PI );
const material = new THREE.MeshStandardMaterial( {
color: 0x99ffff
} );
const objectToCurve = new THREE.Mesh( geometry, material );
flow = new Flow( objectToCurve );
flow.updateCurve( 0, curve );
scene.add( flow.object3D );
const exporter = new OBJExporter();
const result = exporter.parse(flow.object3D);
The vertex displacement of the modifier happens on the GPU (in the vertex shader). So you can't use any of the exporters to export the bent/modified geometry.

How to set a part of a box object with a diffrent color in three.js and the part dimension will get dynamically?

I am new to Three.js. This may be very basic question. But I tried to get a solution, unfortunately i didn't get any even from google. I have a 'mesh' with 'box geometry' and applied a 'MeshStandardMaterial' material. Specified a color in the material. Now I want show the part of this box is filled with some other color.This percentage of part will be dynamically.For example half of water filled in the glass the quantity of water will be get dynamically.
I want to represent level of some filling.Another option to make some part of this box is transparent that is also fine.
let cube_geometry = new THREE.BoxGeometry(tankWidth, tankHeight, tankDepth);
let ct_material = new THREE.MeshStandardMaterial({
color: colourCode,
metalness: 1.0,
roughness: 0.5,
transparent: true,
opacity: params['CT Opacity'],
alphaTest: 0.1
});
cube.name = name;
let cube = new THREE.Mesh(cube_geometry , ct_material);
group_ct.add(cube);//will add set of boxes to this group
//---------------------------
function SetQty(qty)
{
group_ct.traverse(function (tank) {
if (tank instanceof THREE.Mesh) {
tankName = tank.name;
qty =GetQty(tankName);
if(qty > 0) {
//want to display filled level inside the box as per the quantity
}
}
There are a couple ways to do this.
The first and easiest is to create two cubes of your desired material, and scale them along the same axis. The difficulty there is that you would also need to recompute their position so they maintain the same barrier edge for the varying values of "percentage filled".
Another option that requires fewer calculations (but is more conceptually complex) is to employ Material.clippingPlanes (example), and render your geometry using two different materials/meshes.
In the example below, I use the same cube geometry for both Mesh objects, but I use different materials with clipping planes pointing in opposite directions. I update their displacement in the render loop, which has the effect of changing the "fill".
// three.js r110
const W = window.innerWidth
const H = window.innerHeight
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
})
renderer.setSize(W, H)
document.body.appendChild(renderer.domElement)
// turn on clipping!
renderer.localClippingEnabled = true
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(28, W / H, 1, 1000)
camera.position.set(10, 20, 50)
camera.lookAt(scene.position)
scene.add(camera)
const light = new THREE.PointLight(0xffffff, 1)
//scene.add(light)
camera.add(light)
// define the clipping planes
const emptyClip = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)
const fillClip = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0)
const boxGeo = new THREE.BoxGeometry(10, 10, 10)
const emptyMaterial = new THREE.MeshStandardMaterial({
color: 0x55ddff,
metalness: 0,
roughness: 0.2,
transparent: true,
opacity: 0.5,
side: THREE.DoubleSide,
clippingPlanes: [emptyClip]
})
const filledMaterial = new THREE.MeshStandardMaterial({
color: 0xff5555,
metalness: 0,
roughness: 0.2,
side: THREE.DoubleSide,
clippingPlanes: [fillClip]
})
const emptyMesh = new THREE.Mesh(boxGeo, emptyMaterial)
scene.add(emptyMesh)
const filledMesh = new THREE.Mesh(boxGeo, filledMaterial)
scene.add(filledMesh)
// layer separator
const separatorGeo = new THREE.PlaneGeometry(10, 10)
const separatorMat = new THREE.MeshStandardMaterial({
color: 0xff5555,
metalness: 0,
roughness: 0.2,
side: THREE.DoubleSide
})
const separator = new THREE.Mesh(separatorGeo, separatorMat)
separator.rotation.x = Math.PI / 2
scene.add(separator)
let count = 0
function animate() {
requestAnimationFrame(animate)
// compute the clipping plane displacement
let displacement = Math.sin(count) * 0.5 // min = 25%, max = 75%
displacement *= 5 // each cube side is 5 units
// set the displacement of the planes (relative to their normals!)
emptyMaterial.clippingPlanes[0].constant = displacement
filledMaterial.clippingPlanes[0].constant = -displacement
// update the separator
separator.position.y = -displacement
renderer.render(scene, camera)
count += 0.05
}
animate()
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
margin: 0;
padding: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>

ThreeJS image texture on merged objects

Help! I'm trying to create and merge two different objects in THREEjs. I have one image and I need it to be stretched over the entire merged object once. Currently it is doing it twice, once on the top and once on the bottom, like they are still two separate objects.
var texture = new THREE.TextureLoader().load('//s1.postimg.org/26dva6d58v/peace.png');
texture.alphaTest = 0.9;
texture.needsUpdate = true;
color = new THREE.MeshStandardMaterial( {
side: THREE.DoubleSide,
color: 0x949494,
map: texture
} );
var size = {'top': 1.395, 'bottom': 1.11, 'height': 3.5};
cylinderGeo1 = new THREE.CylinderGeometry( size.top, size.bottom, size.height, 50, null, true );
cylinderGeo1.applyMatrix( new THREE.Matrix4().makeTranslation(0, size.height/2, 0) );
cylinder1 = new THREE.Mesh(cylinderGeo1);
cylinderGeo2 = new THREE.CylinderGeometry( size.top * 2,size.top, size.height, 50, null, true );
cylinderGeo2.applyMatrix( new THREE.Matrix4().makeTranslation(0, (size.height/2) + size.height, 0) );
cylinder2 = new THREE.Mesh(cylinderGeo2);
cupGeo = new THREE.Geometry();;
cylinder1.updateMatrix();
cupGeo.merge(cylinderGeo1, cylinder1.matrix);
cylinder2.updateMatrix();
cupGeo.merge(cylinderGeo2, cylinder2.matrix);
cup = new THREE.Mesh(cupGeo, color);
cup.updateMatrix();
scene.add(cup);
https://jsfiddle.net/dj2k6y8t/16/
[edit] You may need to zoom on the canvas to get it render.

Resources