OpenGL入门之入门
programs on the GPU-------shader
顶点着色器--》形状(图元)装配--》几何着色器--》光栅化--》片段着色器--》测试与混合
图形渲染管线的第一个部分是顶点着色器(Vertex Shader),它把一个单独的顶点作为输入。
顶点着色器主要的目的是把3D坐标转为另一种3D坐标(后面会解释),同时顶点着色器允许我们对顶点属性进行一些基本处理。
图元装配(Primitive Assembly)阶段将顶点着色器输出的所有顶点作为输入
(如果是GL_POINTS,那么就是一个顶点),并所有的点装配成指定图元的形状;本节例子中是一个三角形。
图元装配阶段的输出会传递给几何着色器(Geometry Shader)。
几何着色器把图元形式的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状。例子中,它生成了另一个三角形。
几何着色器的输出会被传入光栅化阶段(Rasterization Stage),
这里它会把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。
在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。
片段着色器的主要目的是计算一个像素的最终颜色,这也是所有OpenGL高级效果产生的地方。
通常,片段着色器包含3D场景的数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色。
在所有对应颜色值确定以后,最终的对象将会被传到最后一个阶段,我们叫做Alpha测试和混合(Blending)阶段。
这个阶段检测片段的对应的深度(和模板(Stencil))值(后面会讲),
用它们来判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。
这个阶段也会检查alpha值(alpha值定义了一个物体的透明度)并对物体进行混合(Blend)。
所以,即使在片段着色器中计算出来了一个像素输出的颜色,在渲染多个三角形的时候最后的像素颜色也可能完全不同。
先要配置环境,然后创建窗口。
#include <iostream>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h> float vertices[] = {
-0.5f,-0.5f,0.0f,
0.5f,-0.5f,0.0f,
0.5f,0.5f,0.0f,
-0.5f,0.5f,0.0f,
}; unsigned int indices[] = { // 注意索引从0开始!
, , , //第一个三角形
, , //第二个三角形
}; //顶点着色器
const char* vertexShaderSource =
"#version 330 core \n"
"layout(location = 0) in vec3 aPos; \n"// 位置变量的属性位置值为0
"void main() { \n"
"gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);} \n"; //片段着色器
const char* fragmentShaderSource =
"#version 330 core \n "
"out vec4 FragColor; \n "
"void main() { \n "
"FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);} \n "; //函数在main之前存档
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{//退出键关闭窗口
glfwSetWindowShouldClose(window, true);
}
}
int main(int argc, char* argv[]) { glfwInit();//初始化和创建窗口
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, );//提示用主版本号为3
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, );//次版本号为3,即为3.3版本的OpenGL
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //open GLFW window
GLFWwindow* window = glfwCreateWindow(, , "Test window", NULL, NULL);//800*600的窗口 if (window == NULL) {//如果为空指针
//std::cout << "open window failed." << std::endl;//打印失败
printf("open window failes.");
glfwTerminate();//终止
return -;
//return EXIT_FAILURE;
} glfwMakeContextCurrent(window); //init GLEW
glewExperimental = true; if (glewInit() != GLEW_OK)
{
printf("Init GLEW failed.");
//std::cout << "glew init failed." << std::endl;
glfwTerminate();
return -;//-1代表不正常退出
} glViewport(, , , );//前两个参数控制窗口左下角的位置,后两个参数设置可绘制的像素大小
//逆时针作为三角形的正面
//glEnable(GL_CULL_FACE);
//glCullFace(GL_FRONT);//剔除正面,back--剔除背面 //第一个参数表示我们打算将其应用到所有的三角形的正面和背面,第二个参数告诉我们用线来绘制
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);//线框模式
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);//关掉线框模式 unsigned int VAO;//顶点数组对象
glGenVertexArrays(, &VAO);
glBindVertexArray(VAO); unsigned int VBO;//顶点缓冲对象
glGenBuffers(, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);//顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER,使用glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上
//调用glBufferData函数,它会把之前定义的顶点数据复制到缓冲的内存中
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
/*
glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数。它的第一个参数是目标缓冲的类型:
顶点缓冲对象当前绑定到GL_ARRAY_BUFFER目标上。第二个参数指定传输数据的大小(以字节为单位);
用一个简单的sizeof计算出顶点数据大小就行。第三个参数是我们希望发送的实际数据。 第四个参数指定了我们希望显卡如何管理给定的数据。它有三种形式: GL_STATIC_DRAW :数据不会或几乎不会改变。
GL_DYNAMIC_DRAW:数据会被改变很多。
GL_STREAM_DRAW :数据每次绘制时都会改变。
*/ unsigned int EBO;//索引缓冲对象,储存索引
glGenBuffers(, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); unsigned int vertexShader;//创建一个着色器对象,注意还是用ID来引用的
vertexShader = glCreateShader(GL_VERTEX_SHADER);//创建这个着色器
//把要编译的着色器对象作为第一个参数。第二参数指定了传递的源码字符串数量,这里只有一个。第三个参数是顶点着色器真正的源码,第四个参数我们先设置为NULL。
glShaderSource(vertexShader, , &vertexShaderSource, NULL);
glCompileShader(vertexShader); unsigned int fragmentShader;//创建一个着色器对象,注意还是用ID来引用的
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);//创建这个着色器
//把要编译的着色器对象作为第一个参数。第二参数指定了传递的源码字符串数量,这里只有一个。第三个参数是顶点着色器真正的源码,第四个参数我们先设置为NULL。
glShaderSource(fragmentShader, , &fragmentShaderSource, NULL);
glCompileShader(fragmentShader); unsigned int shaderProgram;//创建一个着色器程序对象
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);//链接(Link)为一个着色器程序对象 //顶点属性,顶点属性的大小(vec3),数据类型,是否数据被标准化,步长,强制类型转换(表示位置数据在缓冲中起始位置的偏移量)
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, * sizeof(float), (void*));
glEnableVertexAttribArray();//启用顶点属性 while (!glfwWindowShouldClose(window))//渲染循环,关闭它之前不断绘制图像并能够接受用户输入
{ processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0);//清屏颜色填充RGB,透明度
glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(VAO);//绑定
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glUseProgram(shaderProgram);//激活这个程序对象
//glDrawArrays(GL_TRIANGLES, 0, 3);//OpenGL图元的类型,顶点数组的起始索引,打算绘制多少个顶点,绘制三角形
glDrawElements(GL_TRIANGLES, , GL_UNSIGNED_INT, );//改为glDrawElements即可。6为索引个数,0表示偏移。
glfwSwapBuffers(window);
glfwPollEvents();//检查有没有触发什么事件
} glfwTerminate();//释放/删除之前的分配的所有资源 return ;
}
结果如图:
函数介绍:
使用glVertexAttribPointer函数告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)了:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer函数的参数非常多,所以我会逐一介绍它们:
第一个参数指定我们要配置的顶点属性。还记得我们在顶点着色器中使用layout(location = 0)
定义了position顶点属性的位置值(Location)吗?它可以把顶点属性的位置值设置为0。
因为我们希望把数据传递到这一个顶点属性中,所以这里我们传入0。
第二个参数指定顶点属性的大小。顶点属性是一个vec3,它由3个值组成,所以大小是3。
第三个参数指定数据的类型,这里是GL_FLOAT(GLSL中vec*都是由浮点数值组成的)。
下个参数定义我们是否希望数据被标准化(Normalize)。如果我们设置为GL_TRUE,
所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为GL_FALSE。
第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。
由于下个组位置数据在3个float之后,我们把步长设置为3 * sizeof(float)。
要注意的是由于我们知道这个数组是紧密排列的(在两个顶点属性之间没有空隙)
我们也可以设置为0来让OpenGL决定具体步长是多少(只有当数值是紧密排列时才可用)。
一旦我们有更多的顶点属性,我们就必须更小心地定义每个顶点属性之间的间隔,
我们在后面会看到更多的例子(译注: 这个参数的意思简单说就是从这个属性第二次出现的地方
到整个数组0位置之间有多少字节)。
最后一个参数的类型是void*,所以需要我们进行这个奇怪的强制类型转换。
它表示位置数据在缓冲中起始位置的偏移量(Offset)。由于位置数据在数组的开头,所以这里是0。
我们会在后面详细解释这个参数。
OpenGL入门之入门的更多相关文章
- babel从入门到入门
babel从入门到入门 来源 http://www.cnblogs.com/gg1234/p/7168750.html 博客讲解内容如下: 1.babel是什么 2.javascript制作规范 3. ...
- Android视频录制从不入门到入门系列教程(一)————简介
一.WHY Android SDK提供了MediaRecorder帮助开发者进行视频的录制,不过这个类很鸡肋,实际项目中应该很少用到它,最大的原因我觉得莫过于其输出的视频分辨率太有限了,满足不了项目的 ...
- Android视频录制从不入门到入门系列教程(三)————视频方向
运行Android视频录制从不入门到入门系列教程(二)————显示视频图像中的Demo后,我们应该能发现视频的方向是错误的. 由于Android中,Camera给我们的视频图片的原始方向是下图这个样子 ...
- springboot + kafka 入门实例 入门demo
springboot + kafka 入门实例 入门demo 版本说明 springboot版本:2.3.3.RELEASE kakfa服务端版本:kafka_2.12-2.6.0.tgz zooke ...
- springboot + mybatisPlus 入门实例 入门demo
springboot + mybatisPlus 入门实例 入门demo 使用mybatisPlus的优势 集成mybatisplus后,简单的CRUD就不用写了,如果没有特别的sql,就可以不用ma ...
- OpenGL ES2.0入门详解
引自:http://blog.csdn.net/wangyuchun_799/article/details/7736928 1.决定你要支持的OpenGL ES的版本.目前,OpenGL ES包含 ...
- (转)OpenGL ES编程入门资源集合
出自:http://blog.csdn.net/u013467442/article/details/44498125 OpenGL ES 2.0中文手册:http://www.dreami ...
- OpenGL ES2.0 入门经典例子
原文链接地址:http://www.raywenderlich.com/3664/opengl-es-2-0-for-iphone-tutorial 免责申明(必读!):本博客提供的所有教程的翻译原稿 ...
- 现代OpenGL教程 01 - 入门指南
原文链接传送门 译序 早前学OpenGL的时候还是1.x版本,用的都是glVertex,glNormal等固定管线API.后来工作需要接触DirectX9,shader也只是可选项而已,跟固定管线一起 ...
随机推荐
- Selenium定位不到元素的解决方法—iframe挡住了去路
刚接触Selenium,在调试过程中发现有些元素定位不到,于是求助了百度,查找到的资料是这么说的:如果需要定位的元素在某个frame里,则单独通过id/name/xpath是定位不到此元素的.比如,原 ...
- 一分钟掌握位运算符—与(&)、非(~)、或(|)、异或(^)
第一个版本: 位运算符的计算主要用在二进制中. 实际开发中也经常会遇到需要用到这些运算符的时候,同时这些运算符也被作为基础的面试笔试题. 所以了解这些运算符对程序员来说是十分必要的. 于此,记录下 ...
- mysql学习1
1.什么是数据库? 数据的仓库,如在ATM的示例中创建了一个db目录,称其为数据库 2.安装 下载 http://dev.mysql.com/downloads/mysql/ 安装 windows: ...
- [LeetCode] Reaching Points 到达指定点
A move consists of taking a point (x, y) and transforming it to either (x, x+y) or (x+y, y). Given a ...
- jdbc的入门学习
一.JDBC相关概念介绍 1.1.数据库驱动 这里的驱动的概念和平时听到的那种驱动的概念是一样的,比如平时购买的声卡,网卡直接插到计算机上面是不能用的,必须要安装相应的驱动程序之后才能够使用声卡和网卡 ...
- 《javascript设计模式与开发实践》--- (单一职责原则)
看的这本书叫<JavaScript设计模式与开发实践> 先规划一下看书的顺序,基础知识我已经大概的浏览了一遍了,没有留下笔记,以后有时间还会补上.本来打算顺着看的.但是我感觉我很难短时间内 ...
- SQL Server 查询某个字段值在哪张表的哪个字段
我要查找值为‘WSCOL1525’的字段. declare @cloumns varchar(40)declare @tablename varchar(40)declare @str varchar ...
- body里面的onload和window.onload,window.load的区别
区别:body里面的onload是在“页面加载完成后执行的动作”window里面的onload是在“页面加载时执行的动作” window.load这个应该只是表明事件方法,但并未执行,比如click表 ...
- mysql5.7.17版本升级源码方式及恢复主主复制
版本升级--自测 从库 ------------ 停止主从复制 stop slave 全库备份 mysqldump -u root -p -S mysql.sock --all-databases&g ...
- Machine Learning, Homework 9, Neural Nets
Machine Learning, Homework 9, Neural NetsApril 15, 2019ContentsBoston Housing with a Single Layer an ...