环境:Windows、VS2012。
简介:一个用OpenGL实现的2D(所有点的Z坐标设为0.0f)随手画,刚开始学opengl的时候,只是按照教程写了一遍代码,对其理解还不是很深刻,本功能把各功能分开,让其看上去更像一个工程。这个随手画其实效率很低,实现的方法是画点,由n个点组成一条线,每个点我定义成一个正n边形(因为点小,近似圆)。
结构图如下:
效果(鼠标点击时画线,鼠标释放线消失):
关键代码在Line.h Point.h 源.cpp中:
//Line.h
#include <string >
#include <iostream>
#include <vector>
#include <GL/glew.h>
#include <cmath>
#include "Point.h"
using namespace std;
class Line{
public :
Line();
Line(GLfloat);
void add(Point,int);
int getLength();
void clear();
//void createBuffer(GLuint&,GLuint&);
void createBuffer();
void deleteBuffer();
GLuint getVAO();
private :
GLfloat radius;
vector<Point> clickPoint;
vector<GLfloat> allPoint;
GLfloat num[999999];
GLuint VAO;
GLuint VBO;
int edges;
};
//设置每个点的大小(类似圆的半径),边的数量
Line::Line(GLfloat radius = 0.1f,int edges = 4){
this->radius = radius;
}
//添加新的点,每个点都是由n个正三角形组成的正n六边形
void Line::add(Point p){
clickPoint.push_back(p);
const GLfloat PI = 3.141592653;
Point pointTemp[16]={p};
for(int i=1;i<=edges;++i){
pointTemp[i].x = p.x + radius * cos(PI*(360/edges * i)/180.0);
pointTemp[i].y = p.y + radius * sin(PI*(360/edges * i)/180.0);
}
for(int i = 1;i <= edges - 1;++i){
allPoint.push_back(pointTemp[0].x);
allPoint.push_back(pointTemp[0].y);
allPoint.push_back(0.0f);
allPoint.push_back(pointTemp[i].x);
allPoint.push_back(pointTemp[i].y);
allPoint.push_back(0.0f);
allPoint.push_back(pointTemp[i+1].x);
allPoint.push_back(pointTemp[i+1].y);
allPoint.push_back(0.0f);
}
allPoint.push_back(pointTemp[0].x);
allPoint.push_back(pointTemp[0].y);
allPoint.push_back(0.0f);
allPoint.push_back(pointTemp[edges].x);
allPoint.push_back(pointTemp[edges].y);
allPoint.push_back(0.0f);
allPoint.push_back(pointTemp[1].x);
allPoint.push_back(pointTemp[1].y);
allPoint.push_back(0.0f);
for(int i=0;i<allPoint.size();++i){
num[i] = allPoint[i];
}
}
int Line::getLength(){return allPoint.size();}
GLuint Line::getVAO(){return VAO;}
//创建VAO
void Line::createBuffer(){
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(num),num, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void Line::clear(){
clickPoint.clear();
allPoint.clear();
memset(num,0,sizeof(num));
}
void Line::deleteBuffer(){
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
}
//Point.h
#ifndef __POINT_HH__
#define __POINT_HH__
struct Point{
GLfloat x,y;
Point() {};
Point(GLfloat x,GLfloat y) {
this->x=x;
this->y=y;
}
};
#endif
//Shader.h
#ifndef SHADER_H
#define SHADER_H
#include <string >
#include <fstream>
#include <sstream>
#include <iostream>
#include <GL/glew.h>
class Shader{
public :
// 程序ID
GLuint Program;
// 构造器读取并构建着色器
Shader(const GLchar* vertexPath, const GLchar* fragmentPath){
//1.从文件路径中获取着色器
std::string vertexCode,fragmentCode;
std::ifstream vShaderFile,fShaderFile;
//保证ifstream异常能抛出
vShaderFile.exceptions(std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::badbit);
try{
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
//读取文件的缓冲内容到流中
std::stringstream vShaderStream,fShaderStream;
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
vShaderFile.close();
fShaderFile.close();
//转换为GLchar数组
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}catch(std::ifstream::failure e){
std::cout<<"ERROR::SHADER::FILEW_NOT_SUCCESS_READ"<<std::endl;
}
const GLchar *vShaderCode = vertexCode.c_str();
const GLchar *fShaderCode = fragmentCode.c_str();
//2.compile shader
GLuint vertex,fragment;
GLint success;
GLchar infoLog[512];
//vertex shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex,1,&vShaderCode,NULL);
glCompileShader(vertex);
glGetShaderiv(vertex,GL_COMPILE_STATUS,&success);
if(!success){
glGetShaderInfoLog(vertex,512,NULL,infoLog);
std::cout<<"ERROR::SHADER_VERTEX_COMPILE_FAILED\n"<<infoLog<<std::endl;
}
//fragment shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment,1,&fShaderCode,NULL);
glCompileShader(fragment);
glGetShaderiv(fragment,GL_COMPILE_STATUS,&success);
if(!success){
glGetShaderInfoLog(fragment,512,NULL,infoLog);
std::cout<<"ERROR::SHADER_FRAGMENT_COMPILE_FAILED\n"<<infoLog<<std::endl;
}
//shader program
this->Program = glCreateProgram();
glAttachShader(this->Program,vertex);
glAttachShader(this->Program,fragment);
glLinkProgram(this->Program);
glGetShaderiv(this->Program,GL_LINK_STATUS,&success);
if(!success){
glGetShaderInfoLog(this->Program,512,NULL,infoLog);
std::cout<<"ERROR::SHADER_PROGRAM_LINKING_FAILED\n" << infoLog<<std::endl;
}
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// 使用程序
void Use(){
glUseProgram(this->Program);
}
};
#endif
//源.cpp
#include <iostream>
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Shader.h"
#include "Point.h"
#include "Line.h"
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow *window,double xpos,double ypos);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) ;
// Window dimensions
const GLuint WIDTH = 800, HEIGHT = 600;
Line line(0.01f);
// The MAIN function, from here we start the application and run the game loop
int main()
{
GLfloat num;
std::cout<<sizeof(num);
// Init GLFW
glfwInit();
// Set all the required options for GLFW
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);
// Create a GLFWwindow object that we can use for GLFW's functions
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
glfwMakeContextCurrent(window);
// Set the required callback functions
glfwSetKeyCallback(window, key_callback);
glfwSetMouseButtonCallback(window,mouse_button_callback);
glfwSetCursorPosCallback(window, mouse_callback); //鼠标输入回调函数
// Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
glewExperimental = GL_TRUE;
// Initialize GLEW to setup the OpenGL Function pointers
glewInit();
// Define the viewport dimensions
int width, height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
Shader ourShader("vertexShader.txt","fragmentShader.txt");
// Game loop
while (!glfwWindowShouldClose(window))
{
line.createBuffer();
glfwPollEvents();
// Render
// Clear the colorbuffer
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw our first triangle
ourShader.Use();
glBindVertexArray(line.getVAO());
glDrawArrays(GL_TRIANGLES, 0, line.getLength());
glBindVertexArray(0);
// Swap the screen buffers
glfwSwapBuffers(window);
line.deleteBuffer();
}
// Terminate GLFW, clearing any resources allocated by GLFW.
glfwTerminate();
return 0;
}
// Is called whenever a key is pressed/released via GLFW
bool mouseLeftIsClick ;
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);
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
if (action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT){
mouseLeftIsClick = true;
}
if (action == GLFW_RELEASE && button == GLFW_MOUSE_BUTTON_LEFT){
mouseLeftIsClick = false;
line.clear();
}
}
//鼠标移动回调函数
void mouse_callback(GLFWwindow *window,double xpos,double ypos){
if(mouseLeftIsClick){
//屏幕坐标和着色器坐标变换
Point p(2.0*xpos/(GLfloat)WIDTH - 1.0f,1.0f - 2.0*ypos/(GLfloat)HEIGHT);
line.add(p);
std::cout<<xpos<<","<<ypos<<","<<line.getLength()<<std::endl;
}
}
//vertexShader.txt
#version 330 core
layout (location = 0) in vec3 position;
void main()
{
gl_Position = vec4(position, 1.0f);
}
//fragmentShader.txt
#version 330 core
out vec4 color;
void main()
{
color = vec4(1.0f,0.5f,0.0f,1.0f);
}