hello guys, I’m doing image target project using aframe , 8th wall xrextras components and manipulate with vanilla js. the main issue is when i manipulate btn click with js, the 3D object model pop up and that’s all ok. but when it will be showed, the 3D object model is shaking. how do i fix that issue? i try many possible ways for it. what would be it that can coz shaking issue? may be coz of image target or 3D object model ?
Are you able to record a video of the issue?
yes, I’ll record and send it here.
cannot upload video coz I’m new user it said!
I’ll attach with drive link
shaking issue
That definitely looks to be shaking more than I would expect, I’ll investigate into this further. In the meantime would you mind sharing your project with the ‘support’ workspace, if it’s not self-hosted?
Update: I would also recommend taking a look at the following video, because this could be a result of poor image target detection.
I’m just sharing my project with ‘support’ workspace. could you have a look ?
After reviewing your model and the associated image targets for your projects, here are the steps I recommend taking to ensure a better experience:
Your model, Cocoa.glb (alongside others), has roughly 250k vertices, while the recommended amount is ≤ 20k. High vertex counts can lead to performance issues, especially on mobile devices. Consider using a lower-quality model and baking the detail onto it from the high-poly model in your modeling software. Additionally, the model contains multiple 2k textures, whereas the recommended size is 1024x1024 or less. Using oversized textures can strain memory and affect rendering performance. Aim for a single 1024x1024 texture for your albedo and smaller textures (around 512x512 ) for normal and roughness maps.
Your image targets could be enhanced by adding more visual points of interest. Currently, they are quite simple, featuring just black text, which might not provide enough detail for effective tracking. Consider incorporating more complex designs or textures to improve tracking stability. Since you have two image targets that are very similar, the shaking might be due to the engine switching between the two targets, as both are set to load automatically. I suggest configuring only one of your image targets to load automatically initially and see if the issue persists.
I also recommend using gltf.report to analyze your model and check for any optimization suggestions it provides. Utilizing the website’s optimization features can significantly enhance performance.
so you mean all of the models in my project has this issue that you mentioned above? and also for the image target, what should I do for the image target? one more thing, does it concern with my code but I don’t think so. I only has these errors on my console. I’ll attach it. and also can I use 3D model format for flx format? does it solve 3D object model shaking when I optimize the image target and 3D object model?
The shaking is definitely, at least partially, coming from the model complexity affecting performance.
As for the image targets here’s a video explaining what separates a good image target from a not-so-good one.
thank you so much for your supp. I have one more thing to ask. it’s about "when a user scan with product bottle frame with text image to image target product bottle, the product bottle frame with text image disappear when detect image target product label. when no detection product bottle frame with text image is appear.
I’ll provide screenshot and video about that.
how do I do that please?
I will also provide with my model-btn.js code snippets
const modelBtnComponent = {
init() {
// Get references to the HTML elements
const model = document.getElementById('ingredient-model')
const videoElement = document.querySelector('video')
const bottleFrame = document.getElementById('bottle-frame') // Reference to the bottle frame
const bottleText = document.getElementById('bottle-text') // Reference to the text
const imageTarget = document.getElementById('image-target') // The image target for the product
let currentModelId = null // Keep track of the current model being displayed
let animationIndex = 0 // Keep track of the current animation
// Function to hide the frame and text when the product image target is detected
const hideFrameAndText = () => {
console.log('Hiding bottle frame and text')
bottleFrame.classList.add('hidden') // Add hidden class to hide the bottle frame
bottleText.classList.add('hidden') // Add hidden class to hide the text
}
// Function to show the frame and text when the product image target is not detected
const showFrameAndText = () => {
console.log('Showing bottle frame and text')
bottleFrame.classList.remove('hidden') // Remove hidden class to show the bottle frame
bottleText.classList.remove('hidden') // Remove hidden class to show the text
}
// Event listener for when the product image target is found
imageTarget.addEventListener('targetFound', () => {
console.log('Image target found!')
hideFrameAndText() // Hide the bottle frame and text when the target is found
})
// Event listener for when the product image target is lost
imageTarget.addEventListener('targetLost', () => {
console.log('Image target lost!')
showFrameAndText() // Show the bottle frame and text again if the target is lost
})
// List of animations that exist in the 3D model
const animationList = []
// Function to cycle through animations
const nextAnimation = () => {
// for (let i = 0; i < animationList.length; i++) {
// animationIndex += animationList[i]
// }
const currentClip = animationList[animationIndex]
// Set animation-mixer component to play the next animation
model.setAttribute('animation-mixer', {
clip: currentClip,
loop: 'repeat', // Repeat the animation
crossFadeDuration: 0.4, // Smooth transition between animations
})
// Cycle to the next animation in the list
animationIndex = (animationIndex + 1) % animationList.length
}
// Hide the current model (used when switching between models)
const hideCurrentModel = () => {
model.setAttribute('visible', 'false') // Hide the current 3D model
model.removeAttribute('gltf-model') // Remove the model so a new one can be loaded
}
// Function to update the AR content (3D model and video)
const updateARContent = (modelId, videoId) => {
// If the same button is clicked again, reset the model's position and play the animation
if (currentModelId === modelId) {
hideCurrentModel()
model.setAttribute('gltf-model', modelId)
model.setAttribute('visible', 'true')
nextAnimation() // Start the animation
return
}
// If another button is clicked, hide the current model, then load the new one
hideCurrentModel()
model.setAttribute('gltf-model', modelId)
model.setAttribute('visible', 'true')
// Animate the position of the 3D model
model.setAttribute('animation', {
property: 'position',
from: '0 0 0',
to: '0 1 0', // Move to the top of the bottle lid
dur: 3000, // Duration of 3 second
easing: 'easeInOutQuad',
})
// Start the 3D model animation
nextAnimation()
// Update the video element source
const videoSrc = document.querySelector(videoId).getAttribute('src')
videoElement.setAttribute('src', videoSrc)
// Update the current model ID
currentModelId = modelId
}
// Define the button mappings: each button corresponds to a model and video
const buttonMappings = {
'avocado-btn': {
modelId: '#avocado-glb',
videoId: '#avocado-label-video',
},
'agave-btn': {
modelId: '#agave-glb',
videoId: '#agave-label-video',
},
'cacao-btn': {
modelId: '#cacao-glb',
videoId: '#cacao-label-video',
},
'oats-btn': {
modelId: '#oats-glb',
videoId: '#oats-label-video',
},
'vegetable-btn': {
modelId: '#vegetable-glb',
videoId: '#vegetable-label-video',
},
}
// Add event listeners to all buttons
const addListenersToButtons = () => {
const buttonGroups = document.querySelectorAll('#btn-group-left button, #btn-group-right button')
buttonGroups.forEach((button) => {
button.addEventListener('click', (event) => {
// Update the AR content when a button is clicked
const buttonId = event.currentTarget.id
const {modelId, videoId} = buttonMappings[buttonId]
updateARContent(modelId, videoId)
// Remove 'dimmed' and 'active' class from all buttons
buttonGroups.forEach((btn) => {
btn.classList.remove('dimmed')
btn.classList.remove('active')
})
// Add 'dimmed' class to all buttons except the clicked one
buttonGroups.forEach((btn) => {
if (btn !== event.currentTarget) {
btn.classList.add('dimmed')
}
})
// Add 'active' class to the clicked button
event.currentTarget.classList.add('active')
})
})
}
// Initialize event listeners on buttons when the component loads
addListenersToButtons()
},
}
export {modelBtnComponent}
The text and border elements can be hidden, using CSS and a class, when the image target is detected, and shown when it’s not.
how do I make that? I already applied with my code. is something of my code logic is wrong? can you suggest me how to do it? coz I already applied so many ways. so what should i need to detect image target event or is smth wrong with catching the image target event?
Could be your .css file, can you share it?
yes. I’m happy to share abt it.
/* Initially, show the bottle frame and text */
#bottle-frame {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10; /* Ensure it’s above the AR scene */
opacity: 1;
transition: opacity 0.3s ease-in-out;
}
#bottle-text {
text-align: center;
color: white;
font-size: 18px;
margin-top: 10px;
opacity: 1;
transition: opacity 0.3s ease-in-out;
}
/* You could add a hidden class to ensure smooth hiding if needed */
.hidden {
opacity: 0;
visibility: hidden;
}
I’m taking a look at your project in the editor now. Would you mind landing your changes? I’m not seeing the overlay or the text anywhere in the files.
but it’s ok now I changed the logic only show text not frame. is there a way to hide the text when detect the image target? and also I want to ask questions about how to optimize loading time when first time scan the qr ? I already minimize the size of 3D model. they are five 3D models. each for like 5MB now.
Going off this simple text setup:
<div>
<p id="text-box">Your text here</p>
</div>
You can add this component to your image target:
AFRAME.registerComponent('hide-text', {
init() {
const scene = this.el.sceneEl
const text = document.getElementById('text-box')
// optional: set the initial display style
text.style.display = 'block'
this.el.sceneEl.addEventListener('xrimagefound', (event) => {
if (event.detail.name === 'target-name') {
// action you want to perform on image found
text.style.display = 'none'
}
})
this.el.sceneEl.addEventListener('xrimagelost', (event) => {
if (event.detail.name === 'target-name') {
// action you want to perform on image lost
text.style.display = 'block'
}
})
},
})
Add it to your image target like this:
<xrextras-named-image-target name="target-name" hide-text>
<!-- your content -->
</xrextras-named-image-target>
It adds the event listeners and checks if the image found or lost is one with a specific name. Then it changes the style, but you could also use opacity 0 to 1 or whatever else suits your needs!
Hope this helps!