Pixi3D
Pixi3D is a JavaScript library which makes it possible to render 3D graphics
using PixiJS. Because Pixi3D is built on top of PixiJS, it plays very nice with
the already established and easy to use PixiJS API. Pixi3D includes several
components which makes it easy to create nice looking 3D scenes out-of-the-box.
- Built on top of the already familiar PixiJS API
- Load models from file or create procedural generated meshes
- Supports physically-based rendering (PBR) and image-based lighting (IBL)
- Morphing and rotation/translation/scale animations
- Customizable materials and shaders
Getting started
Let's create a simple program which renders a rotating cube. Start by getting the latest version of Pixi3D. Pixi3D has a dependency to PixiJS (v5.2+), so that is needed as well.
Create a file app.js with the following contents.
app.js
let app = new PIXI.Application({
backgroundColor: 0xdddddd
})
let box = app.stage.addChild(PIXI3D.Mesh3D.createCube())
let rotation = 0
app.ticker.add(() => {
box.rotation.setEulerAngles(0, rotation++, 0)
})
document.body.appendChild(app.view)
Also create index.html and include the required scripts.
index.html
<!doctype html>
<html lang="en">
<body>
<script type="text/javascript" src="pixi.js"></script>
<script type="text/javascript" src="pixi3d.js"></script>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
Documentation
Pixi3D is built on top of PixiJS (v5.2+) and therefore any program that wants
to use Pixi3D, also needs to include PixiJS. The PixiJS library focuses on 2D
graphics, put since version 5 the library exposes a set of lower level API:s
which is what Pixi3D takes advantage of. PixiJS is a established library and
contains a lot of functionality for making a coders life easier. Such features
could be loading assets, having a hierarchy of objects or handling user
interaction. PixiJS works on both desktop and mobile and Pixi3D should work on
all platforms supported by PixiJS (v5.2+).
Container and transform
A container is what the name suggests - a container for other objects. A
container can be used for creating an object hierarchy with parent-child
relations. Container objects has a transform property which is used to be able
to set the position, rotation and scale of that object. When creating a
hierarchy of objects, the transform of a child object is always relative to
it's parent transform. For example, when moving the parent all of it's children
will move as well (same also for rotation and scaling).
API: Container3D, Transform3D.
Note about Pixi3D and PixiJS transform, 3D/2D.
Mesh and model
A mesh contains the geometry of an object and has a material which is used to
render the mesh geometry. A model is a container for mesh objects and can also
include animations for those meshes. A mesh can be created with geometry which
has been generated by code, or it can be loaded from a glTF file as a model.
glTF™ (GL Transmission Format) is a royalty-free specification for the
efficient transmission and loading of 3D scenes and models by applications.
glTF minimizes both the size of 3D assets, and the runtime processing needed
to unpack and use those assets. glTF defines an extensible, common publishing
format for 3D content tools and services that streamlines authoring workflows
and enables interoperable use of content across the industry.
Read more about glTF at https://www.khronos.org/gltf/
API: Mesh3D, Model3D.
Material and shader
A material is what defines and renders the geometry of a mesh. Which material
to use depends on the requirements of how the object should be displayed. By
using customized materials, it is possible to create any desired graphical
effect. A material uses a shader to describe the displayed output.
Shader programs are written using OpenGL Shading Language (GLSL) which runs on
the GPU. The shader interprets the data of the mesh geometry and describes how
that data should be rendered on screen. There are two types of shaders in
Pixi3D, vertex shaders and fragment shaders. Vertex shaders are used to control
where vertices end up on the screen and pixel shaders are used to set the color
of a pixel.
API: Material, PhysicallyBasedMaterial.
Custom material
color-shader.vert
attribute vec3 position;
uniform mat4 world;
uniform mat4 viewProjection;
void main() {
gl_Position = viewProjection * world * vec4(position, 1.0);
}
color-shader.frag
uniform vec3 color;
void main() {
gl_FragColor = vec4(color, 1.0);
}
app.js
const { Camera3D, Model3D, Material } = PIXI3D
class ColorMaterial extends Material {
constructor() {
super(["position"])
}
updateUniforms(shader) {
shader.uniforms.world = this.mesh.transform.worldTransform.array
shader.uniforms.viewProjection = Camera3D.main.viewProjection
shader.uniforms.color = [0.8, 0.2, 0.7]
}
createShader() {
return new PIXI.Shader(PIXI.Program.from(
app.loader.resources["color-shader.vert"].source,
app.loader.resources["color-shader.frag"].source
))
}
static create() {
return new ColorMaterial()
}
}
let app = new PIXI.Application({ width: 800, height: 600, antialias: true })
Camera3D.main.aspectRatio = 800 / 600
app.loader.add("cube.gltf")
app.loader.add("color-shader.vert")
app.loader.add("color-shader.frag")
app.loader.load(() => {
let model = app.stage.addChild(
Model3D.from("cube.gltf", { materialFactory: ColorMaterial })
)
})
document.body.appendChild(app.view)
Cubemaps
PIXI3D supports loading cubemaps from file. It expects 6 images which all share the same filename format (example below). {{face}}
will be replaced with the following strings: "posx", "negx", "posy", "negy", "posz" and "negz". It can also contain optional mipmaps.
environment.cubemap
{
"source": "folder/environment_{{face}}_128x128.jpg",
"mipmap": [
"folder/environment_{{face}}_64x64.jpg",
"folder/environment_{{face}}_32x32.jpg",
"folder/environment_{{face}}_16x16.jpg"
]
}
app.js
app.loader.add("environment.cubemap")
app.loader.load(() => {
let texture = app.loader.resources["environment.cubemap"].texture
})
Image based lighting
Cmft (https://github.com/dariomanesku/cmft) is a tool which can generate radiance and irradiance cubemaps to be used as diffuse and specular image based lighting.
Diffuse (irradiance)
% cmft --input environment.hdr --filter irradiance --inputGammaDenominator 2.2 --outputNum 1 --output0 diffuse --output0params tga,bgra8,facelist
% mogrify -format jpg *.tga
Specular (radiance)
% cmft --input environment.hdr --filter radiance --srcFaceSize 256 --dstFaceSize 256 --excludeBase true --glossScale 10 --glossBias 1 --lightingModel phongbrdf --useOpenCL true --inputGammaDenominator 2.2 --outputNum 1 --output0 specular --output0params tga,bgra8,facelist
% mogrify -format jpg *.tga
environment.ibl
{
"diffuse": {
"source": "diffuse_{{face}}.jpg"
},
"specular": {
"source": "specular_{{face}}_0_256x256.jpg",
"mipmap": [
"specular_{{face}}_1_128x128.jpg",
"specular_{{face}}_2_64x64.jpg",
"specular_{{face}}_3_32x32.jpg",
"specular_{{face}}_4_16x16.jpg",
"specular_{{face}}_5_8x8.jpg",
"specular_{{face}}_6_4x4.jpg",
"specular_{{face}}_7_2x2.jpg",
"specular_{{face}}_8_1x1.jpg",
]
}
}
app.js
app.loader.add("environment.ibl")
app.loader.load(() => {
LightingEnvironment.main.ibl = app.loader.resources["environment.ibl"].ibl
})
Development
The following command will start a local webserver in "dist" folder and watch all files for changes.
> npm install
> npm start
Building
The following command will build pixi3d.js in "dist" folder with production settings.
> npm install
> npm run build