首页 > 其他分享 >OpenGL(VS2022)---(2)

OpenGL(VS2022)---(2)

时间:2024-11-25 16:28:52浏览次数:8  
标签:VAO OpenGL VBO 0.5 --- window VS2022 GL 着色器

前言:

        上一篇已经开始渲染窗口了,现在将准备好绘制一个图形了吗

着色器

        说到着色器就要引入一张在哪里都能看见的图

简单了解一下就知道,这是一个数据到一张图的过程,这里就不细讲了,我们可以操作哪些部分呢,顶点着色器,几何着色器,片段着色器,其他部分其实是不可操作的部分(当然这话放在以后不够绝对),这些都不重要,我们今天主要需要使用的着色器是顶点着色器和片段着色器(因为GPU不包含顶点着色器和片段着色器,所以必须配置),至于其他的就然计算机自己渲染吧

坐标

        在介绍着色器之前让我们先对坐标有一个简单的了解:在给坐标时,我们的范围在0-1,比如我们设置的window窗口实际大小是600*800,那么在第300*400的位置该如何用坐标表示呢,(300,400)?当然是错的,我们刚刚说过给坐标时的范围是0-1,这个时候我们会将600=1,800=1,对于300*400而言,都是0.5,所以坐标(0.5f,0.5f)就是表示300*400的位置了(后面的f表示是浮点类型的数据,如果你不知道可以补习一点c语言的基础哦),那么120*320用坐标如何表示呢?

答案是(0.2f,0.4f)

记录数据的对象

        VAO顶点数组对象,VBO顶点缓冲对象,EBO元素缓冲对象(这些不是变量,是固定名称,不可以更改哦!)

着色器有自己的语言GLSL,在编译器中如果直接写入则会报错,因为无法识别(后面会用文件的形式封装),现在我们使用字符串的形式让该语言内容被放入变量中,通过使用变量使用它

温馨提示:如果你是看文档的,这个代码不是凭空出现的,是依据于上一讲的内容在不同部分增加的,当然,在这里面会写完整

三角形分析代码内容

const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(0.5f, 0.5f, 0.7f, 1.0f);\n"
"}\n\0";

       1>在main函数之前需要导入GLSL的两个着色器,vertexShaderSource为顶点着色器,fragmentShaderSource为片段着色器,现在解析一下代码的含义

#version 330 core:OpenGL的版本3.3

layout (location = 0) in vec3 aPos:在location = 0的位置是一个输入,vec3是一个变量类型,含有三个值的变量,aPos是一个变量,类似于aPos内部封装了三个vec类型的变量x,y,z

void main(){ gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);}:这里的main并不是你实际的那个main,因为刚刚有说,这个是将着色器语言的内容用字符串写,如果用一个变量存它的地址不就相当于存了里面的内容嘛,这里面又使用了一个值,让他被赋值为vec4类型的值

在片段着色器中,out表示输出,输出的颜色

float vertices[] = {  
	-0.5f,-0.5f,0.0f,
	 0.5f,-0.5f,0.0f,
	 0.5f, 0.5f,0.0f
};

        2>输入顶点的坐标,刚刚我们解释了坐标,这里为什么有三个呢,因为其实这个可以表示3D,所以是x,y,z,我们将z都给0,不就在同一个平面吗,(只需要保证在同一个平面上就是2D,你可以随意设置相同的三个值)

int main() {
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	GLFWwindow* window = glfwCreateWindow(600, 800, "Triangle", NULL,NULL);
	if (window == NULL) {
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		return -1;
	}

        3>简单复习一下,这个是前面讲到的用glfw写一个窗口,glad告诉显卡,这里就不过多的赘述

unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int  success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
	glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
	std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
	glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
	std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_COMPILE_STATUS, &success);
