I am trying copy the data from two arrays and two variables to a bytebuffer. The bytebuffer will hold this data for a uniform block structure in a fragment shader. I can copy the first one in fine but the second always generates an index out of range error.
I've tried using .asFloatBuffer, tried initializing the buffer to twice the size I needed and tried using a FloatBuffer (I need a ByteBuffer but I thought I'd just try to fix this error then work my way back)
The structure from fragment shader:
layout (binding = 0) uniform BlobSettings {
vec4 InnerColor;
vec4 OuterColor;
float RadiusInner;
float RadiusOuter;
};
This is what I have now for code (a bit of a mess but you get the idea...):
//create a buffer for the data
//blockB.get(0) contains the 'size' of the data structure I need to copy (value is 48)
//FloatBuffer blockBuffer = BufferUtil.newFloatBuffer(blockB.get(0));
ByteBuffer blockBuffer = ByteBuffer.allocateDirect(blockB.get(0) * 4);//.asFloatBuffer();
//the following data will be copied to the buffer
float outerColor[] = {0.1f,0.1f,0.1f,0.1f};
float innerColor[] = {1.0f,1.0f,0.75f,1.0f};
float innerRadius = 0.25f;
float outerRadius = 0.45f;
//copy data to buffer at appropriate offsets
//params contains the offsets (0, 16, 32, 36)
//following 4 lines using a FloatBuffer (maybe convert to ByteBuffer after loading?)
blockBuffer.put(outerColor, params.get(0), outerColor.length);
blockBuffer.put(innerColor, params.get(1), innerColor.length); //idx out of range here...
blockBuffer.put(params.get(2), innerRadius);
blockBuffer.put(params.get(3), outerRadius);
//when using ByteBuffer directly - maybe something like the following?
for (int idx=0;idx<4;idx++){
blockBuffer.putFloat(params.get(0) + idx, outerColor[idx]) //????
}
Can anyone tell me how I can properly get that data into a ByteBuffer?
Based on the way I read the spec for uniform blocks, you will need to be careful how the definition in the shader matches up with the values in the buffer. There are several layout options for uniform blocks. The declaration you have does not specify a layout:
layout (binding = 0) uniform BlobSettings {
vec4 InnerColor;
vec4 OuterColor;
float RadiusInner;
float RadiusOuter;
};
Without a specified layout, the default is shared. This does not guarantee a well defined memory layout for the block, and you will have to query the size/offset of values with glGetActiveUniformBlockiv() and glGetActiveUniformsiv(). In the data you provided, the total size came back as 48, which most likely means that padding was added at the end to make the size a multiple of 16.
The simpler option is that you specify the std140 layout option, which guarantees a specific layout:
layout (std140, binding = 0) uniform BlobSettings {
...
};
This should give you a packed layout with a total size of 40 bytes, and the values in the specified order. The std140 layout is not always fully packed, e.g. a vec3 will use the same space as a vec4. You can read the specs for the details. But for vec4 and scalar values, it is packed in this case.
For filling a buffer with float values, there are a few different options. Using your data as an example (changing the order to match the uniform definition):
float innerColor[] = {1.0f, 1.0f, 0.75f, 1.0f};
float outerColor[] = {0.1f, 0.1f, 0.1f, 0.1f};
float innerRadius = 0.25f;
float outerRadius = 0.45f;
Assuming that you want to pass this to OpenGL with a call like glBufferData().
Put into array, then wrap
If you put all the values into a single float array, you can directly turn it into a buffer with FloatBuffer.wrap():
float bufferData[] = {
1.0f, 1.0f, 0.75f, 1.0f,
0.1f, 0.1f, 0.1f, 0.1f,
0.25f,
0.45f);
glBufferData(..., FloatBuffer.wrap(bufferData));
Allocate and use float buffer
For this, you allocate a FloatBuffer, fill it with the data, then use it. Note that the put() methods advance the buffer position, so you can simply add the values one by one. You do have to rewind the buffer to the start position before using it.
FloatBuffer buf = FloatBuffer.allocate(10);
buf.put(innerColor);
buf.put(outerColor);
buf.put(innerRadius);
buf.put(outerRadius);
buf.rewind();
glBufferData(..., buf);
Allocate byte buffer, and use as float buffer
All of the OpenGL Java bindings I have seen accept either FloatBuffer or Buffer arguments. So using a FloatBuffer as in the approaches above is easiest. But if you use OpenGL bindings that really require a ByteBuffer, or need a direct allocation (which at least on Android is only the case for client side vertex arrays), you can allocate a ByteBuffer first, then use it as FloatBuffer:
ByteBuffer byteBuf = ByteBuffer.allocateDirect(10 * 4);
FloatBuffer floatBuf = byteBuf.asFloatBuffer();
floatBuf.put(innerColor);
floatBuf.put(outerColor);
floatBuf.put(innerRadius);
floatBuf.put(outerRadius);
byteBuf.rewind();
glBufferData(..., byteBuf);
Use byte buffer
This seems somewhat cumbersome, but you could use a ByteBuffer all the way:
ByteBuffer buf = ByteBuffer.allocateDirect(10 * 4);
buf.putFloat(innerColor[0]);
buf.putFloat(innerColor[1]);
buf.putFloat(innerColor[2]);
buf.putFloat(innerColor[3]);
buf.putFloat(outerColor[0]);
buf.putFloat(outerColor[1]);
buf.putFloat(outerColor[2]);
buf.putFloat(outerColor[3]);
buf.putFloat(innerRadius);
buf.putFloat(outerRadius);
buf.rewind();
glBufferData(..., buf);
Related
I have been following the tutorial on LWJGL's website (http://wiki.lwjgl.org/wiki/GLSL_Tutorial:_Communicating_with_Shaders.html) to try to send an array of random floats to my fragment shader, but every value in the shader was 0. I tried copy-pasting the "arrayOfInts" example to try and isolate the problem, changing everything to be ints, but I still only get zeros shader-side. According to the documentation ( https://javadoc.lwjgl.org/org/lwjgl/opengl/GL20.html#glUniform1iv(int,int%5B%5D) ), the function glUniform1iv exists and does what I need, but when I try it Eclipse tells me it doesn't exist in the class GL20, both when using an int[] or an IntBuffer, same for glUniform1fv. The location of the uniform variable is 0, therefore it is correctly loaded. The values stored in the java arrays generated are correct.
Vertex Shader:
#version 400 core
in vec3 position;
out vec2 uvPosition;
void main(void){
gl_Position = vec4(position, 1.0);
uvPosition = (position.xy+vec2(1.0, 1.0))/2;
}
Fragment shader
#version 400 core
in vec2 uvPosition;
out vec4 outColor;
uniform int random[50];
int randomIterator = 0;
float randf(){
return random[randomIterator++];
}
void main(void){
outColor = vec4(random[0]/10, 1.0, 1.0, 1.0);
}
Java code where I load the uniform:
final int RAND_AMOUNT = 50;
IntBuffer buffer = BufferUtils.createIntBuffer(RAND_AMOUNT);
int[] array = new int[RAND_AMOUNT];
for(int i = 0; i < RAND_AMOUNT; i++) {
buffer.put((int)(Math.random()*9.999f));
array[i] = (int)(Math.random()*9.999f);
}
buffer.rewind();
System.out.println(buffer.get(0));
GL20.glUniform1(glGetUniformLocation(programID, "random"), buffer); //In this line, using GL20.glUniform1iv tells me it doesn't exist. Same with
Nothing throws errors and the display is cyan, meaning the Red component is 0. Any help in making the random ints or directly sending random floats would help. Feel free to ask any question.
glUniform* sets a value of a uniform in the default uniform block of the currently installed program. Thus the program has to be installed by glUseProgram before.
int random_loc = GL20.glGetUniformLocation(programID, "random")
GL20.glUseProgram(programID);
GL20.glUniform1(random_loc, buffer);
For rendering a 3D object, four separate vertex buffers are created: for vertices, indices, texture coordinates and normals:
private final int[] VBO = new int[4]; // array for vertex buffer objects
private void createVertexBuffers() {
VBO[0] = 0; VBO[1] = 0; VBO[2] = 0; VBO[3] = 0;
GLES20.glGenBuffers(4, VBO, 0);
bufferVertices.position(0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, VBO[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, VERTEX_STRIDE * NUMBER_VERTICES,
bufferVertices, GLES20.GL_STATIC_DRAW); // VBO for vertex
bufferTextureCoordinates.position(0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, VBO[1]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, TEXTURE_STRIDE * NUMBERS_TEXTURES,
bufferTextureCoordinates, GLES20.GL_STATIC_DRAW); // VBO for texture coordinates
bufferNormals.position(0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, VBO[2]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, VERTEX_STRIDE * NUMBER_NORMALS,
bufferNormals, GLES20.GL_STATIC_DRAW); // VBO for normals
bufferIndices.position(0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, VBO[3]);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, INT_SIZE * NUMBER_INDICES,
bufferIndices, GLES20.GL_STATIC_DRAW); // VBO for indices
}
The 3D-objects themselves are also many, respectively, the number of buffers additionally is increasing. Question: Is it a normal approach to use many separate buffers? In mobile apps? I would be grateful for the answers.
Note: looked at similar questions, but I still have uncertainty.
Interleaved attributes (array of structs) are generally more efficient than completely de-interleaved ones (struct of arrays). The reason for this is that you're less likely to load a whole cache line and then only use one value from it.
However recent mobile implementations still like some level of deinterleaving. For tile-based GPUs it's common to process position computation first, and then only process the rest of the vertex shader if the vertex contributes to a visible triangle. For this you want two packed buffer regions one for all attributes that contribute to position computation, and one for everything else.
As always this comes with caveats. If packing as an array of structs forces a lot of padding elements to correct alignment, that rapidly eats into the benefits.
According to lwjgl javadooc, there is a function :
public static void glGenBuffers(int n, ByteBuffer buffer)
But i dont exactly understand how this works.
Do i create an ByteBuffer :
ByteBuffer buffer = ByteBuffer.allocate("how much???")
glGenBuffers(4, buffer);
And especially do i have to fill the Buffer with glBufferSubData OR
is it better to create 4 buffers and bind and fill them?
My thought was, that it is more efficient when i only create 1 Buffer ,which stores vertices,texturecoords,normals and indices.
glGenBuffers(int n, ByteBuffer buffer) generates n vertex buffer objects (VBOs), which you can use to store your data, and puts them in the specified buffer. This buffer is not actually the one that holds your data, but the ids of the VBOs just generated. You have to manually define the VBO data with glBufferData.
The function is useful if you want to create multiple VBOs with a single call. Each VBO id is an integer, and the buffer has to be large enough to hold n (in this case 4) buffers.
ByteBuffer buffer = BufferUtils.createByteBuffer(4 * Integer.BYTES);
glGenBuffers(4, buffer);
However, to make things easier, you can also use an IntBuffer.
IntBuffer buffer = BufferUtils.createIntBuffer(4);
glGenBuffers(4, buffer);
You can then attach the data to each of the VBOs.
glBindBuffer(GL_ARRAY_BUFFER, buffer.get(2); /*value from 0 to 3*/
glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW);
(If you are using a ByteBuffer, you have to call buffer.getInt(2) instead.)
It is generally more efficient to have all data in a single buffer, but note that in this case you have to use at least two because indices have to be in a GL_ELEMENT_ARRAY_BUFFER. However, the performance difference is very small, so it may be easier to use several different VBOs.
It is preferred to tightly pack all the data in a buffer and upload that with glBufferData instead of calling glBufferSubData for each type of data. Your data layout will then look something like this (P = position, T = texture coordinate, N = normal vector):
PPPTTNNNPPPTTNNNPPPTTN...
Now you can setup the vertex attribute pointers to correctly read the values from this buffer.
int vbo = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, tightlyPackedData, GL_STATIC_DRAW);
int stride = (3 + 2 + 3) * Float.BYTES;
glVertexAttribPointer(positionIndex, 3, GL_FLOAT, false, stride, 0);
glVertexAttribPointer(texCoordIndex, 2, GL_FLOAT, false, stride, 3 * Float.BYTES);
glVertexAttribPointer(normalIndex, 3, GL_FLOAT, false, stride, (3 + 2) * Float.BYTES);
positionIndex, texCoordIndex and normalIndex are the attribute locations of your vertex shader attributes. You can get them with glGetAttribLocation.
stride is the number of bytes between the value of one vertex and the value of the next one. We have 3 positions, 2 texture coordinates and 3 normals, which are all floats, so we multiply them with Float.BYTES.
The last parameter to glVertexAttribPointer is the offset in bytes, i.e. the number of bytes from the start of the buffer to where the first value is located.
i'm getting these strange two results when drawing with immediate mode and vertex arrays. In immediate mode i'm passing the normals by glNormal3f. My shader takes the normal and computes something like a shadow, but nothing seriously.
Immediate Mode:
glBegin(GL_TRIANGLES);
for (Triangle tri : this.triangles) {
Vec3d normal = Vec3d.vectorProduct(
Vec3d.sub(tri.getB(), tri.getA()),
Vec3d.sub(tri.getC(), tri.getA())); //calculating normalvector
glNormal3d(normal.x, normal.y, normal.z);
glColor3d(1.0, 1.0, 1.0);
glVertex3d(tri.getA().x, tri.getA().y, tri.getA().z);
glVertex3d(tri.getB().x, tri.getB().y, tri.getB().z);
glVertex3d(tri.getC().x, tri.getC().y, tri.getC().z);
}
glEnd();
Result:
In the VAO variant, i'm storing the normals in a separate buffer, but calculated the same way:
for(Triangle tri : triangles) {
Vec3d normal = Vec3d.vectorProduct(
Vec3d.sub(tri.getB(), tri.getA()),
Vec3d.sub(tri.getC(), tri.getA()));
normals.put((float) normal.x);
normals.put((float) normal.y);
normals.put((float) normal.z);
}
normals.flip();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(4, normals);
glVertexPointer(3,3*4, vertices);
glDrawArrays(GL_TRIANGLES, 0, (vertices.capacity() / 3));
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
Result 2:
Obviously the normals get somehow mismatched but i can't find my mistake.
And the final question: What is the difference between glNormal3f and glNormalPointer considering the values passed to the shader?
The stride of your normal array is suspect. Why are you passing 4 for the stride to glNormalPointer (...)?
You are telling GL that there are 4-bytes (1 float) worth of space between each of your normals. However, normals is built in your code with 12-bytes between each successive normal. Incidentally, this is what you would call tightly packed and therefore passing 0 for the stride implies the same thing (4-bytes per-component x 3-components).
Your vertex array and normal array should actually have the same stride. 4*3 or simply 0.
[edit] Reformatting into question and answer format following fadden# suggestion.
In ExtractMpegFramesTest_egl14.java.txt, method saveFrame(), there is a loop for reordering RGBA into ARGB for Bitmap png compression (see below quotes from that file), how can this be optimised?
// glReadPixels gives us a ByteBuffer filled with what is essentially big-endian RGBA
// data (i.e. a byte of red, followed by a byte of green...). We need an int[] filled
// with little-endian ARGB data to feed to Bitmap.
//
...
// So... we set the ByteBuffer to little-endian, which should turn the bulk IntBuffer
// get() into a straight memcpy on most Android devices. Our ints will hold ABGR data.
// Swapping B and R gives us ARGB. We need about 30ms for the bulk get(), and another
// 270ms for the color swap.
...
for (int i = 0; i < pixelCount; i++) {
int c = colors[i];
colors[i] = (c & 0xff00ff00) | ((c & 0x00ff0000) >> 16) | ((c & 0x000000ff) << 16);
}
It turns out there's an even faster approach.
Using the suggestion in #elmiguelao's answer, I modified the fragment shader to do the pixel swap. This allowed me to remove the swap code from saveFrame(). Since I no longer needed a temporary copy of the pixels in memory, I eliminated the int[] buffer entirely, switching from this:
int[] colors = [... copy from mPixelBuf, swap ...]
Bitmap.createBitmap(colors, mWidth, mHeight, Bitmap.Config.ARGB_8888);
to this:
Bitmap bmp = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
bmp.copyPixelsFromBuffer(mPixelBuf);
As soon as I did that, all of my colors were wrong.
It turns out that Bitmap#copyPixelsFromBuffer() wants the pixels in RGBA order, not ARGB order. The values coming out of glReadPixels() are already in the right format. So by doing it this way I avoid the swap, avoid an unnecessary copy, and don't need to tweak the fragment shader at all.
[edit] Reformatting into question and answer format following fadden# suggestion
I wanted to suggest that this conversion can happen in the FragmentShader by changing the line
gl_FragColor = texture2D(sTexture, vTextureCoord);
into
gl_FragColor = texture2D(sTexture, vTextureCoord).argb;
which is an efficient shortcut to reorder in GPU the shader's output channels, that works in other ways too: .abgr or even .bggr etc.