在美国接触到的第一个project是OpenGL。功能是要实现鼠标点击变色以及移动。第一次接触OpenGL,所以花了很多时间在学习上面,其实语法是和C++一样的,只不过用了很多库,因此在库函数上面花了很多精力。这是一个二维的界面,所以并不十分高级,很适合新手练级。
附上代码:
// Include standard headers
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <sstream>
// Include GLEW
#include <GL/glew.h>
// Include GLFW
#include <glfw3.h>
GLFWwindow* window;
// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
using namespace glm;
using namespace std;
// Include AntTweakBar
#include <AntTweakBar.h>
#include <common/shader.hpp>
#include <common/controls.hpp>
#include <common/objloader.hpp>
#include <common/vboindexer.hpp>
typedef struct {
float XYZW[4];
float RGBA[4];
} Vertex;
int main(void)
{
// Initialise GLFW
if (!glfwInit())
{
fprintf(stderr, "Failed to initialize GLFW\n");
return -1;
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Open a window and create its OpenGL context
window = glfwCreateWindow(1024, 768, "Bohan_Liu_66127941", NULL, NULL);//定义一个窗口
if (window == NULL) {
fprintf(stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// Initialize GLEW
glewExperimental = true; // Needed for core profile
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
return -1;
}
// Initialize the GUI
TwInit(TW_OPENGL_CORE, NULL);
TwWindowSize(1024, 768);
TwBar * GUI = TwNewBar("Picking");
TwSetParam(GUI, NULL, "refresh", TW_PARAM_CSTRING, 1, "0.1");
std::string message;
TwAddVarRW(GUI, "Last picked object", TW_TYPE_STDSTRING, &message, NULL);
// Ensure we can capture the escape key being pressed below
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
glfwSetCursorPos(window, 1024 / 2, 768 / 2);
// 此处为背景颜色
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
// Enable depth test
glEnable(GL_DEPTH_TEST);
// Accept fragment if it closer to the camera than the former one
glDepthFunc(GL_LESS);
// Cull triangles which normal is not towards the camera
glEnable(GL_CULL_FACE);
// Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
glm::mat4 ProjectionMatrix = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
// Or, for an ortho camera :
//glm::mat4 ProjectionMatrix = glm::ortho(-3.0f, 3.0f, -3.0f, 3.0f, 0.0f, 100.0f); // In world coordinates
// Camera matrix
glm::mat4 ViewMatrix = glm::lookAt(
glm::vec3(0, 0, -5), // 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)
);
// 定义点的初始坐标
Vertex Vertices[] =
{
{ { 1.0f, 0.0f, 0.0f, 1.0f },{ 1.0f, 0.0f, 0.0f, 1.0f } }, // 0
{ { -1.0f, 0.0f, 0.0f, 1.0f },{ 0.0f, 1.0f, 0.0f, 1.0f } }, // 1
{ { 0.0f, -1.0f, 0.0f, 1.0f },{ 0.0f, 0.0f, 1.0f, 1.0f } }, // 2
{ { 0.0f, 1.0f, 0.0f, 1.0f },{ 1.0f, 0.0f, 1.0f, 1.0f } }, // 3
{ { 0.7f, 0.7f, 0.0f, 1.0f },{ 0.0f, 1.0f, 1.0f, 1.0f } }, // 4
{ { 0.7f, -0.7f, 0.0f, 1.0f },{ 1.0f, 1.0f, 0.0f, 1.0f } }, // 5
{ { -0.7f, 0.7f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f, 1.0f } }, // 6
{ { -0.7f, -0.7f, 0.0f, 1.0f },{ 0.0f, 0.0f, 0.0f, 1.0f } }, // 7
};
Vertex Vertices2[] = //此处是为了改变颜色,这里的颜色改变颇为手动,位置坐标要跟随鼠标移动的结果
{
{ {Vertices[0].XYZW[0],Vertices[0].XYZW[1],0.0f,1.0f}, { 0.0f, 0.4f, 0.6f, 1.0f } }, // 0
{ { Vertices[1].XYZW[0],Vertices[1].XYZW[1],0.0f,1.0f },{ 0.6f, 0.0f, 0.4f, 1.0f } }, // 1
{ { Vertices[2].XYZW[0],Vertices[2].XYZW[1],0.0f,1.0f },{ 0.4f, 0.6f, 0.0f, 1.0f } }, // 2
{ { Vertices[3].XYZW[0],Vertices[3].XYZW[1],0.0f,1.0f },{ 0.0f, 1.0f, 0.0f, 1.0f } }, // 3
{ { Vertices[4].XYZW[0],Vertices[4].XYZW[1],0.0f,1.0f },{ 1.0f, 0.0f, 0.0f, 1.0f } }, // 4
{ { Vertices[5].XYZW[0],Vertices[5].XYZW[1],0.0f,1.0f },{ 0.0f, 0.0f, 1.0f, 1.0f } }, // 5
{ { Vertices[6].XYZW[0],Vertices[6].XYZW[1],0.0f,1.0f },{ 0.0f, 0.0f, 0.0f, 1.0f } }, // 6
{ { Vertices[7].XYZW[0],Vertices[7].XYZW[1],0.0f,1.0f },{ 0.3f, 0.3f, 0.3f, 1.0f } }, // 7
};
unsigned short Indices[] = {
0, 1, 2, 3, 4, 5, 6, 7
};
const size_t BufferSize = sizeof(Vertices);
const size_t VertexSize = sizeof(Vertices[0]);
const size_t RgbOffset = sizeof(Vertices[0].XYZW);
const size_t IndexCount = sizeof(Indices) / sizeof(unsigned short);
float pickingColor[IndexCount] = { 0 / 255.0f, 1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f, 5 / 255.0f, 6 / 255.0f, 7 / 255.0f }; // set this procedurally for greater number of points
// Create Vertex Array Object
GLuint VertexArrayId;
glGenVertexArrays(1, &VertexArrayId);
glBindVertexArray(VertexArrayId);
// Create and load Buffer for vertex data
GLuint VertexBufferId;
glGenBuffers(1, &VertexBufferId);
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferId);
glBufferData(GL_ARRAY_BUFFER, BufferSize, Vertices, GL_STATIC_DRAW);
// Create Buffer for indices
GLuint IndexBufferId;
glGenBuffers(1, &IndexBufferId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
// Create and compile our GLSL program from the shaders
GLuint programID = LoadShaders("StandardShading.vertexshader", "StandardShading.fragmentshader");
GLuint pickingProgramID = LoadShaders("Picking.vertexshader", "Picking.fragmentshader");
// Get a handle for our "MVP" uniform
GLuint MatrixID = glGetUniformLocation(programID, "MVP");
GLuint ViewMatrixID = glGetUniformLocation(programID, "V");
GLuint ModelMatrixID = glGetUniformLocation(programID, "M");
GLuint PickingMatrixID = glGetUniformLocation(pickingProgramID, "MVP");
// Get a handle for our "pickingColorID" uniform
GLuint pickingColorArrayID = glGetUniformLocation(pickingProgramID, "PickingColorArray");
GLuint pickingColorID = glGetUniformLocation(pickingProgramID, "PickingColor");
// Get a handle for our "LightPosition" uniform
GLuint LightID = glGetUniformLocation(programID, "LightPosition_worldspace");
// For speed computation
double lastTime = glfwGetTime();
int nbFrames = 0;
bool LeftMouseButtonPressedLastFrame = GLFW_RELEASE;
bool RightMouseBUttonPressedLastFrame = GLFW_RELEASE;
int p = 0; double X, Y; glm::mat4 ModelMatrix2;
do {
// Measure speed
double currentTime = glfwGetTime();
nbFrames++;
if (currentTime - lastTime >= 1.0) { // If last prinf() was more than 1sec ago
// printf and reset
printf("%f ms/frame\n", 1000.0 / double(nbFrames));
nbFrames = 0;
lastTime += 1.0;
}
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferId); //更新BUffer,用于改变颜色和拖拽
glBufferSubData(GL_ARRAY_BUFFER, 0, BufferSize, Vertices); // update buffer data
if (LeftMouseButtonPressedLastFrame^glfwGetMouseButton(window,GLFW_MOUSE_BUTTON_LEFT)) {
LeftMouseButtonPressedLastFrame = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); //检测鼠标左键是否离开
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(pickingProgramID);
{
// Only the positions are needed (not the colors)
glEnableVertexAttribArray(0);
glm::mat4 ModelMatrix = glm::mat4(1.0); // TranslationMatrix * RotationMatrix;
glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;
// Send our transformation to the currently bound shader, in the "MVP" uniform
glUniformMatrix4fv(PickingMatrixID, 1, GL_FALSE, &MVP[0][0]);
glUniform1fv(pickingColorArrayID, IndexCount, pickingColor);
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferId);
// Assign vertex attributes
glVertexAttribPointer(
0, // attribute
8, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
VertexSize, // stride
(void*)0 // array buffer offset
);
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferId);
// Draw the ponts
glEnable(GL_PROGRAM_POINT_SIZE);
glDrawElements(
GL_POINTS, // mode
IndexCount, // count
GL_UNSIGNED_SHORT, // type
(void*)0 // element array buffer offset
);
glDisableVertexAttribArray(0);
// Wait until all the pending drawing commands are really done.
// Ultra-mega-over slow !
// There are usually a long time between glDrawElements() and
// all the fragments completely rasterized.
glFlush();
glFinish();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Read the pixel at the center of the screen.
// You can also use glfwGetMousePos().
// Ultra-mega-over slow too, even for 1 pixel,
// because the framebuffer is on the GPU.
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
unsigned char data[4];
glReadPixels(xpos, 768 - ypos, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data);
int pickedID = int(data[0]);//将颜色转化为点的ID
if (pickedID >7 ) { // Full white, must be the background !
message = "background";
}
else {
std::ostringstream oss;
oss << "point " << pickedID;
message = oss.str();
Vertex tmp = {};
swap(Vertices[pickedID].RGBA[0], Vertices2[pickedID].RGBA[0]); //变色
swap(Vertices[pickedID].RGBA[1], Vertices2[pickedID].RGBA[1]);
swap(Vertices[pickedID].RGBA[2], Vertices2[pickedID].RGBA[2]);
}
}
}
if (RightMouseBUttonPressedLastFrame^glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT)) { //拖拽点
if (RightMouseBUttonPressedLastFrame = GLFW_PRESS&&glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_RELEASE) { 检测右键是否释放
double xpos1, ypos1;
glfwGetCursorPos(window, &xpos1, &ypos1);//记录右键释放时的鼠标坐标
Vertex tmp = { { -(xpos1 - 516) / 133,(768 - ypos1 - 384) / 140,0.0f,1.0f },{Vertices[p].RGBA[0],Vertices[p].RGBA[1], Vertices[p].RGBA[2], 1.0f} };//将点移动到新的坐标
Vertices[p] = tmp;
RightMouseBUttonPressedLastFrame = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT);
}
RightMouseBUttonPressedLastFrame = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT);// Clear the screen in white
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(pickingProgramID);
{
// Only the positions are needed (not the colors)
glEnableVertexAttribArray(0);
ModelMatrix2 = glm::mat4(1.0); // TranslationMatrix * RotationMatrix;
glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix2;
// Send our transformation to the currently bound shader, in the "MVP" uniform
glUniformMatrix4fv(PickingMatrixID, 1, GL_FALSE, &MVP[0][0]);
glUniform1fv(pickingColorArrayID, IndexCount, pickingColor); // here we pass in the picking marker array
// 1rst attribute buffer : vertices
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferId);
// Assign vertex attributes
glVertexAttribPointer(
0, // attribute
8, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
VertexSize, // stride
(void*)0 // array buffer offset
);
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferId);
// Draw the ponts
glEnable(GL_PROGRAM_POINT_SIZE);
glDrawElements(
GL_POINTS, // mode
IndexCount, // count
GL_UNSIGNED_SHORT, // type
(void*)0 // element array buffer offset
);
glDisableVertexAttribArray(0);
// Wait until all the pending drawing commands are really done.
// Ultra-mega-over slow !
// There are usually a long time between glDrawElements() and
// all the fragments completely rasterized.
glFlush();
glFinish();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Read the pixel at the center of the screen.
// You can also use glfwGetMousePos().
// Ultra-mega-over slow too, even for 1 pixel,
// because the framebuffer is on the GPU.
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
unsigned char data[4];
glReadPixels(xpos, 768 - ypos, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data); // OpenGL renders with (0,0) on bottom, mouse reports with (0,0) on top
// Convert the color back to an integer ID
int pickedID = int(data[0]);
if (pickedID >7) { // Full white, must be the background !
message = "background";
}
else {
std::ostringstream oss;
oss << "point " << pickedID;
message = oss.str();
p = pickedID;
}
}
// Uncomment these lines to see the picking shader in effect
//glfwSwapBuffers(window);
//continue; // skips the normal rendering
}
// Dark blue background
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
// Re-clear the screen for real rendering
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use our shader
glUseProgram(programID);
{
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glm::mat4 ModelMatrix = glm::mat4(1.0); // TranslationMatrix * RotationMatrix;
glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;
// Send our transformation to the currently bound shader,
// in the "MVP" uniform
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, &ModelMatrix[0][0]);
glUniformMatrix4fv(ViewMatrixID, 1, GL_FALSE, &ViewMatrix[0][0]);
glm::vec3 lightPos = glm::vec3(4, 4, 4);
glUniform3f(LightID, lightPos.x, lightPos.y, lightPos.z);
// 1rst attribute buffer : vertices
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferId);
glVertexAttribPointer(
0, // attribute
4, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
VertexSize, // stride
(void*)0 // array buffer offset
);
// 2nd attribute buffer : colors
glVertexAttribPointer(
1, // attribute
4, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
VertexSize, // stride
(GLvoid*)RgbOffset // array buffer offset
);
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferId);
// Draw the triangles !
glEnable(GL_PROGRAM_POINT_SIZE);
glDrawElements(
GL_POINTS, // mode
IndexCount, // count
GL_UNSIGNED_SHORT, // type
(void*)0 // element array buffer offset
);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
}
// Draw GUI
TwDraw();
// Swap buffers
glfwSwapBuffers(window);
glfwPollEvents();
} // Check if the ESC key was pressed or the window was closed
while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
glfwWindowShouldClose(window) == 0);
// Cleanup VBO and shader
glDeleteBuffers(1, &VertexBufferId);
glDeleteBuffers(1, &IndexBufferId);
glDeleteProgram(programID);
glDeleteProgram(pickingProgramID);
glDeleteVertexArrays(1, &VertexArrayId);
// Close OpenGL window and terminate GLFW
glfwTerminate();
return 0;
}
此处代码有一些值得注意的地方,Vertex变量只允许初始化时修改。也就是说,它不是左键值。所以需要修改的时候,我便用了swap函数。此处需要注意。
这里没有附上shader文件,因为shader文件的代码比较简单,没做出什么大的修改。
改变位置后的变色,使用swap时,应该只swap颜色,不要swap位置,很关键。
实现效果如下:
