ThreeJS中自定义Canvas无法在PlaneGeometry显示GameBoy模拟器画面问题
Hey there! Let's work through why your GameBoy emulator isn't showing up on your Three.js plane. I've looked over your code and hypotheses, and here are concrete fixes and checks to try:
First: Fix the Canvas ID Assignment
You're setting the canvas ID incorrectly here:
x2.style.id = 'display';
The style property handles CSS styles, not the element's core ID. Change this to:
x2.id = 'display';
While you're directly passing the canvas context to the emulator later, fixing this ensures consistency if the library ever references the canvas by ID under the hood.
Check if the Emulator Requires the Canvas to Be in the DOM
Your first hypothesis hits on a common quirk of older JS emulators: some libraries expect the target canvas to exist in the DOM tree (even if it's hidden). The canvas you created is only used as a texture source for Three.js, so it's not attached to the page. Try adding it to the DOM and hiding it:
// After creating screenCanvas screenCanvas.style.position = 'fixed'; screenCanvas.style.top = '-9999px'; screenCanvas.style.left = '-9999px'; document.body.appendChild(screenCanvas);
This tricks the emulator into thinking the canvas is part of the page without it being visible to users.
Force CanvasTexture Updates
While Three.js's CanvasTexture is supposed to auto-update when the canvas content changes, external libraries like the GameBoy emulator might modify the canvas in a way that doesn't trigger Three.js's internal checks. You need to manually flag the texture for update after each emulator frame.
First, store the texture reference when you create it (right now you're just pushing the material to the screen array):
// Declare this array at the top of your code const screenTextures = []; // Inside your loop where you create tex2 let tex2 = new THREE.CanvasTexture(x2); screenTextures.push(tex2); screen.push(new THREE.MeshBasicMaterial({ map: tex2, transparent:true, opacity:0.3 }))
Then, find where the emulator renders frames (look for a requestAnimationFrame loop or setInterval in the jsgameboy code) and add:
// After the emulator draws a frame screenTextures.forEach(texture => texture.needsUpdate = true);
If you can't modify the emulator code directly, set up your own interval to update the texture periodically:
setInterval(() => { screenTextures.forEach(texture => texture.needsUpdate = true); }, 16); // Matches ~60fps
Simplify Context Management
You're calling screenCanvas.getContext("2d") multiple times—this can sometimes lead to unexpected behavior. Store the context in a variable once and reuse it:
// When creating screenCanvas const screenCtx = x2.getContext("2d"); // ... your existing fillStyle/fillRect code ... gb.displaycanvas = screenCtx; screenCtx.scale(2, 2);
Debugging Step: Verify Canvas Content
To narrow down the issue, add a test draw to your canvas right after initializing the emulator:
gb = new GameBoy(arraybuffer); gb.displaycanvas = screenCtx; screenCtx.scale(2,2); // Test draw: fill a red square screenCtx.fillStyle = 'red'; screenCtx.fillRect(0, 0, 50, 50);
If you see the red square on your Three.js plane, that means Three.js is working correctly, and the problem is that the emulator isn't actually drawing to the canvas. In that case, double-check that the emulator's displaycanvas property is set correctly, and that you're initializing it with a valid ROM arraybuffer.
内容的提问来源于stack exchange,提问作者Strum

