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
如果我们需要绘制两个(或者多个)一样的立方体(或者物体),只是位置.缩放.旋转不一样,那么我们可以不需要多次将这个物体的顶点信息.颜色信息等发送到显卡,而是发送一次,绘制多次,仅仅是每次绘制之前应用不 ...
随机推荐
- [19/05/28-星期二] JavaScript_ 对象和引用数据类型
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- RBAC----基于角色的访问权限控制
RBAC是什么? 基于角色的权限访问控制(Role-Based Access Control) 作为传统访问控制(自主访问.强制访问)的有前景的代替 受到了广泛的关注. 在RBAC中,权限与角色相关联 ...
- C++中的函数重载分析(一)
1,重载是 C 语言到 C++ 语言的一个飞跃,C 语言中没有重载的概念,所有的函数 名是不允许有重复的,在 C++ 中因为引进了重载,所以函数名可以重复: 2,自然语言中的上下文: 1,你知道上面词 ...
- 通过metaclass实现精简的ORM框架
摘抄于<python-3-廖雪峰> 使用ORM框架 class User(Model): #定义类的属性到列的映射: id = IntegerFiled('id') name = Stri ...
- 解决跳转出现 No input file specified.
项目根目录中.htaccess文件修改为: <IfModule mod_rewrite.c> Options +FollowSymlinks RewriteEngine On Rewr ...
- homestead修改php版本
登录后 如果之前没有设置过root密码 sudo passwd root 以root 权限执行如下命令,选择对应php版本 # 查看所有 php 版本和当前版本 update-alternatives ...
- 小白学Python(19): Pyinstaller 生成 exe 文件
python 默认并不包含 PyInstaller 模块,因此需要自行安装 PyInstaller 模块. 安装 PyInstaller 模块与安装其他 Python 模块一样,使用 pip 命令安装 ...
- jquery中的插件EChars的使用
首先,进入EChars的官网下载页面:http://echarts.baidu.com/download.html 下载自己需要的版本. 引入jquery包和echars,进入官网的实例:htt ...
- Find The Multiple (水题)
Given a positive integer n, write a program to find out a nonzero multiple m of n whose decimal repr ...
- TMS320F28335——SCI串口
一.IO配置 以SCIA为例:使用的是GPIO35--SCITXDA GPIOA36--SCIRXDA 使用寄存器: GPBPUD :设置上拉 GPIO32-GPIO63 对应位0 使能上 ...