I am making a simple 2d drawing program in which i have an array of vertices(say a1,a2,a3,.....) which is to be drawn using GL_LINE_STRIP .Problem is that , say there are 3 consecutive vertices : p1, p2, p3, now i want that if (p2==some specfic vertex value) then the GL_LINE_STRIP should end at p1 and a new GL_LINE_STRIP start at p3, ignoring p2. I want to make a break at p2 in the strip. How do i do that??
PS: I tried making (line width = 0 at p2 in the loop of line strip but found out that since opengl is a state machine, we cant change width inside glBegin and GLEnd. any other alternative guys???
HERE IS SOME FAILED SAMPLE CODE:
GLfloat thickness;
glBegin(GL_LINE_STRIP);
for(int i = 0; i < points.size(); i++)//points has all vertices stored...points is an array where each element is a structure containing x and y values
{
if(points[i].x==randomPoint.x || points[i-1].x==randomPoint.x){//checking if it is equal to p2(i am only checking x coordinate,which is sufficient for me to know that this point is to be excluded)
thickness = 0;
}else {
thickness = 4;
}
glLineWidth(thickness);
glVertex2i(points[i].x, points[i].y);
}
If I've got the question right you want to stop the linestrip at a vertex if it is a specific value, and start a new one on the next vertex. (Correct me if I've misunderstood)
Call glEnd() after the last vertex you want to connect and start a new glBegin() when you want to start a new strip.
As for the width part you would have to set the width before the call to glBegin(). A linestrip (afaik) can not have it's width changed in a draw state as you say. You would have to end the draw call, change the width, go 1 vertex back and start a new draw call.
Related
I created a new class PieSource (based on vtkCylinderSource) which creates a number of triangles together with the colors they should have:
int vtkPieSource::RequestData(vtkInformation *info,
vtkInformationVector **infoV,
vtkInformationVector *outputVector) {
vtkInformation *outInfo = outputVector->GetInformationObject(0);
vtkPolyData *output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
vtkPoints *newPoints;
vtkFloatArray *newNormals;
vtkUnsignedCharArray *newCols = vtkUnsignedCharArray::New();
newCols->SetNumberOfComponents(3);
newCols->SetName("Colors");
newPoints = vtkPoints::New();
newPoints->SetDataType(VTK_FLOAT);
newPoints->Allocate(iNumVerts);
newPolys = vtkCellArray::New();
newPolys->Allocate(iNumTriangles);
(-- here we create the triangles and determine their colors --)
for (uint i = 0; i < iNumVerts; i+=3) {
vtkTriangle *triangle1 =vtkTriangle::New();
triangle1->GetPointIds()->SetId(0, i);
triangle1->GetPointIds()->SetId(1, i+1);
triangle1->GetPointIds()->SetId(2, i+2);
newPolys->InsertNextCell(triangle1);
}
output->SetPoints(newPoints);
newPoints->Delete();
output->GetPointData()->SetScalars(newCols);
newCols->Delete();
output->SetPolys(newPolys);
newPolys->Delete();
return 0;
}
In my application i create a vtkGlyph3DMapper which uses the output of PieSource
vtkNew<vtkPieSource> pieSource;
pieSource->SetData(5, testData);
vtkNew<vtkGlyph3DMapper> glyph3Dmapper;
glyph3Dmapper->SetSourceConnection(pieSource->GetOutputPort());
glyph3Dmapper->SetInputData(polydata);
// glyph3Dmapper->SelectColorArray("Colors");
(polydata contains 2 triangles with colors -- without this InputData nothing is drawn)
When i run this, the glyph geometries are drawn in the correct places with correct orientations,
but they don't have the colors i have assigned them (all triangles are grey). If i uncomment the line glyph3Dmapper->SelectColorArray("Colors"); the entire glyph is drawn with the same color (the one specified in the array named "Colors").
Is it possible to have a glyph whose triangles are individually colored?
If yes how must i do this?
First, since your newCols array is empty, I don't know which color you are expecting.
Then if you want a color associated to the triangle, you should add your data array in CellData rather than in PointData. The array should have one tuple per cell (triangle here)
A small style remark: instead of vtkXXX * myObj = vtkXXX::New();, it is recommended to use a local-scoped object instanciated with vtkNew<vtkXXX> myObject;, so you do not need to Delete it.
I have vertex position and index and I want vertex normal:
// input
vector<Vec3f> points = ... // position
vector<Vec3i> facets = ... // index (triangles)
// output
vector<Vec3f> norms; // normal
Method 1
I compute normal like this:
norms.resize(points.size()); // for each vertex there is a normal
for (Vec3i f : facets) {
int i0 = f.x();
int i1 = f.y(); // index
int i2 = f.z();
Vec3d pos0 = points.at(i0);
Vec3d pos1 = points.at(i1); // position
Vec3d pos2 = points.at(i2);
Vec3d N = triangleNormal(pos0, pos1, pos2); // face/triangle normal
norms[i0] = N;
norms[i1] = N; // Use the same normal for all 3 vertices
norms[i2] = N;
}
Then, the output mesh is rendered like this with a Phong material:
Method 1 with reversed normal
When I reverse normal direction in method 1:
norms[i0] = -N;
norms[i1] = -N;
norms[i2] = -N;
The dark and light regions are swapped:
The same happens by swapping position 0 with position 1 by:
// Vec3d N = triangleNormal(pos0, pos1, pos2);
Vec3d N = triangleNormal(pos1, pos0, pos2); // Swap pos0 with pos1
Method 2
I compute the normal by this method:
// Count how many faces/triangles a vertex is shared by
vector<int> counters;
counters.resize(points.size());
norms.resize(points.size());
for (Vec3i f : facets) {
int i0 = f.x();
int i1 = f.y(); // index
int i2 = f.z();
Vec3d pos0 = points.at(i0);
Vec3d pos1 = points.at(i1); // position
Vec3d pos2 = points.at(i2);
Vec3d N = triangleNormal(pos0, pos1, pos2);
// Must be normalized
// https://stackoverflow.com/a/21930058/3405291
N.normalize();
norms[i0] += N;
norms[i1] += N; // add normal to all vertices used in face
norms[i2] += N;
counters[i0]++;
counters[i1]++; // increment count for all vertices used in face
counters[i2]++;
}
// https://stackoverflow.com/a/21930058/3405291
for (int i = 0; i < static_cast<int>(norms.size()); ++i) {
if (counters[i] > 0)
norms[i] /= counters[i];
else
norms[i].normalize();
}
This method yields a totally dark final render by a Phong material:
I also tried methods suggested here and there which are similar to method 2. They all result in a final render which looks like that of method 2 i.e. all dark regions without any light one.
Method 2 with reversed normal
I used method 2, but at the end, I reversed the normal direction by:
for (Vec3d & n : norms) {
n = -n;
}
To my surprise, the final render is all darK:
Also in method 2, I tried swapping position 0 with position 1:
// Vec3d N = triangleNormal(pos0, pos1, pos2);
Vec3d N = triangleNormal(pos1, pos0, pos2); // swap pos0 with pos1
The final render is all dark regions without any light ones.
How?
Any idea how I can get my final render to be all light without any dark region?
That looks like your mesh does not have consistent winding rule. So some triangles/faces are defined CW other in CCW order of vertexes causing that some of your normals are facing in opposite direction. There are few things you can do to remedy:
use double sided normals lighting
this is easiest... somwhere in fragment or wherever you are computing the shading something like this:
out_color = face_color*(ambient_light+diffuse_light*max(0.0,dot(face_normal,light_direction)));
when the normal is in wrong direction the result of dot is negative leading to dark color so just use abs value instead:
out_color = face_color*(ambient_light+diffuse_light*abs(dot(face_normal,light_direction)));
In fixed function pipeline there is even switch for this IIRC:
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
repair mesh winding
there must be 3D tools to do this (Blender,3DS,...) or if your mesh is generated on the fly you could update your code to create consistent winding on your own.
Correct winding enables you the use of GL_CULL_FACE which speeds up rendering considerably. Also it enables more advanced stuff like this:
OpenGL - How to create Order Independent transparency?
repair normals
In some cases there are ways to detect if the normal is pointing outwards or inwards to mesh for example like this:
Determing the direction of face normals consistently?
So just negate the wrong ones during computation of normal and that is it. However if your mesh is too complicated (too far from convex) is this not so easily done as you need to use local "centers" of mesh or even inside polygon tests which are expensive.
The averaging method of generating normals gives you dark colors for both directions of normals which means you wrongly computed them and they are most likely zero. For more info about such approach see:
How to achieve smooth tangent space normals?
Anyway to debug problems like this its best to render your normals as lines going from the vertexes of your mesh (use wireframe). Then you would see directly what normals are good and bad. Here example:
I am using OpenGL library in my Visual C++ application where I want to draw say, 100 points in random locations and I would like to check if these points random co-ordinates or random locations that generated are within the screen or window boundaries. I tired using a (x,y,z) vertex option and I get the points vertical running along a line. If I try generating only (x,y) and drawing them then I do get a lot more points scattered but definitely not all 100 within the window dimensions.
my code looks something like this:
GLfloat dots_vert[99];
for (int i = 0; i < 99; i++){
if (i % 2 == 0)
dots_vert[i] = 0.0f;
else
dots_vert[i] = ((GLfloat)rand() / (GLfloat)RAND_MAX)*100.0f - (100.0f / 2);
}
glEnable(GL_POINT_SMOOTH);
glPointSize(3.0f);
glEnableClientState(GL_VERTEX_ARRAY);
GLuint vbo_ID;
glGenBuffers(1, &vbo_ID);
glBindBuffer(GL_ARRAY_BUFFER, vbo_ID);
glBufferData(GL_ARRAY_BUFFER, sizeof(dots_vert), dots_vert, GL_DYNAMIC_DRAW);
while (!GetAsyncKeyState(VK_DOWN)){
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo_ID);
glVertexAttribPointer(
0,
3,
GL_FLOAT,
GL_FALSE,
0,
(void*)0
);
glDrawArrays(GL_POINTS, 0, 100);
SwapBuffers(g_pOpenGLWindow->hDC);
Let me guide you through the glaring mistakes I can immediately see in that code.
First of all the obvious first mistake: you claim to be drawing 100 points but your dots_vert array is only 99 elements long. This is repeated in the following loop, where you go from 0 to 98 for a total of 99 times.
So first of all:
GLfloat dots_vert[100];
for (int i = 0; i < 100; ++i)
{
[...]
}
There is another huge mistake in there but we'll keep that for later, let's move on for now.
The second mistake is about the knowledge of the OpenGL API and computer graphics. First of all, your goal is to pass points to the GPU, so you need the glVertexAttribPointer function, that much you figured out. The absolute first thing you wanna do is to look at the glVertexAttribPointer reference documentation, so you have an idea of what you need. You need an index, a size, a type, a normalized flag, a stride and an offset.
Let's look at what the reference documentation says about the size parameter:
size
Specifies the number of components per generic vertex attribute. Must be 1, 2, 3, 4. Additionally, the symbolic constant GL_BGRA is accepted by glVertexAttribPointer. The initial value is 4.
This is immediately obvious to be crucial in determining what kind of data you're trying to pass to the GPU. You set the parameter to 3, which means that you have an x, a y and a z. But the previous code contradicts this. For starters, your dots_vert array is 100 elements long, and you want to draw 100 points, so you have enough for 100/100 = 1 component per point, not 3. But even worse, the inside of the for loop contradicts this even further, so let's go back and check the mistake I mentioned previously.
Mistake number three: your for loop consists of an if {} else {} statement, where you set the current element of the dots_vert array to a value of 0.0f if the index of the loop is even (if (i % 2 == 0)), and a random value between -50.0f and 50.0f otherwise. Assuming 1 component per point, this means that you're only generating the x coordinates, so you're working in a single dimension.
Clearly this is not what you intended to do, also because half of your points will be 0.0f and therefore they'll all overlap. So I assume you were trying to generate a random value for x and y, and set z to 0.0f, which would make much more sense. First of all, you have 3 components per point and therefore you'll need an array with 100*3 = 300 elements. So first of all, let's fix the previous code:
GLfloat dots_vert[300];
for (int i = 0; i < 300; ++i)
{
[...]
}
Much better. Now we need to generate a random x and y valye for each point, and set z to 0.0f since we don't need it. You wanna do all of the components at once in a single loop, so you want your loop to step by 3, not 1, so once again let's fix the previous code:
GLfloat dots_vert[300];
for (int i = 0; i < 300; i += 3)
{
[...]
}
Now we can generate x, y and z together in a single loop. This is the crucial part where understanding how computer graphics work, specifically in the context of the OpenGL API. OpenGL uses a coordinate system where the origin is in the middle of the screen, the x axis moves horizontally (positive x points to your right), the y axis moves vertically (positive y points up), and the z axis goes straight through the screen (positive z points out of the screen, towards you). Now this is the very important part: x, y and z are clipped to a specific range of values; anything outside of this range is ignored. For all coordinates, the range goes from -1.0f to 1.0f. Anything below of above that is not drawn at all.
So if you want to have 100 points to be inside the screen, ignoring projection which is outside of the scope of this exercise, you want to generate x and y in the -1.0f to 1.0f range, not -50.0f to 50.0f like you're doing there. You can keep z to 0.0f, doesn't really matter in this case. This is why most of your points fall outside of the screen: with that range, statistically speaking, around 98% of your points will fall outside of the clip space and will be ignored.
So ultimately this is what you want:
GLfloat dots_vert[300];
for (int i = 0; i < 300; i += 3)
{
dots_vert[i] = ((GLfloat)rand() / (GLfloat)RAND_MAX)*2.0f - 1.0f; // this is x
dots_vert[i+1] = ((GLfloat)rand() / (GLfloat)RAND_MAX)*2.0f - 1.0f; // this is y
dots_vert[i+2] = 0.0f; // this is z
}
Finally a reminder: when you do glDrawArrays(GL_POINTS, 0, 100); you're telling the GPU to draw 100 points. Each point is made of however many components you specified in the size parameter of the glVertexAttribPointer function. In this case you wanna draw 100 points, each point is made of 3 components, so the GPU expects an array of 100*3 = 300 floats. numbers. Anything less could result in either a segmentation fault or even worse an undefined behavior (which means anything can happen), so pay close attention to what you're doing and make sure you know exactly what kind of data you're passing to the GPU because you might end up with a nonsense result and you'll be stuck trying to figure out what went wrong. In this case, you have basically no code at all to check so it's easy to fix, but when you'll end up with a decent amount of code (and you will eventually), an error like this could mean hours or even days wasted trying to find the error.
As a bonus, feel free to ignore this one: technically a point is made of 4 components. This component is called w and its use is outside of the scope of this exercise so don't worry about it, just remember that it should always be set to 1.0f, unless you are doing projection.
So technically you could do this too:
GLfloat dots_vert[400];
for (int i = 0; i < 400; i += 4)
{
dots_vert[i] = ((GLfloat)rand() / (GLfloat)RAND_MAX)*2.0f - 1.0f; // this is x
dots_vert[i+1] = ((GLfloat)rand() / (GLfloat)RAND_MAX)*2.0f - 1.0f; // this is y
dots_vert[i+2] = 0.0f; // this is z
dots_vert[i+3] = 1.0f; // this is w
}
Then you set the size parameter of glVertexAttribPointer to 4 instead of 3, the result should be exactly the same.
Need some direction on 3d point cloud display using openGl in c++ (vs2008). I am trying to do a 3d point cloud display with a texture. I have 3 2D arrays (each same size 1024x512) representing x,y,z of each point. I think I am on the right track with
glBegin(GL_POINTS);
for(int i=0; i<1024; i++)
{
for(int j=0; j<512; j++)
{
glVertex3f(x[i][j], y[i][j], z[i][j]);
}
}
}
glEnd();
Now this loads all the vertices in the buffer (i think) but from here I am not sure how to proceed. Or I am completely wrong here.
Then I have another 2D array (same size) that contains color data (values from 0-255) that I want to use as texture on the 3D point cloud and display.
The point drawing code is fine as is.
(Long term, you may run into performance problems if you have to draw these points repeatedly, say in response to the user rotating the view. Rearranging the data from 3 arrays into 1 with x, y, z values next to each other would allow you to use faster vertex arrays/VBOs. But for now, if it ain't broke, don't fix it.)
To color the points, you need glColor before each glVertex. It has to be before, not after, because in OpenGL glVertex loosely means that's a complete vertex, draw it. You've described the data as a point cloud, so don't change glBegin to GL_POLYGON, leave it as GL_POINTS.
OK, you have another array with one byte color index values. You could start by just using that as a greyscale level with
glColor3ub(color[i][j], color[i][j], color[i][j]);
which should show the points varying from black to white.
To get the true color for each point, you need a color lookup table - I assume there's either one that comes with the data, or one you're creating. It should be declared something like
static GLfloat ctab[256][3] = {
1.0, 0.75, 0.33, /* Color for index #0 */
...
};
and used before the glVertex with
glColor3fv(ctab[color[i][j]]);
I've used floating point colors because that's what OpenGL uses internally these days. If you prefer 0..255 values for the colors, change the array to GLubyte and the glColor3fv to glColor3ub.
Hope this helps.
I'm attempting ray casting an octree on the CPU (I know the GPU is better, but I'm unable to get that working at this time, I believe my octree texture is created incorrectly).
I understand what needs to be done, and so far I cast a ray for each pixel, and check if that ray intersects any nodes within the octree. If it does and the node is not a leaf node, I check if the ray intersects it's child nodes. I keep doing this until a leaf node is hit. Once a leaf node is hit, I get the colour for that node.
My question is, what is the best way to draw this to the screen? Currently im storing the colours in an array and drawing them with glDrawPixels, but this does not produce correct results, with gaps in the renderings, as well as the projection been wrong (I am using glRasterPos3fv).
Edit: Here is some code so far, it needs cleaning up, sorry. I have omitted the octree ray casting code as I'm not sure it's needed, but I will post if it'll help :)
void Draw(Vector cameraPosition, Vector cameraLookAt)
{
// Calculate the right Vector
Vector rightVector = Cross(cameraLookAt, Vector(0, 1, 0));
// Set up the screen plane starting X & Y positions
float screenPlaneX, screenPlaneY;
screenPlaneX = cameraPosition.x() - ( ( WINDOWWIDTH / 2) * rightVector.x());
screenPlaneY = cameraPosition.y() + ( (float)WINDOWHEIGHT / 2);
float deltaX, deltaY;
deltaX = 1;
deltaY = 1;
int currentX, currentY, index = 0;
Vector origin, direction;
origin = cameraPosition;
vector<Vector4<int>> colours(WINDOWWIDTH * WINDOWHEIGHT);
currentY = screenPlaneY;
Vector4<int> colour;
for (int y = 0; y < WINDOWHEIGHT; y++)
{
// Set the current pixel along x to be the left most pixel
// on the image plane
currentX = screenPlaneX;
for (int x = 0; x < WINDOWWIDTH; x++)
{
// default colour is black
colour = Vector4<int>(0, 0, 0, 0);
// Cast the ray into the current pixel. Set the length of the ray to be 200
direction = Vector(currentX, currentY, cameraPosition.z() + ( cameraLookAt.z() * 200 ) ) - origin;
direction.normalize();
// Cast the ray against the octree and store the resultant colour in the array
colours[index] = RayCast(origin, direction, rootNode, colour);
// Move to next pixel in the plane
currentX += deltaX;
// increase colour arry index postion
index++;
}
// Move to next row in the image plane
currentY -= deltaY;
}
// Set the colours for the array
SetFinalImage(colours);
// Load array to 0 0 0 to set the raster position to (0, 0, 0)
GLfloat *v = new GLfloat[3];
v[0] = 0.0f;
v[1] = 0.0f;
v[2] = 0.0f;
// Set the raster position and pass the array of colours to drawPixels
glRasterPos3fv(v);
glDrawPixels(WINDOWWIDTH, WINDOWHEIGHT, GL_RGBA, GL_FLOAT, finalImage);
}
void SetFinalImage(vector<Vector4<int>> colours)
{
// The array is a 2D array, with the first dimension
// set to the size of the window (WINDOW_WIDTH * WINDOW_HEIGHT)
// Second dimension stores the rgba values for each pizel
for (int i = 0; i < colours.size(); i++)
{
finalImage[i][0] = (float)colours[i].r;
finalImage[i][1] = (float)colours[i].g;
finalImage[i][2] = (float)colours[i].b;
finalImage[i][3] = (float)colours[i].a;
}
}
Your pixel drawing code looks okay. But I'm not sure that your RayCasting routines are correct. When I wrote my raytracer, I had a bug that caused horizontal artifacts in on the screen, but it was related to rounding errors in the render code.
I would try this...create a result set of vector<Vector4<int>> where the colors are all red. Now render that to the screen. If it looks correct, then the opengl routines are correct. Divide and conquer is always a good debugging method.
Here's a question though....why are you using Vector4 when later on you write the image as GL_FLOAT? I'm not seeing any int->float conversion here....
You problem may be in your 3DDDA (octree raycaster), and specifically with adaptive termination. It results from the quantisation of rays into gridcell form, that causes certain octree nodes which lie slightly behind foreground nodes (i.e. of a higher z depth) and which thus should be partly visible & partly occluded, to not be rendered at all. The smaller your voxels are, the less noticeable this will be.
There is a very easy way to test whether this is the problem -- comment out the adaptive termination line(s) in your 3DDDA and see if you still get the same gap artifacts.