3D Computer Grapihcs Using OpenGL - 06 Vertex and Fragment Shaders
从这里就接触到了可编程图形渲染管线。
下面介绍使用Vertex Shader (顶点着色器)和 Fragment Shader(像素着色器)的方法。
我们的目标是使用这两个着色器给三角形填充绿色。
添加了一个cpp文件存放Shader文件,MyShaderCode.cpp:
1 const char* vertexShaderCode =
2 " #version 430 \r\n"
3 " \r\n"
4 " in layout(location=0) vec2 position; \r\n"
5 " \r\n"
6 " \r\n"
7 " \r\n"
8 " void main() \r\n"
9 " { \r\n"
10 " gl_Position= vec4(position,0.0,1.0); \r\n"
11 " } \r\n"
12 " \r\n"
13 " \r\n";
14
15 const char* fragmentShaderCode =
16 " #version 430 \r\n"
17 " \r\n"
18 " out vec4 finalColor; \r\n"
19 " \r\n"
20 " \r\n"
21 " void main() \r\n"
22 " { \r\n"
23 " finalColor = vec4(0.0,1.0,0.0,1.0); \r\n"
24 " } \r\n"
25 " \r\n"
26 " \r\n";
书写Shader使用的是GLSL语言,和C语言语法非常相似。
Vertex Shader
#version 430 表示opengl的版本号,要使用显卡支持的版本,否则会编译出错
vertex Shader第四行的 in 关键字表示这里定义的是一个输入。layout(location=0)表示这里接收的是位置信息。vec2表示这里是一个2维向量。position是自己定义的接受数据的变量名称。
gl_Position是GLSL定义的关键字,使用在Vertex shader中就表示它将接收一个四维向量来表示顶点位置信息。
vec4(position,0.0,1.0)是GLSL的一种特殊语法,用于方便的构造一个四维向量,因为position是二维的,后面再添加两个数字就表示了四维向量。最后一位是1.0,表示没有缩放。
Fragment Shader
第18行使用out关键字,表示这里是一个输出。
而Fragment Shader在渲染管线中只有一个输出,就是每个Fragment(暂时可以理解为像素,实际上不等同于像素)的颜色。
在main中直接将所有的像素颜色都设置成绿色。
MyGlWindow.cpp
#include <gl\glew.h>
#include "MyGlWindow.h" extern const char* vertexShaderCode;
extern const char* fragmentShaderCode; GLuint programID; void MyGlWindow::sendDataToOpenGL()
{
GLfloat verts[] =
{
+0.0f, +0.0f, //
+1.0f, +1.0f, //
-1.0f, +1.0f, //
-1.0f, -1.0f, //
+1.0f, -1.0f, //
}; GLuint vertexBufferID;
glGenBuffers(, &vertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); GLushort indices[] =
{
,,,
,,,
};
GLuint indexBufferID;
glGenBuffers(, &indexBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glEnableVertexAttribArray();
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, , );
} void MyGlWindow::installShaders()
{
GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(vertexShaderID, , &vertexShaderCode, );
glShaderSource(fragmentShaderID, , &fragmentShaderCode, ); glCompileShader(vertexShaderID);
glCompileShader(fragmentShaderID); programID = glCreateProgram();
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID); glLinkProgram(programID); glDeleteShader(vertexShaderID);
glDeleteShader(fragmentShaderID); glUseProgram(programID);
} void MyGlWindow::initializeGL()
{
glewInit();
sendDataToOpenGL();
installShaders();
} void MyGlWindow::paintGL()
{
glViewport(, , width(), height());
glDrawElements(GL_TRIANGLES, , GL_UNSIGNED_SHORT, );
} MyGlWindow::~MyGlWindow()
{
glUseProgram();
glDeleteProgram(programID);
}
MyGlWindow.h
#pragma once
#include <QtOpenGL\qgl.h>
class MyGlWindow :public QGLWidget
{
protected:
void sendDataToOpenGL();
void installShaders();
void initializeGL();
void paintGL(); public:
~MyGlWindow();
};
对MyGlWindow.cpp文件进行了一个重构,把上节initializeGL()中的内容除了第一句glewInit()之外全部封装进了sendDataToOpenGL()函数,因为这些代码都是向OpenGL函数发送数据的。
为了使用Shader,我们新增加了一个installShaders()函数,所有使用Shader的功能都写在其中。
在initializeGL函数中先调用sendDataToOpengGL(),然后调用installShaders()。
使用Shader的步骤清单
- 创建Shader并分配ID,glCreateShader
- 设置Shader内容, glSourceShader
- 编译Shader, glCompileShader
- 创建Program,glCreateProgram
- 附加Shader,glAttachShader
- 链接Shader, glLinkProgram
- 删除Shader,glDeleteShader
- 使用Program, glUseProgram
- 删除Program,glDeleteProgram
下面就按照这六个步骤逐步解析代码。
1. 创建Shader并分配ID
39 - 40 行使用glCreateShader分别创建了vertex shader和 fragment shader,并分别将ID保存在GLuint对象里(和前面使用GLuint存储Buffer对象类似)
函数的参数是固定的宏
2. 设置Shader内容
42 - 43 行使用glShaderSource函数给Shader提供具体内容。
我们额外增加了一个文件MyShaderCode用来存放Shader的代码,代码具体内容放在两个较长的字符串里。
然后在MyGlWindow.cpp的开头使用extern 声明了vertexShaderCode和fragmentShaderCode字符串变量。
glShaderSource的
- 第一个参数表示Shader的ID
- 第二个参数表示Shader内容的字符串有几个数组,这里只有1个
- 第三个参数表示字符串数组本身,但是需要注意参数的类型是const char **,所以还要在前面增加一个取地址符号
- 第四个参数使用0表示由OpenGL自己计算字符串的长度
3. 编译Shader
45 - 46 行使用glCompileShader分别对两个Shader进行编译
4.创建Program
进行后续操作之前需要创建一个Program,这里使用glCreateProgram()创建了一个Porgram,并将其ID保存在GLuint对象里。
5.附加Shader
glAttachShader有两个参数,第一个参数是Program的ID,第二个参数是Shader的ID
6.链接Shader
虽然通常称为“链接SHader”, 但其实是链接Program, 函数glLinkProgram的参数就是Program的ID
7.删除Shader
Shader使用完成后一定要删除Shader,第56和57行进行了这步操作。在useProgram之前进行也没问题,只要编译和链接了Shader就行。
8.使用Program
glUseProgram的参数是Program的ID
9. 删除Program
使用完成Program之后要删除Program,这里在析构函数里进行是比较合适的,分两步,如77和78行所示。
完成后编译,我们将得到两个绿色的三角形。

