我正在尝试将两个圆锥体对象渲染到屏幕上,但屏幕有点闪烁。它一次只显示一个圆锥体。下面是我到目前为止的代码。我按照互联网上的一些说明进行操作,人们说将
glBindVertexArray(0)
和 glfwSwapBuffers(gWindow)
放在循环末尾,而不是放在每个 Render 函数调用下方。我在渲染函数和循环中尝试过,但它不起作用。如果我把它们放在循环中,它只显示第二个圆锥体。如果我将它们放入 Render() 中,它就会闪烁。我在这里做错了什么?
#include <iostream> // cout, cerr
#include <cstdlib> // EXIT_FAILURE
#include <GL/glew.h> // GLEW library
#include <GLFW/glfw3.h> // GLFW library
// GLM Math Header inclusions
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Cone.h" // Include the header file
using namespace std; // Standard namespace
/*Shader program Macro*/
#ifndef GLSL
#define GLSL(Version, Source) "#version " #Version " core \n" #Source
#endif
// Unnamed namespace
namespace
{
const char* const WINDOW_TITLE = "Thi Nguyen - Module Three Milestone"; // Macro for window title
// Variables for window width and height
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
// Main GLFW window
GLFWwindow* gWindow = nullptr;
// Shader program
GLuint gProgramId;
}
/* User-defined Function prototypes to:
* initialize the program, set the window size,
* redraw graphics on the window when resized,
* and render graphics on the screen
*/
bool UInitialize(int, char* [], GLFWwindow** window);
void UResizeWindow(GLFWwindow* window, int width, int height);
void UProcessInput(GLFWwindow* window);
bool UCreateShaderProgram(const char* vtxShaderSource, const char* fragShaderSource, GLuint& programId);
void UDestroyShaderProgram(GLuint programId);
/* Vertex Shader Source Code*/
const GLchar* vertexShaderSource = GLSL(440,
layout(location = 0) in vec3 position; // Vertex data from Vertex Attrib Pointer 0
layout(location = 1) in vec4 color; // Color data from Vertex Attrib Pointer 1
out vec4 vertexColor; // variable to transfer color data to the fragment shader
//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
vertexColor = color; // references incoming color data
}
);
/* Fragment Shader Source Code*/
const GLchar* fragmentShaderSource = GLSL(440,
in vec4 vertexColor; // Variable to hold incoming color data from vertex shader
out vec4 fragmentColor;
void main()
{
fragmentColor = vec4(vertexColor);
}
);
int main(int argc, char* argv[])
{
if (!UInitialize(argc, argv, &gWindow))
return EXIT_FAILURE;
// Create the mesh
//UCreateMesh(gMesh); // Calls the function to create the Vertex Buffer Object
Cone Cone1;
Cone1.CreateMesh();
Cone Cone2;
Cone2.CreateMesh();
// Create the shader program
if (!UCreateShaderProgram(vertexShaderSource, fragmentShaderSource, gProgramId))
return EXIT_FAILURE;
// Sets the background color of the window to black (it will be implicitely used by glClear)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// render loop
// -----------
while (!glfwWindowShouldClose(gWindow))
{
// input
UProcessInput(gWindow);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 scale = glm::scale(glm::vec3(1.0f, 1.0f, 1.0f));
glm::mat4 rotation = glm::rotate(-90.0f, glm::vec3(1.0, 1.0f, 1.0f));
glm::mat4 translation = glm::translate(glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 model = translation * rotation * scale;
glm::mat4 view = glm::translate(glm::vec3(0.0f, 0.0f, -5.0f));
glm::mat4 scale2 = glm::scale(glm::vec3(2.0f, 2.0f, 2.0f));
glm::mat4 rotation2 = glm::rotate(0.0f, glm::vec3(1.0, 1.0f, 1.0f));
glm::mat4 translation2 = glm::translate(glm::vec3(-1.0f, 0.0f, 0.0f));
glm::mat4 model2 = translation2 * rotation2 * scale2;
// Transforms the camera: move the camera back (z axis)
glm::mat4 view2 = glm::translate(glm::vec3(0.0f, 0.0f, -5.0f));
Cone1.Render(gWindow, gProgramId, model, view);
Cone2.Render(gWindow, gProgramId, model2, view2);
// unbind
glBindVertexArray(0);
glUseProgram(0);
glfwSwapBuffers(gWindow);
glfwPollEvents();
}
// Release shader program
UDestroyShaderProgram(gProgramId);
exit(EXIT_SUCCESS); // Terminates the program successfully
}
// Initialize GLFW, GLEW, and create a window
bool UInitialize(int argc, char* argv[], GLFWwindow** window)
{
// GLFW: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// GLFW: window creation
// ---------------------
* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, NULL, NULL);
if (*window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return false;
}
glfwMakeContextCurrent(*window);
glfwSetFramebufferSizeCallback(*window, UResizeWindow);
// GLEW: initialize
// ----------------
// Note: if using GLEW version 1.13 or earlier
glewExperimental = GL_TRUE;
GLenum GlewInitResult = glewInit();
if (GLEW_OK != GlewInitResult)
{
std::cerr << glewGetErrorString(GlewInitResult) << std::endl;
return false;
}
// Displays GPU OpenGL version
cout << "INFO: OpenGL Version: " << glGetString(GL_VERSION) << endl;
return true;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
void UProcessInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
void UResizeWindow(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// Implements the UCreateShaders function
bool UCreateShaderProgram(const char* vtxShaderSource, const char* fragShaderSource, GLuint& programId)
{
// Compilation and linkage error reporting
int success = 0;
char infoLog[512];
// Create a Shader program object.
programId = glCreateProgram();
// Create the vertex and fragment shader objects
GLuint vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
// Retrive the shader source
glShaderSource(vertexShaderId, 1, &vtxShaderSource, NULL);
glShaderSource(fragmentShaderId, 1, &fragShaderSource, NULL);
// Compile the vertex shader, and print compilation errors (if any)
glCompileShader(vertexShaderId); // compile the vertex shader
// check for shader compile errors
glGetShaderiv(vertexShaderId, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShaderId, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
return false;
}
glCompileShader(fragmentShaderId); // compile the fragment shader
// check for shader compile errors
glGetShaderiv(fragmentShaderId, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShaderId, sizeof(infoLog), NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
return false;
}
// Attached compiled shaders to the shader program
glAttachShader(programId, vertexShaderId);
glAttachShader(programId, fragmentShaderId);
glLinkProgram(programId); // links the shader program
// check for linking errors
glGetProgramiv(programId, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(programId, sizeof(infoLog), NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
return false;
}
glUseProgram(programId); // Uses the shader program
return true;
}
void UDestroyShaderProgram(GLuint programId)
{
glDeleteProgram(programId);
}
// UIManager.h
#ifndef UIMANAGER_H
#define UIMANAGER_H
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream> // cout, cerr
#include <cstdlib> // EXIT_FAILURE
#include <GL/glew.h> // GLEW library
#include <GLFW/glfw3.h> // GLFW library
namespace
{
// Stores the GL data relative to a given mesh
struct GLMesh
{
GLuint vao; // Handle for the vertex array object
GLuint vbos[2]; // Handles for the vertex buffer objects
GLuint nIndices; // Number of indices of the mesh
};
}
class Cone {
public:
Cone();
void CreateMesh();
void Render(GLFWwindow* gWindow, GLuint& gProgramId, glm::mat4& modelMatrix, glm::mat4& viewMatrix);
private:
// Member variables, if any
GLMesh gMesh;
};
#endif // CONE_H
#include "Cone.h"
#include <GL/glew.h>
#include <glm/trigonometric.hpp>
// CTOR
Cone::Cone() {
}
void Cone::CreateMesh() {
const int numSlices = 50; // Increase the number of slices for smoother base
const int numVertices = numSlices + 1;
GLfloat verts[numVertices * 7]{};
// For the top vertex
verts[0] = 0.0f; // X position
verts[1] = 1.0f; // Y position
verts[2] = 0.0f; // Z position
verts[3] = 1.0f; // Red color
verts[4] = 0.0f; // Green color
verts[5] = 0.0f; // Blue color
verts[6] = 1.0f; // Alpha (transparency)
// For the other vertices
for (int i = 0; i < numSlices; ++i) {
float theta = (2.0f * 3.14159265359f * float(i)) / float(numSlices);
float x = cos(theta);
float z = sin(theta);
int vertexIndex = (i + 1) * 7;
verts[vertexIndex] = x;
verts[vertexIndex + 1] = 0.0f;
verts[vertexIndex + 2] = z;
verts[vertexIndex + 3] = 0.0f; // Set your desired color here (e.g., 0.0f for red, 1.0f for green, etc.)
verts[vertexIndex + 4] = 1.0f;
verts[vertexIndex + 5] = 0.0f;
verts[vertexIndex + 6] = 1.0f; // Alpha
}
GLushort indices[numSlices * 3];
for (int i = 0; i < numSlices; ++i) {
indices[i * 3] = 0;
indices[i * 3 + 1] = i + 1;
indices[i * 3 + 2] = (i + 1) % numSlices + 1;
}
const GLuint floatsPerVertex = 3;
const GLuint floatsPerColor = 4;
glGenVertexArrays(1, &gMesh.vao); // we can also generate multiple VAOs or buffers at the same time
glBindVertexArray(gMesh.vao);
// Create 2 buffers: first one for the vertex data; second one for the indices
glGenBuffers(2, gMesh.vbos);
glBindBuffer(GL_ARRAY_BUFFER, gMesh.vbos[0]); // Activates the buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); // Sends vertex or coordinate data to the GPU
gMesh.nIndices = sizeof(indices) / sizeof(indices[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gMesh.vbos[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Strides between vertex coordinates is 6 (x, y, z, r, g, b, a). A tightly packed stride is 0.
GLint stride = sizeof(float) * (floatsPerVertex + floatsPerColor);// The number of floats before each
// Create Vertex Attribute Pointers
glVertexAttribPointer(0, floatsPerVertex, GL_FLOAT, GL_FALSE, stride, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, floatsPerColor, GL_FLOAT, GL_FALSE, stride, (char*)(sizeof(float) * floatsPerVertex));
glEnableVertexAttribArray(1);
}
void Cone::Render(GLFWwindow* gWindow, GLuint& gProgramId, glm::mat4& modelMatrix, glm::mat4& viewMatrix) {
// Enable z-depth
glEnable(GL_DEPTH_TEST);
// Clear the frame and z buffers
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Creates a perspective projection
glm::mat4 projection = glm::perspective(45.0f, (GLfloat)800 / (GLfloat)600, 0.1f, 100.0f);
// Set the shader to be used
glUseProgram(gProgramId);
// Retrieves and passes transform matrices to the Shader program
GLint modelLoc = glGetUniformLocation(gProgramId, "model");
GLint viewLoc = glGetUniformLocation(gProgramId, "view");
GLint projLoc = glGetUniformLocation(gProgramId, "projection");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMatrix));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(viewMatrix));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// Activate the VBOs contained within the mesh's VAO
glBindVertexArray(gMesh.vao);
// Draws the triangles
glDrawElements(GL_TRIANGLES, gMesh.nIndices, GL_UNSIGNED_SHORT, NULL); // Draws the triangle
// Deactivate the Vertex Array Object
glBindVertexArray(0);
glfwSwapBuffers(gWindow); // Flips the the back buffer with the front buffer every frame.
}
“网上”的人是正确的:
glfwSwapBuffers
只能在绘制完所有内容后调用。交换缓冲区使当前状态对用户可见,如果您在中间这样做,则将显示一半渲染的图像,这被认为是闪烁的。
您只能看到第二个立方体的原因是
glClear
只能在帧的开头调用,而不是在帧之间调用。由于 glClear
清除后台缓冲区,所有已绘制的内容都将被删除。在您的代码中,渲染第二个圆锥体时清除屏幕将删除第一个圆锥体的像素。