现代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

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • pycharm向左缩进_vscode整体缩进

    pycharm向左缩进_vscode整体缩进整体右移(缩进):鼠标全选住要缩进的代码,按tab整体左移(反缩进):鼠标全选住要缩进的代码,按tab+shift

    2022年8月25日
    3
  • Linux中用tar命令对文件夹进行打包压缩

    Linux中用tar命令对文件夹进行打包压缩一、打包的概念     打包:指将多个文件(或目录)合并成一个文件,方便在不同节点之间传递或在服务器集群上部署。     压缩或打包文件常见扩展名    *.tar,*.tar.gz,*.gz,*.bz2,*.Z;     Linux系统一般文件的扩展名用途不大,但是压缩或打包文件的扩展名时必须的,因为linux支持的压缩命令较多,不同的压缩技术使…

    2022年6月7日
    123
  • java前端开发自动生成器_一个简单实用的java代码生成器

    java前端开发自动生成器_一个简单实用的java代码生成器做现在这破项目boss还觉得进度慢,时间其实都浪费在了基础的CRUD上了,一个上午好不容易调通从action到service在到dao在到数据库的crud。工欲善其事必先利其器,有一款趁手的工具可有效提高开发效率,推荐一个简单实用的在线java代码生成器。代码生成器只是xjr快速开发框架功能的一部分。这款代码生成器是一款国内最新自主研发的JAVA代码自动生成器,目前来看这是一款十分赞的代码…

    2022年5月23日
    40
  • Method org.postgresql.jdbc4.Jdbc4Statement.setQueryTimeout(int) is not yet implemented错误解决

    Method org.postgresql.jdbc4.Jdbc4Statement.setQueryTimeout(int) is not yet implemented错误解决17:25:17:166 [Druid-ConnectionPool-Create-1566729816] ERROR log=c.a.d.p.DruidDataSource,traceId=,userId=,msg=create connection SQLException, url: jdbc:postgresql://…:5432/…, errorCode 0, state 0A0…

    2022年6月24日
    28
  • 弗曼学习法,你在用吗?

    弗曼学习法,你在用吗?今天简单的谈论一下弗曼学习法,是被学术界认为最niubi的学习方法。理查德.弗曼(1918-1988)1965年获得诺贝尔物理学奖,美籍犹太人,也是最早提出纳米的人。之所以以他的名字命名改学习方法,想必不用说大家也都知道了,总之很厉害一个人就是了。 弗曼学习法的原理,可以用一句话来概括(透过现象看本质),比方说我们刚学习、接触一个知识点,按照正常的逻辑就是去学会怎么使用它就行了,而带来的弊端就是,当时,亦或者一段时间内我们能记得,但是随着时间加长,没有使用过这个知识点,我们便会很快就忘记了。因为我

    2022年6月13日
    35
  • 白嫖免费域名+免费服务器[通俗易懂]

    点击蓝字关注我们免费注册地址https://byet.host/不要翻墙是真的免费吗?byethost.com是一个老牌的免费空间商,从2006年起就开始提供免费空间了,其免费服务非常稳…

    2022年4月16日
    78

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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