现代OpenGL教程(一):绘制三角形(imgui+OpenGL3.3)

前言:imgui是一个开源的GUI框架,自带的例子里面直接集成了glfw+gl3w环境,本例使用的版本是imguiv1.61,下载地址:https://github.com/ocornut/imgui/tags教程目录(持续更新中):现代OpenGL教程(一):绘制三角形(imgui+OpenGL3.3)现代OpenGL教程(二):矩阵变换(imgui+OpenGL3.3)

大家好,又见面了,我是你们的朋友全栈君。

前言:imgui 是一个开源的GUI框架,自带的例子里面直接集成了glfw+gl3w环境,本例使用的版本是imgui v1.61,下载地址:https://github.com/ocornut/imgui/tags

教程目录(持续更新中):
现代OpenGL教程(一):绘制三角形(ImGui+OpenGL3.3)
现代OpenGL教程(二):矩阵变换(ImGui+OpenGL3.3)
现代OpenGL教程(三):绘制彩色立方体(ImGui+OpenGL3.3)
现代OpenGL教程(四):立方体纹理贴图(ImGui+OpenGL3.3)
现代OpenGL教程(五):obj模型加载(ImGui+OpenGL3.3)
现代OpenGL教程(六):鼠标和键盘(ImGui+OpenGL3.3)
现代OpenGL教程(七):基础光照——颜色(ImGui+OpenGL3.3)
现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)
现代OpenGL教程(九):基础光照——材质(ImGui+OpenGL3.3)


本节要点:

1. OpenGL图形渲染管线
2. OpenGL着色器语言(OpenGL Shading Language, GLSL)
3. 顶点数组对象:Vertex Array Object,VAO
4. 顶点缓冲对象:Vertex Buffer Object,VBO

运行效果:
现代OpenGL教程(一):绘制三角形(imgui+OpenGL3.3)


OpenGL图形渲染管线、shader和GLSL

现代OpenGL教程(一):绘制三角形(imgui+OpenGL3.3)

OpenGL的图形渲染管线的作用是将3D坐标转为能显示在屏幕上有色2D像素数组,主要由两部分组成:把3D坐标转换为2D坐标,把2D坐标转变为实际的有颜色的像素。上图的每一个阶段都能够被GPU的一种特定的小程序(即shader,着色器)并行执行,从而实现数据的快速处理。OpenGL着色器是用OpenGL着色器语言(OpenGL Shading Language, GLSL)写成的。现代OpenGL中,我们必须定义至少一个顶点着色器和一个片段着色器(因为GPU中没有默认的顶点/片段着色器)。GLSL必须在运行时编译,每次启动程序时,所有的着色器将重新编译。

定义一个三角形

static const GLfloat g_vertex_buffer_data[] = { 
   
   -1.0f, -1.0f, 0.0f,
   1.0f, -1.0f, 0.0f,
   0.0f,  1.0f, 0.0f,
};

顶点数组对象(VAO)

创建一个VAO,注意:这一步必须在其他OpenGL调用前完成。

GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);

顶点缓冲对象(VBO)

就像OpenGL中的其它对象一样,这个缓冲有一个独一无二的ID,所以我们可以使用glGenBuffers函数和一个缓冲ID生成一个VBO对象。着色器和缓冲对象一样不能直接访问,我们仅拥有其ID,其真正的实现隐藏在驱动程序中。OpenGL有很多缓冲对象类型,顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER。

glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数。它的第一个参数是目标缓冲的类型:顶点缓冲对象当前绑定到GL_ARRAY_BUFFER目标上。第二个参数指定传输数据的大小(以字节为单位);用一个简单的sizeof()计算出顶点数据大小就行。第三个参数是我们希望发送的实际数据。第四个参数指定了我们希望显卡如何管理给定的数据。

//定义顶点缓冲,并将顶点缓冲传给OpenGL
GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
// 把我们的顶点数组复制到一个顶点缓冲中,供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

现在已经把顶点数据储存在显卡的内存中,用vertexbuffer这个顶点缓冲对象来管理这些顶点数据。


完整代码

顶点着色器代码:SimpleVertexShader.vertexshader

#version 330 core

// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;

void main(){ 
   
    gl_Position.xyz = vertexPosition_modelspace;
    gl_Position.w = 1.0;
}

片段着色器代码:SimpleFragmentShader.fragmentshader

#version 330 core

// Ouput data
out vec3 color;

void main()
{ 
   
	// Output color = red 
	color = vec3(1,0,0);
}

shader.hpp

#ifndef SHADER_HPP
#define SHADER_HPP

GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path);

#endif

shader.cpp

