OpenGL Combining multiple cubes into a single mesh [duplicate] - c++

This question already exists:
OpenGL Converting from busy vertex buffer to element buffer
Closed 6 months ago.
I am having trouble merging multiple simple objects into a single mesh with OpenGL.
Before I go on I am aware of instancing and I'm certain that it's not what I want.
I'm drawing a cube with the following buffers:
float cubeVertices[] = {
-0.5f, 0.5f, 0.5f, //front top left
0.5f, 0.5f, 0.5f, //front top right
-0.5f, -0.5f, 0.5f, //front bottom left
0.5f, -0.5f, 0.5f, //front bottom right
-0.5f, 0.5f, -0.5f, //back top left
0.5f, 0.5f, -0.5f, //back top right
-0.5f, -0.5f, -0.5f, //back bottom left
0.5f, -0.5f, -0.5f //back bottom right
};
unsigned int cubeIndices[] = {
0, 2, 1, //FRONT
1, 2, 3,
0, 1, 4, //TOP
4, 1, 5,
1, 3, 5, //RIGHT
5, 3, 7,
2, 7, 3, //BOTTOM
7, 2, 6,
6, 2, 0, //LEFT
0, 4, 6,
6, 4, 5, //BACK
5, 7, 6
};
I then call glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, nullptr) which works as wanted.
Now my problem is if I want to draw multiple cubes in the same draw call (Still don't want instancing).
My current way of rendering multiple cubes is by:
for (int y = 0; y < 2; ++y)
{
for (int x = 0; x < 2; ++x)
{
for (int z = 0; z < 2; ++z)
{
model = glm::mat4{1.0f};
model = glm::translate(model, glm::vec3{x, y, z});
shader.SetMat4("uModel", model);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, nullptr);
}
}
}
Which is fine for a 2x2x2 cube of cubes but if I want to go larger like 32x32x32 it starts to fall apart performance wise.
So I was wondering how I can merge this 2x2x2 cube into a single mesh and call glDrawElements(GL_TRIANGLES, 36 * 8, GL_UNSIGNED_INT, nullptr) or similar.
I've tried:
std::array<glm::vec3, 8> locations{};
for (int x = 0; x < 2; ++x)
{
for (int y = 0; y < 2; ++y)
{
for (int z = 0; z < 2; ++z)
{
locations[x + 2 * (y + 2 * z)] = glm::vec3{x, y, z};
}
}
}
glBindVertexArray(cubeVAO);
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO[1]);
glBufferData(GL_ARRAY_BUFFER, locations.size(), locations.data(), GL_DYNAMIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
glEnableVertexAttribArray(1);
glBindVertexArray(0);
And using gl_Position = uProjection * uView * uModel * vec4(aPos * aLocation, 1.0f); where uModel is just a glm::mat4{1.0f}, aPos is from cubeVertices and aLocation is from the locations array.
I've got a couple ideas on why it doesn't work which I'll list.
First off: aLocation is different for every vertex instead of staying constant for the entire cube and then changing to the next location. Not sure how to do that. I could copy and paste the same location for every vertex I assume but that seems to defeat the purpose of using an element buffer in the first place.
Second: cubeIndices[] only holds enough indices for a single cube so I could also fix that by copy pasting the cubeIndices 8 times but doesn't that defeat the purpose of using an element buffer if I just have the same data 8 times?
So maybe a double element buffer could work?
I know instancing does seem like a really obvious solution but I guarantee that it's not what I am after.
I'm quite new to OpenGL so I don't have the most solid grasp of what I'm doing yet so I'm just trying to figure OpenGL out by making small projects. If someone could help it would be greatly appreciated.

It really looks like it is instancing that you want.
Do your objects moves but the geometry of each one stays the same? instancing.
Does each object move with bones animations? instancing.
Does the geometry moves every frame and not bone animated? not instancing, dynamic buffers.
One of the bottleneck of GPU is main memory=>GPU data transfers. You will struggle to update a lot of geometry at once because you need to transfer all the vertex every frame (~100 times a sec).
Your 2nd code is good. If you have performance issues its for another reason.
If you try to draw 32*32*32 cubes, that's 6*2*32*32*32=393 216 triangles. That could be a lot depending on your hardware.
If you used a big index buffer, everything would be the same, but you would use 6*3*2*32*32*32*sizeof(UINT)=4.7MB instead of 6*3*2*sizeof(UINT)=144B of memory space for your index buffer.
If the vertex positions are changing, you don't have to update the index buffer, there is probably a way to select a different start vertex. There is in Direct3D but IDK for opengl.

Related

Why is my Index Generation Function not correctly building the triangle primitives?

I am trying to code a function which automatically populates a mesh's index vector container. The function should work without issue in theory as it generates the proper indices in their correct order; however, the triangles do not form! Instead, I am left with a single line.
My mesh generation code is supposed to build an octahedron and then render it in the main game loop. The mesh class is shown below in its entirety:
struct vertex
{
glm::vec3 position;
glm::vec3 color;
};
class Mesh
{
public:
GLuint VAO, VBO, EBO;
std::vector <vertex> vtx;
std::vector <glm::vec3> idx;
glm::mat4 modelMatrix = glm::mat4(1.f);
Mesh(glm::vec3 position, glm::vec3 scale)
{
vertexGen(6);
idx = indexGen(6);
modelMatrix = glm::scale(glm::translate(modelMatrix, position), scale);
initMesh();
};
void Render(Shader shaderProgram, Camera camera, bool wireframe)
{
glUseProgram(shaderProgram.ID);
glPatchParameteri(GL_PATCH_VERTICES, 3); // Indicates to the VAO that each group of three vertices is one patch (triangles)
glProgramUniformMatrix4fv(shaderProgram.ID, 0, 1, GL_FALSE, glm::value_ptr(modelMatrix));
glProgramUniformMatrix4fv(shaderProgram.ID, 1, 1, GL_FALSE, glm::value_ptr(camera.camMatrix));
glProgramUniform3fv(shaderProgram.ID, 2, 1, glm::value_ptr(camera.Position));
glBindVertexArray(VAO); // Binds the VAO to the shader program
if (wireframe)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_CULL_FACE);
}
else
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//glEnable(GL_CULL_FACE);
}
glDrawElements(GL_PATCHES, idx.size(), GL_UNSIGNED_INT, 0); // Tells the shader program how to draw the primitives
}
private:
void vertexGen(int n) {
// Populate the base six vertices
vtx.push_back(vertex{ glm::vec3( 0.0f, 0.5f, 0.0f), glm::vec3(0.f, 1.f, 0.f) });
vtx.push_back(vertex{ glm::vec3(-0.5f, 0.0f, 0.0f), glm::vec3(0.f, 1.f, 0.f) });
vtx.push_back(vertex{ glm::vec3( 0.0f, 0.0f, -0.5f), glm::vec3(0.f, 1.f, 0.f) });
vtx.push_back(vertex{ glm::vec3( 0.5f, 0.0f, 0.0f), glm::vec3(0.f, 1.f, 0.f) });
vtx.push_back(vertex{ glm::vec3( 0.0f, 0.0f, 0.5f), glm::vec3(0.f, 1.f, 0.f) });
vtx.push_back(vertex{ glm::vec3( 0.0f,-0.5f, 0.0f), glm::vec3(0.f, 1.f, 0.f) });
}
std::vector<glm::vec3> indexGen(int n) {
std::vector<glm::vec3> indices;
// Calculate the indices for the top 4 triangles
indices.push_back(glm::vec3( 0, n - 5, n - 4 ));
indices.push_back(glm::vec3( 0, n - 4, n - 3 ));
indices.push_back(glm::vec3( 0, n - 3, n - 2 ));
indices.push_back(glm::vec3( 0, n - 2, n - 5 ));
// Calculate the indices for the bottom 4 triangles
indices.push_back(glm::vec3( 5, n - 5, n - 4));
indices.push_back(glm::vec3( 5, n - 4, n - 3));
indices.push_back(glm::vec3( 5, n - 3, n - 2));
indices.push_back(glm::vec3( 5, n - 2, n - 5));
return indices;
}
void initMesh()
{
glCreateVertexArrays(1, &VAO); // Sets the address of the uint VAO as the location of a gl vertex array object
glCreateBuffers(1, &VBO); // Sets the address of the uint VBO as the location of a gl buffer object
glCreateBuffers(1, &EBO); // Sets the address of the uint EBO as the location of a gl buffer object
glNamedBufferData(VBO, vtx.size() * sizeof(vtx[0]), vtx.data(), GL_STATIC_DRAW); // Sets the data of the buffer named VBO
glNamedBufferData(EBO, idx.size() * sizeof(idx[0]), idx.data(), GL_STATIC_DRAW); // Sets the data of the buffer named EBO
glEnableVertexArrayAttrib(VAO, 0); // Enables an attribute of the VAO in location 0
glEnableVertexArrayAttrib(VAO, 1); // Enables an attribute of the VAO in location 1
glVertexArrayAttribBinding(VAO, 0, 0); // Layout Location of Position Vectors
glVertexArrayAttribBinding(VAO, 1, 0); // Layout Location of Color Values
glVertexArrayAttribFormat(VAO, 0, 3, GL_FLOAT, GL_FALSE, 0); // Size, and Type of Position Vectors
glVertexArrayAttribFormat(VAO, 1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat)); // For the Color Values
glVertexArrayVertexBuffer(VAO, 0, VBO, 0, 6 * sizeof(GLfloat)); // Sets the VBO to indicate the start, offset, and stride of vertex data in the VAO
glVertexArrayElementBuffer(VAO, EBO); // Sets the EBO to index the VAO vertex connections
}
};
I took this problem step by step and did all of the basic math on paper. The index generation function returns the expected indices in their correct order as just having the indices written out, but it differs in that the written-out indices generate the desired result whereas the generation function only produces a single line when rendered:
I suspect that the issue lies in my mesh initialization function (initMesh), specifically in the glNamedBufferData or glVertexArrayVertexBuffer, but my knowledge of the functions is very limited. I tried changing the parameter of the glNamedBufferData function to different variations of idx.size()*sizeof(idx[0].x), but that yielded the same results, so I am at a loss. Could someone help me fix this, please?
glm::vec3 is a vector of floats (I think) but you are telling OpenGL to read them as unsigned ints.
Float 0.0 is 0x00000000 (i.e. same as int 0), but float 1.0 is 0x3f800000 (same as int 1065353216). They aren't compatible ways to store numbers. You could try glm::ivec3 which is a vector of ints, but I think most people would use std::vector<int> (or unsigned int) and use 3 entries per triangle.
I think it's okay in this case, but I don't like to use types like ivec3 when I mean to have 3 separate ints isn't always a good practice, because the compiler can insert padding in unexpected places. It's possible that on some platforms, ivec3 could be 3 ints and an extra 4 bytes of padding, making 16 bytes in total, and the extra padding bytes throw off the layout you're relying on. glDrawArrays wouldn't skip over padding after every 3 indices and there would be no way to tell it to do that. It's okay for vertices, since you can tell OpenGL exactly where the data is.