if (!success) {
	glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
	std::cout << "ERROR::SHADER::PROGRAM::LINK_FAILED\n" << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

这段代码看似很复杂,其实从三个unsigned地方断开就很好理解了,第一个是vertexshader是顶点着色器,第二个fragmentshader是片段着色器,我们会发现两个的内容及其相似,那么我们就拎出一个顶点着色器进行解说:

1.用一个名词(变量vertexshader)来使用,至于它是怎么和真正的着色器关联的,首先glCreaterShader创建了一个顶点着色器,创建了就有吗,当然不是,我们说过这需要着色器语言,所以我们需要将自己的着色器连接到真实的着色器的位置(GLSL语言写的内容里)

2.glShaderSource里面有四个参数,主要关心第一个和第三个,第一个就是那个名词,第三个就是取刚刚GLSL中那个指针的地址,这两个值是什么我想你们应该也能找到了

3.既然已经关联好了,那就需要用glcompileshader编译,现在这个名词就是正在的顶点着色器了

4.至于success部分,其实只是为了防止不成功后,知道错误的位置,这样可以更便于修改代码

片段着色器也是同理,如果你问可不要可以改名什么的,如果你理解了,其实在变量的地方都是可以的,只需要你前后保持一致即可,但是不要顶点着色器用片段着色器的名字,你要知道,代码可读性也很重要,几年后你忘记了,你也会用错的,这种低级错误我并不希望你们犯

第三部分则是把一切做成一个流程,刚刚也说过一个坐标到三角形,是经过这些流程的,那我们就要自己写一个流程,我们说过正常流程里(GPU中)是不会包含片段着色器和顶点着色器的,所以我们需要将它们绑定进创建的流程

1.glAttachShader绑定两个着色器

2.glLinkProgram将他们链接

4.又到了判断自己所做的一切是不是成功的阶段了,如果你问可不可以不要,那我只想说你完全上道有点远了

当绑定进了流程里,就可以不需要他们了,就好像编译好的可执行文件没有源文件也可以不断执行,当然你可以选择不删,多浪费一点资源而已,毕竟你的程序也没多大

unsigned int VBO,VAO;
glGenBuffers(1, &VBO);
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);

前面只是对流程进行了处理,但是却没有对数据处理,这一段则是对数据进行处理,对于三角形我们只需要VAO和VBO,因为我们希望数据是在缓存好了被一次性运输的

1.VBO是Buffers,VAO是VertexArrays,前两个函数前面的是创建VAO和VBO的个数,因为我们现在的变量数组中只放了位置变量,所以一个缓存和一个记录就够了

2.创建好了则需要进行绑定,两个含有Bind的则是绑定

3.glBufferData(),主要是表示存储哪些数据,第一个参数是存储到哪,第二个是存储数据的长度,第三个是存取的值,最后一个是使用方式(保持就好)

4.glVertexAttribPointer()这个函数的参数主要是索引,几个一组,通用类型(是它便于移植的数据类型),标准(不变就好)总长度和偏移量,因为我们现在的值只要位置,所以不需要考虑偏移位置长度设置(虽然用词不一定准确,但是可以这么理解),你可能会说数据不是已经写入了吗,可以开始了,为什么又加了一步,因为你把数据一口气给它了,他怎么知道我们是三个一个坐标呢,如果画矩形需要四个一个坐标呢,所以我们需要告知,把这些数据作为索引0,3个一组,类型是float,总长度是3*sizeof(float),没有偏移量(后面可能会有更深入的了解)

5.使能索引0

6.解绑之前绑定的VAO和VBO,给它们重新绑定0即解绑

while (!glfwWindowShouldClose(window)) {
	processInput(window);
	glClearColor(0.6f, 0.2f, 0.3f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);
	glUseProgram(shaderProgram);
	glBindVertexArray(VAO);
	glDrawArrays(GL_TRIANGLES, 0, 3);
	glfwSwapBuffers(window);
	glfwPollEvents();
	
}

然后这里就是前面说过的渲染窗口了,那我们的三角形是不是应该也在里面绘制呢,当然,我们要在绘制前告诉它我们的流程,glUseProgram(shaderProgram);
2.glBindVertexArray(VAO);再绑定一下数据,我们可以把VAO当作领导视察,任何活动开启了VAO再干活等于干了且被领导看见,但是没有开启你干了,没领导看见等于没有干,所以我们要绘制一定要领导看见,那如果你问前面不解绑不就好了,我觉得可以,但是VBO后面不需要再用,如果不解绑就浪费资源,所以看你自己啦
3.最后告知要绘制三角形,从第0个开始那,拿3个绘制,glDrawArrays(GL_TRIANGLES, 0, 3);

	glfwTerminate();
	glDeleteProgram(shaderProgram);
	glDeleteBuffers(1,&VBO);
	glDeleteVertexArrays(1, &VAO);
	return 0;

}

最后也是一系列删除和节省资源的工作,到这里你觉得会报错吗?

当然会啦,你用了glfwSetFramebufferSizeCallback调整窗口的视口和processInput(window);等待按键的函数却没有创建它们,所以你还得加上

