「Modern OpenGL系列(四)」在OpenGL中使用Shader

在[上一篇文章]( http://davidsheh.github.io/post/「Modern OpenGL系列(三)」用OpenGL绘制一个三角形/)中已经介绍了OpenGL窗口的创建。本文接着说如何用OpenGL绘制一个三角形。
1 . 添加头文件shader.h,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#pragma once

#include <string>
#include <GL\glew.h>

class Shader
{
public:
Shader(const std::string& fileName);
void Bind();

virtual ~Shader();
protected:
private:
static const unsigned int NUM_SHADERS = 2;
Shader(const Shader& other){}
void operator=(const Shader& other){}

GLuint m_program;
GLuint m_shaders[NUM_SHADERS];
};

2 . 添加类shader.cpp,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include "shader.h"
#include <iostream>
#include <fstream>

static void CheckShaderError(GLuint shader, GLuint flag, bool isProgram, const std::string& errorMessage);
static std::string LoadShader(const std::string& fileName);
static GLuint CreateShader(const std::string& text, GLenum shaderType);

Shader::Shader(const std::string& fileName)
{
m_program = glCreateProgram();
m_shaders[0] = CreateShader(LoadShader(fileName + ".vs"), GL_VERTEX_SHADER);
m_shaders[1] = CreateShader(LoadShader(fileName + ".fs"), GL_FRAGMENT_SHADER);

for (unsigned int i = 0; i < NUM_SHADERS; i++)
{
glAttachShader(m_program, m_shaders[i]);
}

//glBindAttribLocation(m_program, 0, "position");

glLinkProgram(m_program);
CheckShaderError(m_program, GL_LINK_STATUS, true, "Error: Program linking failed!");

glValidateProgram(m_program);
CheckShaderError(m_program, GL_VALIDATE_STATUS, true, "Error: Program invalid!");

}

Shader::~Shader()
{
for (unsigned int i = 0; i < NUM_SHADERS; i++)
{
glDetachShader(m_program, m_shaders[i]);
glDeleteShader(m_shaders[i]);
}

glDeleteProgram(m_program);
}

void Shader::Bind()
{
glUseProgram(m_program);
}

static GLuint CreateShader(const std::string& text, GLenum shaderType)
{
GLuint shader = glCreateShader(shaderType);

if (shader == 0)
std::cerr << "Error: Shader creation failed!" << std::endl;

const GLchar* shaderSourceStrings[1];
GLint shaderSourceStringLengths[1];

shaderSourceStrings[0] = text.c_str();

glShaderSource(shader, 1, shaderSourceStrings, shaderSourceStringLengths);
glCompileShader(shader);

CheckShaderError(shader, GL_COMPILE_STATUS, false, "Error: Shader compilation failed!");

return shader;
}

static std::string LoadShader(const std::string& fileName)
{
std::ifstream file;
file.open((fileName).c_str());

std::string output;
std::string line;

if (file.is_open())
{
while (file.good())
{
getline(file, line);
output.append(line + "\n");
}
}
else
{
std::cerr << "Unable to load shader: " << fileName << std::endl;
}

return output;
}

static void CheckShaderError(GLuint shader, GLuint flag, bool isProgram, const std::string& errorMessage)
{
GLint success = 0;
GLchar error[1024] = { 0 };

if (isProgram)
glGetProgramiv(shader, flag, &success);
else
glGetShaderiv(shader, flag, &success);

if (success == GL_FALSE)
{
if (isProgram)
glGetProgramInfoLog(shader, sizeof(error), NULL, error);
else
glGetShaderInfoLog(shader, sizeof(error), NULL, error);

std::cerr << errorMessage << ": '" << error << "'" << std::endl;
}
}

3 . 在项目文件夹下新建res文件夹,如下图:这里写图片描述
4 . 在新建的res文件夹下,新建顶点着色器文件basicShader.vs,代码如下:

1
2
3
4
5
6
7
8
#version 120

attribute vec3 position;

void main()
{
gl_Position = vec4(position, 1.0);
}

5 . 在新建的res文件夹下,新建片元着色器文件basicShader.fs,代码如下:

1
2
3
4
5
6
7
8
#version 120

attribute vec3 position;

void main()
{
gl_Position = vec4(position, 1.0);
}

6 . 修改主类main.cpp,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <GL\glew.h>
#include "display.h"
#include "mesh.h"
#include "shader.h"

int main(int argc, char** argv)
{
// 设置窗体大小和标题
Display display(400, 300, "hello world!");

// 设置三角形顶点
Vertex vertices[] = { Vertex(glm::vec3(-0.5, -0.5, 0)), Vertex(glm::vec3(0, 0.5, 0)), Vertex(glm::vec3(0.5, -0.5, 0)), };

// 生成网格
Mesh mesh(vertices, sizeof(vertices) / sizeof(vertices[0]));
// 加载着色器
Shader shader("./res/basicShader");

while (!display.IsClosed())
{
display.Clear(0.0f, 1.0f, 0.0f, 1.0f);

// 绑定着色器
shader.Bind();
// 绘制三角形
mesh.Draw();

display.Update();// 刷新
}

return 0;
}

7 . 运行项目。运行后会显示一个绿色的OpenGL窗口中有一个红色三角形。如图:
效果图

注意:本文中的项目使用的是VS2015,建议是用VS2015打开。点此下载源码

本文整理自YouTube视频教程#3.5 Intro to Modern OpenGL Tutorial: Shaders


同系列文章

[「Modern OpenGL系列(一)」十步搞定OpenGL开发环境](http://davidsheh.github.io/post/「Modern OpenGL系列(一)」十步搞定OpenGL开发环境/)

[「Modern OpenGL系列(二)」创建OpenGL窗口](http://davidsheh.github.io/post/「Modern OpenGL系列(二)」创建OpenGL窗口/)

[「Modern OpenGL系列(三)」用OpenGL绘制一个三角形](http://davidsheh.github.io/post/「Modern OpenGL系列(三)」用OpenGL绘制一个三角形/)

[「Modern OpenGL系列(四)」在OpenGL中使用Shader](http://davidsheh.github.io/post/「Modern OpenGL系列(四)」在OpenGL中使用Shader/)