OpenGL 学习记录2-绘制三角形

OpenGL 学习记录2-绘制三角形

基本概念

在上次学习窗口的过程,其实有个窗口和渲染 buffer切换的概念。三角形的概念定义很多,也是最基本最关键的,推荐记住如下名词

VAO: 顶点数组对象 Vertex Array Object
VBO: 顶点缓冲对象 Vertex Buffer Object 用于一次性发送大量VAO到显卡
Opengl中用glGenBuffers函数和一个缓冲ID生成一个VBO对象

unsigned int VBO;
glGenBuffers(1, &VBO)

EBO/IBO: 索引缓冲对象 Element Buffer Object、 Index Buffer Object

Primitive: 图元,任何一个绘制指令的调用都是把图元传递给OPENGL,比如GL_POINTS ,GL_TRIANGLES,GL_LINE_STRIP

Vertext Shader: 顶点着色器, 把一个单独的顶点作为输入,主要哦目的是把3D坐标转化成另外一个3D坐标
Primtive Assembly:图元装配,讲顶点着色器输出的所有顶点作为注入,并所有的点装配成图元的形状
Geometry Shader: 几何着色器,图元装配阶段的输出会传递给几何着色器,
Rasterization Stage:光栅化阶段,几何着色器的输出会传递到这个光栅化阶段,最终把图元银色到屏幕的像素,生成供 片段着色器 使用的片段。 在片段着色器运行之前会进行 裁切(Clipping)—把屏幕之外的像素都丢弃,提升效率
Fragment Shader: 片段着色器 :主要是计算一个像素的最终颜色(包含3D场景数据-光照 阴影 光的颜色)

Alpha 测试(透明度)/Blending 阶段(混合):最终可见的颜色是基于透明度和混合后的结果来显示,而不是片段着色器的结果
上面顶点着色器和片段着色器是必须步骤
NDC: normalized device coordinates 标准化设备坐标–正常顶点坐标呗顶点着色器处理就应该是NDC.
因为手机屏幕等都是2D平面,所以任何3D都需要变成2D来在屏幕显示,需要对3D的坐标进行处理
X,Y,Z ,最终投射到屏幕XY来实现。最简单的场景就是Z是0,其他Z不是0的场景,深度会被前面的给遮挡的情况。 X Y 都为0正常设置为屏幕的中心。

定义并复制顶点数据到显卡的内存

OpenGL允许绑定多个缓冲,主要是不同缓冲类型,
VBO 顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER ,要用如下glBindBuffer函数来把新创建的VBO绑定达到GL_ARRAY_BUFFER目标上。然后后续使用这个GL_ARRAY_BUFFER都会调用配置绑定的缓冲(VBO),这样用glBufferData函数就可以把前面定义的顶点数据复制到缓冲的内存中
glBufferData函数的第一个参数是是目标缓冲的类型:顶点缓冲对象当前绑定到GL_ARRAY_BUFFER目标上。第二个参数指定传输数据的大小(以字节为单位);用一个简单的sizeof计算出顶点数据大小就行。第三个参数是我们希望发送的实际数据。第四个参数指定了我们希望显卡如何管理给定的数据,有三种形态,分别表示数据基本不变或者会变或者每次都变GL_STATIC_DRAW,GL_DYNAMIC_DRAW,GL_STREAM_DRAW

unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);  
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

定义顶点着色器

该文比较详细的解释了参数信息
https://blog.csdn.net/huhaoxuan2010/article/details/77717519?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159220686519724835858267%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=159220686519724835858267&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v1~rank_blog_v1-16-77717519.pc_v1_rank_blog_v1&utm_term=apos

如下,先编写一个顶点着色器, 从教程摘录


#version 330 core   //申明opengl版本3.3
layout (location = 0) in vec3 aPos;   // 定义一个aPos变量,vec3类型, layout(location = 0)设置输入变量的位置是0 也就是屏幕中心。

void main()
{
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); //glsl的向量数据类型float gl_Position,,main函数最后,**gl_Position设置的值会成为该顶点着色器的输出**,gl_Position是vec4类型的,
}

在openGL里面有日下的数据类型
int, float, double, uint以及bool,还有两种容器类型向量(Vector)和矩阵(Matrix)。
在GLSL中一个向量有最多4个分量,每个分量值都代表空间中的一个坐标,它们可以通过vec.x、vec.y、vec.z和vec.w来表示这个容器里面的分量
vecn 来表示向量有几个分量,如上分别是vec3 和vec4,表示这个容器里面有3个分量和4个分量,float类型。
ivecn ,表示包含N个int类型的分量
bvecn,N个bool分量的向量
uvecn:包含n个unsigned int分量的向量
dvecn:包含n个double分量的向量