#include <stdio.h>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <sstream>
using namespace std;
#include <stdlib.h>
#include <string.h>
#include <GL/gl3w.h>
#include "shader.hpp"
GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){ 

// Create the shaders
GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
// Read the Vertex Shader code from the file
std::string VertexShaderCode;
std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
if(VertexShaderStream.is_open()){ 

std::stringstream sstr;
sstr << VertexShaderStream.rdbuf();
VertexShaderCode = sstr.str();
VertexShaderStream.close();
}else{ 

printf("Impossible to open %s. Are you in the right directory ? Don't forget to read the FAQ !\n", vertex_file_path);
getchar();
return 0;
}
// Read the Fragment Shader code from the file
std::string FragmentShaderCode;
std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
if(FragmentShaderStream.is_open()){ 

std::stringstream sstr;
sstr << FragmentShaderStream.rdbuf();
FragmentShaderCode = sstr.str();
FragmentShaderStream.close();
}
GLint Result = GL_FALSE;
int InfoLogLength;
// Compile Vertex Shader
printf("Compiling shader : %s\n", vertex_file_path);
char const * VertexSourcePointer = VertexShaderCode.c_str();
glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
glCompileShader(VertexShaderID);
// Check Vertex Shader
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){ 

std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
printf("%s\n", &VertexShaderErrorMessage[0]);
}
// Compile Fragment Shader
printf("Compiling shader : %s\n", fragment_file_path);
char const * FragmentSourcePointer = FragmentShaderCode.c_str();
glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
glCompileShader(FragmentShaderID);
// Check Fragment Shader
glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){ 

std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
printf("%s\n", &FragmentShaderErrorMessage[0]);
}
// Link the program
printf("Linking program\n");
GLuint ProgramID = glCreateProgram();
glAttachShader(ProgramID, VertexShaderID);
glAttachShader(ProgramID, FragmentShaderID);
glLinkProgram(ProgramID);
// Check the program
glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){ 

std::vector<char> ProgramErrorMessage(InfoLogLength+1);
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
printf("%s\n", &ProgramErrorMessage[0]);
}
glDetachShader(ProgramID, VertexShaderID);
glDetachShader(ProgramID, FragmentShaderID);
glDeleteShader(VertexShaderID);
glDeleteShader(FragmentShaderID);
return ProgramID;
}

主程序代码:main.cpp

#include "imgui.h"
#include "imgui_impl_glfw_gl3.h"
#include <stdio.h>
#include <GL/gl3w.h> // 使用gl3w,glad也行,注意要在项目工程中添加gl3w.c(或者glad.c/使用glad)
#include <GLFW/glfw3.h>
#include <iostream>
#include <common/shader.hpp>
void window_size_callback(GLFWwindow *window, int width, int height);
// 设置窗口大小
const unsigned int Window_width = 1600;
const unsigned int Window_height = 1200;
int main()
{ 

// 实例化GLFW窗口
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//下面这条语句是为了适应苹果系统
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// 创建一个窗口对象,这个窗口对象存放了所有和窗口相关的数据,而且会被GLFW的其他函数频繁地用到。
// 此外增加 if (window == NULL) 判断窗口是否创建成功
GLFWwindow *window = glfwCreateWindow(Window_width, Window_height, "ImGui Triangle", NULL, NULL);
if (window == NULL)
{ 

std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, window_size_callback);
glfwSwapInterval(1);
//初始化gl3w
gl3wInit();
//创建并绑定ImGui
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
(void)io;
ImGui_ImplGlfwGL3_Init(window, true);
ImGui::StyleColorsDark();
//初始化各种数据
bool ImGui = true;
bool the_same_color = false;
bool draw_trangle_without_render = false;
bool draw_trangle = false;
bool bonus_draw_line = false;
bool bonus_draw_another_trangle = false;
unsigned int VBO, VAO, EBO;
bool show_demo_window = true;
//创建一个VAO,并将它设为当前对象
GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
//绑定顶点数组对象
glBindVertexArray(VertexArrayID);
// 加载shader文件,创建并编译GLSL程序
GLuint programID = LoadShaders("SimpleVertexShader.vertexshader", "SimpleFragmentShader.fragmentshader");
ImVec4 v1 = ImVec4(-0.25f, -0.25f, 0.0f, 1.00f);
ImVec4 v2 = ImVec4(0.25f, -0.25f, 0.0f, 1.00f);
ImVec4 v3 = ImVec4(0.0f, 0.25f, 0.0f, 1.00f);
//定义顶点缓冲,并将顶点缓冲传给OpenGL
GLuint vertexbuffer;
// 渲染循环
while (!glfwWindowShouldClose(window))
{ 

GLfloat g_vertex_buffer_data[] = { 

v1.x,
v1.y,
v1.z,
v2.x,
v2.y,
v2.z,
v3.x,
v3.y,
v3.z,
};
glGenBuffers(1, &vertexbuffer);
// 把我们的顶点数组复制到一个顶点缓冲中,供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
// 创建ImGui
glfwPollEvents();
ImGui_ImplGlfwGL3_NewFrame();
ImGui::Begin("change vertex", &ImGui, ImGuiWindowFlags_MenuBar);
ImGui::SliderFloat("", &v3.y, -1.0f, 1.0f, "v3.y = %.3f");
ImGui::End();
if (show_demo_window)
{ 

ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver); // Normally user code doesn't need/want to call this because positions are saved in .ini file anyway. Here we just want to make the demo initial state a bit more friendly!
ImGui::ShowDemoWindow(&show_demo_window);
}
// 渲染窗口颜色
int view_width, view_height;
glfwGetFramebufferSize(window, &view_width, &view_height);
glViewport(0, 0, view_width, view_height);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ImGui::Render();
ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
glUseProgram(programID);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
//设定顶点属性指针
glVertexAttribPointer(
0,        // attribute 0. No particular reason for 0, but must match the layout in the shader.
3,        // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0,        // stride
(void *)0 // array buffer offset
);
// 画三角形
glDrawArrays(GL_TRIANGLES, 0, 3); // 3 indices starting at 0 -> 1 triangle
glDisableVertexAttribArray(0);
// 双缓冲。前缓冲保存着最终输出的图像,它会在屏幕上显示;而所有的的渲染指令都会在后缓冲上绘制。
glfwSwapBuffers(window);
}
// 释放VAO、VBO、EBO资源
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
// 释放ImGui资源
ImGui_ImplGlfwGL3_Shutdown();
ImGui::DestroyContext();
// 清除所有申请的glfw资源
glfwTerminate();
return 0;
}
void window_size_callback(GLFWwindow *window, int width, int height)
{ 

glViewport(0, 0, width, height);
}

