Related
I'm trying to combine texture and lighting on a pyramid in OpenGL. I basically started by merging two separate codes, and now, I'm working to make changes to smooth out the merge. However, I am having 2 issues.
I need to remove the object color and replace it with texture, but I'm not sure how to approach that issue with this code since object color is deeply ingrained in the code.
I'm not sure how to list the coordinates for position, normals, and texture. Their current arrangement seems to be causing a lot of issues with the output.
For issue one, I have tried replacing pyramidColor and objectColor with texture, but it seemed to create more issues.
For issue two, I have tried rearranging the list order as position, texture, and normals, which helped for a few of the triangles. However, it still isn't right.
/*Header Inclusions*/
#include <iostream>
#include <GL/glew.h>
#include <GL/freeglut.h>
//GLM Math Header Inclusions
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
//SOIL image loader Inclusion
#include "SOIL2/SOIL2.h"
using namespace std; //Standard namespace
#define WINDOW_TITLE "Pyramid" //Window title Macro
/*Shader program Macro*/
#ifndef GLSL
#define GLSL(Version, Source) "#version " #Version "\n" #Source
#endif
/*Variable declarations for shader, window size initialization, buffer and array objects */
GLint pyramidShaderProgram, lampShaderProgram, WindowWidth = 800, WindowHeight = 600;
GLuint VBO, PyramidVAO, LightVAO, texture;
//Subject position and scale
glm::vec3 pyramidPosition(0.0f, 0.0f, 0.0f);
glm::vec3 pyramidScale(2.0f);
//pyramid and light color
glm::vec3 objectColor(1.0f, 1.0f, 1.0f);
glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
//Light position and scale
glm::vec3 lightPosition(0.5f, 0.5f, -3.0f);
glm::vec3 lightScale(0.3f);
//Camera position
glm::vec3 cameraPosition(0.0f, 0.0f, -6.0f);
//Camera rotation
float cameraRotation = glm::radians(-25.0f);
/*Function prototypes*/
void UResizeWindow(int, int);
void URenderGraphics(void);
void UCreateShader(void);
void UCreateBuffers(void);
void UGenerateTexture(void);
/*Pyramid Vertex Shader Source Code*/
const GLchar * pyramidVertexShaderSource = GLSL(330,
layout (location = 0) in vec3 position; //Vertex data from Vertex Attrib Pointer 0
layout (location = 1) in vec3 normal; //VAP position 1 for normals
layout (location = 2) in vec2 textureCoordinate;
out vec3 FragmentPos; //For outgoing color / pixels to fragment shader
out vec3 Normal; //For outgoing normals to fragment shader
out vec2 mobileTextureCoordinate;
//Global variables for the transform matrices
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main(){
gl_Position = projection * view * model * vec4(position, 1.0f); //transforms vertices to clip coordinates
FragmentPos = vec3(model * vec4(position, 1.0f)); //Gets fragment / pixel position in world space only (exclude view and projection)
Normal = mat3(transpose(inverse(model))) * normal; //get normal vectors in world space only and exclude normal translation properties
mobileTextureCoordinate = vec2(textureCoordinate.x, 1 - textureCoordinate.y); //flips the texture horizontal
}
);
/*Pyramid Fragment Shader Source Code*/
const GLchar * pyramidFragmentShaderSource = GLSL(330,
in vec3 FragmentPos; //For incoming fragment position
in vec3 Normal; //For incoming normals
in vec2 mobileTextureCoordinate;
out vec4 pyramidColor; //For outgoing pyramid color to the GPU
out vec4 gpuTexture; //Variable to pass color data to the GPU
//Uniform / Global variables for object color, light color, light position, and camera/view position
uniform vec3 objectColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 viewPosition;
uniform sampler2D uTexture; //Useful when working with multiple textures
void main(){
/*Phong lighting model calculations to generate ambient, diffuse, and specular components*/
//Calculate Ambient Lighting
float ambientStrength = 0.1f; //Set ambient or global lighting strength
vec3 ambient = ambientStrength * lightColor; //Generate ambient light color
//Calculate Diffuse Lighting
vec3 norm = normalize(Normal); //Normalize vectors to 1 unit
vec3 lightDirection = normalize(lightPos - FragmentPos); //Calculate distance (light direction) between light source and fragments/pixels on
float impact = max(dot(norm, lightDirection), 0.0); //Calculate diffuse impact by generating dot product of normal and light
vec3 diffuse = impact * lightColor; //Generate diffuse light color
//Calculate Specular lighting
float specularIntensity = 0.8f; //Set specular light strength
float highlightSize = 128.0f; //Set specular highlight size
vec3 viewDir = normalize(viewPosition - FragmentPos); //Calculate view direction
vec3 reflectDir = reflect(-lightDirection, norm); //Calculate reflection vector
//Calculate specular component
float specularComponent = pow(max(dot(viewDir, reflectDir), 0.0), highlightSize);
vec3 specular = specularIntensity * specularComponent * lightColor;
//Calculate phong result
vec3 phong = (ambient + diffuse + specular) * objectColor;
pyramidColor = vec4(phong, 1.0f); //Send lighting results to GPU
gpuTexture = texture(uTexture, mobileTextureCoordinate);
}
);
/*Lamp Shader Source Code*/
const GLchar * lampVertexShaderSource = GLSL(330,
layout (location = 0) in vec3 position; //VAP position 0 for vertex position data
//Uniform / Global variables for the transform matrices
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view *model * vec4(position, 1.0f); //Transforms vertices into clip coordinates
}
);
/*Fragment Shader Source Code*/
const GLchar * lampFragmentShaderSource = GLSL(330,
out vec4 color; //For outgoing lamp color (smaller pyramid) to the GPU
void main()
{
color = vec4(1.0f); //Set color to white (1.0f, 1.0f, 1.0f) with alpha 1.0
}
);
/*Main Program*/
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(WindowWidth, WindowHeight);
glutCreateWindow(WINDOW_TITLE);
glutReshapeFunc(UResizeWindow);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
std::cout<< "Failed to initialize GLEW" << std::endl;
return -1;
}
UCreateShader();
UCreateBuffers();
UGenerateTexture();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //Set background color
glutDisplayFunc(URenderGraphics);
glutMainLoop();
//Destroys Buffer objects once used
glDeleteVertexArrays(1, &PyramidVAO);
glDeleteVertexArrays(1, &LightVAO);
glDeleteBuffers(1, &VBO);
return 0;
}
/*Resizes the window*/
void UResizeWindow(int w, int h)
{
WindowWidth = w;
WindowHeight = h;
glViewport(0, 0, WindowWidth, WindowHeight);
}
/*Renders graphics*/
void URenderGraphics(void)
{
glEnable(GL_DEPTH_TEST); //Enable z-depth
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clears the screen
GLint modelLoc, viewLoc, projLoc, objectColorLoc, lightColorLoc, lightPositionLoc, viewPositionLoc;
glm::mat4 model;
glm::mat4 view;
glm::mat4 projection;
/*********Use the pyramid Shader to activate the pyramid Vertex Array Object for rendering and transforming*********/
glUseProgram(pyramidShaderProgram);
glBindVertexArray(PyramidVAO);
//Transform the pyramid
model = glm::translate(model, pyramidPosition);
model = glm::scale(model, pyramidScale);
//Transform the camera
view = glm::translate(view, cameraPosition);
view = glm::rotate(view, cameraRotation, glm::vec3(0.0f, 1.0f, 0.0f));
//Set the camera projection to perspective
projection = glm::perspective(45.0f,(GLfloat)WindowWidth / (GLfloat)WindowHeight, 0.1f, 100.0f);
//Reference matrix uniforms from the pyramid Shader program
modelLoc = glGetUniformLocation(pyramidShaderProgram, "model");
viewLoc = glGetUniformLocation(pyramidShaderProgram, "view");
projLoc = glGetUniformLocation(pyramidShaderProgram, "projection");
//Pass matrix data to the pyramid Shader program's matrix uniforms
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
//Reference matrix uniforms from the pyramid Shader program for the pyramid color, light color, light position, and camera position
objectColorLoc = glGetUniformLocation(pyramidShaderProgram, "objectColor");
lightColorLoc = glGetUniformLocation(pyramidShaderProgram, "lightColor");
lightPositionLoc = glGetUniformLocation(pyramidShaderProgram, "lightPos");
viewPositionLoc = glGetUniformLocation(pyramidShaderProgram, "viewPosition");
//Pass color, light, and camera data to the pyramid Shader programs corresponding uniforms
glUniform3f(objectColorLoc, objectColor.r, objectColor.g, objectColor.b);
glUniform3f(lightColorLoc, lightColor.r, lightColor.g, lightColor.b);
glUniform3f(lightPositionLoc, lightPosition.x, lightPosition.y, lightPosition.z);
glUniform3f(viewPositionLoc, cameraPosition.x, cameraPosition.y, cameraPosition.z);
glDrawArrays(GL_TRIANGLES, 0, 18); //Draw the primitives / pyramid
glBindVertexArray(0); //Deactivate the Pyramid Vertex Array Object
/***************Use the Lamp Shader and activate the Lamp Vertex Array Object for rendering and transforming ************/
glUseProgram(lampShaderProgram);
glBindVertexArray(LightVAO);
//Transform the smaller pyramid used as a visual cue for the light source
model = glm::translate(model, lightPosition);
model = glm::scale(model, lightScale);
//Reference matrix uniforms from the Lamp Shader program
modelLoc = glGetUniformLocation(lampShaderProgram, "model");
viewLoc = glGetUniformLocation(lampShaderProgram, "view");
projLoc = glGetUniformLocation(lampShaderProgram, "projection");
//Pass matrix uniforms from the Lamp Shader Program
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
glBindTexture(GL_TEXTURE_2D, texture);
//Draws the triangles
glDrawArrays(GL_TRIANGLES, 0, 18);
glBindVertexArray(0); //Deactivate the Lamp Vertex Array Object
glutPostRedisplay();
glutSwapBuffers(); //Flips the back buffer with the front buffer every frame. Similar to GL Flush
}
/*Create the Shader program*/
void UCreateShader()
{
//Pyramid Vertex shader
GLint pyramidVertexShader = glCreateShader(GL_VERTEX_SHADER); //Creates the Vertex shader
glShaderSource(pyramidVertexShader, 1, &pyramidVertexShaderSource, NULL); //Attaches the Vertex shader to the source code
glCompileShader(pyramidVertexShader); //Compiles the Vertex shader
//Pyramid Fragment Shader
GLint pyramidFragmentShader = glCreateShader(GL_FRAGMENT_SHADER); //Creates the Fragment Shader
glShaderSource(pyramidFragmentShader, 1, &pyramidFragmentShaderSource, NULL); //Attaches the Fragment shader to the source code
glCompileShader(pyramidFragmentShader); //Compiles the Fragment Shader
//Pyramid Shader program
pyramidShaderProgram = glCreateProgram(); //Creates the Shader program and returns an id
glAttachShader(pyramidShaderProgram, pyramidVertexShader); //Attaches Vertex shader to the Shader program
glAttachShader(pyramidShaderProgram, pyramidFragmentShader); //Attaches Fragment shader to the Shader program
glLinkProgram(pyramidShaderProgram); //Link Vertex and Fragment shaders to the Shader program
//Delete the Vertex and Fragment shaders once linked
glDeleteShader(pyramidVertexShader);
glDeleteShader(pyramidFragmentShader);
//Lamp Vertex shader
GLint lampVertexShader = glCreateShader(GL_VERTEX_SHADER); //Creates the Vertex shader
glShaderSource(lampVertexShader, 1, &lampVertexShaderSource, NULL); //Attaches the Vertex shader to the source code
glCompileShader(lampVertexShader); //Compiles the Vertex shader
//Lamp Fragment shader
GLint lampFragmentShader = glCreateShader(GL_FRAGMENT_SHADER); //Creates the Fragment shader
glShaderSource(lampFragmentShader, 1, &lampFragmentShaderSource, NULL); //Attaches the Fragment shader to the source code
glCompileShader(lampFragmentShader); //Compiles the Fragment shader
//Lamp Shader Program
lampShaderProgram = glCreateProgram(); //Creates the Shader program and returns an id
glAttachShader(lampShaderProgram, lampVertexShader); //Attach Vertex shader to the Shader program
glAttachShader(lampShaderProgram, lampFragmentShader); //Attach Fragment shader to the Shader program
glLinkProgram(lampShaderProgram); //Link Vertex and Fragment shaders to the Shader program
//Delete the lamp shaders once linked
glDeleteShader(lampVertexShader);
glDeleteShader(lampFragmentShader);
}
/*Creates the Buffer and Array Objects*/
void UCreateBuffers()
{
//Position and Texture coordinate data for 18 triangles
GLfloat vertices[] = {
//Positions //Normals //Texture Coordinates
//Back Face //Negative Z Normals
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.5f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
//Front Face //Positive Z Normals
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
//Left Face //Negative X Normals
0.0f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.5f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
//Right Face //Positive X Normals
0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
//Bottom Face //Negative Y Normals
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
};
//Generate buffer ids
glGenVertexArrays(1, &PyramidVAO);
glGenBuffers(1, &VBO);
//Activate the PyramidVAO before binding and setting VBOs and VAPs
glBindVertexArray(PyramidVAO);
//Activate the VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //Copy vertices to VBO
//Set attribute pointer 0 to hold position data
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0); //Enables vertex attribute
//Set attribute pointer 1 to hold Normal data
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
//Set attribute pointer 2 to hold Texture coordinate data
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
glEnableVertexAttribArray(2);
glBindVertexArray(0); //Unbind the pyramid VAO
//Generate buffer ids for lamp (smaller pyramid)
glGenVertexArrays(1, &LightVAO); //Vertex Array for pyramid vertex copies to serve as light source
//Activate the Vertex Array Object before binding and setting any VBOs and Vertex Attribute Pointers
glBindVertexArray(LightVAO);
//Referencing the same VBO for its vertices
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//Set attribute pointer to 0 to hold Position data (used for the lamp)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
}
/*Generate and load the texture*/
void UGenerateTexture(){
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
int width, height;
unsigned char* image = SOIL_load_image("brick.jpg", &width, &height, 0, SOIL_LOAD_RGB); //Loads texture file
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
glBindTexture(GL_TEXTURE_2D, 0); //Unbind the texture
}
Expected results: A brick textured pyramid with lighting.
Actual results: A bunch of assorted triangles.
I see the following issues with your code:
In the fragment shader:
Remove the objectColor uniform and the gpuTexture output.
Replace the last three lines of main() with:
//Calculate phong result
vec3 objectColor = texture(uTexture, mobileTextureCoordinate).xyz;
vec3 phong = (ambient + diffuse) * objectColor + specular;
pyramidColor = vec4(phong, 1.0f); //Send lighting results to GPU
In your rendering code:
Replace all mentions of objectColor with texture setup:
uTextureLoc = glGetUniformLocation(pyramidShaderProgram, "uTexture");
glUniform1i(uTextureLoc, 0); // texture unit 0
Bind the texture before you call glDrawArrays of the textured pyramid:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glDrawArrays(GL_TRIANGLES, 0, 18);
(Right now you bind it before drawing the LightVAO, which doesn't use the texture.)
All your glVertexAttribPointer calls have an incorrect stride of 6 * sizeof(GLfloat), but the buffer you provide has eight (8) floats per vertex, so it shall be 8 * sizeof(GLfloat). Remember that this parameter is the number of bytes that the GL has to advance to fetch the next vertex. Other than that your VAO setup is alright.
I have this OpenGL code the draws a cube and pyramid. However, this program rotates the pyramid and cube together. I am tasked with only making the cube itself move not both objects at the same time. I know for this to happen I have to implement shaders for both. I'm not sure how to go about implementing both of the shaders at once. Any tips?
/*
This program demonstrates simple lighting.
A pyramid is lighted by a point light and can be rotated by mouse.
Ying Zhu
Georgia State University
October 2016
*/
// GLEW header
#include <GL/glew.h> // This must appear before freeglut.h
// Freeglut header
#include <GL/freeglut.h>
// GLM header files
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
// #include <glm/gtx/transform2.hpp>
#include <glm/gtc/matrix_access.hpp>
// #include <glm/gtx/projection.hpp>
#include <glm/gtc/matrix_inverse.hpp>
#include <glm/gtc/type_ptr.hpp>
// C++ header files
#include <iostream>
using namespace std;
using namespace glm;
#define BUFFER_OFFSET(offset) ((GLvoid *) offset)
// VBO buffer IDs
GLuint vertexArrayBufferID = 0;
GLuint normalArrayBufferID = 0;
GLuint cubePosition = 0;
GLuint cubeElements = 0;
GLuint program; // shader program ID
// Shader variable IDs
GLint vPos; // vertex attribute: position
GLint normalID; // vertex attribute: normal
GLint mvpMatrixID; // uniform variable: model, view, projection matrix
GLint modelMatrixID; // uniform variable: model, view matrix
GLint normalMatrixID; // uniform variable: normal matrix for transforming normals
GLint lightSourcePositionID; // uniform variable: for lighting calculation
GLint diffuseLightProductID; // uniform variable: for lighting calculation
GLint ambientID;
GLint attenuationAID;
GLint attenuationBID;
GLint attenuationCID;
// Transformation matrices
mat4 projMatrix;
mat4 mvpMatrix;
mat4 modelMatrix;
mat4 viewMatrix;
mat3 normalMatrix; // Normal matrix for transforming normals
// Light parameters
vec4 lightSourcePosition = vec4(0.0f, 4.0f, 0.0f, 1.0f);
vec4 diffuseMaterial = vec4(0.5f, 0.5f, 0.0f, 1.0f);
vec4 diffuseLightIntensity = vec4(1.0f, 1.0f, 1.0f, 1.0f);
vec4 ambient = vec4(0.2f, 0.2f, 0.2f, 1.0f);
float attenuationA = 1.0f;
float attenuationB = 0.2f;
float attenuationC = 0.0f;
vec4 diffuseLightProduct;
// Camera parameters
vec3 eyePosition = vec3(0.0f, 0.0f, 4.0f);
vec3 lookAtCenter = vec3(0.0f, 0.0f, 0.0f);
vec3 upVector = vec3(0.0f, 1.0f, 0.0f);
float fieldOfView = 30.0f;
float nearPlane = 0.1f;
float farPlane = 1000.0f;
// Mouse controlled rotation angles
float rotateX = 0;
float rotateY = 0;
struct VertexData {
GLfloat vertex[3];
VertexData(GLfloat x, GLfloat y, GLfloat z) {
vertex[0] = x; vertex[1] = y; vertex[2] = z;
}
};
//---------------------------------------------------------------
// Initialize vertex arrays and VBOs
void prepareVBOs() {
// Define a 3D pyramid.
GLfloat vertices[][4] = {
{1.0f, -1.0f, 1.0f, 1.0f}, // face 1
{-1.0f, -1.0f, -1.0f, 1.0f},
{1.0f, -1.0f, -1.0f, 1.0f},
{ 1.0f, -1.0f, -1.0f, 1.0f }, // face 2
{0.0f, 1.0f, 0.0f, 1.0f},
{ 1.0f, -1.0f, 1.0f, 1.0f },
{ 1.0f, -1.0f, 1.0f, 1.0f }, // face 3
{ 0.0f, 1.0f, 0.0f, 1.0f },
{-1.0f, -1.0f, 1.0f, 1.0f},
{ -1.0f, -1.0f, 1.0f, 1.0f }, // face 4
{ 0.0f, 1.0f, 0.0f, 1.0f },
{ -1.0f, -1.0f, -1.0f, 1.0f },
{ 0.0f, 1.0f, 0.0f, 1.0f }, // face 5
{ 1.0f, -1.0f, -1.0f, 1.0f },
{ -1.0f, -1.0f, -1.0f, 1.0f },
{ 1.0f, -1.0f, 1.0f, 1.0f }, // face 6
{ -1.0f, -1.0f, 1.0f, 1.0f },
{ -1.0f, -1.0f, -1.0f, 1.0f }
};
GLfloat normals[][4] = {
{0.0f, -1.0f, 0.0f, 1.0f}, // normal 1
{0.0f, -1.0f, 0.0f, 1.0f },
{0.0f, -1.0f, 0.0f, 1.0f },
{0.8944f, 0.4472f, 0.0f, 1.0f}, // normal 2
{ 0.8944f, 0.4472f, 0.0f, 1.0f },
{ 0.8944f, 0.4472f, 0.0f, 1.0f },
{-0.0f, 0.4472f, 0.8944f, 1.0f}, // normal 3
{ -0.0f, 0.4472f, 0.8944f, 1.0f },
{ -0.0f, 0.4472f, 0.8944f, 1.0f },
{-0.8944f, 0.4472f, 0.0f, 1.0f}, // normal 4
{ -0.8944f, 0.4472f, 0.0f, 1.0f },
{ -0.8944f, 0.4472f, 0.0f, 1.0f },
{0.0f, 0.4472f, -0.8944f, 1.0f}, // normal 5
{ 0.0f, 0.4472f, -0.8944f, 1.0f },
{ 0.0f, 0.4472f, -0.8944f, 1.0f },
{ 0.0f, -1.0f, 0.0f, 1.0f }, // normal 6
{ 0.0f, -1.0f, 0.0f, 1.0f },
{ 0.0f, -1.0f, 0.0f, 1.0f }
};
// Cube positioins
VertexData vertexData[] = {
VertexData(0.0, 0.0, 0.0), /* Index 0 */
VertexData(0.0, 0.0, 1.0), /* Index 1 */
VertexData(0.0, 1.0, 0.0), /* Index 2 */
VertexData(0.0, 1.0, 1.0), /* Index 3 */
VertexData(1.0, 0.0, 0.0), /* Index 4 */
VertexData(1.0, 0.0, 1.0), /* Index 5 */
VertexData(1.0, 1.0, 0.0), /* Index 6 */
VertexData(1.0, 1.0, 1.0), /* Index 7 */
};
// Cube elements
GLubyte indices[] = {
4, 5, 7, // +X face
4, 7, 6,
0, 2, 3, // ‐X face
0, 3, 1,
2, 6, 7, // +Y face
2, 7, 3,
0, 1, 5, // ‐Y face
0, 5, 4,
0, 4, 6, // +Z face
0, 6, 2,
1, 3, 7, // ‐Z face
1, 7, 5
};
// Get an unused buffer object name. Required after OpenGL 3.1.
glGenBuffers(1, &vertexArrayBufferID);
// If it's the first time the buffer object name is used, create that buffer.
glBindBuffer(GL_ARRAY_BUFFER, vertexArrayBufferID);
// Allocate memory for the active buffer object.
// 1. Allocate memory on the graphics card for the amount specified by the 2nd parameter.
// 2. Copy the data referenced by the third parameter (a pointer) from the main memory to the
// memory on the graphics card.
// 3. If you want to dynamically load the data, then set the third parameter to be NULL.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &normalArrayBufferID);
glBindBuffer(GL_ARRAY_BUFFER, normalArrayBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(normals), normals, GL_STATIC_DRAW);
glGenBuffers(1, &cubePosition);
glBindBuffer(GL_ARRAY_BUFFER, cubePosition);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData),
vertexData, GL_STATIC_DRAW);
glGenBuffers(1, &cubeElements);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cubeElements);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices),
indices, GL_STATIC_DRAW);
}
//---------------------------------------------------------------
// Print out the output of the shader compiler
void printLog(GLuint obj)
{
int infologLength = 0;
char infoLog[1024];
if (glIsShader(obj)) {
glGetShaderInfoLog(obj, 1024, &infologLength, infoLog);
}
else {
glGetProgramInfoLog(obj, 1024, &infologLength, infoLog);
}
if (infologLength > 0) {
cout << infoLog;
}
}
//-------------------------------------------------------------------
void prepareShaders() {
// Vertex shader source code
// A point light source is implemented.
// For simplicity, only the ambient and diffuse components are implemented.
// The lighting is calculated in world space, not in camera space.
const char* vSource = {
"#version 330\n"
"in vec4 vPos;"
"in vec4 normal;"
"uniform mat4x4 mvpMatrix;"
"uniform mat4x4 modelMatrix;"
"uniform mat3x3 normalMatrix;"
"uniform vec4 lightSourcePosition;"
"uniform vec4 diffuseLightProduct;"
"uniform vec4 ambient;"
"uniform float attenuationA;"
"uniform float attenuationB;"
"uniform float attenuationC;"
"out vec4 color;"
"void main() {"
" gl_Position = mvpMatrix * vPos;"
// Transform the vertex position to the world space.
" vec4 transformedVertex = modelMatrix * vPos;"
// Transform the normal vector to the world space.
" vec3 transformedNormal = normalize(normalMatrix * normal.xyz);"
// Light direction
" vec3 lightVector = normalize(transformedVertex.xyz - lightSourcePosition.xyz);"
// Distance between the light source and vertex
" float dist = distance(lightSourcePosition.xyz, transformedVertex.xyz);"
// Attenuation factor
" float attenuation = 1.0f / (attenuationA + (attenuationB * dist) + (attenuationC * dist * dist));"
// Calculate the diffuse component of the lighting equation.
" vec4 diffuse = attenuation * (max(dot(transformedNormal, lightVector), 0.0) * diffuseLightProduct);"
// Combine the ambient component and diffuse component.
" color = ambient + diffuse;"
"}"
};
// Fragment shader source code
const char* fSource = {
"#version 330\n"
"in vec4 color;"
"out vec4 fragColor;"
"void main() {"
" fragColor = color;"
"}"
};
// Declare shader IDs
GLuint vShader, fShader;
// Create empty shader objects
vShader = glCreateShader(GL_VERTEX_SHADER);
fShader = glCreateShader(GL_FRAGMENT_SHADER);
// Attach shader source code the shader objects
glShaderSource(vShader, 1, &vSource, NULL);
glShaderSource(fShader, 1, &fSource, NULL);
// Compile shader objects
glCompileShader(vShader);
printLog(vShader);
glCompileShader(fShader);
printLog(fShader);
// Create an empty shader program object
program = glCreateProgram();
// Attach vertex and fragment shaders to the shader program
glAttachShader(program, vShader);
glAttachShader(program, fShader);
// Link the shader program
glLinkProgram(program);
printLog(program);
}
//---------------------------------------------------------------
// Retrieve the IDs of the shader variables. Later we will
// use these IDs to pass data to the shaders.
void getShaderVariableLocations(GLuint shaderProgram) {
// Retrieve the ID of a vertex attribute, i.e. position
vPos = glGetAttribLocation(shaderProgram, "vPos");
normalID = glGetAttribLocation(shaderProgram, "normal");
mvpMatrixID = glGetUniformLocation(shaderProgram, "mvpMatrix");
modelMatrixID = glGetUniformLocation(shaderProgram, "modelMatrix");
normalMatrixID = glGetUniformLocation(shaderProgram, "normalMatrix");
lightSourcePositionID = glGetUniformLocation(shaderProgram, "lightSourcePosition");
diffuseLightProductID = glGetUniformLocation(shaderProgram, "diffuseLightProduct");
ambientID = glGetUniformLocation(shaderProgram, "ambient");
attenuationAID = glGetUniformLocation(shaderProgram, "attenuationA");
attenuationBID = glGetUniformLocation(shaderProgram, "attenuationB");
attenuationCID = glGetUniformLocation(shaderProgram, "attenuationC");
}
//---------------------------------------------------------------
void setShaderVariables() {
// value_ptr is a glm function
glUniformMatrix4fv(mvpMatrixID, 1, GL_FALSE, value_ptr(mvpMatrix));
glUniformMatrix4fv(modelMatrixID, 1, GL_FALSE, value_ptr(modelMatrix));
glUniformMatrix3fv(normalMatrixID, 1, GL_FALSE, value_ptr(normalMatrix));
glUniform4fv(lightSourcePositionID, 1, value_ptr(lightSourcePosition));
glUniform4fv(diffuseLightProductID, 1, value_ptr(diffuseLightProduct));
glUniform4fv(ambientID, 1, value_ptr(ambient));
glUniform1f(attenuationAID, attenuationA);
glUniform1f(attenuationBID, attenuationB);
glUniform1f(attenuationCID, attenuationC);
}
//---------------------------------------------------------------
// Set lighting related parameters
void setLightingParam() {
diffuseLightProduct = diffuseMaterial * diffuseLightIntensity;
}
//---------------------------------------------------------------
// Build the model matrix. This matrix will transform the 3D object to the proper place.
mat4 buildModelMatrix() {
mat4 rotationXMatrix = rotate(mat4(1.0f), radians(rotateX), vec3(1.0f, 0.0f, 0.0f));
mat4 rotationYMatrix = rotate(mat4(1.0f), radians(rotateY), vec3(0.0f, 1.0f, 0.0f));
mat4 matrix = rotationYMatrix * rotationXMatrix;
return matrix;
}
//---------------------------------------------------------------
void buildMatrices() {
modelMatrix = buildModelMatrix();
mvpMatrix = projMatrix * viewMatrix * modelMatrix;
normalMatrix = column(normalMatrix, 0, vec3(modelMatrix[0][0], modelMatrix[0][1], modelMatrix[0][2]));
normalMatrix = column(normalMatrix, 1, vec3(modelMatrix[1][0], modelMatrix[1][1], modelMatrix[1][2]));
normalMatrix = column(normalMatrix, 2, vec3(modelMatrix[2][0], modelMatrix[2][1], modelMatrix[2][2]));
// Use glm::inverseTranspose() to create a normal matrix, which is used to transform normal vectors.
normalMatrix = inverseTranspose(normalMatrix);
}
//---------------------------------------------------------------
// Handles the display event
void display()
{
// Clear the window with the background color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
buildMatrices();
setShaderVariables();
// Activate the shader program
glUseProgram(program);
// If the buffer object already exists, make that buffer the current active one.
// If the buffer object name is 0, disable buffer objects.
glBindBuffer(GL_ARRAY_BUFFER, vertexArrayBufferID);
// Associate the vertex array in the buffer object with the vertex attribute: "position"
glVertexAttribPointer(vPos, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
// Enable the vertex attribute: "position"
glEnableVertexAttribArray(vPos);
glBindBuffer(GL_ARRAY_BUFFER, normalArrayBufferID);
glVertexAttribPointer(normalID, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(normalID);
// Start the shader program. Draw the object. The third parameter is the number of triangles.
glDrawArrays(GL_TRIANGLES, 0, 18);
glBindBuffer(GL_ARRAY_BUFFER, cubePosition);
glVertexAttribPointer(vPos, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(vPos);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cubeElements);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
// Refresh the window
glutSwapBuffers();
}
//---------------------------------------------------------------
// Handles the reshape event
void reshape(int width, int height)
{
// Specify the width and height of the picture within the window
glViewport(0, 0, width, height);
projMatrix = perspective(fieldOfView, (float)width / (float)height, nearPlane, farPlane);
viewMatrix = lookAt(eyePosition, lookAtCenter, upVector);
}
//---------------------------------------------------------------
// Read mouse motion data and convert them to rotation angles.
void passiveMotion(int x, int y) {
rotateY = (float)x * -0.8f;
rotateX = (float)y * -0.8f;
// Generate a dislay event to force refreshing the window.
glutPostRedisplay();
}
//-----------------------------------------------------------------
void init() {
prepareVBOs();
prepareShaders();
getShaderVariableLocations(program);
setLightingParam();
// Specify the background color
glClearColor(1, 1, 1, 1);
glEnable(GL_DEPTH_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
//---------------------------------------------------------------
void main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow("Lighting Demo");
glutReshapeWindow(800, 800);
glewInit();
init();
// Register the display callback function
glutDisplayFunc(display);
// Register the reshape callback function
glutReshapeFunc(reshape);
// Register the passive mouse motion call back function
// This function is called when the mouse moves within the window
// while no mouse buttons are pressed.
glutPassiveMotionFunc(passiveMotion);
// Start the event loop
glutMainLoop();
}
Well, the most obvious culprit here would be setting a single ModelMatrix for both - I can't see any logic in your code to set them independently for each object you're rendering.
Since each object has a different rotation (and presumably, unless you're planning to draw one on top of the other, a different translation), you would want to be generating / loading a different model matrix for each draw call.
You dont need to use different shaders, you just need to use different model matricies. Say you have two objects in you scene something like this:
while (!myWindow(shouldClose))
{
myShader.use();
glBindVertexArray(myVao1);
glDrawArrays(GL_TRIANGLES, 0, x); // Draw pyramid
glBindVertaxArray(myVao2);
glDrawArrays(GL_TRIANGLES, 0, x); // Draw cube
}
Say you want only the second model to rotate on the y axis, you could do something like this:
float rotationDegree = 0;
while (!myWindow(shouldClose))
{
myShader.use();
myShader.setMat4(glm::mat4(1.0f)) // Make sure to set it to normal matrix for the pyrmamid
glBindVertexArray(myVao1);
glDrawArrays(GL_TRIANGLES, 0, x); // Draw pyramid
glBindVertaxArray(myVao2);
glm::mat4 model = glm::mat4(1.0f);
glm::rotate(model, glm::radians(rotationDegree), glm::vec3(0.0f, 1.0f, 0.0f));
rotateionDegree += 0.01;
myShader.setMat4("model", model); // Set you model matrix in your shader.
glDrawArrays(GL_TRIANGLES, 0, x); // Draw cube
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I am supposed to draw 6 different 2D shapes (dot, line, triangle, square, star, circle) and assign them each a different color. I managed to draw all of them and color them, except for my circle. The color i gave (g_colors_circle) the circle however is drawn from the center of the circle but does not reach the edges. Here is the current output:
Can someone tell me how to fix it?
Here is my code:
SimpleTriangle.cpp:
#include <cstdio> // for C++ i/o
#include <iostream>
using namespace std; // to avoid having to use std::
#define GLEW_STATIC // include GLEW as a static library
#include <GLEW/glew.h> // include GLEW
#include <GLFW/glfw3.h> // include GLFW (which includes the OpenGL header)
#include <glm/glm.hpp> // include GLM (ideally should only use the GLM headers that are actually used)
using namespace glm; // to avoid having to use glm::
#include "shader.h"
/*-------------------- Circle Code --------------------*/
//Constants for Circle
#define PI 3.14159265
#define MAX_SLICES 32
#define MIN_SLICES 8
#define MAX_VERTICES (MAX_SLICES+2)*3 // a triangle fan should have a minimum of 3 vertices
#define CIRCLE_RADIUS 0.5
GLuint g_VBO_circle[2]; // identifiers
GLuint g_VAO_circle = 0;
//Vertices for the circle
GLfloat g_vertices_circle[MAX_VERTICES] = {
0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f,
};
GLfloat g_colors_circle[] = {
1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f
};
GLuint g_slices = MIN_SLICES; // number of circle slices
//raz: i think this generates the vertice values for array of the circle
void generate_circle()
{
float angle = PI * 2 / static_cast<float>(g_slices); // used to generate x and y coordinates
float scale_factor = static_cast<float>(768) / 1024; // scale to make it a circle instead of an elipse
int index = 0; // vertex index
g_vertices_circle[3] = CIRCLE_RADIUS * scale_factor; // set x coordinate of vertex 1
// generate vertex coordinates for triangle fan
for (int i = 2; i < g_slices + 2; i++)
{
// multiply by 3 because a vertex has x, y, z coordinates
index = i * 3;
g_vertices_circle[index] = CIRCLE_RADIUS * cos(angle) * scale_factor;
g_vertices_circle[index + 1] = CIRCLE_RADIUS * sin(angle);
g_vertices_circle[index + 2] = 0.0f;
// update to next angle
angle += PI * 2 / static_cast<float>(g_slices);
}
}
static void init_circle()
{
// generate vertices of triangle fan
generate_circle();
// create VBO (vertice positions) and buffer the data
glGenBuffers(2, g_VBO_circle);
glBindBuffer(GL_ARRAY_BUFFER, g_VBO_circle[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * (g_slices + 2), g_vertices_circle, GL_DYNAMIC_DRAW);
// create VBO (vertice color) and buffer the data
glBindBuffer(GL_ARRAY_BUFFER, g_VBO_circle[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * (g_slices + 2), g_colors_circle, GL_STATIC_DRAW);
// create VAO and specify VBO data
glGenVertexArrays(1, &g_VAO_circle);
glBindVertexArray(g_VAO_circle);
glBindBuffer(GL_ARRAY_BUFFER, g_VBO_circle[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // specify the form of the data
glBindBuffer(GL_ARRAY_BUFFER, g_VBO_circle[1]);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0); // specify the form of the data
glEnableVertexAttribArray(0); // enable vertex attributes
glEnableVertexAttribArray(1);
}
/*---------------------------------------------------------*/
// global variables
GLuint g_VBO[2]; // vertex buffer object identifier
GLuint g_VAO = 0; // vertex array object identifier
GLuint g_shaderProgramID = 0; // shader program identifier
static void init()
{
glClearColor(0.0, 0.0, 0.0, 1.0); // set clear background colour
// create and compile our GLSL program from the shader files
g_shaderProgramID = loadShaders("SimpleVS.vert", "SimpleFS.frag");
// enable point size
glEnable(GL_PROGRAM_POINT_SIZE);
// set line width
glLineWidth(5.0);
GLfloat verticesPosition[] = {
//Dot
-0.7f, 0.7f, 0.0f,
//Line
-0.5f, 0.7f, 0.0f,
-0.2f, 0.7f, 0.0f,
//Triangle
0.2f, 0.75f, 0.0f,
0.0f, 0.4f, 0.0f,
0.4f, 0.4f, 0.0f,
//Star
0.0f, 0.2f, 0.0f,
0.1f, 0.1f, 0.0f,
0.2f, 0.05f, 0.0f,
0.1f, 0.0f, 0.0f,
0.2f, -0.1f, 0.0f,
0.0f, 0.0f, 0.0f,
-0.2f, -0.1f, 0.0f,
-0.1f, 0.0f, 0.0f,
-0.2f, 0.05f, 0.0f,
-0.1f, 0.1f, 0.0f,
//Rectangle
-0.8f, 0.4f, 0.0f,
-0.8f, 0.0f, 0.0f,
-0.3f, 0.4f, 0.0f,
-0.3f, 0.0f, 0.0f
};
GLfloat verticesColor[] = {
//Dot
1.0f, 1.0f, 1.0f,
//Line
0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f,
//Triangle
0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f,
//Star
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
//Rectangle
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
};
// create VBO and buffer the data
glGenBuffers(2, g_VBO);
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[0]); // bind the VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesPosition), verticesPosition, GL_STATIC_DRAW); // copy data to buffer
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[1]); // bind the VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesColor), verticesColor, GL_STATIC_DRAW); // copy data to buffer
// create VAO and specify VBO data
glGenVertexArrays(1, &g_VAO);
glBindVertexArray(g_VAO);
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[0]); // bind the VBO
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // specify the form of the data
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[1]); // bind the VBO
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0); // specify the form of the data
glEnableVertexAttribArray(0); // enable vertex attributes
glEnableVertexAttribArray(1);
}
// function used to render the scene
static void render_scene()
{
glClear(GL_COLOR_BUFFER_BIT); // clear colour buffer
glUseProgram(g_shaderProgramID); // use the shaders associated with the shader program
glBindVertexArray(g_VAO); // make VAO active
glDrawArrays(GL_POINTS, 0, 1); //Draw dot
glDrawArrays(GL_LINES, 1, 2); //Draw line
glDrawArrays(GL_TRIANGLES, 3, 3); //Draw triangle
glDrawArrays(GL_LINE_LOOP, 6, 10); //Draw star
glDrawArrays(GL_TRIANGLE_STRIP, 16, 4); //Draw rectangle
glFlush(); // flush the pipeline
//To draw circle
glBindVertexArray(g_VAO_circle); // make VAO for circle active
glDrawArrays(GL_TRIANGLE_FAN, 0, g_slices + 2); // display the vertices based on the primitive type
glFlush(); // flush the pipeline
}
// key press or release callback function
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
// quit if the ESCAPE key was press
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
// set flag to close the window
glfwSetWindowShouldClose(window, GL_TRUE);
return;
}
else if (key == GLFW_KEY_W && action == GLFW_PRESS)
{
// renderer using wireframe
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
return;
}
else if (key == GLFW_KEY_S && action == GLFW_PRESS)
{
// renderer using wireframe
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
return;
}
else if (key == GLFW_KEY_UP && action == GLFW_PRESS)
{
if (g_slices < MAX_SLICES)
{
g_slices++; // increment number of slices
// generate vertices of triangle fan
generate_circle();
// bind and copy data to GPU
glBindBuffer(GL_ARRAY_BUFFER, g_VBO_circle[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * (g_slices + 2), g_vertices_circle, GL_DYNAMIC_DRAW);
}
return;
}
else if (key == GLFW_KEY_DOWN && action == GLFW_PRESS)
{
if (g_slices > MIN_SLICES)
{
g_slices--; // decrement number of slices
// generate vertices of triangle fan
generate_circle();
// bind and copy data to GPU
glBindBuffer(GL_ARRAY_BUFFER, g_VBO_circle[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * (g_slices + 2), g_vertices_circle, GL_DYNAMIC_DRAW);
return;
}
}
}
// error callback function
static void error_callback(int error, const char* description)
{
cerr << description << endl; // output error description
}
int main(void)
{
GLFWwindow* window = NULL; // pointer to a GLFW window handle
glfwSetErrorCallback(error_callback); // set error callback function
// initialise GLFW
if(!glfwInit())
{
// if failed to initialise GLFW
exit(EXIT_FAILURE);
}
// minimum OpenGL version 3.3
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
// create a window and its OpenGL context
window = glfwCreateWindow(1024, 768, "Assignment 1", NULL, NULL);
// if failed to create window
if(window == NULL)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window); // set window context as the current context
glfwSwapInterval(1); // swap buffer interval
// initialise GLEW
if(glewInit() != GLEW_OK)
{
// if failed to initialise GLEW
cerr << "GLEW initialisation failed" << endl;
exit(EXIT_FAILURE);
}
// set key callback function
glfwSetKeyCallback(window, key_callback);
// initialise rendering states
init();
init_circle();
// the rendering loop
while(!glfwWindowShouldClose(window))
{
render_scene(); // render the scene
glfwSwapBuffers(window); // swap buffers
glfwPollEvents(); // poll for events
}
// clean up
glDeleteProgram(g_shaderProgramID);
glDeleteBuffers(1, g_VBO);
glDeleteVertexArrays(1, &g_VAO);
glDeleteBuffers(1, g_VBO_circle);
glDeleteVertexArrays(1, &g_VAO_circle);
// close the window and terminate GLFW
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
SimpleVS.vert:
#version 330 core
// input data (different for all executions of this shader)
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec3 aColor;
// output data (will be interpolated for each fragment)
out vec3 vColor; //raz: this output is passed as input to the fragment shader
void main()
{
// set point size
gl_PointSize = 10.0;
// set vertex position
gl_Position = vec4(aPosition, 1.0);
// the color of each vertex will be interpolated
// to produce the color of each fragment
vColor = aColor;
}
SimpleFS.frag:
#version 330 core
// interpolated values from the vertex shaders
in vec3 vColor;
// output data
out vec3 fColor;
void main()
{
// set output color
fColor = vColor;
}
For each vertex in your circle geometry, you must set a color attribute. The 2 array buffers g_vertices_circle and g_colors_circle, which you are using for your attribute buffers, must have the same number of elements.
If the circle has to be unicolor, you must always use the same color for each element in the color attribute buffer.
In between the primitives the attributes are interpolate according to its barycentric coordinates. You did set the color for the center of the circle and the first outer point, but not the colors for the other outer points and left them undefined (probably black), this causes the effect you can see in your example.
Create a buffer for the color attributes in the same length as your vertex buffer and fill it up when you generate your vertices.
Your revised code should look something like this:
GLfloat g_colors_circle[MAX_VERTICES] = {
1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f
};
for (int i = 2; i < g_slices + 2; i++)
{
// multiply by 3 because a vertex has x, y, z coordinates
index = i * 3;
g_vertices_circle[index] = CIRCLE_RADIUS * cos(angle) * scale_factor;
g_vertices_circle[index + 1] = CIRCLE_RADIUS * sin(angle);
g_vertices_circle[index + 2] = 0.0f;
g_colors_circle[index] = 1.0f;
g_colors_circle[index + 1] = 1.0f;
g_colors_circle[index + 2] = 0.0f;
// update to next angle
angle += PI * 2 / static_cast<float>(g_slices);
}
Note, if you only want to have single-color geometry, you can omit the color attributes and set the color through a single uniform variable.
My ultimate goal is to render 1 million spheres of different sizes and colors at 60 fps. I want to be able to move the camera around the screen as well.
I have modified the code on this page of the tutorial I am studying to try to instance many spheres. However, I find that at as little as 64 spheres my fps falls below 60, and at 900 spheres my fps is a measly 4. My understanding of instancing is naive, but I believe that I should be getting more frames-per-second than this. 60 fps should be attainable with only 64 spheres. I believe that I am, in some way, causing the CPU and GPU to communicate more often than they should have to. So my question is: How do I instance so many objects (ideally millions) without causing the fps to fall low (ideally 60 fps)?
I am calculating fps by calculating (10 / time_elapsed) every 10 frames, where time_elapsed is the time that has elapsed since the last fps call. I am printing this out using printf on line 118 of my code.
I have been learning OpenGL through this tutorial and so I use 32-bit GLEW and 32-bit GLFW in Visual Studio 2013. I have 8 GB of RAM on a 64-bit operating system (Windows 7) with a 2.30 GHz CPU.
I have tried coding my own example based on the tutorial above. Source code:
(set line #2 to be the number of spheres to be instanced. Make sure line#2 has a whole-number square root. Set line 4 to be the detail of the sphere, the lowest it can go is 0. Higher number = more detailed.)
// Make sure NUM_INS is a square number
#define NUM_INS 1
// Detail up to 4 is probably good enough
#define SPHERE_DETAIL 4
#include <vector>
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// GL includes
#include "Shader.h"
// GLM Mathemtics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// Properties
GLuint screenWidth = 800, screenHeight = 600;
// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
std::vector<GLfloat> create_sphere(int recursion);
// The MAIN function, from here we start our application and run the Game loop
int main()
{
// Init GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
glfwMakeContextCurrent(window);
// Set the required callback functions
glfwSetKeyCallback(window, key_callback);
// Initialize GLEW to setup the OpenGL Function pointers
glewExperimental = GL_TRUE;
glewInit();
// Define the viewport dimensions
glViewport(0, 0, screenWidth, screenHeight);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Comment to remove wireframe mode
// Setup OpenGL options
glEnable(GL_DEPTH_TEST);
// Setup and compile our shader(s)
Shader shader("core.vs", "core.frag");
// Generate a list of 100 quad locations/translation-vectors
std::vector<glm::vec2> translations(NUM_INS);
//glm::vec2 translations[NUM_INS];
int index = 0;
GLfloat offset = 1.0f / (float)sqrt(NUM_INS);
for (GLint y = -(float)sqrt(NUM_INS); y < (float)sqrt(NUM_INS); y += 2)
{
for (GLint x = -(float)sqrt(NUM_INS); x < (float)sqrt(NUM_INS); x += 2)
{
glm::vec2 translation;
translation.x = (GLfloat)x / (float)sqrt(NUM_INS) + offset;
translation.y = (GLfloat)y / (float)sqrt(NUM_INS) + offset;
translations[index++] = translation;
}
}
// Store instance data in an array buffer
GLuint instanceVBO;
glGenBuffers(1, &instanceVBO);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * NUM_INS, &translations[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// create 12 vertices of a icosahedron
std::vector<GLfloat> vv = create_sphere(SPHERE_DETAIL);
GLuint quadVAO, quadVBO;
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, vv.size() * sizeof(GLfloat), &vv[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat)));
// Also set instance data
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribDivisor(2, 1); // Tell OpenGL this is an instanced vertex attribute.
glBindVertexArray(0);
// For printing frames-per-second
float counter = 0;
double get_time = 0;
double new_time;
// Game loop
while (!glfwWindowShouldClose(window))
{
// Print fps by printing (number_of_frames / time_elapsed)
counter += 1;
if (counter > 10) {
counter -= 10;
new_time = glfwGetTime();
printf("fps: %.2f ", (10/(new_time - get_time)));
get_time = new_time;
}
// Check and call events
glfwPollEvents();
// Clear buffers
//glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Draw 100 instanced quads
shader.Use();
glm::mat4 model;
model = glm::rotate(model, 0.0f, glm::vec3(1.0f, 0.0f, 0.0f));
// Camera/View transformation
glm::mat4 view;
GLfloat radius = 10.0f;
GLfloat camX = sin(glfwGetTime()) * radius;
GLfloat camZ = cos(glfwGetTime()) * radius;
view = glm::lookAt(glm::vec3(camX, 0.0f, camZ), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
// Projection
glm::mat4 projection;
projection = glm::perspective(45.0f, (GLfloat)screenWidth / (GLfloat)screenHeight, 0.1f, 100.0f);
// Get the uniform locations
GLint modelLoc = glGetUniformLocation(shader.Program, "model");
GLint viewLoc = glGetUniformLocation(shader.Program, "view");
GLint projLoc = glGetUniformLocation(shader.Program, "projection");
// Pass the matrices to the shader
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
glBindVertexArray(quadVAO);
glDrawArraysInstanced(GL_TRIANGLES, 0, vv.size() / 3, NUM_INS); // 100 triangles of 6 vertices each
glBindVertexArray(0);
// Swap the buffers
glfwSwapBuffers(window);
}
glfwTerminate();
return 0;
}
// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
std::vector<GLfloat> add_color(std::vector<GLfloat> sphere) {
// Add color
std::vector<GLfloat> colored_sphere;
for (GLint i = 0; i < sphere.size(); i+=9) {
colored_sphere.push_back(sphere[i]);
colored_sphere.push_back(sphere[i+1]);
colored_sphere.push_back(sphere[i+2]);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(sphere[i+3]);
colored_sphere.push_back(sphere[i+4]);
colored_sphere.push_back(sphere[i+5]);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(sphere[i+6]);
colored_sphere.push_back(sphere[i+7]);
colored_sphere.push_back(sphere[i+8]);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
}
return colored_sphere;
}
std::vector<GLfloat> tesselate(std::vector<GLfloat> shape, int recursion) {
if (recursion > 0) {
std::vector<GLfloat> new_sphere = {};
for (GLint i = 0; i < shape.size(); i += 9) {
// 1.902113 approximately
GLfloat radius = sqrt(1.0f + pow((1.0f + sqrt(5.0f)) / 2.0f, 2));
// Every 9 points is a triangle. Take 1 triangle and turn it into 4 triangles.
GLfloat p_one[] = {shape[i], shape[i + 1], shape[i + 2]};
GLfloat p_two[] = {shape[i + 3], shape[i + 4], shape[i + 5]};
GLfloat p_thr[] = {shape[i + 6], shape[i + 7], shape[i + 8]};
GLfloat p_one_two[] = { (p_one[0] + p_two[0]) / 2.0f, (p_one[1] + p_two[1]) / 2.0f, (p_one[2] + p_two[2]) / 2.0f };
GLfloat p_one_thr[] = { (p_one[0] + p_thr[0]) / 2.0f, (p_one[1] + p_thr[1]) / 2.0f, (p_one[2] + p_thr[2]) / 2.0f };
GLfloat p_two_thr[] = { (p_two[0] + p_thr[0]) / 2.0f, (p_two[1] + p_thr[1]) / 2.0f, (p_two[2] + p_thr[2]) / 2.0f };
GLfloat r_one_two = sqrt((p_one_two[0]*p_one_two[0]) + (p_one_two[1]*p_one_two[1]) + (p_one_two[2]*p_one_two[2]));
GLfloat r_one_thr = sqrt((p_one_thr[0]*p_one_thr[0]) + (p_one_thr[1]*p_one_thr[1]) + (p_one_thr[2]*p_one_thr[2]));
GLfloat r_two_thr = sqrt((p_two_thr[0]*p_two_thr[0]) + (p_two_thr[1]*p_two_thr[1]) + (p_two_thr[2]*p_two_thr[2]));
GLfloat t_one_two[] = { radius * p_one_two[0] / r_one_two, radius * p_one_two[1] / r_one_two, radius * p_one_two[2] / r_one_two };
GLfloat t_one_thr[] = { radius * p_one_thr[0] / r_one_thr, radius * p_one_thr[1] / r_one_thr, radius * p_one_thr[2] / r_one_thr };
GLfloat t_two_thr[] = { radius * p_two_thr[0] / r_two_thr, radius * p_two_thr[1] / r_two_thr, radius * p_two_thr[2] / r_two_thr };
// Triangle 1:
new_sphere.push_back(p_one[0]);
new_sphere.push_back(p_one[1]);
new_sphere.push_back(p_one[2]);
new_sphere.push_back(t_one_two[0]);
new_sphere.push_back(t_one_two[1]);
new_sphere.push_back(t_one_two[2]);
new_sphere.push_back(t_one_thr[0]);
new_sphere.push_back(t_one_thr[1]);
new_sphere.push_back(t_one_thr[2]);
// Triangle 2:
new_sphere.push_back(p_two[0]);
new_sphere.push_back(p_two[1]);
new_sphere.push_back(p_two[2]);
new_sphere.push_back(t_one_two[0]);
new_sphere.push_back(t_one_two[1]);
new_sphere.push_back(t_one_two[2]);
new_sphere.push_back(t_two_thr[0]);
new_sphere.push_back(t_two_thr[1]);
new_sphere.push_back(t_two_thr[2]);
// Triangle 3:
new_sphere.push_back(p_thr[0]);
new_sphere.push_back(p_thr[1]);
new_sphere.push_back(p_thr[2]);
new_sphere.push_back(t_one_thr[0]);
new_sphere.push_back(t_one_thr[1]);
new_sphere.push_back(t_one_thr[2]);
new_sphere.push_back(t_two_thr[0]);
new_sphere.push_back(t_two_thr[1]);
new_sphere.push_back(t_two_thr[2]);
// Center Triangle:
new_sphere.push_back(t_one_two[0]);
new_sphere.push_back(t_one_two[1]);
new_sphere.push_back(t_one_two[2]);
new_sphere.push_back(t_one_thr[0]);
new_sphere.push_back(t_one_thr[1]);
new_sphere.push_back(t_one_thr[2]);
new_sphere.push_back(t_two_thr[0]);
new_sphere.push_back(t_two_thr[1]);
new_sphere.push_back(t_two_thr[2]);
}
return tesselate(new_sphere, recursion - 1);
}
printf("number of vertices to be rendered: %d || ", shape.size());
return shape;
}
std::vector<GLfloat> create_sphere(int recursion) {
// Define the starting icosahedron
GLfloat t_ = (1.0f + sqrt(5.0f)) / 2.0f;
std::vector<GLfloat> icosahedron = {
-1.0f, t_, 0.0f, -t_, 0.0f, 1.0f, 0.0f, 1.0f, t_,
-1.0f, t_, 0.0f, 0.0f, 1.0f, t_, 1.0f, t_, 0.0f,
-1.0f, t_, 0.0f, 1.0f, t_, 0.0f, 0.0f, 1.0f, -t_,
-1.0f, t_, 0.0f, 0.0f, 1.0f, -t_, -t_, 0.0f, -1.0f,
-1.0f, t_, 0.0f, -t_, 0.0f, -1.0f, -t_, 0.0f, 1.0f,
1.0f, t_, 0.0f, 0.0f, 1.0f, t_, t_, 0.0f, 1.0f,
0.0f, 1.0f, t_, -t_, 0.0f, 1.0f, 0.0f, -1.0f, t_,
-t_, 0.0f, 1.0f, -t_, 0.0f, -1.0f, -1.0f, -t_, 0.0f,
-t_, 0.0f, -1.0f, 0.0f, 1.0f, -t_, 0.0f, -1.0f, -t_,
0.0f, 1.0f, -t_, 1.0f, t_, 0.0f, t_, 0.0f, -1.0f,
1.0f, -t_, 0.0f, t_, 0.0f, 1.0f, 0.0f, -1.0f, t_,
1.0f, -t_, 0.0f, 0.0f, -1.0f, t_,-1.0f, -t_, 0.0f,
1.0f, -t_, 0.0f,-1.0f, -t_, 0.0f, 0.0f, -1.0f, -t_,
1.0f, -t_, 0.0f, 0.0f, -1.0f, -t_, t_, 0.0f, -1.0f,
1.0f, -t_, 0.0f, t_, 0.0f, -1.0f, t_, 0.0f, 1.0f,
0.0f, -1.0f, t_, t_, 0.0f, 1.0f, 0.0f, 1.0f, t_,
-1.0f, -t_, 0.0f, 0.0f, -1.0f, t_,-t_, 0.0f, 1.0f,
0.0f, -1.0f, -t_,-1.0f, -t_, 0.0f,-t_, 0.0f, -1.0f,
t_, 0.0f, -1.0f, 0.0f, -1.0f, -t_, 0.0f, 1.0f, -t_,
t_, 0.0f, 1.0f, t_, 0.0f, -1.0f, 1.0f, t_, 0.0f,
};
// Tesselate the icososphere the number of times recursion
std::vector<GLfloat> colorless_sphere = tesselate(icosahedron, recursion);
// Add color and return
return add_color(colorless_sphere);
}
Vertex Shader: (named core.vs)
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 offset;
out vec3 fColor;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(position.x + offset.x, position.y + offset.y, position.z, 1.0f);
fColor = color;
}
Fragment Shader: (named core.frag)
#version 330 core
in vec3 fColor;
out vec4 color;
void main()
{
color = vec4(fColor, 1.0f);
}
Shader class: (named Shader.h)
#ifndef SHADER_H
#define SHADER_H
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <GL/glew.h>
class Shader
{
public:
GLuint Program;
// Constructor generates the shader on the fly
Shader(const GLchar* vertexPath, const GLchar* fragmentPath)
{
// 1. Retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
// ensures ifstream objects can throw exceptions:
vShaderFile.exceptions(std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::badbit);
try
{
// Open files
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// Read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
fShaderFile.close();
// Convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const GLchar* vShaderCode = vertexCode.c_str();
const GLchar * fShaderCode = fragmentCode.c_str();
// 2. Compile shaders
GLuint vertex, fragment;
GLint success;
GLchar infoLog[512];
// Vertex Shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
// Print compile errors if any
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertex, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
// Print compile errors if any
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragment, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Shader Program
this->Program = glCreateProgram();
glAttachShader(this->Program, vertex);
glAttachShader(this->Program, fragment);
glLinkProgram(this->Program);
// Print linking errors if any
glGetProgramiv(this->Program, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(this->Program, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// Delete the shaders as they're linked into our program now and no longer necessery
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// Uses the current shader
void Use()
{
glUseProgram(this->Program);
}
};
#endif
My ultimate goal is to render 1 million spheres of different sizes and colors at 60 fps.
This is an unreasonable expectation.
Let's say that each sphere consists of 50 triangles. Kinda small for a good sphere shape, but lets assume they're that small.
1 million spheres at 50 tris per sphere is 50 million triangles per frame. At 60 FPS, that's 3 billion triangles per second.
No commercially available GPU is good enough to do that. And that's just a 50 triangle sphere; your 4x tessellated icosahedron will be over 5,000 triangles.
Now yes, drawing 60 such spheres is only ~300,000 triangles per frame. But even that at 60 FPS is ~18 million triangles per second. Hardware does exist that can handle that many triangles, but it's very clearly a lot. And you're definitely not going to get 1 million of them.
This is not a matter of GPU/CPU communication or overhead. You're simply throwing more work at your GPU than it could handle. You might be able to improve a couple of things here and there, but nothing that's going to get you even one tenth of what you want.
At least, not with this overall approach.
For your particular case of wanting to draw millions of spheres, I would use raytraced impostors rather than actual geometry of spheres. That is, you draw quads, who's positions are generated by the vertex (or geometry) shader. You generate a quad per sphere, such that the quad circumscribes the sphere. Then the fragment shader does a simple ray-sphere intersection test to see if the fragment in question (from the direction of the camera view) hits the sphere or not. If the ray doesn't hit the sphere, you discard the fragment.
You would also need to modify gl_FragDepth to give the impostor the proper depth value, so that intersecting spheres can work.
I have problems to draw something using glDrawArrays() and no other question here could help me solve it. The code should draw a sphere, but for debuging I fill "m_vertices" with the vertices for a simple cube.
My Code is following:
First the init function.
init()
{
m_shader.compileShaders("Shaders/colorShading.vert", "Shaders/colorShading.frag");
m_shader.addAttribute("position");
m_shader.addAttribute("normal");
m_shader.linkShaders();
//setup VAO and VBO
glGenVertexArrays(1, &m_vao);
glGenBuffers(1, &m_vbo);
//init VAO
glBindVertexArray(m_vao);
//bind Buffer used by VAO
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
//enable needed AttributeArrays
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//position attribute pointer
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexBuffer), (void*)offsetof(VertexBuffer, vertex));
glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, sizeof(VertexBuffer), (void*)offsetof(VertexBuffer, normal));
//release VAO
glBindVertexArray(0);
//release VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
//Set-Up a static scene
// Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
glm::mat4 projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
// Camera matrix
glm::mat4 view = glm::lookAt(
glm::vec3(4, 3, 3), // Camera is at (4,3,3), in World Space
glm::vec3(0, 0, 0), // and looks at the origin
glm::vec3(0, 1, 0) // Head is up (set to 0,-1,0 to look upside-down)
);
// Model Matrix
glm::mat4 model = glm::mat4(1.0f);
//set ModelViewProjection-Matrix of this object
m_mvp = projection * view * model;
}
This function is used to set up everything needed to draw my Object.
Then there is an update function:
update()
{
//update Sphere and get current vertices
//currently setting vertices for a cube
m_sphere->updateSphere(m_vertices);
//m_vertices is passed in per reference
//it stores 6 quads based on 2 triangles (36 vertices)
//the vertices are stored counter clockwise
//update VBO
//bind buffer
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
//orphan the buffer
glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(VertexBuffer), nullptr, GL_STATIC_DRAW);
//upload data
glBufferSubData(GL_ARRAY_BUFFER, 0, m_vertices.size() * sizeof(VertexBuffer), &m_vertices[0]);
//release buffer
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
The update function updates the VBO. This is needed cause the final Sphere should implement LevelOfDetail.
And finally the draw function:
draw()
{
glEnable(GL_DEPTH_TEST);
//activate shader
m_shader.use();
//set-up the mvp-uniform
GLuint matrixID = m_shader.getUniformLocation("MVP");
glUniformMatrix4fv(matrixID, 1, GL_FALSE, &m_mvp[0][0]);
//draw Planet
glBindVertexArray(m_vao);
glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
glBindVertexArray(0);
//deactivate shader
m_shader.unuse();
glDisable(GL_DEPTH_TEST);
}
This function should just binds/sets up the shader and draw the object.
The problem is that it doesn't draw the object and I have no idea why.
If someone could spot my mistake and explain what I did wrong here, I would be very grateful.
EDIT:
So as requested I add my code, which fills in the cube data:
static const std::vector<GLfloat> g_vertex_buffer_data = {
-1.0f,-1.0f,-1.0f,
-1.0f,-1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f,-1.0f,
-1.0f,-1.0f,-1.0f,
-1.0f, 1.0f,-1.0f,
1.0f,-1.0f, 1.0f,
-1.0f,-1.0f,-1.0f,
1.0f,-1.0f,-1.0f,
1.0f, 1.0f,-1.0f,
1.0f,-1.0f,-1.0f,
-1.0f,-1.0f,-1.0f,
-1.0f,-1.0f,-1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f,-1.0f,
1.0f,-1.0f, 1.0f,
-1.0f,-1.0f, 1.0f,
-1.0f,-1.0f,-1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f,-1.0f, 1.0f,
1.0f,-1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f,-1.0f,-1.0f,
1.0f, 1.0f,-1.0f,
1.0f,-1.0f,-1.0f,
1.0f, 1.0f, 1.0f,
1.0f,-1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f,-1.0f,
-1.0f, 1.0f,-1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f,-1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f,-1.0f, 1.0f
};
updateSphere(std::vector<VertexBuffer> &vertices)
{
if(!vertices.empty())
vertices.clear();
for (int i = 0; i < g_vertex_buffer_data.size();)
{
VertexBuffer buffer;
glm::vec3 vertex(g_vertex_buffer_data[i], g_vertex_buffer_data[i+1], g_vertex_buffer_data[i+2]);
buffer.setVertex(vertex);
buffer.setNormal(glm::normalize(vertex));
vertices.emplace_back(buffer);
i += 3;
}
}
Also the VertexBuffer struct:
struct Vector3 {
float x;
float y;
float z;
};
struct VertexBuffer {
Vector3 vertex;
Vector3 normal;
void setVertex(glm::vec3 vec)
{
vertex.x = vec.x;
vertex.y = vec.y;
vertex.z = vec.z;
}
void setNormal(glm::vec3 vec)
{
normal.x = vec.x;
normal.y = vec.y;
normal.z = vec.z;
}
};
My shader class is already tested in serveral other projects and works perfectly fine. I assume that I missmanage the VAO and VBO or the cube data. But I could be completly wrong here.
And I corrected the VAO generation, that was my bad and I wonder why I didn't see this. But that didn't fix the problem here.
The color and depth buffer get cleared in the mainloop of my project. Should I clear them on every draw call of an Object?