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
如果我们需要绘制两个(或者多个)一样的立方体(或者物体),只是位置.缩放.旋转不一样,那么我们可以不需要多次将这个物体的顶点信息.颜色信息等发送到显卡,而是发送一次,绘制多次,仅仅是每次绘制之前应用不 ...
随机推荐
- Mailx安装与使用
1.卸载sendmail与postfix yum -y install mailx 2.安装mailx yum -y remove sendmail postfix 3.配置mail.rc vim / ...
- Python模块unittest
Python自带一个单元测试框架,unittest模块,用它来做单元测试,它里面封装好了一些校验返回结果的方法和一些用例执行前的初始化操作 在说unittest之前,先说几个概念: TestCase: ...
- restful风格详解
一.概念 RESTful架构,就是目前最流行的一种互联网软件架构.它结构清晰.符合标准.易于理解.扩展方便,所以正得到越来越多网站的采用. REST这个词,是Roy Thomas Fielding在他 ...
- Linux文件拷贝(6)
本篇介绍文件拷贝操作,主要讲两个命令: 命令 对应英文 作用 tree[目录名] tree 以树状图列出文件目录结构 cp 源文件 目标文件 copy 复制文件或者目录 tree tree命令可以将一 ...
- JetBrains视图
三种视图模式:
- 1000行MySQL学习笔记
/* 启动MySQL */ net start mysql /* 连接与断开服务器 */ mysql -h 地址 -P 端口 -u 用户名 -p 密码 /* 跳过权限验证登录MySQL */ mysq ...
- windows10操作系统上使用virtualenv虚拟环境
前提win10上已经安装了Python环境! virtualenv库的使用: 安装 如果win10上同时安装了Python2和python3的安装virtualenv时用; Python2:pip i ...
- letsencrypt 免费SSL证书申请, 自动更新
Let's Encrypt 泛域名 证书申请 及自动更新 关键字:SSL证书.HTTPS 初次申请 1. 下载certbot wget https://dl.eff.org/certbot-auto ...
- mysql时间类型
转自:http://www.cnblogs.com/Matrix54/archive/2012/05/01/2478158.html AVA中获取当前系统时间 一. 获取当前系统时间和日期并格式化输出 ...
- spring整合apache-shiro的简单使用
这里不对shiro进行介绍,介绍什么的没有意义 初学初用,不求甚解,简单使用 一.导入jar包(引入坐标) <!--shiro和spring整合--> <dependency> ...