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


相关推荐

  • 忆贵州三年的教书编程岁月:不弛于空想,不骛于虚声「建议收藏」

    忆贵州三年的教书编程岁月:不弛于空想,不骛于虚声「建议收藏」回首,2016年7月他离开北京回到了家乡贵州,成为了贵州财经大学的一名青年教师。转眼,2019年7月他迎来了人生的第三张通知书,即将辗转第三个城市,开始新的征途。教书三年,讲台前的每一次分享都值得回味,学生的每一句“老师好”,每一个问候和祝福,都留下了深刻的印象。

  • java输入语句怎么写_java中输入语句是怎么写的「建议收藏」

    java输入语句怎么写_java中输入语句是怎么写的「建议收藏」java中用户输入语句的写法是:1、【Scannersc=newScanner(System.in);】;2、【Stringinput=sc.next();】。(视频教程推荐:java视频)具体代码如下:importjava.util.Scanner;publicclassNumber{/***@paramargs*author:sun*time:2011-05-2…

  • 协同过滤推荐算法在python上的实现

    1.引言信息大爆炸时代来临,用户在面对大量的信息时无法从中迅速获得对自己真正有用的信息。传统的搜索系统需要用户提供明确需求,从用户提供的需求信息出发,继而给用户展现信息,无法针对不同用户的兴趣爱好提供相应的信息反馈服务。推荐系统相比于搜索系统,不需要提供明确需求,便可以为每个用户实现个性化推荐结果,让每个用户更便捷地获取信息。它是根据用户的兴趣特点和购买行为,向用户推荐用户感兴趣…

  • uniapp的swiper_vue轮播图插件

    uniapp的swiper_vue轮播图插件swiper详细数据参照uni-app官方swiper属性<!–swiper轮播图–><uni-swiper-dot:info=”bannerList”><swiperclass=”swiper-box” autoplay=”true” circular=”true” indicatorDots=”true” indicator-active-color=”#fcf6f3″ >

    2022年10月29日
  • dex文件格式

    dex文件格式dex文件格式Android4.0源码Dalvik/docs目录下文档dex-format.html有详细介绍dex文件格式1.dex文件中的数据结构dex文件使用到的数据类型u1~u8表示1到8字节的无符号数,而sleb128、uleb128与uleb128p1则是dex文件中特有的LEB128数据类型。每个LEB128由1~5个字节组成,所有的字节组合在一起表示一…

  • 论文笔记之STN_论文笔记软件

    论文笔记之STN_论文笔记软件这篇文章是15年谷歌DeepMind团队推出的一个可以学习一种变换,这种变换可以将进行了仿射变换的目标进行矫正的网络——SpatialTransformerNetwork(STN)。STN具有空间不变性特点,也就是说STN可以使图片经过各种变换后的识别效果和未变换前的识别效果保持不变。Note:空间不变性:旋转、平移、缩放、裁剪不变性。传统的池化方式(MaxPooling/AveragePooling)所带来卷积网络的位移不变性和旋转不变性只是局部的和固定的(池化的大小一般都很小,比如3×.

    2022年10月19日

发表回复

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

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