void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
	glViewport(0, 0, width, height);
}
void processInput(GLFWwindow* window) {
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
}

tips:如果在main后加前面记得声明

在上一讲忘记要引用的头文件了,这里补一下

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>

因为用了打印输出cout,所以引入了标准输入输出流

最后效果如下:

标签:VAO,OpenGL,VBO,0.5,---,window,VS2022,GL,着色器
From: https://blog.csdn.net/weixin_54193640/article/details/143947211

相关文章

  • GaussDB数据库基础函数介绍-上
    ​目录一、函数在数据库中的作用二、GaussDB常用基础函数介绍与示例1、数字操作函数2、时间和日期处理函数3、类型转换函数4、数组函数5、范围函数6、窗口函数7、聚集函数8、安全函数9、系统信息函数10、动态脱敏函数.Tip:由于篇幅缘故,“5.范围函数、6.窗口函数、7.......
  • GaussDB数据库SQL系列-自定义函数
    一、前言华为云GaussDB数据库是一款高性能、高安全性的云原生数据库,在GaussDB中,自定义函数是一个不容忽视的重要功能。本文将简单介绍一下自定义函数在GaussDB中的使用场景、使用优缺点、示例及示例解析等,为读者提供指导与帮助。二、自定义函数(Function)概述在SQL中,自定义函数(Fu......
  • GaussDB数据库SQL系列-游标管理
    一、前言在数据库中,游标(cursor)是一种非常重要的工具,用于在数据库查询结果集中进行定位和操作。游标提供了一种在多行数据结果集中逐行处理每一行的机制,允许开发人员对每一行的数据进行操作,如检索、过滤、修改等。本文将结合GaussDB数据库,简单的给大家做一介绍。二、概述(GaussDB)......
  • Day38--捕获和抛出异常
    Day38--捕获和抛出异常异常处理机制抛出异常捕获异常异常处理五个关键字try、catch、finally、throw、throws例如:在Test里面,a=1、b=0,输出a/b,看结果-----一定会有异常packagecom.liu.exception;publicclassTest{inta=1;intb=0;publicstaticvoi......
  • 旺财有一只狗-《分析模式》漫谈48
    DDD领域驱动设计批评文集做强化自测题获得“软件方法建模师”称号《软件方法》各章合集“AnalysisPatterns”的第4章“企业财务观察”有这么一小段:Theactualwould bean actualstatustypewithtimeoffsetofzero.Theprioryear isan actualwithtimeoffse......
  • No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-
    一、报错信息二、解决方法1、打开SDKManager,卸载本地NDK。2、然后点击Apply——>OK,重新启动Android工程,成功运行。思想成体系,培养成意识,自律成习惯。我强烈推荐4本可以改变命运的经典著作:《寿康宝鉴》在线阅读白话文《欲海回狂》在线阅读白话文《阴律无情》在线阅......
  • element-plus教程:Pagination 分页
    软考鸭微信小程序:助力软考备考的便捷工具一、基础用法Pagination分页组件允许用户通过点击页码或输入页码来浏览不同的数据集。当数据量过大时,使用分页可以分解数据,提高页面的加载速度和用户体验。<template><el-paginationlayout="prev,pager,next":tot......
  • GaussDB数据库特性-物化视图简介
    一、前言随着企业数据量的不断增长和业务需求的复杂性增加,选择一个高效、可靠且智能的数据存储和管理解决方案变得越来越重要。GaussDB是一种先进的关系型数据库管理系统,为企业提供了强大的数据处理能力,其物化视图(MaterializedViews)功能在数据查询和管理方面具有重要作用。本文......
  • GaussDB数据库SQL系列-LOCK TABLE
    一、前言GaussDB是一款高性能、高可用的分布式数据库,广泛应用于各类行业和场景。在GaussDB中,锁是实现并发控制的关键机制之一,用于协调多个事务之间的数据访问,确保数据的一致性和完整性。本文将围绕GaussDB数据库的LOCKTABLE做一简单介绍。二、GaussDB数据库的锁GaussDB提供了......
  • GaussDB数据库SQL系列-动态语句
    一、前言在数据库中构建动态SQL语句是指根据不同的条件或参数创建不同的SQL语句。这通常是为了适应不同的业务需求,提高SQL的灵活性和效率。GaussDB数据库是一款具备高性能、高可用性和高扩展性的关系型数据库,它提供了丰富的功能和工具,支持动态SQL语句的构建。下面我们将介绍如何......