I am trying to break into the shader world but am running into a problem.
Everything I draw is relative to the screen.
In my LWJGL I am translating and rotating the world based upon camera location (which will move), then object location (which may move) before drawing. This works fine for Immediate mode or Display Lists, even for VBOs without shaders. However if I pass this info to the shader, I lose the eye location and everything draws in absolute coords relative to the screen.
I saw a few posts about passing the matrix to the shader directly, however I couldn’t get it to work. I couldn’t get OpenGL to pass anything to the shaders (couldn’t get the command to even compile) and of course nothing was received by the shader except what was in the VBOs.
How do I draw vertexes relative to the camera position, not the screen?
As you note, with shaders you typically build the transform matrix or matrices yourself and assign them to shader uniforms. If you tried that and it didn’t work, perhaps you could post the relevant code along with whatever errors you were getting.
That sounds like you know exactly what I am trying to accomplish. I have tried dozens of combinations of arguments to get even a single Uniform command to work. Not one combination I can come up with will get rid of that little red squiggly line in netbeans. And not one place online was I able to find an example patch of code to show me what goes where in the arguments list.
A simple demo of that 1 line of code on the Java side, (I think I get the receiving side part, but not 100%), and how to use the value in the shader code itself would work wonders. Nothing I have is even remotely functional.
‘matrixBuffer’ is of type FloatBuffer, and ‘transformLocation’ is an int acquired using getUniformLocation(). Note that the exact function name and arguments may differ depending on what OpenGL wrapper you’re using (e.g. LWJGL 2.x, LWJGL 3, etc.).
The uniform is declared in the vertex shader like this:
If that doesn’t help, maybe you could post what wrapper you’re using (including version, e.g. LWJGL 2.x vs. 3), the code that’s generating the compiler error, and the compiler error itself.
How do I convert the existing MODELVIEW matrix into a ‘matrixBuffer’ FloatBuffer?
Doesn’t the name (either part of the transformLocation’s retrieval with the getUniformLocation() command, OR the name matrixBuffer) have to match what the shader receives (i.e. ‘transform’)?
If by ‘MODELVIEW’ you mean the fixed-function modelview matrix, then the short answer is that you don’t use the fixed-function matrix stack. Instead, you build the matrix yourself and store the results in a float buffer.
The slightly longer answer is that I think there was a time when some aspects of the fixed-function pipeline were in fact exposed to the programmable pipeline. I don’t know the details (or whether that’s even accurate), as I never had need to investigate it, but in any case, that’s not how you would do it in modern OpenGL. The modern approach is as described above, where you build the matrix yourself and then assign it to a uniform.
[quote]2) Doesn’t the name (either part of the transformLocation’s retrieval with the getUniformLocation() command, OR the name matrixBuffer) have to match what the shader receives (i.e. ‘transform’)?
[/quote]
Yes, glGetUniformLocation() takes a ‘name’ argument, which should match the name of the uniform in the shader (‘transform’ in my example).
Does that mean I need to retrieve the:
x, y, z, pitch, yaw, roll of the camera
x, y, z, pitch, yaw, roll of the object
and figure out some sort of calculation to figure out a sort of offset for where the vertex should be drawn?
When there is a matrix already calculated right there that has it all in it? And we can’t access?
Wait! the matrix is deprecated? Oh, this is SO confusing!
What I need is a good example. Some code that uses all the modern techniques and has all the features. If I could see it, and replicate it, I can tweak it to my needs.
Does anyone know a good link? I have been searching for Months, hundreds of pages, tutorials, forum posts. None address the whole package. I am stuck trying to fit dozens of pieces together, only to find out that this piece is deprecated, that piece is old, this one is new, and they don’t play well together.
Exception in thread "main" org.lwjgl.opengl.OpenGLException: Invalid value (1281)
at org.lwjgl.opengl.Util.checkGLError(Util.java:59)
at org.lwjgl.opengl.GL20.glGetUniformLocation(GL20.java:664)
at Methods.M03_VBO.drawRoutine(M03_VBO.java:321)
at Methods.M00_Pattern.draw(M00_Pattern.java:85)
at Application.Application.draw(Application.java:265)
at Application.Application.runRenderLoop(Application.java:91)
at Application.Application.startHere(Application.java:71)
at Application.Application.main(Application.java:67)
Java Result: 1
For now, I am passing in null as the matrix as I don’t know how to generate/get it yet. I assume that is the cause.
[quote]When there is a matrix already calculated right there that has it all in it? And we can’t access?
[/quote]
Full disclosure: you can in fact access the fixed-function matrix stack using glGetFloatv() (assuming your wrapper exposes it). However, you shouldn’t because queries can be slow, and because the matrix stack is deprecated anyway.
So in answer to your question, yes, you have to build the matrix yourself. Fortunately, there are some libraries that will do at least some of this work for you (there was some discussion here recently about a library called JOML - you might look into that).
[quote]Does anyone know a good link? I have been searching for Months, hundreds of pages, tutorials, forum posts. None address the whole package. I am stuck trying to fit dozens of pieces together, only to find out that this piece is deprecated, that piece is old, this one is new, and they don’t play well together.
[/quote]
I sympathize. Unfortunately, the evolution of OpenGL has left the internet littered with references and tutorials that can be anywhere from completely outdated to only partially relevant. I think the best I can suggest is to search explicitly for ‘modern opengl tutorial’, as there are some tutorials out there that make clear they intend to describe modern practices. You might also try searching for specific versions, e.g. ‘opengl 4 tutorial’.
Meanwhile, here’s a very brief summary of the current state of things (to the best of my understanding). The fixed-function pipeline is either deprecated or entirely gone, depending on OpenGL version and configuration. This includes client-side vertex arrays and everything related to the matrix stack (glLoadIdentity(), glTranslatef(), etc.). The current fundamentals are VAOs (required in newer versions of OpenGL), VBOs, and shader programs. What the matrix stack used to do for you, you now have to do yourself. Specifically, you create transform matrices on the client side using your own (or existing) code, and pass them to shaders, where you are responsible for applying them as well. It’s more complicated than it used to be, but also more flexible.
[quote]For now, I am passing in null as the matrix as I don’t know how to generate/get it yet. I assume that is the cause.
[/quote]
Probably.
Thanks Jesse, for mentioning JOML.
Yes, JOML was built for exactly this very reason: The OpenGL matrix functions got deprecated and now people are rightly wondering how they would replace it and do the math on their own.
On the C/C++/native side we have the fantastic GLM library. But there is no real matching standalone pendant on the JVM side. And so, JOML was born.
Among the common matrix functions, it contains convenient stuff such as:
building a reflection matrix
building a normal matrix
computing the frustum planes
computing the frustum corners and rays through the frustum (good for software raytracing/raycasting)
testing whether a point, sphere or axis-aligned box is in the frustum defined by a 4x4 matrix (when this matrix is eventually used to convert to OpenGL clip space)
and some more. While doing so JOML tries to be most CPU- and memory- efficient (for example not building the matrix inverse and unprojecting NDC).
JOML also has an emulation of the OpenGL matrix stack (with pushMatrix, popMatrix, etc.), if one likes to keep using it. That can be useful for traversing a scene graph, for example.
Every algorithm JOML uses is documented/referenced in the JavaDocs mostly as a hyperlink to a paper/website, in case you want to build your own library, which I would really encourage you to do instead of using JOML right away, as building the math functions on your own will teach a lot.
Even though JOML now contains everything you possibly need, it is still quite small (few classes) and everything is centered around a single 4x4 matrix class.
All further questions you might have are likely answered by JOML’s README (can be viewed on its GitHub site).
please use fixed-pipeline as long as you dont understand it fully. jumping into “modern” GL is just pain in the arse. fixed-pipeline is simple and - yes, you can mix it with the “new way”. in the long run will replace it with the “new way”. there is no problem with mixing it. tho’ i’m talking about vertex processing, dont even try fixed-pipeline lightning, fogging or shadow mapping.
dont use forward compatibility when you init GL.
in your shaders just use [icode]compatibility[/icode], for instance [icode]#version 400 compatibility[/icode] or [icode]#version 150 compatibility[/icode], you get the point.
actually before that, forget about “camera” and “position”. it has not much to do with the matrix operation. “camera” is an abstract concept which may look like a couple vec3 and floats :
position
direction (normalized)
orientation : up, side (normalized)
field of view
… focusing plane
… distortion
… exposure
… etc.
if you want to pass your “camera” to your shaders : write a camera class and use uniforms like :
but that has nothing to do with the way the vertex shader would process the vertices. i use that “concept” to create visual effects.
to answer some of your questions :
first, create a camera like described above. at this point decide - is it 2D or 3D ? i’ll stick to 2D here.
convert your abstract camera it to a modelview and projection matrix. use JOML or any math lib that suits you for that.
here’s a example how to do it with plain numbers :
a matrix is just a array of 16 floats. 4x4. maybe a float[4][4].
is it row-major or column-major ? openGL (fixed-pipeline) uses column-major matrices. most math libs use row-major format. (good math libs support both ;)) you should not worry about that tho’, it’s very simple to convert from one layout to the other. i use double-row-major here :
// for a row-major matrix :
public static void setOrtho(
final double[][] matrix,
final double left,
final double right,
final double top,
final double bottom,
final double near,
final double far
)
{
// row 0
matrix[0][0] = 2.0/( right - left );
matrix[0][1] = 0.0;
matrix[0][2] = 0.0;
matrix[0][3] = -( left + right )/( right - left );
// row 1
matrix[1][0] = 0.0;
matrix[1][1] = 2.0/( top - bottom );
matrix[1][2] = 0.0;
matrix[1][3] = -( top + bottom )/( top - bottom );
// row 2
matrix[2][0] = 0.0;
matrix[2][1] = 0.0;
matrix[2][2] = -2.0/( far - near );
matrix[2][3] = ( far + near )/( far - near );
// row 3
matrix[3][0] = 0.0;
matrix[3][1] = 0.0;
matrix[3][2] = 0.0;
matrix[3][3] = 1.0;
}
feed in the right numbers :
final double[] projection_matrix = new double[4][4];
setOrtho( projection_matrix, viewport_x, viewport_width, viewport_height, viewport_y, -1.0, 1.0 );
// for a "fullscreen projection" (world coords (0.0,0.0) at the bottom left) you can use :
setOrtho( projection_matrix, 0.0, screen_width, screen_height, 0.0, -1.0, 1.0 );
// near and far are -1.0 and 1.0 cos' there is
// no "depth" in 2D so the clipping planes are not interesting yet
to implement the “concept” of a camera in 2D you can play around with the left,right,top,bottom values. - or play around with the modelview matrix. paning is just translating, zooming is just scaling and tilting is just rotate.
// world (0.0,0.0) at the screen center :
setOrtho( projection_matrix, -screen_width * 0.5, screen_width * 0.5, screen_height * 0.5, -screen_height * 0.5, -1.0, 1.0 );
// zoom
double zoom = 2.0; // zoom out
setOrtho( projection_matrix, -screen_width * 0.5 * zoom, screen_width * 0.5 * zoom, screen_height * 0.5 * zoom, -screen_height * 0.5 * zoom, -1.0, 1.0 );
// .. and so on.
now to get GL to use that we need to copy it to a [icode]FloatBuffer[/icode] and use [icode]glLoadMatrix()[/icode]
final FloatBuffer projection = ByteBuffer.allocateDirect(16*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
final FloatBuffer modelview = ByteBuffer.allocateDirect(16*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
/** input row-major, output column major */
public static FloatBuffer copyTransposed(final double[][] matrix, final FloatBuffer buffer)
{
buffer.clear();
buffer.put((float)matrix[0][0]);
buffer.put((float)matrix[1][0]);
buffer.put((float)matrix[2][0]);
buffer.put((float)matrix[3][0]);
buffer.put((float)matrix[0][1]);
buffer.put((float)matrix[1][1]);
buffer.put((float)matrix[2][1]);
buffer.put((float)matrix[3][1]);
buffer.put((float)matrix[0][2]);
buffer.put((float)matrix[1][2]);
buffer.put((float)matrix[2][2]);
buffer.put((float)matrix[3][2]);
buffer.put((float)matrix[0][3]);
buffer.put((float)matrix[1][3]);
buffer.put((float)matrix[2][3]);
buffer.put((float)matrix[3][3]);
buffer.flip();
return buffer;
}
[..]
// somewhere at the beginning of the game/draw loop, before you draw anything :
public void render()
{
// setup matrix
copyTransposed( projection_matrix, projection );
copyTransposed( modelview_matrix, modelview ); // identity matrix in that example
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadMatrix(projection);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadMatrix(modelview);
// important to keep GL_MODELVIEW the last active matrix
// so glTranslate glScale and glRotate will work as usual
// next setup the glViewport :
GL11.glViewport( 0, 0, screen_width, screen_height );
[...]
// draw your stuff
}
thats basically it :
setup two matrices
copy to GL
use fixed function glLoadMatrix()
your vertex shaders do not need to be changed (yet) :
now from here to 3D or the “new way” is not far. but still, get a simple thing to work first.
for 3D you just need to setup the matrices in a different way. and for the “new way” you need to get used to matrix uniforms and probably UBO’s. there are many ways to implement it but eventually it’s always the same :
have GL source its matrices data from some “buffer” :
fairly compliated, lots of this to do wrong and you only see a correct result if all things in the chain work. not sure if you really want to try that yet.
to me it looks totally fine to keep the vertex processing using the fixed pipeline (gl_ModelViewMatrix) and use ubo/tbo/ssbo’s for things that would be really slow when implemented in fixed. we’re “just” moving two matrices around, 32 floats. any method to do that will be fast enough.
To try to understand this better, I am working through the awesome tutorials on the LWJGL 3 wiki. Very nice explanations. So far so good. Not too hard to understand yet, but I’m only on the 3rd one.