-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add example and guide for three + pixi #145
Merged
Merged
Changes from 1 commit
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
2959b5b
add example and guide for three + pixi
GoodBoyDigital 24a48d0
add example and guide for three + pixi
GoodBoyDigital 3368644
Merge branch 'feat-mixing-renderers' of https://github.com/pixijs/pix…
GoodBoyDigital c874ba8
Merge branch 'main' into feat-mixing-renderers
Zyie 60a0cef
update
Zyie aa302b4
Merge branch 'main' into feat-mixing-renderers
Zyie 64b9f2e
use latest
Zyie 171a566
remove unused code
Zyie File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import Example from '@site/src/components/Example/index'; | ||
import version from '@site/docs/pixi-version.json'; | ||
|
||
# Mixing PixiJS and Three.js | ||
|
||
In many projects, developers aim to harness the strengths of both 3D and 2D graphics. Combining the advanced 3D rendering capabilities of Three.js with the speed and versatility of PixiJS for 2D can result in a powerful, seamless experience. Together, these technologies create opportunities for dynamic and visually compelling applications. Lets see how to do this. | ||
|
||
:::info NOTE | ||
This guide assumes PixiJS will be used as the top layer to deliver UI over a 3D scene rendered by Three.js. However, developers can render either in any order, as many times as needed. This flexibility allows for creative and dynamic applications. | ||
::: | ||
|
||
--- | ||
|
||
### What You’ll Learn | ||
|
||
- Setting up PixiJS and Three.js to share a single WebGL context. | ||
- Using `resetState` to manage renderer states. | ||
- Avoiding common pitfalls when working with multiple renderers. | ||
|
||
--- | ||
|
||
### Setting Up | ||
|
||
#### Step 1: Initialize Three.js Renderer and Scene | ||
|
||
Three.js will handle the 3D rendering the creation of the dom element and context. | ||
|
||
```javascript | ||
const WIDTH = window.innerWidth; | ||
const HEIGHT = window.innerHeight; | ||
|
||
const threeRenderer = new THREE.WebGLRenderer({ | ||
antialias: true, | ||
stencil: true // so masks work in pixijs | ||
}); | ||
|
||
threeRenderer.setSize(WIDTH, HEIGHT); | ||
threeRenderer.setClearColor(0xdddddd, 1); | ||
document.body.appendChild(threeRenderer.domElement); | ||
|
||
const scene = new THREE.Scene(); | ||
const camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT); | ||
camera.position.z = 50; | ||
scene.add(camera); | ||
|
||
const boxGeometry = new THREE.BoxGeometry(10, 10, 10); | ||
const basicMaterial = new THREE.MeshBasicMaterial({ color: 0x0095dd }); | ||
const cube = new THREE.Mesh(boxGeometry, basicMaterial); | ||
cube.rotation.set(0.4, 0.2, 0); | ||
scene.add(cube); | ||
``` | ||
:::info NOTE | ||
We used the dom element and context created by the three.js renderer to pass to the pixijs renderer. | ||
This was the simplest way to ensure that the two renderers were using the same WebGL context. You could have done it the other way round | ||
if you wanted to. | ||
::: | ||
|
||
#### Step 2: Initialize PixiJS Renderer and Stage | ||
|
||
PixiJS will handle the 2D overlay. | ||
|
||
```javascript | ||
const pixiRenderer = new PIXI.WebGLRenderer(); | ||
|
||
await pixiRenderer.init({ | ||
context: threeRenderer.getContext(), | ||
width: WIDTH, | ||
height: HEIGHT, | ||
clearBeforeRender: false, // Prevent PixiJS from clearing the Three.js render | ||
}); | ||
|
||
const stage = new PIXI.Container(); | ||
const amazingUI = new PIXI.Graphics() | ||
.roundRect(20, 80, 100, 100, 5) | ||
.roundRect(220, 80, 100, 100, 5) | ||
.fill(0xffff00); | ||
|
||
stage.addChild(amazingUI); | ||
``` | ||
|
||
--- | ||
|
||
### Rendering Loop | ||
|
||
To ensure smooth transitions between the renderers, reset their states before each render: | ||
|
||
```javascript | ||
function render() { | ||
// Render the Three.js scene | ||
threeRenderer.resetState(); | ||
threeRenderer.render(scene, camera); | ||
|
||
// Render the PixiJS stage | ||
pixiRenderer.resetState(); | ||
pixiRenderer.render({ container: stage }); | ||
|
||
requestAnimationFrame(render); | ||
} | ||
|
||
requestAnimationFrame(render); | ||
|
||
``` | ||
|
||
--- | ||
|
||
### Example: Combining 3D and 2D Elements | ||
|
||
Here’s the complete example integrating PixiJS and Three.js: | ||
|
||
<Example id="advanced.threeAndPixi" pixiVersion={version} mode={"embedded"} /> | ||
|
||
--- | ||
|
||
### Gotchas | ||
|
||
- **Enable Stencil Buffers:** | ||
|
||
- When creating the Three.js renderer, ensure `stencil` is set to `true`. This allows PixiJS masks to work correctly. | ||
|
||
- **Keep Dimensions in Sync:** | ||
|
||
- Ensure both renderers use the same `width` and `height` to avoid visual mismatches—so be careful when resizing one, you need to resize the other! | ||
|
||
- **Pass the WebGL Context:** | ||
|
||
- Pass the WebGL context from Three.js to PixiJS during initialization using `pixiRenderer.init({ context: threeRenderer.getContext() });`. | ||
|
||
- **Disable Clear Before Render:** | ||
|
||
- Set `clearBeforeRender: false` when initializing the PixiJS renderer. This prevents PixiJS from clearing the Three.js content that was rendered before it. | ||
- Alternatively you can set `clear: false` in the `pixiRenderer.render()` call. eg `pixiRenderer.render({ container: stage, clear: false });`. | ||
|
||
- **Manage Render Order:** | ||
|
||
- In this example, Three.js is rendered first, followed by PixiJS for UI layers. However, this order is flexible. You can render pixi -> three -> pixi is you want, just make sure you reset the state when switching renderer. | ||
|
||
- **Separate Resources:** | ||
|
||
- Remember that resources like textures are not shared between PixiJS and Three.js. A PixiJS texture cannot be directly used as a Three.js texture and vice versa. | ||
|
||
--- | ||
|
||
### Conclusion | ||
|
||
Mixing PixiJS and Three.js can be a powerful way to create dynamic and visually appealing applications. By carefully managing the rendering loop and states, you can achieve seamless transitions between 3D and 2D layers. This approach allows you to leverage the strengths of both technologies, creating applications that are both visually stunning and performant. | ||
|
||
This technique can be used with other renderers too - as long as they have their own way of resetting their state (which the main ones do) you can mix them. Popular 3D engines like Babylon.js and PlayCanvas both support state management through their respective APIs, making them compatible with this mixing approach. This gives you the flexibility to choose the 3D engine that best suits your needs while still leveraging PixiJS's powerful 2D capabilities. | ||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,101 @@ | ||||||
// Import required classes from PixiJS and Three.js | ||||||
import { Container, Graphics, Text, WebGLRenderer } from 'pixi.js'; | ||||||
import * as THREE from 'three'; | ||||||
|
||||||
// Self-executing async function to set up the demo | ||||||
(async () => | ||||||
{ | ||||||
// Initialize window dimensions | ||||||
let WIDTH = window.innerWidth; | ||||||
let HEIGHT = window.innerHeight; | ||||||
|
||||||
// === THREE.JS SETUP === | ||||||
// Create Three.js WebGL renderer with antialiasing and stencil buffer | ||||||
const threeRenderer = new THREE.WebGLRenderer({ antialias: true, stencil: true }); | ||||||
|
||||||
// Configure Three.js renderer size and background color | ||||||
threeRenderer.setSize(WIDTH, HEIGHT); | ||||||
threeRenderer.setClearColor(0xdddddd, 1); // Light gray background | ||||||
document.body.appendChild(threeRenderer.domElement); | ||||||
|
||||||
// Create Three.js scene | ||||||
const scene = new THREE.Scene(); | ||||||
|
||||||
// Set up perspective camera with 70° FOV | ||||||
const threeCamera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT); | ||||||
|
||||||
threeCamera.position.z = 50; // Move camera back to see the scene | ||||||
scene.add(threeCamera); | ||||||
|
||||||
// Create a simple cube mesh | ||||||
const boxGeometry = new THREE.BoxGeometry(30, 30, 30); | ||||||
const basicMaterial = new THREE.MeshBasicMaterial({ color: 0x0095dd }); // Blue color | ||||||
const cube = new THREE.Mesh(boxGeometry, basicMaterial); | ||||||
|
||||||
scene.add(cube); | ||||||
|
||||||
// === PIXI.JS SETUP === | ||||||
// Create PixiJS renderer that shares the WebGL context with Three.js | ||||||
const pixiRenderer = new WebGLRenderer(); | ||||||
|
||||||
// Initialize PixiJS renderer with shared context | ||||||
await pixiRenderer.init({ | ||||||
context: threeRenderer.getContext(), | ||||||
width: WIDTH, | ||||||
height: HEIGHT, | ||||||
clearBeforeRender: false, // Don't clear the canvas as Three.js will handle that | ||||||
}); | ||||||
|
||||||
// Create PixiJS scene graph | ||||||
const stage = new Container(); | ||||||
|
||||||
// Create a yellow rounded rectangle UI element | ||||||
const uiLayer = new Graphics().roundRect(20, 80, 300, 300, 20).fill(0xffff00); | ||||||
|
||||||
// Add text overlay | ||||||
const text = new Text({ text: 'Pixi and Three.js', style: { fontFamily: 'Arial', fontSize: 24, fill: 'black' } }); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The 'Text' constructor is called incorrectly. It should be 'new Text('Pixi and Three.js', { fontFamily: 'Arial', fontSize: 24, fill: 'black' })'.
Suggested change
Copilot is powered by AI, so mistakes are possible. Review output carefully before use. |
||||||
|
||||||
uiLayer.addChild(text); | ||||||
stage.addChild(uiLayer); | ||||||
|
||||||
// Animation loop | ||||||
function loop() | ||||||
{ | ||||||
// Rotate cube continuously | ||||||
cube.rotation.x += 0.01; | ||||||
cube.rotation.y += 0.01; | ||||||
|
||||||
// Animate UI layer position using sine wave | ||||||
uiLayer.y = ((Math.sin(Date.now() * 0.001) + 1) * 0.5 * WIDTH) / 2; | ||||||
|
||||||
// Render Three.js scene | ||||||
threeRenderer.resetState(); | ||||||
threeRenderer.render(scene, threeCamera); | ||||||
|
||||||
// Render PixiJS scene | ||||||
pixiRenderer.resetState(); | ||||||
pixiRenderer.render({ container: stage }); | ||||||
|
||||||
// Continue animation loop | ||||||
requestAnimationFrame(loop); | ||||||
} | ||||||
|
||||||
// Start animation loop | ||||||
requestAnimationFrame(loop); | ||||||
|
||||||
// Handle window resizing | ||||||
window.addEventListener('resize', () => | ||||||
{ | ||||||
WIDTH = window.innerWidth; | ||||||
HEIGHT = window.innerHeight; | ||||||
|
||||||
// Update Three.js renderer | ||||||
threeRenderer.setSize(WIDTH, HEIGHT); | ||||||
// Update Three.js camera aspect ratio so it renders correctly | ||||||
threeCamera.aspect = WIDTH / HEIGHT; | ||||||
threeCamera.updateProjectionMatrix(); | ||||||
|
||||||
// Update PixiJS renderer | ||||||
pixiRenderer.resize(WIDTH, HEIGHT); | ||||||
}); | ||||||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method 'roundRect' should be 'drawRoundedRect' in PixiJS.
Copilot is powered by AI, so mistakes are possible. Review output carefully before use.