JOML
A Java math library for OpenGL rendering calculations
Install / Use
/learn @JOML-CI/JOMLREADME
JOML – Java OpenGL Math Library
A Java math library for OpenGL rendering calculations | use it on: Desktop / Android / GWT
Design goals
The goal of JOML [ʤˈɒml̩] is to provide easy-to-use, feature-rich and efficient linear algebra operations, needed by any 3D application. At the same time, JOML tries to pose the lowest possible requirements to an execution environment by being compatible with Java 1.4 and not making use of JNI.
If you like to know more about JOML's design, see the corresponding Wiki page.
Vector arithmetic
All operations in JOML are designed to modify the object on which the operation is invoked. This helps in completely eliminating any object allocations, which the client could otherwise not control and which impact the GC performance resulting in small hickups. The client is responsible to allocate the needed working objects.
Vector3f v = new Vector3f(0.0f, 1.0f, 0.0f);
Vector3f a = new Vector3f(1.0f, 0.0f, 0.0f);
// v = v + a
v.add(a);
// a = a x v
a.cross(v);
// a = a/|a|
a.normalize();
Matrix API
Using JOML you can build matrices out of basic transformations, such as scale, translate and rotate, using a fluent interface style. All such operations directly modify the matrix instance on which they are invoked. The following example builds a transformation matrix which effectively first scales all axes by 0.5 and then translates x by 2.0:
Vector3f v = ...;
new Matrix4f().translate(2.0f, 0.0f, 0.0f)
.scale(0.5f)
.transformPosition(v);
// v is now transformed by the specified transformation
Common transformation idioms, such as rotating about a given axis using a specific rotation center, can be expressed in a simple way. The following example rotates the point (0, 4, 4) about the x-axis and uses (0, 3, 4) as the rotation center:
Vector3f center = new Vector3f(0.0f, 3.0f, 4.0f);
Vector3f pointToRotate = new Vector3f(0.0f, 4.0f, 4.0f);
new Matrix4f().translate(center)
.rotate((float) Math.toRadians(90.0f), 1.0f, 0.0f, 0.0f)
.translate(center.negate())
.transformPosition(pointToRotate);
The vector pointToRotate will now represent (0, 3, 5).
Post-multiplication
All transformation operations in the matrix and quaternion classes act in the same way as OpenGL and GLU by post-multiplying the operation's result to the object on which they are invoked. This allows to chain multiple transformations in the same way as with OpenGL's legacy matrix stack operations, and allows to decompose the resulting effective matrix as a decomposition of multiple matrix multiplications.
One such common decomposition are the projection and modelview matrices, written as: P * MV. The modelview matrix of course can be further decomposed into individual matrix multiplications, as far as this seems necessary.
When invoking transformation methods in JOML's matrix classes, a convenient way now is to think of Java's dot operator as a matrix multiplication. If multiple matrix operations are chained after one another, as shown in the above example, each individual operation/method creates its matrix which is then post-multiplied to the matrices built before.
In addition to the post-multiplying methods, there are still ways to set a matrix or quaternion to a given transformation regardless of what that matrix or quaternion was before:
Matrix4f m = new Matrix4f();
Vector3f point = new Vector3f(1.0f, 2.0f, 3.0f);
Vector3f offset = new Vector3f(1.0f, 0.0f, 0.0f);
...
m.translation(offset).transformPosition(point);
In the above example, the matrix m is being set to a translation, instead of applying the translation to it. These methods are useful when the same matrix is being used in a sequence of consecutive operations or repeatedly in a loop without having to set it to the identity each time.
Building a camera transformation
In the same way that you can concatenate multiple simple affine transformations, you can use the methods perspective(), frustum() and ortho() to specify a perspective or orthographic projection and lookAt() to create an orthonormal transformation that mimics a camera looking at a given point. Those methods resemble the ones known from GLU and act in the same way (i.e. they apply their transformations to an already existing transformation):
Matrix4f m = new Matrix4f()
.perspective((float) Math.toRadians(45.0f), 1.0f, 0.01f, 100.0f)
.lookAt(0.0f, 0.0f, 10.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
// the camera transformation is now in m
The above transformation can then be used as a "view-projection" matrix in a shader.
By convention, the methods perspective() and lookAt() will assume a right-handed coordinate system. This convention was established for OpenGL and first realized in the OpenGL Utility Library (GLU). JOML follows this convention.
In addition, JOML also supports a left-handed coordinate system, as is used by Direct3D's matrix library. To use a left-handed coordinate system, there are the methods perspectiveLH() and lookAtLH(), as well as others, whose names end with LH:
Matrix4f m = new Matrix4f()
.perspectiveLH((float) Math.toRadians(45.0f), 1.0f, 0.01f, 100.0f)
.lookAtLH(0.0f, 0.0f, -10.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
Computation result
Usually, instance methods operate on the object (matrix, vector, quaternion) on which they are invoked by writing the computation result back into that object. Most of the methods however also allow to specify another destination object to write the result into. This is useful if you do not want to overwrite the original object with the computation result. This can be useful for computing the view-projection matrix and its inverse in one go:
Matrix4f viewProj = new Matrix4f();
Matrix4f invViewProj = new Matrix4f();
viewProj.perspective((float) Math.toRadians(45.0f), 1.0f, 0.01f, 100.0f)
.lookAt(0.0f, 1.0f, 3.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f)
.invert(invViewProj);
The invViewProj matrix now contains the inverse of the viewProj matrix, but the latter is still intact.
Using with LWJGL
JOML can be used together with LWJGL to build a transformation matrix and set it as a uniform mat4 in a shader. For this, the Matrix4f class provides a method to transfer a matrix into a Java NIO FloatBuffer, which can then be used by LWJGL when calling into OpenGL:
try (MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer fb = new Matrix4f()
.perspective((float) Math.toRadians(45.0f), 1.0f, 0.01f, 100.0f)
.lookAt(0.0f, 0.0f, 10.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f)
.get(stack.mallocFloat(16));
glUniformMatrix4fv(mat4Location, false, fb);
}
The above example first creates a transformation matrix and then uploads that matrix to a uniform variable of the active shader program using the LWJGL 3 method glUniformMatrix4fv.
Also please read Memory Management in LWJGL 3 on how to properly manage native memory that JOML will store the matrices to.
Instead of using the uniform methods, one or multiple matrices can also be uploaded to an OpenGL buffer object and then sourced from that buffer object from within a shader when used as an uniform buffer object or a shader storage buffer object. The following uploads a matrix to an OpenGL buffer object which can then be used as an uniform buffer object in a shader:
Matrix4f m = ...; // <- the matrix to upload
int ubo = ...; // <- name of a created and already initialized UBO
try (MemoryStack stack = MemoryStack.stackPush()) {
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferSubData(GL_UNIFORM_BUFFER, 0, m.get(stack.mallocFloat(16)));
}
If you prefer not to use shaders but the fixed-function pipeline and want to use JOML to build the transformation matrices, you can do so. Instead of uploading the matrix as a shader uniform you can then use the OpenGL API call glLoadMatrixf() provided by LWJGL to set a JOML matrix as the current matrix in OpenGL's matrix stack:
Matrix4f m = new Matrix4f();
m.setPerspective((float) Math.toRadians(45.0f), 1.0f, 0.01f, 100.0f);
glMatrixMode(GL_PROJECTION);
try (MemoryStack stack = MemoryStack.stackPush()) {
glLoadMatrixf(m.get(stack.mallocFloat(16)));
}
m.setLookAt(0.0f, 0.0f, 10.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
glMatrixMode(GL_MODELVIEW);
try (MemoryStack stack = MemoryStack.stackPush()) {
glLoadMatrixf(m.get(stack.mallocFloat(16)));
}
Using with Vulkan and LWJGL 3
Related Skills
node-connect
342.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
85.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
342.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
342.5kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
