Why Doesn't My Heightmap-Generated Terrain Texture Correctly? - c++

I'm building a heightmap out of a 2D array of shorts. The code to generate the vertices is
MeshVertex v; // has position, normal, and texCoord fields
v.position = glm::vec3(
(float) x * 150,
height * 64,
(float) z * 150
);
When I generate the indices for the terrain mesh, I followed the tutorial on learnopengl.com, where I loop through each row, loop through each column, and assign the two sets of indices for the two triangles in each cell.
for(unsigned int i = 0; i < mapSize-1; i++) // for each row a.k.a. each strip
{
for(unsigned int j = 0; j < mapSize; j++) // for each column
{
for(unsigned int k = 0; k < 2; k++) // for each side of the strip
{
indices.push_back(j + mapSize * (i + k));
}
}
}
Lastly, I thought GL_TRIANGLE_STRIP draws the tris in a counter clockwise direction, based on indices 0, 1, 2; 2, 1, 3 as per wikipedia. Assuming 0-based numbering, wouldn't this mean that vertices 1 and 4 share a texture coordinate and vertices 2 and 3 share a texture coordinate? I'm building my texture coords based on this with the following code
switch(index % 6) {
case 0:
v.texCoords = glm::vec2(0, (bandHeight * 1) + bandHeight);
break;
case 1:
case 4:
v.texCoords = glm::vec2(0, (bandHeight * 1));
break;
case 2:
case 3:
v.texCoords = glm::vec2(1, (bandHeight * 1));
break;
case 5:
v.texCoords = glm::vec2(1, (bandHeight * 1) + bandHeight);
break;
}
I don't know that it's relevant, but the bandHeight variable is due to my textures being a variable length vertical image of subimages, where the first texture is row 0, second texture is row 1, etc. That's all that is, with 1 meaning we're looking at the second texture, which I've hardcoded just for testing.
When I render the terrain mesh, only one quad in every 6 looks right. The rest are warped, and I'm not sure why. What is the right formula for generating texture coordinates based on which index the loop is processing?
Here's what a full picture of the terrain looks like, with each cell having the texture coordinates generated with the current cell's intended texture.
Edit
As there have been some votes to close the question as "not enough code to reproduce", here's a link to the full repository. You'll need your own copy of HUNTDAT from Carnivores2; I can't provide that due to copyright reasons. It can easily be found through some google searches, though.
It compiles on Windows 10 with MinGW 8, although the cmakelists file should be easy enough to customize to other platforms.
The file that generates the terrain is found here.

Related

How do I draw an OBJ file in OpenGL using tinyobjloader?

