从这里就接触到了可编程图形渲染管线。

下面介绍使用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的步骤清单

  1. 创建Shader并分配ID,glCreateShader
  2. 设置Shader内容, glSourceShader
  3. 编译Shader, glCompileShader
  4. 创建Program,glCreateProgram
  5. 附加Shader,glAttachShader
  6. 链接Shader, glLinkProgram
  7. 删除Shader,glDeleteShader
  8. 使用Program, glUseProgram
  9. 删除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的更多相关文章

  1. 3D Computer Grapihcs Using OpenGL - 19 Vertex Array Object(顶点数组对象)

    大部分OpenGL教程都会在一开始就讲解VAO,但是该教程的作者认为这是很不合理的,因为要理解它的作用需要建立在我们此前学过的知识基础上.因此直到教程已经进行了一大半,作者才引入VAO这个概念.在我看 ...

  2. 3D Computer Grapihcs Using OpenGL - 07 Passing Data from Vertex to Fragment Shader

    上节的最后我们实现了两个绿色的三角形,而绿色是直接在Fragment Shader中指定的. 这节我们将为这两个三角形进行更加自由的着色——五个顶点各自使用不同的颜色. 要实现这个目的,我们分两步进行 ...

  3. 3D Computer Grapihcs Using OpenGL - 09 Enable Depth Test

    启用Depth Test OpenGL是个3D绘图API,也就是说不只有xy坐标轴,还有第三个坐标轴z,z轴的方向是垂直于屏幕,指向屏幕内. 靠近人眼的方向是负方向,标准化设备坐标的最小值是-1, 最 ...

  4. 3D Computer Grapihcs Using OpenGL - 16 使用DrawElementsInstanced绘制立方体

    我们使用15节学到的知识来绘制14节的立方体. 在第14节我们使用了两次glDrawElements实现了OpenGL实例化,发现这样仍然不太方便,如果需要绘制成千上万的立方体,就需要手写成千上万次的 ...

  5. 3D Computer Grapihcs Using OpenGL - 11 Model View Projection Matrices

    本节我们将绘制一个3维物体,立方体. 如果要渲染3D物体,我们需要了解MVP(Model View Projection),它表示三个转换矩阵.实际上这个名字不够明确,更加确切的释义如下: Model ...

  6. 3D Computer Grapihcs Using OpenGL - 10 Color Buffer

    本节我们将尝试利用三角形制作一个“走马灯”效果. 一个三角形如图示方式,从左向右依次移动. 先看一下代码: MyGlWindow.cpp #include <gl\glew.h> #inc ...

  7. 3D Computer Grapihcs Using OpenGL - 20 结合Buffer

    在上一节的案例中,我们使用了四个Buffer Object,立方体的VertexBuffer,立方体的索引Buffer,四面体的VertexBuffer,四面体的索引Buffer. 我们这节尝试把两个 ...

  8. 3D Computer Grapihcs Using OpenGL - 15 Draw Element Instanced

    友情提示:继续本节之前,需要保存此前的代码,本节为了试验,会对代码做一些修改,但后续的修改需要我们把代码返回之前的进度. OpenGL内置支持Instancing,有专门的函数来处理这件事情. 为了方 ...

  9. 3D Computer Grapihcs Using OpenGL - 14 OpenGL Instancing

    如果我们需要绘制两个(或者多个)一样的立方体(或者物体),只是位置.缩放.旋转不一样,那么我们可以不需要多次将这个物体的顶点信息.颜色信息等发送到显卡,而是发送一次,绘制多次,仅仅是每次绘制之前应用不 ...

随机推荐

  1. 图解 SQL 里的各种 JOIN

    约定 下文将使用两个数据库表 Table_A 和 Table_B 来进行示例讲解,其结构与数据分别如下: mysql> SELECT * FROM Table_A ORDER BY PK ASC ...

  2. Java并发编程:锁的释放

    Java并发编程:锁的释放 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #839496;} Ja ...

  3. Linux下libaio的一个简单例子

    转载:http://www.cnblogs.com/aLittleBitCool/archive/2011/10/18/2216646.html 异步io,很好玩的一个东西,从接口来看,封装的比较厉害 ...

  4. if练习

    练习: 1.简述变量命名规范 1.变量名由字母.下划线.数字组成 2.变量名不能以数字开头 3.变量不能使用python中的关键字 4.变量不能使用中文和拼音 5.区分大小写 6.变量名要具有描述性 ...

  5. 搜索专题: HDU1016Prime Ring Problem

    Prime Ring Problem Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  6. 小a的轰炸游戏(差分,前缀和)

    题目传送门 题意: 给出一个n*m的矩形,然后有两个操作. 1操作,对一个给出的菱形,对菱形范围内的东西进行+1. 2操作,对一个上半菱形的区域,进行+1操作. 最后求矩形内各个数的异或和. 思路: ...

  7. gomock

    参考 使用Golang的官方mock工具--gomock

  8. 错误: JMX 连接器服务器通信错误: service:jmx:rmi://***

    电脑没有空间了,正想清理一下硬盘空间,这时不知道金山毒霸啥时候装上了,就想把它卸载了,卸载的过程中看到有空间清理,随手一点,清理出了10个G,然后再打开idea运行项目就报出这个错. 错误: JMX ...

  9. 利用WebSocket和EventSource实现服务端推送

    可能有很多的同学有用 setInterval 控制 ajax 不断向服务端请求最新数据的经历(轮询)看下面的代码: setInterval(function() { $.get('/get/data- ...

  10. Redis数据结构&命令手册

    Redis数据结构&命令手册 Redis数据结构 Redis可以存储键与5种不同数据结构之间的映射,这五种数据结构类型分别为STRING(字符串).LIST(列表).SET(集合).HASH( ...