I’m trying to do an ortho projection onto a plane, which represents a map – think “floor plan”. I’m running into trouble because openGL 4 is new to me (I last used 1.1, and the world has changed) and because what I’m trying to do isn’t much like common examples online. My problem is scaling and translating.
The data that describes the map is a series of lines with endpoints are in what I’ll call “dungeon coordinates units”. When I render the image I want to have a fixed rule of “1 unit is 1 pixel”.
My coordinates are all in the first quadrant, with (0,0) representing the lower left of the map. I’d like (0,0) to show up in the lower left of the screen.
Now for the tricky bits. When I render the “floor” in the fragment shader, I’m being handed gl_FragCoord, which is ideal. It’s effectively a pixel location, which means for my purposes it is equivalent to a dungeon coordinate. I can look up all the information I passed to the shader (also in dungeon coordinates) and figure out how to paint (or discard) that pixel. It works, except… it draws (0,0) is in the center of the screen, not the low left.
Worse, There are some things, like lines (“walls”), that I render with skinny triangles in dungeon coordinates in a second pass. They don’t show up where I want them. (In fact I’m pretty sure that the triangles I’m using to tile the floor are also wrong and are only covering the screen by coincidence.)
I really, really need openGL to use a coordinate system that puts 0,0 at the lower left of the image and lets me specify triangle vertices in my units, which happen to map straight to pixels.
This seems like a simple case of scaling and translating. But I’m obviously applying the scale and translate incorrectly.
The vertex code is simple:
#version 430
layout (location = 0) in vec3 Position;
uniform mat4 gWorld;
out vec4 Color; //unused; the fragment shader caslculates all colors
void main()
{
gl_Position = gWorld * vec4(Position, 1.0);
}
Building the 2 triangles for the map floor (a simple rectangle for now) seems simple:
Vector3f Vertices[4];
Vertices[0] = Vector3f(0.f, 0.f, 0.0f);
Vertices[1] = Vector3f(0.f, mapEdges.maxs.y, 0.0f);
Vertices[2] = Vector3f(mapEdges.maxs.x, 0.f, 0.0f);
Vertices[3] = Vector3f(mapEdges.maxs.x, mapEdges.maxs.y, 0.0f);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
unsigned int Indices[] = { 0, 1, 2,
1, 2, 3 };
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
and I use an indexed draw for them.
The C++ code (using glm) sets up the world matrix:
glUseProgram(ShaderProgram); //this selects the shader
gWorldLocation = glGetUniformLocation(ShaderProgram, "gWorld");
assert(gWorldLocation != 0xFFFFFFFF);
...and when rendering…
//try to fix openGL’s desire to think my buffer is -1 to 1 across
float scale = 1/1024.f; //test map is about 1024 units across
glm::mat4 sm = glm::scale(
glm::mat4( 1.0f ),
glm::vec3( scale, scale, 1.0f )
);
glm::mat4 ts = glm::translate(
sm,
glm::vec3( -512.0f, -512.0f, 0.0f ) //shove left and down
);
glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, &ts[0][0]);
Since my test map is about 1024 units across, I’d have thought this would have shoved things into position. But no. The floor (which, remember, is using gl_FragCoord to decide where and what to draw) is painted from screen center and up and right, though it otherwise looks as I’d expect. The walls, which are painted by skinny triangles in dungeon coordinates, are nowhere to be seen, probably scaled off into the aether somewhere.
Basically I’m not convincing openGL that I want x=0 to be the left edge of the image and my scaling is obviously completely wrong. Sadly I had one version that (incorrectly) drew some walls on the screen at one point, but I don’t have that code anymore. Still, it tells me that I’m not completely off in generating the walls, just laying them down.
How do I get openGL to use my units?
You transpose the matrix when you set the matrix uniform. Since the vector is multiplied to the matrix from the right in your shader program, this is wrong. See GLSL Programming/Vector and Matrix Operations
glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, &ts[0][0]);
glUniformMatrix4fv(gWorldLocation, 1, GL_FALSE, &ts[0][0]);
Instead of scaling and translating the vertices you can set an orthographic projection with matrix with glm::ortho:
glm::mat4 projection = glm::ortho(0.0f, 1024.0f, 0.0f, 1024.0f, -1.0f, 1.0f);
glUniformMatrix4fv(gWorldLocation, 1, GL_FALSE, glm::value_ptr(projection));
Related
Hi, I am trying to display two objects using OpenGL viz., 1) a rotating cube with a mix of two textures (a wooden crate pattern and a smiley) in the foreground and 2) rectangular plate with just one texture (dark grey wood) as a background. When I comment out the part of the code governing the display of rectangular plate, the rotating cube displays both the textures (wooden crate and smiley). Otherwise, the cube displays only the wooden crate texture and the dark grey wood texture is also displayed on the rectangular plate, i.e. the smiley texture disappears from the rotating cube. Please find the images 1) http://oi68.tinypic.com/2la4r3c.jpg (with the rectangular plate portion of code commented) and 2) http://i67.tinypic.com/9u9rpf.jpg (without the rectangular plate portion of code commented). The relavant portion of the code is pasted below
// Rotating Cube ===================================================
// Texture of wooden crate
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUniform1i(glGetUniformLocation(ourShader_box.Program, "ourTexture1"), 0);
// Texture of a smiley
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
glUniform1i(glGetUniformLocation(ourShader_box.Program, "ourTexture2"), 1);
// lets use the box shader for the cube
ourShader_box.Use();
// transformations for the rotating cube ---------------------------------
glm::mat4 model_box, model1, model2;
glm::mat4 view_box;
glm::mat4 perspective;
perspective = glm::perspective(45.0f, (GLfloat)width_screen/(GLfloat)height_screen, 0.1f, 200.0f);
model1 = glm::rotate(model_box, (GLfloat)glfwGetTime()*1.0f, glm::vec3(0.5f, 1.0f, 0.0f));
model2 = glm::rotate(model_box, (GLfloat)glfwGetTime()*1.0f, glm::vec3(0.0f, 1.0f, 0.5f));
model_box = model1 * model2;
view_box= glm::translate(view_box, glm::vec3(1.0f, 0.0f, -3.0f));
GLint modelLoc_box = glGetUniformLocation(ourShader_box.Program, "model");
GLint viewLoc_box = glGetUniformLocation(ourShader_box.Program, "view");
GLint projLoc_box = glGetUniformLocation(ourShader_box.Program, "perspective");
glUniformMatrix4fv(modelLoc_box, 1, GL_FALSE, glm::value_ptr(model_box));
glUniformMatrix4fv(viewLoc_box, 1, GL_FALSE, glm::value_ptr(view_box));
glUniformMatrix4fv(projLoc_box, 1, GL_FALSE, glm::value_ptr(perspective));
// --------------------------------------------------------------------
// Draw calls
glBindVertexArray(VAO_box);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
// Rectangular Plate =====================================================
// Background Shader
ourShader_bg.Use();
// Texture of dark grey wood
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, texture_wood);
glUniform1i(glGetUniformLocation(ourShader_bg.Program, "ourTexture3"), 2);
// Transformations -------------------------------------------
glm::mat4 model_bg;
glm::mat4 view_bg;
GLint modelLoc_bg = glGetUniformLocation(ourShader_bg.Program, "model");
GLint viewLoc_bg= glGetUniformLocation(ourShader_bg.Program, "view");
GLint projLoc_bg = glGetUniformLocation(ourShader_bg.Program, "perspective");
glUniformMatrix4fv(modelLoc_bg, 1, GL_FALSE, glm::value_ptr(model_bg));
glUniformMatrix4fv(viewLoc_bg, 1, GL_FALSE, glm::value_ptr(view_bg));
glUniformMatrix4fv(projLoc_bg, 1, GL_FALSE, glm::value_ptr(perspective));
// -----------------------------------------------------------
// Draw calls
glBindVertexArray(VAO_bg);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
// =================================================================
I have a two questions regarding this code.
Why is the smiley disappearing?
Is this how multiple objects are supposed to be rendered? I know OpenGL does not care about objects, it only cares about vertices, but in this case these are separate, disjoint objects. So, should I be organizing them as two VBO's bound to a single VAO or as separate VBO's each bound to two VAO's for each object? Or is the case such that, either way is fine - depends on coder's choice and elegance of code?
You are using the same shader, same matrices and you have the same geometry type for the two objects (triangles), so why set the shader twice ?
Did you try to;
Set shader
Bind buffer #1
Bind texture #1
Draw object #1
Bind buffer #2
Bind texture #2
Draw object #2
I'm very new to openGL and I am doing a mini project where I experiment with the depth buffer. I got to the stage of displaying it to the screen. However I want to draw it as screen coordinates instead of converting to floats. I read somewhere that I need to use a projection matrix. I have looked for ages and tested loads of different options but I can't seem to get it right.
Can anyone point me to a useful resource or explain how I would go about doing this?
EDIT
At the moment my matrix looks like this:
projectionMat = glm::ortho(0.0f, (float)_cols, 0.0f, (float)_rows, 0.0f, (float)_maxDepthVal);
projection = glGetUniformLocation(_program, "Projection");
glUniformMatrix4fv(projection, 1, GL_FALSE, glm::value_ptr(projectionMat));
EDIT 2
With some fiddling I found that cols had to be negative for some strange reason before it would display. I twill now display correctly on the screen but for some reason it his a gap around the sides opposite the origin, why is this? Even a small move in the camera position and target cause all of it to vanish so I don't think that would be the problem.
Pixel Art Representation!!
OOOO!!
OOOO!!
OOOO!!
!!!!!!!!!!!!!!
New code
glm::mat4 Projection = glm::ortho(0.0f, -static_cast<float>(_cols), 0.0f, static_cast<float>(_rows), 0.0f, static_cast<float>(_maxDepthVal));
projection = glGetUniformLocation(_program, "Projection");
glm::mat4 View = glm::lookAt(
glm::vec3(0.0f, 0.0f, -0.1f),
glm::vec3(0.0f , 0.0f, 0.0f), // and looks at the origin
glm::vec3(0,1,0) // Head is up (set to 0,-1,0 to look upside-down)
);
// Model matrix : an identity matrix (model will be at the origin)
glm::mat4 Model = glm::mat4(1.0f);
projectionMat = Projection * View * Model;
glUniformMatrix4fv(projection, 1, GL_FALSE, glm::value_ptr(projectionMat));
EDIT 3
I can translate it using the Model matrix but it has a gap of 5 pixels around it that I can't get rid of, any help on that would be appreciated but thanks for taken an interest.
UPDATE
As per request my draw code
glUseProgram(_program);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
SDL_GL_SwapWindow(_window);
glPointSize(1);
glEnableVertexAttribArray(0);
//Insert matrix here
glVertexAttribPointer(0, 3, GL_UNSIGNED_INT, GL_FALSE, 0, 0);
glDrawArrays(GL_POINTS, 0, _dataCount)
glDisableVertexAttribArray(0);
my vbo:
glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, _dataCount * 4 * sizeof(unsigned int), NULL, GL_STATIC_DRAW);
if(_vbo == 0 || glGetError() != GL_NO_ERROR)
{
_errorMessage = "VBO COULD NOT BE CREATED";
error();
}
checkCudaErrors(cudaGraphicsGLRegisterBuffer(&vbo, _vbo, cudaGraphicsMapFlagsNone));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0);
I'm also having issues with the write as when it converts to floats(for drawing) it loses precision so if I read the value out again it rounds to the nearest factor(0, 256, 512 etc.). Is there another way to do it that stores it as unsigned int. (I realize this is getting slightly off topic but any help would be appreciated)
The issue appeared to be with the cols variable, it needed to be inverted to work otherwise it was off the screen.
I'm getting black triangles along the edges of my skybox. They disappear as I approach them and get bigger when I move further away from them with the camera. Where might my problem be? Thanks
mat4 VP = camera[currentCamera]->GetViewProjectionMatrix();
glm::mat4 S = glm::scale(glm::mat4(1),glm::vec3(150.0, 150.0, 150.0));
glm::mat4 MVP = VP*S;
skybox->Render(glm::value_ptr(MVP));
And the render function is:
void Renderable::Render(const GLfloat* MVP)
{
shader.Use();
glUniformMatrix4fv(shader("MVP"), 1, GL_FALSE, MVP);
SetCustomUniforms();
glBindVertexArray(vaoID);
glDrawElements(primType, totalIndices, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
shader.UnUse();
}
It looks like your skybox geometry is getting clipped by the far plane of the viewing frustum. You should probably increase the distance to the far plane, which is probably configurable on your camera class.
I am trying to understand how to specify texture coordinates for a GL_QUAD_STRIP.
I have managed to get things working for one quad:
float vertices[] = { 0.0f, 0.0f, 1.0f, +1.0f, 0.0f, 0.0f, // bottom line
0.0f, 1.0f, 1.0f, +1.0f, 1.0f, 0.0f}; // top line
unsigned int indices[] = {2, 0, // x = 0
3, 1}; // x = +1
float textureCoordinates[] = { 1.0f, 0.0f,
0.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f};
...
glBindBuffer(GL_ARRAY_BUFFER, 0); // unbinds any buffer object previously bound
glTexCoordPointer(2, GL_FLOAT, 0, textureCoordinates);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibufferid);
glDrawElements(GL_QUAD_STRIP, 4, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
And here is how the result looks (white rectangle with image, rest is drawn on to help explain):
However I do not understand the logic behind the choice of textureCoordinates[] :-(.
The first texture coordinate is (1,0); I would assume that this corresponds to lower right corner?
Also I would assume that when OpenGL reads the first index: 2, it uses this to look up the vertex: (0,1,1): upper left corner. Next it reads the first texture coordinate: (1,0).
But as mentioned above I would assume this to be the lower right corner of the texture !?
However the texture is shown unrotated so this can not be the case!?
Just like the vertices, the texture coordinates are also selected based on the indices used by glDrawElements(). So the first texture coordinate is not (1,0), but (1,1) because the first index is 2. Vertices and coordinates would be according to the following table, where i = index, v = vertex and t = texture coordinate. (I'll only take the x and y coordinates into consideration for the vertices, as the z coordinate doesn't really matter in this case.)
i v t
2 (0,1) (1,1)
0 (0,0) (1,0)
3 (1,1) (0,1)
1 (1,0) (0,0)
If we draw this on a piece of paper, we can see that this means the coordinates make more sense, since the indices matter. (I recommend that you do this! I had to do that to understand what was going on.) Notice in the table how the y coordinates match perfectly between the vertex and texture coordinate for a given index. But the x coordinates don't match: when the vertex has x = 0, the texture coordinate has x = 1 and vice versa. I assume this would make the image appear mirrored around the y axis instead of rotated in any way. What does the original image look like? Is it mirrored compared to what we see in the image you posted so that the building is on the left? If so, the texture coordinates would be the explanation. In that case, texture coordinate 2 and 3 should switch places.
In case you are curious, you could take a look at the OpenGL 2.1 specification on page 18, Figure 2.5(a), to see why the vertex indices were selected as they were. It would create a quad with vertices specified in a counterclockwise direction when projected on the screen. This is good because the initial value for glFrontFace() is GL_CCW, which means we see the front face of the polygons in the rendered image and the polygons would not have been culled if culling was enabled (see glCullFace()). (Culling is not enabled by default though, so it may or may not have mattered in your case.)
I hope this helped. Do comment if something is unclear!
I'm trying to put a texture on one surface of a cube (if facing the XY plane the texture would be facing you).
No texture is getting drawn, only the wireframe and I'm wondering what I'm doing wrong. I think it's the vertex coordinates?
Here's some code:
struct paperVertex {
D3DXVECTOR3 pos;
DWORD color; // The vertex color
D3DXVECTOR2 texCoor;
paperVertex(D3DXVECTOR3 p, DWORD c, D3DXVECTOR2 t) {pos = p; color = c; texCoor = t;}
paperVertex() {pos = D3DXVECTOR3(0,0,0); color = 0; texCoor = D3DXVECTOR2(0,0);}
};
D3DCOLOR color1 = D3DCOLOR_XRGB(255, 255, 255);
D3DCOLOR color2 = D3DCOLOR_XRGB(200, 200, 200);
vertices[0] = paperVertex(D3DXVECTOR3(-1.0f, -1.0f, -1.0f), color1, D3DXVECTOR2(1,0)); // bottom left corner of tex
vertices[1] = paperVertex(D3DXVECTOR3(-1.0f, 1.0f, -1.0f), color1, D3DXVECTOR2(0,0)); // top left corner of tex
vertices[2] = paperVertex(D3DXVECTOR3( 1.0f, 1.0f, -1.0f), color1, D3DXVECTOR2(0,1)); // top right corner of tex
vertices[3] = paperVertex(D3DXVECTOR3(1.0f, -1.0f, -1.0f), color1, D3DXVECTOR2(1,1)); // bottom right corner of tex
vertices[4] = paperVertex(D3DXVECTOR3(-1.0f, -1.0f, 1.0f), color1, D3DXVECTOR2(0,0));
vertices[5] = paperVertex(D3DXVECTOR3(-1.0f, 1.0f, 1.0f), color2, D3DXVECTOR2(0,0));
vertices[6] = paperVertex(D3DXVECTOR3(1.0f, 1.0f, 1.0f), color2, D3DXVECTOR2(0,0));
vertices[7] = paperVertex(D3DXVECTOR3(1.0f, -1.0f, 1.0f), color1, D3DXVECTOR2(0,0));
D3DXCreateTextureFromFile( md3dDev, "texture.bmp", &gTexture);
md3dDev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
md3dDev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
md3dDev->SetTexture(0, gTexture);
md3dDev->SetStreamSource(0, mVtxBuf, 0, sizeof(paperVertex));
md3dDev->SetVertexDeclaration(paperDecl);
md3dDev->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
md3dDev->SetIndices(mIndBuf);
md3dDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, VTX_NUM, 0, NUM_TRIANGLES);
disclaimer: I have no Direct3D experience, but solid OpenGL and general computer graphics experience. And since the underlying concepts don't really differ, I attempt an answer, of whose correctness I'm 99% sure.
You call md3dDev->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME) immediately before rendering and wonder why only the wireframe is drawn?
Keep in mind that using a texture doesn't magically turn a wireframe model into a solid model. It is still a wireframe model with the texture only applied to the wireframe. You can only draw the whole primitve as wireframe or not.
Likewise does using texture coordinates of (0,0) not magically disable texturing for individual faces. You can only draw the whole primitive textured or not, though you might play with the texture coordinates and the texture's wrapping mode (and maybe the texture border) to make the "non-textured" faces use a uniform color from the texture and thus look like not textured.
But in general to achieve such deviating render styles (like textured/non-textured, but especially wireframe/solid) in a single primitive, you won't get around splitting the primitive into multiple ones and drawing each one with its dedicated render style.
EDIT: According to your comment: If you don't need wireframe, why enable it then? Besides disabling wireframe, with your current texture coordinates the other faces won't just have a single color from the texture but some strange distorted version of the texture. This is because your vertices (and their texture coordinates) are shared between different faces, but the texture coordinates at the moment are created only for the front face to look reasonable.
In such a situation, you won't get around duplicating vertices, so that each face uses a set of 4 unique vertices. In the case of a cube you won't actually need an index array anymore, because each face needs its own vertices. This is due to the fact, that a vertex conceptually represents all of the vertex' attributes (position, color, texCoord, ...) and you cannot have a two vertices sharing a position but having different texture coordinates (you can but you need two distinct vertices). Once you've duplicated the vertices accordingly, you can give each of the corner vertices their respective texture coordinates (which would be the usual [0,1]-quad if you want them textured normally, or all 0s if you want them to have a single color, in this case the color of the bottom left (or top left in D3D?) corner of the texture).
The same problem arises if you want to light the cube and need normals per-face, istead of interpolated per-vertex normals. In this case you also have to introduce duplicate vertices only deviating in their normal attribute. Always keep in mind that a vertex conceptually consists of all the vertex attributes and if two vertices have the same position but a different color/normal/texCoord/... they are conceptually (and practically) different vertices.