I've made a little program with lwjgl that displays a colored triangle using Vulkan, at first I was sending all the required data to my shaders through a single vertex buffer (color + position).
While knowing that it is the best practice to use only one vertex buffer I still wanted to try to send the positions and the colors through 2 different vertex buffer
To add my second vertex buffer I've created it the same way I created the first, I then added a binding and attributed the second attribute to it like this :
VkVertexInputBindingDescription.Buffer bindingDescriptor = VkVertexInputAttributeDescription.calloc(2)
.binding(0)
.stride(2 * 4)
.inputRate(VK_VERTEX_INPUT_RATE_VERTEX);
//I added this
bindingDescriptor.binding(1)
.stride(3 * 4)
.inputRate(VK_VERTEX_INPUT_RATE_VERTEX);
VkVertexInputAttributeDescription.Buffer attributeDescription = VkVertexInputAttributeDescription.calloc(2)
.get(0)
.binding(0)
.location(0)
.format(VK_FORMAT_R32G32_SFLOAT)
.offset(0);
attributeDescription.get(1)
//changed the binding from 0 to 1
.binding(1)
.location(1)
.format(VK_FORMAT_R32G32B32_SFLOAT)
.offset(0);
then in the definition of the render pass I've added my second vertex buffer to the buffer submitted to vkCmdBindVertexBuffers like this :
LongBuffer offsets = memAllocLong(2);
offsets.put(0, 0L);
//added this
offsets.put(1, 0L);
LongBuffer pBuffers = memAllocLong(2);
pBuffers.put(0, verticesBuf);
//added this
pBuffers.put(1, colorBuf);
vkCmdBindVertexBuffers(renderCommandBuffers[i], 0, pBuffers, offsets);
The problem is that when I add my second vertex buffer nothing is displayed anymore, the only thing left is my background color
Am I missing something ? Is there something special to do to use multiple vertex buffers ?
Related
I am a little bit new to OpenGL. I am trying to draw 3D dynamic trail for aircraft using Java OpenGL and WorldWind Java I can draw it by using glDrawArrays. Since the trail of the aircraft increases in every frame(25fps) I put new vertice values to verticeBuffer. I also use rightFloatBuffer and leftFloatBuffer to draw GL_LINE_STRIP to the both sides of the trail as you may see in the attached firstpicture. Since the trail gets longer and longer as the aircraft flies I thought that I need to create a large FloatBuffer for the triangles (verticeBuffer) and 2 large FloatBuffers for the left and right lines.
My first question: What is the most efficient way to draw to many triangles? Based on my code I think after 5 hours of flight the FloatBuffers will be full. If I try to update values with for loop in each frame and if I have, say 50-75 aircraft at the same time, this will reduce the performance. And because of that, I update one triangle at each frame.
Second question: I want to draw a trail like in the second picture. As you see trail gets more transparent as it gets closer to aircraft. And when the aircraft turns color the bottom side of the trail seems different. How can I do it?
Third question: I use gl.DepthMask(false) and draw line_strip and gl.DepthMask(true) to draw smooth lines without a gap between the lines. But this time aircraft trail which is added to the scene first always seems on the top no matter if it is under another trail. What can I do to overcome this? Or what can I do to draw smooth lines without gaps considering the amount of the vertices?
My code to draw the trail is below:
private final FloatBuffer verticeBuffer = GLBuffers.newDirectFloatBuffer(3000000);
private final FloatBuffer rightFloatBuffer = GLBuffers.newDirectFloatBuffer(1500000);
private final FloatBuffer leftFloatBuffer = GLBuffers.newDirectFloatBuffer(1500000);
protected void drawTrail() {
gl.glPushAttrib(GL2.GL_CURRENT_BIT | GL2.GL_COLOR_BUFFER_BIT | GL2.GL_LINE_BIT | GL2.GL_ENABLE_BIT
| GL2.GL_DEPTH_BUFFER_BIT);
try {
gl.glEnable(GL.GL_BLEND);
gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);
gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
doDrawTrail(dc);
gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
gl.glDisable(GL.GL_BLEND);
} finally {
gl.glPopAttrib();
}
}
protected void doDrawTrail() {
updateTrailVertices();
float[] colors = new float[]{trailColor.getRed() / 255.f, trailColor.getGreen() / 255.f, trailColor.getBlue() / 255.f};
gl.glColor4f(colors[0], colors[1], colors[2], 0.6f);
gl.glEnable(GL2.GL_LINE_SMOOTH);
gl.glHint(GL2.GL_LINE_SMOOTH_HINT, GL2.GL_NICEST);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, verticeBuffer.rewind());
gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, verticeBuffer.limit() / 3);
gl.glColor3f(colors[0], colors[1], colors[2]);
gl.glLineWidth(3f);
//To draw smooth lines
gl.glDepthMask(false);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, rightFloatBuffer.rewind());
gl.glDrawArrays(GL.GL_LINE_STRIP, 0, rightFloatBuffer.limit() / 3);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, leftFloatBuffer.rewind());
gl.glDrawArrays(GL.GL_LINE_STRIP, 0, leftFloatBuffer.limit() / 3);
gl.glDepthMask(true);
}
protected void updateTrailVertices() {
// In each frame when the aircraft position changes this function updates the last vertices
if (positionChange) {
positionChange = false;
//I need to set the position and the limit of the buffers to draw only updated parts
verticeBuffer.position(lastIndex * 2);
rightFloatBuffer.position(lastIndex);
leftFloatBuffer.position(lastIndex);
verticeBuffer.limit((lastIndex * 2) + 6);
rightFloatBuffer.limit(lastIndex + 3);
leftFloatBuffer.limit(lastIndex + 3);
List<Vec4> pointEdges = computeVec4(this.currentPosition, this.currentHeading, this.currentRoll, this.span);
verticeBuffer.put((float) pointEdges.get(0).x).put((float) pointEdges.get(0).y).put((float) pointEdges.get(0).z);
verticeBuffer.put((float) pointEdges.get(1).x).put((float) pointEdges.get(1).y).put((float) pointEdges.get(1).z);
rightFloatBuffer.put((float) pointEdges.get(0).x).put((float) pointEdges.get(0).y).put((float) pointEdges.get(0).z);
leftFloatBuffer.put((float) pointEdges.get(1).x).put((float) pointEdges.get(1).y).put((float) pointEdges.get(1).z);
lastIndex = rightFloatBuffer.position();
}
}
If you can use geometry shaders, the most efficient way to display the flight track is to have one vertexbuffer and render it as a line strip. The vertexbuffer contains the earlier locations and a normal vector (plane up direction). With these two values the the geometry shader you can transform it into quads. These quads should contain texture coordinates, which can be used in the fragment shader to display the borders.
You need only one draw call and reduce the data stored on the gpu to the absolute minimum.
The fading of the flight track can be done by using a uniform with the plane coordinates. One of your shades can calculate the distance to the plane and with that a alpha value for the pixel.
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.
I've been trying to render an 8x8 texture. I've used code from 2 tutorials, but the texture doesn't render correctly. For now I have this initialization code:
int shaderProgram,fragmentShader,vertexShader,texture,elementBuffer,vertexBuffer, vertexArray;
public Texture2D(String texturePath_, String vertexShader_,String fragmentShader_)
{
vertexArray=GL30.glGenVertexArrays();
GL30.glBindVertexArray(vertexArray);
String[] vertexshader=Utilities.loadShaderFile(vertexShader_,getClass());
String[] fragmentshader=Utilities.loadShaderFile(fragmentShader_,getClass());
if(vertexshader==null)
throw new NullPointerException("The vertex shader is null");
if(fragmentshader==null)
throw new NullPointerException("The fragment shader is null");
vertexShader=GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
GL20.glShaderSource(vertexShader,vertexshader);
GL20.glCompileShader(vertexShader);
Utilities.showShaderCompileLog(vertexShader);
fragmentShader=GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
GL20.glShaderSource(fragmentShader,fragmentshader);
GL20.glCompileShader(fragmentShader);
Utilities.showShaderCompileLog(fragmentShader);
shaderProgram= GL20.glCreateProgram();
GL20.glAttachShader(shaderProgram,fragmentShader);
GL20.glAttachShader(shaderProgram,vertexShader);
GL30.glBindFragDataLocation(shaderProgram,0,"fragcolor");
GL20.glLinkProgram(shaderProgram);
GL20.glUseProgram(shaderProgram);
texture= GL11.glGenTextures();
GL11.glBindTexture(GL11.GL_TEXTURE_2D,texture);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_BORDER);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_WRAP_T,GL13.GL_CLAMP_TO_BORDER);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_MIN_FILTER,GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_MAG_FILTER,GL11.GL_LINEAR);
ByteBuffer image;
FloatBuffer verteces;
IntBuffer imagewidth,imageheight, positions,imagechannels;
try(MemoryStack memoryStack=MemoryStack.stackPush())
{
imageheight=memoryStack.mallocInt(1);
imagewidth=memoryStack.mallocInt(1);
positions=memoryStack.mallocInt(6);
imagechannels=memoryStack.mallocInt(1);
image= STBImage.stbi_load(texturePath_,imagewidth,imageheight,imagechannels,0);
if(image==null) throw new NullPointerException("Failed to load image");
verteces=memoryStack.mallocFloat(28);
}
positions.put(0).put(1).put(2).put(2).put(3).put(0).flip();
int width=imagewidth.get();
int height=imageheight.get();
GL11.glTexImage2D(GL11.GL_TEXTURE_2D,0,GL11.GL_RGBA,width,height,0,GL11.GL_RGBA,GL11.GL_UNSIGNED_BYTE,image);
elementBuffer=GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER,elementBuffer);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER,positions,GL15.GL_STATIC_DRAW);
float x1=0f, x2=1f;
float y1=1f,y2=-1f;
verteces.put(x1).put(y1).put(1).put(1).put(1).put(0).put(0);
verteces.put(x1).put(y2).put(1).put(1).put(1).put(1).put(0);
verteces.put(x2).put(y2).put(1).put(1).put(1).put(1).put(1);
verteces.put(x2).put(y1).put(1).put(1).put(1).put(0).put(1).flip();
vertexBuffer=GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,vertexBuffer);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER,verteces,GL15.GL_STATIC_DRAW);
int uniform=GL20.glGetUniformLocation(shaderProgram,"texture_image");
GL20.glUniform1i(uniform,0);
int position=GL20.glGetAttribLocation(shaderProgram,"position");
GL20.glEnableVertexAttribArray(position);
GL20.glVertexAttribPointer(position,2,GL11.GL_FLOAT,false,0,0);
int color=GL20.glGetAttribLocation(shaderProgram,"color");
GL20.glEnableVertexAttribArray(color);
GL20.glVertexAttribPointer(color,3,GL11.GL_FLOAT,false,7*Float.BYTES, 2 * Float.BYTES);
int textureST=GL20.glGetAttribLocation(shaderProgram,"textureCoord");
GL20.glEnableVertexAttribArray(textureST);
GL20.glVertexAttribPointer(textureST,3,GL11.GL_FLOAT,false,7*Float.BYTES, 5 * Float.BYTES);
Utilities.showErrors(1);
}
The result is:
But I'd like the texture to occupy all area. The shaders compile fine, and there are no GL errors.
If I change values to the ones from the tutorial:
verteces.put(-1f).put(1f).put(1).put(1).put(1).put(0).put(0);
verteces.put(1f).put(1f).put(1).put(1).put(1).put(1).put(0);
verteces.put(1f).put(-1f).put(1).put(1).put(1).put(1).put(1);
verteces.put(-1f).put(-1f).put(1).put(1).put(1).put(0).put(1).flip();
I get:
The tutorials: https://open.gl/textures and https://github.com/SilverTiger/lwjgl3-tutorial/wiki/Textures
I'm using profile 3.0 with shaders version 300 ES. The texture's format is PNG.
The vertex attribute layout:
GL20.glVertexAttribPointer(position,2,GL11.GL_FLOAT,false,0,0);
GL20.glVertexAttribPointer(color,3,GL11.GL_FLOAT,false,7*Float.BYTES, 2 * Float.BYTES);
GL20.glVertexAttribPointer(textureST,3,GL11.GL_FLOAT,false,7*Float.BYTES, 5 * Float.BYTES);
doesn't look correct. There are multiple problems with it:
The texture coordinates try to read 3 floats from the array. In combination with the stride, your last vertex will read outside the VBO. Most probably texture coordinates should only read 2 floats.
The total number of floats used (2+3+3=8) does not fit to the data where only 7 floats per vertex are given. This is solved when texture coordinates read only two floats.
The stride of the positions look wrong. 0 means that all positions are tightly packed. Basically, the positions use the first 8 floats in the VBO. If you look at them: {-1, 1, 1, 1, 1, 0, 0, 1}, then this is exactly the geometry you see. It was just luck that it worked in first place. Solution: Change position layout to:
GL20.glVertexAttribPointer(position,2,GL11.GL_FLOAT,false,7*Float.BYTES,0);
I made me a pixel based sprite class for a simple game in java and swing, and I don't want to let some sprites go through other sprites. So I wrote two loops that are supposed to add the pixels of every other sprite to the "material" array of the level. Material should not be passable. With the level it does work. There the sprite can't pass through its material. But with other sprites it doesn't. It can go through them. And that's the bug I actually want to fix. It seems that the sprites' pixel arrays aren't appended.
Any help is greatly appreciated !
Code :
int applied_pixels=lvl.material.length;
Sprite[] others=new Sprite[] {other sprites};
/*EDIT : others[i].frameborders[others[i].frame].all is the point array of the sprites' pixels
others[i].frame is the frame of the sprite object, because they contain an array of BufferedImages. Frame is the one that should be taken*/
Level lvl=the level; //Containing a couple of point arrays of pixels of some types, for example containing the material array of pixels
int apply_pixels=0; //How many pixels are needed ?
for (int i=0; i < others.length; i++) {
if (others[i] != null) { //Isn't the sprite null
apply_pixels=apply_pixels+others[i].frameborders[others[i].frame].all.length; //How many points does it has to add ?
}
}
level=lvl.clone(); //Copy level to be able to later append points to the material array
level.material=new Point[apply_pixels];
System.arraycopy(lvl.material,0,level.material,0,lvl.material.length); //Copy old material array points
int appending_position=0;
appending_position=lvl.material.length; //Which destination position to append the points at ?
for (int i=0; i < others.length; i++) {
if (others[i] != null) { //Isn't the sprite null
System.arraycopy(others[i].frameborders[others[i].frame].all,0,level.material,appending_position,others[i].frameborders[others[i].frame].all.length); //Copy the points from the sprite to the material array
appending_position=appending_position+others[i].frameborders[others[i].frame].all.length; //Position to append at is now plus the length of appended points
}
}
I see two possible problems with your code as posted.
The first is that level.material=new Point[apply_pixels]; only allocates elements for the new pixels. It should probably read level.material=new Point[lvl.material.length + apply_pixels];. Alternatively, you can initialize apply_pixels as int apply_pixel = lvl.material.length instead of to zero.
The second problem is that you never show us how lvl replaces the original level. Presumably the code you posted is part of a method somewhere and level is an input that is passed in, but is accessed through a field by other parts of the program. Unless the modified lvl is correctly returned and replaces the original, the code here will have no effect. However, this is only speculation because OP refuses to post the relevant code.
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.