Align a matrix to a vector in OpenGL

I'm trying to visualize normals of triangles.
I have created a triangle to use as the visual representation of the normal but I'm having trouble aligning it to the normal.
I have tried using glm::lookAt but the triangle ends up in some weird position and rotation after that. I am able to move the triangle in the right place with glm::translate though.
Here is my code to create the triangle which is used for the visualization:
// xyz rgb
float vertex_data[] =
{
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.25f, 0.0f, 0.025f, 0.0f, 1.0f, 1.0f,
0.25f, 0.0f, -0.025f, 0.0f, 1.0f, 1.0f,
};
unsigned int index_data[] = {0, 1, 2};
glGenVertexArrays(1, &nrmGizmoVAO);
glGenBuffers(1, &nrmGizmoVBO);
glGenBuffers(1, &nrmGizmoEBO);
glBindVertexArray(nrmGizmoVAO);
glBindBuffer(GL_ARRAY_BUFFER, nmrGizmoVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, nrmGizmoEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index_data), index_data, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
and here is the code to draw the visualizations:
for(unsigned int i = 0; i < worldTriangles->size(); i++)
{
Triangle *tri = &worldTriangles->at(i);
glm::vec3 wp = tri->worldPosition;
glm::vec3 nrm = tri->normal;
nrmGizmoMatrix = glm::mat4(1.0f);
//nrmGizmoMatrix = glm::translate(nrmGizmoMatrix, wp);
nrmGizmoMatrix = glm::lookAt(wp, wp + nrm, glm::vec3(0.0f, 1.0f, 0.0f));
gizmoShader.setMatrix(projectionMatrix, viewMatrix, nrmGizmoMatrix);
glBindVertexArray(nrmGizmoVAO);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
When using only glm::translate, the triangles appear in right positions but all point in the same direction. How can I rotate them so that they point in the direction of the normal vector?
Your code doesn't work because lookAt is intended to be used as the view matrix, thus it returns the transform from world space to local (camera) space. In your case you want the reverse -- from local (triangle) to world space. Taking an inverse of lookAt should solve that.
However, I'd take a step back and look at (haha) the bigger picture. What I notice about your approach:
It's very inefficient -- you issue a separate call with a different model matrix for every single normal.
You don't even need the entire model matrix. A triangle is a 2-d shape, so all you need is two basis vectors.
I'd instead generate all the vertices for the normals in a single array, and then use glDrawArrays to draw that. For the actual calculation, observe that we have one degree of freedom when it comes to aligning the triangle along the normal. Your lookAt code resolves that DoF rather arbitrary. A better way to resolve that is to constrain it by requiring that it faces towards the camera, thus maximizing the visible area. The calculation is straightforward:
// inputs: vertices output array, normal position, normal direction, camera position
void emit_normal(std::vector<vec3> &v, const vec3 &p, const vec3 &n, const vec3 &c) {
static const float length = 0.25f, width = 0.025f;
vec3 t = normalize(cross(n, c - p)); // tangent
v.push_back(p);
v.push_back(p + length*n + width*t);
v.push_back(p + length*n - width*t);
}
// ... in your code, generate normals through:
std::vector<vec3> normals;
for(unsigned int i = 0; i < worldTriangles->size(); i++) {
Triangle *tri = &worldTriangles->at(i);
emit_normal(normals, tri->worldPosition, tri->normal, camera_position);
}
// ... create VAO for normals ...
glDrawArrays(GL_TRIANGLES, 0, normals.size());
Note, however, that this would make the normal mesh camera-dependent -- which is desirable when rendering normals with triangles. Most CAD software draws normals with lines instead, which is much simpler and avoids many problems:
void emit_normal(std::vector<vec3> &v, const vec3 &p, const vec3 &n) {
static const float length = 0.25f;
v.push_back(p);
v.push_back(p + length*n);
}
// ... in your code, generate normals through:
std::vector<vec3> normals;
for(unsigned int i = 0; i < worldTriangles->size(); i++) {
Triangle *tri = &worldTriangles->at(i);
emit_normal(normals, tri->worldPosition, tri->normal);
}
// ... create VAO for normals ...
glDrawArrays(GL_LINES, 0, normals.size());

Trying to Make Endless Runner C++ with OpenGL

I have an array of cube objects initialised like so (index 0 not used here as that's for the player):
game_object[1] = new GameObject();
game_object[1]->setPosition(vec3(7.0f, 0.0f, 0.0f));
game_object[2] = new GameObject();
game_object[2]->setPosition(vec3(14.0f, 0.0f, 0.0f));
game_object[3] = new GameObject();
game_object[3]->setPosition(vec3(21.0f, 0.0f, 0.0f));
game_object[4] = new GameObject();
game_object[4]->setPosition(vec3(36.0f, 0.0f, 0.0f));
game_object[5] = new GameObject();
game_object[5]->setPosition(vec3(42.0f, 0.0f, 0.0f));
I have a render function in which they are drawn:
glDrawElements(GL_TRIANGLES, 3 * INDICES, GL_UNSIGNED_INT, NULL);
In my update they move to the left as expected. To do this I am just adding another vector to their positions:
for (int i = 1; i < MAX_CUBES; i++)
{
game_object[i]->setPosition(game_object[i]->getPosition() + vec3(-0.03, 0.0, 0.00));
}
However, I want the cubes to repeat this until the user exits the game. I made a reset function to send them back to their starting positions:
void Game::reset()
{
game_object[0]->setPosition(vec3(0.0f, 0.0f, 0.0f));
game_object[1]->setPosition(vec3(7.0f, 0.0f, 0.0f));
game_object[2]->setPosition(vec3(14.0f, 0.0f, 0.0f));
game_object[3]->setPosition(vec3(21.0f, 0.0f, 0.0f));
game_object[4]->setPosition(vec3(36.0f, 0.0f, 0.0f));
game_object[5]->setPosition(vec3(42.0f, 0.0f, 0.0f));
}
This function gets called in the update when the final cube's position is off screen to the left:
if (game_object[5]->getPosition().x <= 0.0)
{
reset();
}
However, this isn't working. Nothing resets after the last cube goes to the left.
Not sure how you are using game_object here but looks very error prone. If you have MAX_CUBES = 5 (as you do have 5 cubes), then that for-loop will miss the last one. Adding further objects (e.g. for gaps, vertical rules, hazards, etc.) will make it even more so.
for (int i = 1; i < MAX_CUBES; i++)
{
game_object[i]->setPosition(game_object[i]->getPosition() + vec3(-0.03, 0.0, 0.00));
}
If MAX_CUBES = 5, then it will move index 1, 2, 3, 4, and not 5, which is the one you check in the condition. 5 will just stay at 42 permanently (is that off-screen?).
Stepping through the code in a debugger will make a problem like this pretty clear regardless, and is an essential tool for programming. Maybe the code just never reaches the if (game_object[5]->getPosition().x <= 0.0) check in the first place? Is there any return in that update function, or is that condition inside another one of some sorts?
Because in your comment you noted that game_object[5]->getPosition().x returns a correct value, the most likely problem is with your reset() function and the setPosition function you are using.
1. Check if set position is working in the first place
Perhaps there is an error with setPosition().
After you set the position using setPosition() and then log the object's coordinates using getPosition() does it return the position you expect?
If not, something is wrong with setPosition.
If so, then...
2. You probably changed the position but failed to render it!
This is a very common problem lol
There is a very high chance you changed the position of the object BUT didn't update what's shown on the screen!
3. Side note for scalability
There is a much more efficient and scalable way of doing a reset if you have eventually have more than 5 objects, by placing their reset values in an array and looping through them:
#define MAX_CUBES 6
double resetPositions_x[MAX_CUBES] = {0.0, 7.0, 14.0, 21.0, 36.0, 42.0};
void Game::reset()
{
for(int i=0;i<MAX_CUBES;i++){
game_object[i]->setPosition(vec3(resetPositions_x[i], 0.0f, 0.0f));
}
}
(Also, it seems every x reset position is a multiple of 7 except 36.0 -> is that a mistake?)

openGL es 2.0 TextureCoordinates parameter ios

I have to crop a video.
So i modify the TextureCoordinates.
The code is as below.
const GLfloat squareTextureCoordinates[] = {
0.22f , 1.0f,
0.22f, 0.0f,
0.78f, 1.0f,
0.78f, 0.0f,
};
glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, 0, 0, squareVertices);
glVertexAttribPointer(inputTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, squareTextureCoordinates);
This code is work.
However,i need to support many size of iphone .(example:iphone6 have different size to iphone5)
So i use variable rather than const in TextureCoordinates.
And the new code is as below;
const float cropX=(float)(SCREEN_HEIGHT-SCREEN_WIDTH)/(float)(SCREEN_HEIGHT*2);
const float cropXR=1- (float)(SCREEN_HEIGHT-SCREEN_WIDTH)/(float)(SCREEN_HEIGHT*2);
const GLfloat squareTextureCoordinates[] = {
cropX , 1.0f,
cropX, 0.0f,
cropXR, 1.0f,
cropXR, 0.0f,
};
glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, 0, 0, squareVertices);
glVertexAttribPointer(inputTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, squareTextureCoordinates);
However, it doesn't work.
The screen is display nothing.
i cant figure out why i can't use const variable for TextureCoordinates.
I think you could test the second part of your code by changing the const values to the constants you used in the first part:
const float cropX = 0.22f;
const float cropXR = 0.78f;
const GLfloat squareTextureCoordinates[] = {
cropX , 1.0f,
cropX, 0.0f,
cropXR, 1.0f,
cropXR, 0.0f,
};
glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, 0, 0, squareVertices);
glVertexAttribPointer(inputTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, squareTextureCoordinates);
If this works (it should), you will see you can use const variables as a texture coordinate. However, I reckon the wrong part of the code is this:
const float cropX=(float)(SCREEN_HEIGHT-SCREEN_WIDTH)/(float)(SCREEN_HEIGHT*2);
const float cropXR=1- (float)(SCREEN_HEIGHT-SCREEN_WIDTH)/(float)(SCREEN_HEIGHT*2);
In texture coordinates, correct values are between 0 and 1. If width is bigger than height, this code won't work. In the specific case of an iPhone 5 and 6, which have the same proportion, same texture coordinates should work if the view that renders OpenGL code was resized (if you use autolayout for that view, you will probably solve the problem automatically).