参考资料:
1.OpenGL–使用ImGui渲染三角形 – Pan_Chengyuan的博客 – CSDN博客
2.第二课:绘制第一个三角形 – opengl-tutorial
3.你好,三角形 – LearnOpenGL CN

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/126560.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 三十而立,从零开始学ios开发

    三十而立,从零开始学ios开发三十而立,从零开始学ios开发(二十):ApplicationSettingsandUserDefaults(下)摘要:在上一篇的学习中,我们知道了如何为一个App添加它的Settings设置项,在Settings设置项中我们可以添加哪些类型的控件,这些控件都是通过一个plist来进行管理的,我们只需对plist进行修改添加,就可以映射到Settings中。但是在上一篇中

  • checkbox选中与取消选择[通俗易懂]

    checkbox选中与取消选择[通俗易懂]checkbox选中与取消选择1.html&lt;form&gt;&lt;inputtype="checkbox"name="items"value="足球"/&gt;足球&lt;inputtype="checkbox"name="items"value="篮球"/&gt;篮球&

  • connectionstrings-sql server 2012[通俗易懂]

    connectionstrings-sql server 2012[通俗易懂]最近发现了超全connectionstrings网站:https://www.connectionstrings.comSQLServer2012connectionstrings.NETFrameworkDataProviderforSQLServerStandardSecurityServer=myServerAddress;Databas…

  • SSM-Spring(1)「建议收藏」

    SSM-Spring(1)「建议收藏」SpringSpring是一个开源的免费的框架Spring是一个轻量级的,非入侵式的框架控制反转(IOC),面向切面编程(AOP)支持事务的处理,对框架整合的支持IOC理论UserDaoUserDaoImpUserSeviceUserServiceImp在之前,用户的需求可能会影响原来的代码。使用一个set。public void setUserDao(UserDao userDao){ this.userDao = userDao;}之前是主动创建对象,控制

  • Java网页版仿QQ实现在线聊天功能(附带源码)[通俗易懂]

    Java网页版仿QQ实现在线聊天功能(附带源码)[通俗易懂]疫情期间,整天闷在家里又不能聚会,大把的空余时间差点让我发霉,后来有个客户发来新年祝贺,情不自禁想起了一件事情,就是他曾经提起过,要是在后台管理系统里面整合个聊天功能该多好啊,有了这个念头,马上行动起来!!!一.系统演示1.1聊天窗体主界面演示1.2模拟两人在线聊天(点击图片查看演示视频)1.3在线演示(真实系统展示)在线演示,点击进入系统到这里,若是您想要的,接下来听我娓娓道来↘↘二.开发工具开发软件:JDK7.0、MyEclipse2014数据库:MySQL5.6服

  • 到底该不该用RTOS——rtos的优点

    到底该不该用RTOS——rtos的优点我现在要不要学习RTOS? 学习RTOS有什么好处? 我的项目要不要跑RTOS?······等等一些关于RTOS的问题,

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号