3D Computer Grapihcs Using OpenGL - 06 Vertex and Fragment Shaders的更多相关文章
- 3D Computer Grapihcs Using OpenGL - 19 Vertex Array Object(顶点数组对象)
大部分OpenGL教程都会在一开始就讲解VAO,但是该教程的作者认为这是很不合理的,因为要理解它的作用需要建立在我们此前学过的知识基础上.因此直到教程已经进行了一大半,作者才引入VAO这个概念.在我看 ...
- 3D Computer Grapihcs Using OpenGL - 07 Passing Data from Vertex to Fragment Shader
上节的最后我们实现了两个绿色的三角形,而绿色是直接在Fragment Shader中指定的. 这节我们将为这两个三角形进行更加自由的着色——五个顶点各自使用不同的颜色. 要实现这个目的,我们分两步进行 ...
- 3D Computer Grapihcs Using OpenGL - 09 Enable Depth Test
启用Depth Test OpenGL是个3D绘图API,也就是说不只有xy坐标轴,还有第三个坐标轴z,z轴的方向是垂直于屏幕,指向屏幕内. 靠近人眼的方向是负方向,标准化设备坐标的最小值是-1, 最 ...
- 3D Computer Grapihcs Using OpenGL - 16 使用DrawElementsInstanced绘制立方体
我们使用15节学到的知识来绘制14节的立方体. 在第14节我们使用了两次glDrawElements实现了OpenGL实例化,发现这样仍然不太方便,如果需要绘制成千上万的立方体,就需要手写成千上万次的 ...
- 3D Computer Grapihcs Using OpenGL - 11 Model View Projection Matrices
本节我们将绘制一个3维物体,立方体. 如果要渲染3D物体,我们需要了解MVP(Model View Projection),它表示三个转换矩阵.实际上这个名字不够明确,更加确切的释义如下: Model ...
- 3D Computer Grapihcs Using OpenGL - 10 Color Buffer
本节我们将尝试利用三角形制作一个“走马灯”效果. 一个三角形如图示方式,从左向右依次移动. 先看一下代码: MyGlWindow.cpp #include <gl\glew.h> #inc ...
- 3D Computer Grapihcs Using OpenGL - 20 结合Buffer
在上一节的案例中,我们使用了四个Buffer Object,立方体的VertexBuffer,立方体的索引Buffer,四面体的VertexBuffer,四面体的索引Buffer. 我们这节尝试把两个 ...
- 3D Computer Grapihcs Using OpenGL - 15 Draw Element Instanced
友情提示:继续本节之前,需要保存此前的代码,本节为了试验,会对代码做一些修改,但后续的修改需要我们把代码返回之前的进度. OpenGL内置支持Instancing,有专门的函数来处理这件事情. 为了方 ...
- 3D Computer Grapihcs Using OpenGL - 14 OpenGL Instancing
如果我们需要绘制两个(或者多个)一样的立方体(或者物体),只是位置.缩放.旋转不一样,那么我们可以不需要多次将这个物体的顶点信息.颜色信息等发送到显卡,而是发送一次,绘制多次,仅仅是每次绘制之前应用不 ...
随机推荐
- 【VS开发】C++调用外部程序
关于三个SDK函数:WinExec, ShellExecute,CreateProcess的其他注意事项:[1]定义头文件必须定义以下两个头文件: [cpp] view plain copy #inc ...
- springcloud(一)
Spring Cloud是什么鬼? Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理.服务发现.断路器.智能路由.微代理.控制总线. ...
- C#获取当前路径7中方法
//获取模块的完整路径. string path1 = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; //获取 ...
- CentOS 8 下 nginx 服务器安装及配置笔记
参考文档 nginx官方文档 安装 在CentOS下,nginx官方提供了安装包可以安装 首先先安装前置软件 sudo yum install yum-utils 然后将nginx官方源加入到yum源 ...
- C语言黑与白问题
问题描述: 有A.B.C.D.E这5个人,每个人额头上都帖了一张黑或白的纸.5人对坐,每 个人都可以看到其他人额头上纸的颜色.5人相互观察后: A说:“我看见有3人额头上贴的是白纸,1人额头上贴的是黑 ...
- 使用redis+flask维护动态代理池
在进行网络爬虫时,会经常有封ip的现象.可以使用代理池来进行代理ip的处理. 代理池的要求:多站抓取,异步检测.定时筛选,持续更新.提供接口,易于提取. 代理池架构:获取器,过滤器,代理队列,定时检测 ...
- 最长公共子序列(LCS) Medium1
In a few months the European Currency Union will become a reality. However, to join the club, the Ma ...
- 最大连续和 Easy
Given a sequence a[1],a[2],a[3]......a[n], your job is to calculate the max sum of a sub-sequence. F ...
- Delphi7所使用的WinAPI大全(摘自VCL源码,一共1200个函数)
经过我整理的,去掉了A和W的重复.虽然没写注释,但以后要一个一个研究.有这些WINAPI就够用了. kernel32 = 'kernel32.dll'; gdi32 = 'gdi32.dll'; us ...
- 剑指offer-用两个栈来实现一个队列-队列与栈-python
用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 思路:使用两个栈,stackA 用来接收node stackB 用来接收 stackA 的出栈 # -*- cod ...