I have had a terrain generation algorithm for a while and have had a lot of success with it. However when I decided to start work on Level of Detail (Has not been implemented), I started to notice that my terrain would begin to draw underneath itself as it got larger (when it gets to around 257 by 257). I'm not too sure why it starts to do this however I have a feeling its either due to me converting the 2d vector into a 1d or when i generate my indices but i am not 100 percent. If someone could have a look that would be amazing
Terrain at 100 by 100
Terrain at 1000 by 1000
Code used to Generate Vertices
void TerrainObject::LoadFlatTerrain()
{
float F_terrainDimentions = terrainDimentions; //used to stop integer division
m_vertexCount = terrainDimentions * terrainDimentions; //terrainDimentions = the width and depth of the terrain
float halfWidth = 0.5f * F_terrainDimentions;
float halfDepth = 0.5f * F_terrainDimentions;
// project the grid into xz plane
float dx = F_terrainDimentions / (F_terrainDimentions - 1);
float dz = F_terrainDimentions / (F_terrainDimentions - 1);
float du = 1.0f / (F_terrainDimentions - 1); // texture co-ordinates
float dv = 1.0f / (F_terrainDimentions - 1);
//Resizes Vectors for Vertices
vertices.resize(m_vertexCount);
verts2d.resize(terrainDimentions);
for (int i = 0; i < verts2d.size(); i++)
{
verts2d[i].resize(terrainDimentions);
}
//Generates Vertices for 2d array
for (int i = 0; i < terrainDimentions; i++)
{
float z = halfDepth - i * dz;
for (int j = 0; j < terrainDimentions; j++)
{
float x = -halfWidth + j * dx;
float y = 0;
verts2d[i][j].Position.x = x;
verts2d[i][j].Position.y = y;
verts2d[i][j].Position.z = z;
verts2d[i][j].Normal = XMFLOAT3(0.0f, 1.0f, 0.0f);
verts2d[i][j].Tangent = XMFLOAT3(1.0f, 0.0f, 0.0f);
verts2d[i][j].TexCoord.x = j * du;
verts2d[i][j].TexCoord.y = i * dv;
}
}
//Converts to 1d array
for (int i = 0; i < terrainDimentions; i++)
{
for (int j = 0; j < terrainDimentions; j++)
{
vertices[i * terrainDimentions + j].Position = verts2d[i][j].Position;
vertices[i * terrainDimentions + j].TexCoord = verts2d[i][j].TexCoord;
vertices[i * terrainDimentions + j].Normal = verts2d[i][j].Normal;
vertices[i * terrainDimentions + j].Tangent = verts2d[i][j].Tangent;
}
}
// Create vertex buffer
D3D11_BUFFER_DESC vbd;
vbd.Usage = D3D11_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof(ComplexVertex) * m_vertexCount;
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;
vbd.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA vinitData;
vinitData.pSysMem = &(vertices[0]);
m_pd3dDevice->CreateBuffer(&vbd, &vinitData, &m_pVertexBuffer);
CreateIndices();
//Sets terrains Current position in the world to be the top corner
m_position = verts2d[terrainDimentions - 1][terrainDimentions - 1].Position;
}
Code to Generate Indices
HRESULT TerrainObject::CreateIndices()
{
UINT faceCount = (terrainDimentions - 1) * (terrainDimentions - 1) * 2; // each quad consists of two triangles
int index = 0;
indices.resize(faceCount * 3);
for (UINT i = 0; i < terrainDimentions - 1; ++i)
{
for (UINT j = 0; j < terrainDimentions - 1; ++j)
{
indices[index] = i * terrainDimentions + j;
indices[index + 1] = i * terrainDimentions + j + 1;
indices[index + 2] = (i + 1) * terrainDimentions + j;
indices[index + 3] = (i + 1) * terrainDimentions + j;
indices[index + 4] = i * terrainDimentions + j + 1;
indices[index + 5] = (i + 1) * terrainDimentions + j + 1;
index += 6; // next quad
}
}
m_indexCount = indices.size();
// Creates index buffer
D3D11_BUFFER_DESC ibd;
ibd.Usage = D3D11_USAGE_DEFAULT;
ibd.ByteWidth = sizeof(WORD) * m_indexCount;
ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
ibd.CPUAccessFlags = 0;
ibd.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA iinitData;
iinitData.pSysMem = &indices[0];
return m_pd3dDevice->CreateBuffer(&ibd, &iinitData, &m_pIndexBuffer);
}
I'm trying to create a class that can procedurally create prisms (or cylinders if the precision is high enough) but only the sides of the 3d model are showing (not the top and bottom). This is using openGL and c++. Not going for efficiency, just modifying a previous class that made a sphere.
#define numSlices 2
Prism::Prism() {
init(3);
}
Prism::Prism(int prec) {
init(prec);
}
float Prism::toRadians(float degrees) { return (degrees * 2.0f * 3.14159f) / 360.0f; }
void Prism::init(int prec) {
prec = (prec < 3) ? 3 : prec;
numVertices = (prec + 1) * (numSlices+1);
numIndices = prec * numSlices * 6;
for (int i = 0; i < numVertices; i++) { vertices.push_back(glm::vec3()); }
for (int i = 0; i < numVertices; i++) { texCoords.push_back(glm::vec2()); }
for (int i = 0; i < numVertices; i++) { normals.push_back(glm::vec3()); }
for (int i = 0; i < numVertices; i++) { tangents.push_back(glm::vec3()); }
for (int i = 0; i < numIndices; i++) { indices.push_back(0); }
// calculate triangle vertices
for (int i = 0; i <= numSlices; i++) {
for (int j = 0; j <= prec; j++) {
float y = i;
float x = -(float)cos(toRadians(j * 360.0f / (float)prec));
float z = (float)sin(toRadians(j * 360.0f / (float)prec));
vertices[i * (prec + 1) + j] = glm::vec3(x, y, z);
texCoords[i * (prec + 1) + j] = glm::vec2(((float)j / prec), ((float)i / numSlices));
}
}
// calculate triangle indices
for (int i = 0; i < numSlices; i++) {
for (int j = 0; j < prec; j++) {
indices[6 * (i * prec + j) + 0] = i * (prec + 1) + j;
indices[6 * (i * prec + j) + 1] = i * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 2] = (i + 1) * (prec + 1) + j;
indices[6 * (i * prec + j) + 3] = i * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 4] = (i + 1) * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 5] = (i + 1) * (prec + 1) + j;
}
}
}
Any tips or solutions that stick closely to the code already written would much appreciated.
To render the top and bottom of the cylinder, you can create a "triangle fan" that starts from a vertex at the center of the top/bottom of the cylinder and creates one triangle for every side.
Adapting your code: (untested, I may have made mistakes against winding order)
int bottom_center = vertices.length(); vertices.push_back(glm::vec3(0,0,0));
int top_center = vertices.length(); vertices.push_back(glm::vec3(0,numSlices,0));
// Bottom
for (int j = 0; j < prec; j++) {
int base = 0;
indices.push_back(bottom_center);
indices.push_back(base+j);
indices.push_back(base+j+1);
}
// Top
for (int j = 0; j < prec; j++) {
int base = numSlices * (prec+1);
indices.push_back(top_center);
indices.push_back(base+j);
indices.push_back(base+j+1);
}
See http://www.songho.ca/opengl/gl_cylinder.html for a more worked-out example.
I'm trying to make a grid of triangles for a terrain generation project in DirectX 11, but when it gets drawn to the screen it draws in all three axis, instead of just the x and z.
I do get the correct amount of vertices and indices, but in the vector of indices it has a size of 972, the first 486 of them are set to 0 instead of the actual values.
I was wondering if I could get some clarification on whether I was setting the vertex/index buffers correctly.
Below is an example for a 10 by 10 grid.
GenerateTerrain method
void Application::GenerateTerrain(int vertRows, int vertCols)
{
HRESULT hr;
// ------------------------------------- Create Vertex Buffer --------------------------------------
totalCellRows = vertRows - 1;
totalCellCols = vertCols - 1;
// Width and Total Width
float dx = 1.0f;
float totalWidth = totalCellCols * dx;
// Depth and Total Depth
float dz = 1.0f;
float totalDepth = totalCellRows * dz;
// X and Z Offsets
float xOffset = -totalWidth * 0.5f;
float zOffset = totalDepth * 0.5f;
totalVertices = vertRows * vertCols;
totalTriangles = (totalCellRows * totalCellCols) * 2;
totalIndices = totalTriangles * 3;
terrainVertices = new SimpleVertex[totalVertices];
// Array Version
int k = 0;
for (int i = 0; i < vertRows; i++)
{
for (int j = 0; j < vertCols; j++)
{
SimpleVertex newVertex;
terrainVertices[k].PosL = XMFLOAT3(j * dx + xOffset, 0.0f, -(i * dz) + zOffset);
terrainVertices[k].NormL = XMFLOAT3(0.0f, 1.0f, 0.0f);
terrainVertices[k].Tex = XMFLOAT2(0.0f, 0.0f);
k++;
}
}
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(SimpleVertex) * totalVertices;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = &terrainVertices;
hr = _pd3dDevice->CreateBuffer(&bd, &InitData, &_pGridVertexBuffer);
// ------------------------------------- Create Index Buffer --------------------------------------
// Vector Version
indices.resize(totalIndices);
for (WORD i = 0; i < (WORD)vertRows - 1; i++)
{
for (WORD j = 0; j < (WORD)vertCols - 1; j++)
{
indices.push_back(i * vertCols + j);
indices.push_back(i * vertCols + (j + 1));
indices.push_back((i + 1) * vertCols + j);
indices.push_back((i + 1) * vertCols + j);
indices.push_back((i * vertCols + (j + 1)));
indices.push_back((i + 1) * vertCols + (j + 1));
}
}
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(WORD) * totalIndices;
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
ZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = &indices;
hr = _pd3dDevice->CreateBuffer(&bd, &InitData, &_pGridIndexBuffer);
}
indices.resize(totalIndices); <----- !!!!!!ERROR!!!! u mean reserve
for (WORD i = 0; i < (WORD)vertRows - 1; i++)
{
for (WORD j = 0; j < (WORD)vertCols - 1; j++)
{
indices.push_back(i * vertCols + j);
indices.push_back(i * vertCols + (j + 1));
indices.push_back((i + 1) * vertCols + j);
indices.push_back((i + 1) * vertCols + j);
indices.push_back((i * vertCols + (j + 1)));
indices.push_back((i + 1) * vertCols + (j + 1));
}
}
You write resize, so the vector is resized, and then you add more indices with pushback. I think you want to reserve memory "reserve" and add them with pushback.
Also i would recommend to use a pointer to the first value, instead of a pointer to the vector
you:
InitData.pSysMem = &indices;
better:
InitData.pSysMem = &indices[0];
Buffer initialization seems to be ok.
Good luck
For an embedded design I am attempting to implement sobel's edge detection on a board without the use of a buffer. i.e. I am reading and writing directly from the screen. I can however, store about one or two imge width full of data to be referenced later. This is due to limitations set forth by the board. However I have fallen into some issue. All that I recieve is noise regardless if I attempt to do sobel or another edge detection algorithm. The code is below, does anyone have any suggestions
Version 1
void sobelEdgeDetection2() {
int GX[3][3];
int GY[3][3];
int sumX[3];
int sumY[3];
int SUM[3];
int piX = 0;
int piY = 0;
//uint8_t R, G, B = 0;
int I, J = 0;
//UnpackedColour pixVal;
uint16_t *buffer;
// allocate space for even scan lines and odd scan lines
buffer = new uint16_t[_gl->getWidth()];
//buffer for previous line
uint16_t *bufT;
// allocate space for even scan lines and odd scan lines
bufT = new uint16_t[_gl->getWidth()];
// Masks //////////////////////////////////////
//X//
GX[0][0] = -1;
GX[0][1] = 0;
GX[0][2] = 1;
GX[1][0] = -2;
GX[1][1] = 0;
GX[1][2] = 2;
GX[2][0] = -1;
GX[2][1] = 0;
GX[2][2] = 1;
//Y//
GY[0][0] = 1;
GY[0][1] = 2;
GY[0][2] = 1;
GY[1][0] = 0;
GY[1][1] = 0;
GY[1][2] = 0;
GY[2][0] = -1;
GY[2][1] = -2;
GY[2][2] = -1;
for (int Y = 0; Y < _gl->getHeight(); Y++) {
for (int X = 0; X < _gl->getWidth(); X++) {
sumX[0] = sumX[1] = sumX[2] = 0;
sumY[0] = sumY[1] = sumY[2] = 0;
if (Y == 0 || Y == _gl->getHeight() - 1) {
SUM[0] = SUM[1] = SUM[2] = 0;
} else if (X == 0 || X == _gl->getWidth() - 1) {
SUM[0] = SUM[1] = SUM[2] = 0;
} else {
for (I = -1; I <= 1; I++) {
for (J = -1; J <= 1; J++) {
piX = J + X;
piY = I + Y;
pixel16 pix = getPixel(piX, piY);
uint8_t Red = pix.Red;
uint8_t Green = pix.Green;
uint8_t Blue = pix.Blue;
sumX[0] += (Red) * GX[J + 1][I + 1];
sumX[1] += (Green) * GX[J + 1][I + 1];
sumX[2] += (Blue) * GX[J + 1][I + 1];
sumY[0] += (Red) * GY[J + 1][I + 1];
sumY[1] += (Green) * GY[J + 1][I + 1];
sumY[2] += (Blue) * GY[J + 1][I + 1];
}
}
SUM[0] = abs(sumX[0]) + abs(sumY[0]);
SUM[1] = abs(sumX[1]) + abs(sumY[1]);
SUM[2] = abs(sumX[2]) + abs(sumY[2]);
}
if (SUM[0] > 255)
SUM[0] = 255;
if (SUM[0] < 0)
SUM[0] = 0;
if (SUM[1] > 255)
SUM[1] = 255;
if (SUM[1] < 0)
SUM[1] = 0;
if (SUM[2] > 255)
SUM[2] = 255;
if (SUM[2] < 0)
SUM[2] = 0;
int newPixel[3];
newPixel[0] = (255 - ((unsigned char) (SUM[0])));
newPixel[1] = (255 - ((unsigned char) (SUM[1])));
newPixel[2] = (255 - ((unsigned char) (SUM[2])));
pixel16 pix(newPixel[0], newPixel[1], newPixel[2]);
buffer[X] = packColour(pix).packed565;
}
//Need to move cursor back
// draw it
this->paintRow(Point(0, Y), buffer, _gl->getWidth());
}
delete[] buffer;
}
Version2
/**
* https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/image-processing/edge_detection.html
* 1 Iterate over every pixel in the image
* 2 Apply the x gradient kernel
* 3 Apply the y gradient kernel
* 4 Find the length of the gradient using pythagoras' theorem
* 5 Normalise the gradient length to the range 0-255
* 6 Set the pixels to the new values
*/
void sobelEdgeDetection4() {
UnpackedColour colour;
for (int x = 1; x < _gl->getWidth() - 1; x++) {
for (int y = 1; y < _gl->getHeight() - 1; y++) {
// initialise Gx and Gy to 0
int Gx = 0;
int Gy = 0;
unsigned int intensity = 0;
// Left column
pixel16 pixel = this->getPixel(x - 1, y - 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += -intensity;
Gy += -intensity;
pixel = this->getPixel(x - 1, y);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += -2 * intensity;
pixel = this->getPixel(x - 1, y + 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += -intensity;
Gy += +intensity;
// middle column
pixel = this->getPixel(x, y - 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gy += -2 * intensity;
pixel = this->getPixel(x, y + 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gy += +2 * intensity;
// right column
pixel = this->getPixel(x + 1, y - 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += +intensity;
Gy += -intensity;
pixel = this->getPixel(x + 1, y);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += +2 * intensity;
pixel = this->getPixel(x + 1, y + 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += +intensity;
Gy += +intensity;
// calculate the gradient length
unsigned int length = (unsigned int) sqrt(
(float) (Gx * Gx) + (float) (Gy * Gy));
// normalise the length to 0 to 255
length = length / 17;
// draw the pixel on the edge image
pixel16 pixel2(length,length,length);
this->setPixel(x, y, pixel2);
}
}
}
Version 3
// sobel map for the x axis
const double _SOBEL_Gx[3][3] = { { -1.0, +0.0, +1.0 }, { -2.0, +0.0, +2.0 },
{ -1.0, +0.0, +1.0 } };
// sobel map for the y axis
const double _SOBEL_Gy[3][3] = { { +1.0, +2.0, +1.0 }, { +0.0, +0.0, +0.0 },
{ -1.0, -2.0, -1.0 } };
double get_sobel_gradient(int width, int height, int x, int y) {
double sobel_gradient_x = 0, sobel_gradient_y = 0;
int mx = 0, my = 0, sx = 0, sy = 0;
for (mx = x; mx < x + 3; mx++) {
sy = 0;
for (my = y; my < y + 3; my++) {
if (mx < width && my < height) {
//int r, g, b, idx;
int idx = (mx + width * my) * 3;
pixel16 pixVal = this->getPixel(idx);
//r = pixVal.Red;
//g = pixVal.Green;
//b = pixVal.Blue;
UnpackedColour col = this->packColour(pixVal);
sobel_gradient_x += col.packed565 * _SOBEL_Gx[sx][sy];
sobel_gradient_y += col.packed565 * _SOBEL_Gy[sx][sy];
}
sy++;
}
sx++;
}
return abs(sobel_gradient_x) + abs(sobel_gradient_y);
}
void sobelEdgeDetection3() {
double threshold = 50000.0;
UnpackedColour colour;
for (int y = 0; y < _gl->getHeight(); y++) {
for (int x = 0; x < _gl->getWidth(); x++) {
if (get_sobel_gradient(_gl->getWidth(), _gl->getHeight(), x, y)
>= threshold) {
colour.packed565 = 0x0000; //set white
} else {
colour.packed565 = 0xFFFF; //set black
}
this->setPixel(x, y, colour);
}
}
}
For Version 1, after you allocate 2 buffers (just use buffer and bufT), create 2 pointers to point to the current and previous rows, like this:
uint16_t *currentRow = buffer;
uint16_t *prevRow = bufT;
Inside the row loop, write to currentRow instead of buffer:
pixel16 pix(newPixel[0], newPixel[1], newPixel[2]);
currentRow[X] = packColour(pix).packed565;
Because the Sobel filter reads from the previous row, you can't overwrite a row until after you have finished calculating the filtered values for the row after it. So at the end of the loop, where you are currently calling paintRow(), draw the previous row (if one exists), and then swap the buffers so that the current becomes the previous, and the previous becomes the new current row (to be overwritten on the next pass through the loop). On the last row the current row is also drawn, because otherwise it won't be since the outer loop is about to terminate.
if(Y > 0) // draw the previous row if this is not the first row:
this->paintRow(Point(0, Y-1), prevRow, _gl->getWidth());
if(Y == _gl->getHeight()-1) // draw the current row if it is the last:
this->paintRow(Point(0, Y), currentRow, _gl->getWidth());
// swap row pointers:
uint16_t *temp = prevRow;
prevRow = currentRow;
currentRow = temp;
The same strategy should work for the other versions.
I have some Irrlicht code that generates a rectangular mesh given a width and height. Here is the code that generates the vertices and indices:
int iNumVertices = (width + 1) * (height + 1);
S3DVertex * vertices = new S3DVertex[iNumVertices];
memset(vertices,0,sizeof(S3DVertex) * iNumVertices);
for(int i=0;i<=height;++i)
{
for(int j=0;j<=width;++j)
{
int iIndex = (i*(width + 1)) + j;
vertices[iIndex].Pos.X = i * 2.0f;
vertices[iIndex].Pos.Y = 0.0f;
vertices[iIndex].Pos.Z = j * 2.0f;
vertices[iIndex].Color.color = 0xFFFFFFFF;
vertices[iIndex].TCoords.X = i;
vertices[iIndex].TCoords.Y = j;
}
}
int iNumIndices = 6 * width * height;
u16 * indices = new u16[iNumIndices];
for(int i=0;i<height;++i)
{
for(int j=0;j<width;++j)
{
int iIndex = ((i*width) + j) * 6;
int tmp_offset = j + (i * (width + 1));
indices[iIndex + 0] = tmp_offset + 1;
indices[iIndex + 1] = tmp_offset + width + 1;
indices[iIndex + 2] = tmp_offset;
indices[iIndex + 3] = tmp_offset + 1;
indices[iIndex + 4] = tmp_offset + width + 2;
indices[iIndex + 5] = tmp_offset + width + 1;
}
}
Then the vertices and indices are added to the mesh and the bounding box is recalculated:
SMeshBuffer * buffer = new SMeshBuffer();
buffer->append(vertices,iNumVertices,indices,iNumIndices);
buffer->recalculateBoundingBox();
However, when rendered, the bounding box is nowhere close to the right size:
The end result of this is that the mesh doesn't get rendered when the small bounding box goes behind the camera.
Turns out that the problem was that I was calling recalculateBoundingBox() on the buffer instead of the mesh.