DirectX9 C++ Recoloring Vertex data in real time

I'm very new to DirectX and I'm starting to get a grasp on how the API functions.
I've managed to get triangles showing and rendering properly using these functions:
Initializing the vertices:
void Menu::InitializeMenu(float x, float y, float width, float height, D3DCOLOR color, IDirect3DDevice9* d3dDevice)
{
CUSTOMVERTEX vertices[] =
{
{ x, y, 0.5f, 1.0f, color },
{ x + width, y, 0.5f, 1.0f, color },
{ x + width, y + height, 0.5f, 1.0f, color },
{ x, y, 0.5f, 1.0f, color },
{ x , y + height, 0.5f, 1.0f, color },
{ x + width, y + height, 0.5f, 1.0f, color },
};
if (FAILED(d3dDevice->CreateVertexBuffer(6 * sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_vertexBuffer, NULL)))
return;
void *locked_buffer;
if (FAILED(m_vertexBuffer->Lock(0, sizeof(vertices), (void **)&locked_buffer, 0)))
return;
memcpy(locked_buffer, vertices, sizeof(vertices));
m_vertexBuffer->Unlock();
}
Everything here is defined within the Menu class.
Drawing:
void Menu::RenderMenu(IDirect3DDevice9 *d3dDevice)
{
d3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
d3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
d3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DRS_DESTBLENDALPHA);
d3dDevice->SetStreamSource(0, m_vertexBuffer, 0, sizeof(CUSTOMVERTEX));
d3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
}
Everything works perfect, I get my two triangles rendered, which in turn produce a semi-transparent quad.
Now the Issue:
I want to be able to change the colors of the vertices in my triangles after the program has started rendering (everything has been initialized already and rendered at least once).
Things I've thought about:
-I've thought about calling the InitializeMenu function with different parameters to reinitialize the vertices with different color, reason I haven't done it is because it seems very inefficient and not practical.
-Materials: I have not implemented materials, this is because I don't know how (yet) and because I'm hoping to find a simpler alternative. All I need is the vertex colors. If materials are the only way to accomplish this, I will implement.
-Shaders: I understand you can color vertices with shaders, but I have very little shader experience, and as stated before I'd rather find a simpler alternative. Yes, I know shaders are simple, I've gotten to the point where I can change the color of vertices in a shader in real time. It was in GLSL but I'm sure it doesn't differ too much. Issue comes when I want to add multiple quads (collection of 2 triangles for ea quad). I only know how to change the color of all vertices coming into the vertex shader. As before though, if shaders is the only way to accomplish, I'll implement. Please just point me in the right direction. I have VERY little understanding on how shaders work on the low level (I understand the concept and flow, just don't know how to use to my advantage to use effectively).
-Research: I've looked everywhere, maybe I'm not asking my question properly, but I cannot find an answer anywhere.
This is actually my first time posting a question, usually someone has already asked my questions. I've tried to explain my problem as best as I could, but if it's still unclear feel free to ask for more code or information.
P.S: I'm using windows 8 desktop, not sure if that really matters.
To update the vertices, you will need to do something similar to InitializeMenu, but without calling CreateVertexBuffer again. You will also need make a slight modification to how you create the vertex buffer.
There are two types of vertex buffers: static and dynamic. A static vertex buffer does not allow access by the CPU to make changes whereas a dynamic vertex buffer does.
To create a dynamic vertex buffer, add D3DUSAGE_DYNAMIC to CreateVertexBuffer:
d3dDevice->CreateVertexBuffer(6 * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_vertexBuffer, NULL)
You can then make a new function like this to change color:
void Menu::ChangeColor(D3DColor color) {
CUSTOMVERTEX *locked_buffer;
if (FAILED(m_vertexBuffer->Lock(0, 0, (void **)&locked_buffer, 0))) {
return;
}
for (int i=0; i<6; i++) {
// use whatever you called color in your CUSTOMVERTEX struct
locked_buffer[i].color = color;
}
m_vertexBuffer->Unlock();
}
This code essentially gets the vertex data from the GPU and allows you to update it on the CPU. You don't need to recreate the vertex buffer, you just update the values you want.
You can define a random function and then add color elements i.e: Green, Blue, Yellow, Red, Purple etc into an array. Then you call the random function to randomly select the colors inside an array.
Like this:
int arr[15] = {10, 210, 140, 180, 250, 189, 183, 107, 183, 107, 60, 2, 55, 85, 48};
D3DCOLOR color1; D3DCOLOR color2; D3DCOLOR color3; D3DCOLOR color4;
srand(time(NULL));
CUSTOMVERTEX vertices[] =
{{ x, y, 0.5f, 1.0f, color = D3DCOLOR_XRGB(arr[rand() % 14 + 0], arr[rand() % 14 + 0], arr[rand() % 14 + 0])},
{ x + width, y, 0.5f, 1.0f, color = D3DCOLOR_XRGB(arr[rand() % 14 + 0], arr[rand() % 14 + 0], arr[rand() % 14 + 0])},
{ x + width, y + height, 0.5f, 1.0f, color = D3DCOLOR_XRGB(arr[rand() % 14 + 0], arr[rand() % 14 + 0], arr[rand() % 14 + 0])},
{ x, y, 0.5f, 1.0f, color = D3DCOLOR_XRGB(arr[rand() % 14 + 0], arr[rand() % 14 + 0], arr[rand() % 14 + 0])},
{ x , y + height, 0.5f, 1.0f, color = D3DCOLOR_XRGB(arr[rand() % 14 + 0], arr[rand() % 14 + 0], arr[rand() % 14 + 0])},
{ x + width, y + height, 0.5f, 1.0f, color = D3DCOLOR_XRGB(arr[rand() % 14 + 0], arr[rand() % 14 + 0], arr[rand() % 14 + 0])},
};
One thing, you might have to use different D3DCOLOR color variables i.e; color1, color2, color3 etc to avoid dereferencing and value overwritting.