layout 元数据指定输入变量,
layout (location = 0),顶点着色器需要为它的输入提供一个额外的layout标识以把它链接到顶点数据
也可忽略layout (location = 0)标识符,在opengl代码中使用glGetAttribLocation查询属性位置值

输入输出
使用in关键字设定输入,使用out关键字设定输出。若打算从一个着色器向另外一个着色器发送数据,必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似输入。当类型和名字都一样时,OpenGL会把两个变量链接到一起,它们之间就能发送数据

编译着色器

创建一个着色器对象,因为是ID引用所以用无符号整型, glCreateShader 来创建着色器

unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);

下一步需要把着色器源码附加到着色器对象上,编译它

glShaderSource(vertexShader, 1, &vertextShaderSource,NULL);
glCompileShader(vertexShader);

glShaderSource函数把要编译的着色器对象作为第一个参数。第二参数指定了传递的源码字符串数量,这里只有1个。第三个参数是顶点着色器真正的源码,第四个参数我们先设置为NULL。
glCompileShader 是编译它。
因为编译成功是前提,正常用下面代码来实现检查编译是否成功。

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;
}

infoLOG用来存储失败的消息,glGetShaderiv是用来检查编译是否成功,
glGetShaderInfoLog函数用来获取错误信息

片段着色器

Fragment Shader 用来计算像素最后的颜色输出,
在计算机图形中颜色被表示为有4个元素的数组:红色、绿色、蓝色和alpha(透明度)分量,通常缩写为RGBA

#version 330 core
out vec4 FragColor;   // out 用来输出特定数据的输出变量,本次是vec4

void main()
{
    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);  //颜色
} 

编译片段着色器

如下,一样创建一个无符号的整型类型。 用GL_FRAGMENT_SHADER 常量作为着色器类型,和前面顶点着色器的GL_VERTEXT_SHADER 类似。

unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);

着色器链接到着色器程序

着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本,然后再渲染对象的时候激活这个着色器程序, 被渲染调用

如下,还是先定义对象,然后用glCreateProgram来创建一个程序,然后用glAttachShader 去把前面编译好的顶点着色器和片段着色器附加到程序对象,然后 再用glLinkProgram来链接它们

unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);

这个执行完成后,shaderProgram就是一个程序对象,然后可以被 glUseProgram使用,这个对象作为参数,激活这个程序对象

glUseProgram(shaderProgram)  

glDeleteShader(vertexShader);   //着色器链接到程序对象后,记得删掉,我们不再需要
glDeleteShader(fragmentShader);

惯例需要确定程序是否失败,先获取程序是否成功,再获取失败日志打印

glGetProgramiv(shaderProgram, GL_LINK_STATUS,&success);
if(!success)
{ 
 glGetProgramInfoLog(shaderProgram, 512,NULL, infoLOG);
}

链接顶点属性

告诉GPU如何处理这个内存的顶点数据,以把顶端数据及链接到顶点着色器的属性上
这时候需要用glVertexAttribPointer这个函数,范例如下:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

第一个参数 0: 顶点属性, 也就是layout(location = 0)已经定义
第二个参数 3: 表示3个数值,vec3
第三个参数 GL_FLOAT:数据类型,表示vec3 3个参数都是float的
第四个参数GL_FALSE: 表示数据是否标准化 Normalize,,也就是坐标系是否都因素到0-1之间,如果不是就是false,如果是,就是GL_TRUE.
第五个参数 3* sizeof(float): 步长(stride),本次表示下个数组(顶点属性组)在3个float之后。(该参数需要深入了解含义和用途)
第六个个参数 (void*)0: 偏移量 offset, 表示数据在缓冲起始位置的缓冲量,因为位置数据在数组的开头,所以是0.

glBindBuffer(GL_ARRAY_BUFFER,VBO);
glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices, GL_STATIC_DRAW); //复制顶点数组到缓冲
glVertexAttribPoint(0,3,GL_FLOAT,GL_FALSE,3*sizeof(float),(void*)0);//
glEnableVertexAttribArray(0);//启用顶点属性0
glUseProgram(shaderProgram);//使用着色器程序

顶点数组对象 VAO

VAO也需要和VBO一样被绑定,这样后续的顶点属性调用都会存储在这个VAO中,
当配置顶点属性指针时,只需要调用一次,后续再绘制就只需要绑定对应的VAO就好了

**

OpenGL的核心模式要求我们使用VAO,所以它知道该如何处理我们的顶点输入。如果我们绑定VAO失败,OpenGL会拒绝绘制任何东西。

**
如下,VAO

//创建一个VAO
unsigned int VAO;
glGenVertextArrays(1,&VAO);
glBindVertexArray(VAO);//绑定
glDrawArrays(GL_TRINGLES,0,3); //绘制三角形,0是顶点数组的其实索引,最后一个参数3小时3个顶点的意思
glfwSwapBuffers(window);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值