I am trying to draw this free airwing model from Starfox 64 in OpenGL. I converted the .fbx file to .obj in Blender and am using tinyobjloader to load it (all requirements for my university subject).
I pretty much slapped the example code (with the modern API) into my program, replaced the file name, and grabbed the attrib.vertices and attrib.normals vectors to draw the airwing.
I can view the vertices with GL_POINTS:
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
glDrawArrays(GL_POINTS, 0, vertices.size() / 3);
glDisableClientState(GL_VERTEX_ARRAY);
Which looks correct (I ... think?):
But I'm not sure how to render a solid model. Simply replacing GL_POINTS with GL_TRIANGLES (shown) or GL_QUADS doesn't work:
I am using OpenGL 1.1 w/ GLUT (again, university). I think I just don't know what I'm doing, really. Help?
E: When I wrote this answer originally I had only worked with vertices and normals. I've figured out how to get materials and textures working, but don't have time to write that out at the moment. I will add that in when I have some time, but it's largely the same logic if you wanna poke around the tinyobj header yourselves in the meantime. :-)
I've learned a lot about TinyOBJLoader in the last day so I hope this helps someone in the future. Credit goes to this GitHub repository which uses TinyOBJLoader very clearly and cleanly in fileloader.cpp.
To summarise what I learned studying that code:
Shapes are of type shape_t. For a single model OBJ, the size of shapes is 1. I'm assuming OBJ files can contain multiple objects but I haven't used the file format much to know.
shape_t's have a member mesh of type mesh_t. This member stores the information parsed from the face rows of the OBJ. You can figure out the number of faces your object has by checking the size of the material_ids member.
The vertex, texture coordinate and normal indices of each face are stored in the indices member of the mesh. This is of type std::vector<index_t>. This is a flattened vector of indices. So for a model with triangulated faces f1, f2 ... fi, it stores v1, t1, n1, v2, t2, n2 ... vi, ti, ni. Remember that these indices correspond to the whole vertex, texture coordinate or normal. Personally I triangulated my model by importing into Blender and exporting it with triangulation turned on. TinyOBJ has its own triangulation algorithm you can turn on by setting the reader_config.triangulate flag.
I've only worked with the vertices and normals so far. Here's how I access and store them to be used in OpenGL:
Convert the flat vertices and normal arrays into groups of 3, i.e. 3D vectors
for (size_t vec_start = 0; vec_start < attrib.vertices.size(); vec_start += 3) {
vertices.emplace_back(
attrib.vertices[vec_start],
attrib.vertices[vec_start + 1],
attrib.vertices[vec_start + 2]);
}
for (size_t norm_start = 0; norm_start < attrib.normals.size(); norm_start += 3) {
normals.emplace_back(
attrib.normals[norm_start],
attrib.normals[norm_start + 1],
attrib.normals[norm_start + 2]);
}
This way the index of the vertices and normals containers will correspond with the indices given by the face entries.
Loop over every face, and store the vertex and normal indices in a separate object
for (auto shape = shapes.begin(); shape < shapes.end(); ++shape) {
const std::vector<tinyobj::index_t>& indices = shape->mesh.indices;
const std::vector<int>& material_ids = shape->mesh.material_ids;
for (size_t index = 0; index < material_ids.size(); ++index) {
// offset by 3 because values are grouped as vertex/normal/texture
triangles.push_back(Triangle(
{ indices[3 * index].vertex_index, indices[3 * index + 1].vertex_index, indices[3 * index + 2].vertex_index },
{ indices[3 * index].normal_index, indices[3 * index + 1].normal_index, indices[3 * index + 2].normal_index })
);
}
}
Drawing is then quite easy:
glBegin(GL_TRIANGLES);
for (auto triangle = triangles.begin(); triangle != triangles.end(); ++triangle) {
glNormal3f(normals[triangle->normals[0]].X, normals[triangle->normals[0]].Y, normals[triangle->normals[0]].Z);
glVertex3f(vertices[triangle->vertices[0]].X, vertices[triangle->vertices[0]].Y, vertices[triangle->vertices[0]].Z);
glNormal3f(normals[triangle->normals[1]].X, normals[triangle->normals[1]].Y, normals[triangle->normals[1]].Z);
glVertex3f(vertices[triangle->vertices[1]].X, vertices[triangle->vertices[1]].Y, vertices[triangle->vertices[1]].Z);
glNormal3f(normals[triangle->normals[2]].X, normals[triangle->normals[2]].Y, normals[triangle->normals[2]].Z);
glVertex3f(vertices[triangle->vertices[2]].X, vertices[triangle->vertices[2]].Y, vertices[triangle->vertices[2]].Z);
}
glEnd();

Mesh color is messed up probably due to bad vertex normal computation

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:

Marching Cubes Issues

I've been trying to implement the marching cubes algorithm with C++ and Qt. Anyway, so far all the steps have been written, but I'm getting a really bad result. I'm looking for orientation or advices about what can be going wrong. I suspect one of the problems may be with the voxel conception, specifically about which vertex goes in which corner (0, 1, ..., 7). Also, I'm not a 100% sure about how to interpret the input for the algorithm (I'm using datasets). Should I read it in the ZYX order and move the marching cube in the same way or it doesn't matter at all? (Leaving aside the fact that no every dimension has to have the same size).
Here is what I'm getting against what it should look like...
http://i57.tinypic.com/2nb7g46.jpg
http://en.wikipedia.org/wiki/Marching_cubes
http://en.wikipedia.org/wiki/Marching_cubes#External_links
Paul Bourke. "Overview and source code".
http://paulbourke.net/geometry/polygonise/
Qt_MARCHING_CUBES.zip: Qt/OpenGL example courtesy Dr. Klaus Miltenberger.
http://paulbourke.net/geometry/polygonise/Qt_MARCHING_CUBES.zip
The example requires boost, but looks like it probably should work.
In his example, it has in marchingcubes.cpp, a few different methods for calculating the marching cubes: vMarchCube1 and vMarchCube2.
In the comments it says vMarchCube2 performs the Marching Tetrahedrons algorithm on a single cube by making six calls to vMarchTetrahedron.
Below is the source for the first one vMarchCube1:
//vMarchCube1 performs the Marching Cubes algorithm on a single cube
GLvoid GL_Widget::vMarchCube1(const GLfloat &fX, const GLfloat &fY, const GLfloat &fZ, const GLfloat &fScale, const GLfloat &fTv)
{
GLint iCorner, iVertex, iVertexTest, iEdge, iTriangle, iFlagIndex, iEdgeFlags;
GLfloat fOffset;
GLvector sColor;
GLfloat afCubeValue[8];
GLvector asEdgeVertex[12];
GLvector asEdgeNorm[12];
//Make a local copy of the values at the cube's corners
for(iVertex = 0; iVertex < 8; iVertex++)
{
afCubeValue[iVertex] = (this->*fSample)(fX + a2fVertexOffset[iVertex][0]*fScale,fY + a2fVertexOffset[iVertex][1]*fScale,fZ + a2fVertexOffset[iVertex][2]*fScale);
}
//Find which vertices are inside of the surface and which are outside
iFlagIndex = 0;
for(iVertexTest = 0; iVertexTest < 8; iVertexTest++)
{
if(afCubeValue[iVertexTest] <= fTv) iFlagIndex |= 1<<iVertexTest;
}
//Find which edges are intersected by the surface
iEdgeFlags = aiCubeEdgeFlags[iFlagIndex];
//If the cube is entirely inside or outside of the surface, then there will be no intersections
if(iEdgeFlags == 0)
{
return;
}
//Find the point of intersection of the surface with each edge
//Then find the normal to the surface at those points
for(iEdge = 0; iEdge < 12; iEdge++)
{
//if there is an intersection on this edge
if(iEdgeFlags & (1<<iEdge))
{
fOffset = fGetOffset(afCubeValue[ a2iEdgeConnection[iEdge][0] ],afCubeValue[ a2iEdgeConnection[iEdge][1] ], fTv);
asEdgeVertex[iEdge].fX = fX + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][0] + fOffset * a2fEdgeDirection[iEdge][0]) * fScale;
asEdgeVertex[iEdge].fY = fY + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][1] + fOffset * a2fEdgeDirection[iEdge][1]) * fScale;
asEdgeVertex[iEdge].fZ = fZ + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][2] + fOffset * a2fEdgeDirection[iEdge][2]) * fScale;
vGetNormal(asEdgeNorm[iEdge], asEdgeVertex[iEdge].fX, asEdgeVertex[iEdge].fY, asEdgeVertex[iEdge].fZ);
}
}
//Draw the triangles that were found. There can be up to five per cube
for(iTriangle = 0; iTriangle < 5; iTriangle++)
{
if(a2iTriangleConnectionTable[iFlagIndex][3*iTriangle] < 0) break;
for(iCorner = 0; iCorner < 3; iCorner++)
{
iVertex = a2iTriangleConnectionTable[iFlagIndex][3*iTriangle+iCorner];
vGetColor(sColor, asEdgeVertex[iVertex], asEdgeNorm[iVertex]);
glColor4f(sColor.fX, sColor.fY, sColor.fZ, 0.6);
glNormal3f(asEdgeNorm[iVertex].fX, asEdgeNorm[iVertex].fY, asEdgeNorm[iVertex].fZ);
glVertex3f(asEdgeVertex[iVertex].fX, asEdgeVertex[iVertex].fY, asEdgeVertex[iVertex].fZ);
}
}
}
UPDATE: Github working example, tested
https://github.com/peteristhegreat/qt-marching-cubes
Hope that helps.
Finally, I found what was wrong.
I use a VBO indexer class to reduce the ammount of duplicated vertices and make the render faster. This class is implemented with a std::map to find and discard already existing vertices, using a tuple of < vec3, unsigned short >. As you may imagine, a marching cubes algorithm generates structures with thousands if not millions of vertices. The highest number a common unsigned short can hold is 65536, or 2^16. So, when the output geometry had more than that, the map index started to overflow and the result was a mess, since it started to overwrite vertices with the new ones. I just changed my implementation to draw with common VBO and not indexed while I fix my class to support millions of vertices.
The result, with some minor vertex normal issues, speaks for itself:
http://i61.tinypic.com/fep2t3.jpg

c++ 2D TileMap Render optimization

I have a question about 2D TileMap Optimization.
I rendered the Tilemap but the speed is too slow (Frame-rate == 50)
I think I can specify tiles to be rendered. So instead of rendering all tiles, just render tiles on Screen (Device).
This is my current method.
//Lower Layer
for(int y = 0; y < Height; ++y)
{
for(int x = 0; x < Width; ++x)
{
//if the tile index number is -1, then this is null tile (non drawing)
if( Layer1[y][x] != -1)
{
// this EPN_CheckCollision() Function is the AABB Collision Function.
// if there 2 Rect value's are collide , then return true.
if(EPN_CheckCollision( EPN_Rect(EPN_Pos(x*32, y*32)-CharacterPos) , EPN_Rect(0-32,0-32,1024+32,768+32) ) )
{
//EPN_Pos structure is consist of (float PosX , float PosY)
EPN_Pos TilePos = EPN_Pos(x * 32, y * 32)-CharacterPos;
//EPN_Rect structure is consist of below members
//float Top X
//float Top Y
//float BottomX (not Width)
//float BottomY (not Height)
EPN_Rect TileRect = EPN_Rect( Layer1[y][x] % 8 * 32, Layer1[y][x] / 8 * 32, Layer1[y][x] % 8 * 32 + 32, Layer1[y][x] / 8 * 32+32);
//Blt is Texture render function.
// 2nd Parameter is Render Pos and 3rd parameter is Render Texture's Rect.
pEPN_TI->Blt("MapTileset", TilePos, TileRect );
}
}
}
This is my TileMapRender Method.
( I use EPN Engine made by directX which is Unknown. So I annotated my code)
I rendered the tilemap that collides with the DeviceScreen ( 1024 * 768 , but for margin)
because I want to render visible tilemap on screen (I do not render tiles out of device screen).
So I Check the AABB Collision each tile and (1024, 768) device Screen, now I only render necessary tiles.
But I think this method has a problem, that it does not render out of screen tiles.
For statement also repeat all maptiles; what a inefficient method...
Maybe my games frame-rate problem is in this method. So may I ask STACK OVERFLOW how I could do this?
Is there another ways to optimize tilemap rendering?
Give me some tips please.
P.S
I'm sorry about my knotty question.
Please excuse my English ability.
You should only be rendering enough tiles to cover the screen, for example if your screen size is 640x480, and your tile size is 16x16 then:
Max tiles on X = (640/16)+2;
Max tiles on Y = (480/16)+2;
Notice how we add 2 for a margin on each side. Next thing we need to do is work out where we are in the tile map, for this we simply divide the camera x position by the tile width.
For example if the camera is at x=500 and y=20 then:
X tile index = 500/16
Y tile index = 20/16
You must also render your tile grid at an offset of 500%16 and 20%16 to account for the "sub tile pixel" scrolling.
For the collision its even easier, you only need to check collision with the tiles the player is on, so:
If the player size is 16x20 pixels and at position 120,200:
X tile index = 120/16
Y tile index = 200/16
Num X tiles to check = 16/16
Num Y tiles to check = 20/16
Hopefully this makes sense.

CPU Ray Casting

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.