代码放在github上
本文根据教程:ogldev进行扩充学习,一步步从零开始,记录学习历程
这节相比上一节有了本质上的区别,OpenGL实际上是通过渲染管线(rendering pipeline),经过一系列的数据处理,将应用程序的数据转换到最终渲染的图像。
在《OpenGL Programming Guide 9th》讲解了渲染管线,下图即是OpenGL 4.5版本的管线:
GLSL是OpenGL的着色语言,跟使用C语言作为基础高阶着色语言,通过了解渲染管线我们知道了顶点着色器和片元着色器必不可少的,所以我们用GLSL语言构建顶点着色器和片元着色器。
shader.vs(顶点着色器):
#version 330
layout (location = 0) in vec3 Position;
void main()
{
gl_Position = vec4(0.5 * Position.x, 0.5 * Position.y, Position.z, 1.0);
}
shader.fs(片元着色器):
#version 330
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
#version 330
void main()
{
// code
}
layout (location = 0) in vec3 Position;
gl_Position = vec4(0.5 * Position.x, 0.5 * Position.y, Position.z, 1.0);
out vec4 FragColor;
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
通过上面的学习,应该对顶点着色器和片元着色器有了一个直观的感受: 顶点着色器决定要绘制图形的位置,片元着色器决定要绘制图形的颜色
opengl_math.h
#ifndef __OPENGL_MATH_H
#define __OPENGL_MATH_H
//向量
typedef float Vector3f[3];
//向量赋值
inline void LoadVector3(Vector3f v, const float x, const float y, const float z)
{
v[0] = x; v[1] = y; v[2] = z;
}
#endif
这段数学头文件没有改变,因为还是画一个三角形,只是这回用到了着色器,以后会有很多东西要往上添加
main.cpp:
#include
#include
#include
#include
#include
#include "opengl_math.h"
using namespace std;
GLuint VBO;
const char* pVSFileName = "shader.vs";
const char* pFSFileName = "shader.fs";
static void Render()
{
glClear(GL_COLOR_BUFFER_BIT);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(0);
glutSwapBuffers();
}
static void InitializeGlutCallbacks()
{
glutDisplayFunc(Render);
}
static void CreateVertexBuffer()
{
Vector3f Vertices[3];
LoadVector3(Vertices[0], -1.0f, -1.0f, 0.0f);
LoadVector3(Vertices[1], 1.0f, -1.0f, 0.0f);
LoadVector3(Vertices[2], 0.0f, 1.0f, 0.0f);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
}
bool ReadFile(const char* pFileName, string &outFile)
{
ifstream f(pFileName);
bool ret = false;
if (f.is_open()) {
string line;
while (getline(f, line)) {
outFile.append(line);
outFile.append("\n");
}
f.close();
ret = true;
}
else {
fprintf(stderr, "%s:%d: unable to open file `%s`\n", __FILE__,__LINE__,pFileName);
}
return ret;
}
static void AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType)
{
GLuint ShaderObj = glCreateShader(ShaderType);
//check if it is successful
if (ShaderObj == 0) {
fprintf(stderr, "Error creating shader type %d\n", ShaderType);
exit(0);
}
//define shader code source
const GLchar* p[1];
p[0] = pShaderText;
GLint Lengths[1];
Lengths[0] = strlen(pShaderText);
glShaderSource(ShaderObj, 1, p, Lengths);
//Compiler shader object
glCompileShader(ShaderObj);
//check the error about shader
GLint success;
glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
if (!success) {
GLchar InfoLog[1024];
glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog);
fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
exit(1);
}
//bound the shader object to shader program
glAttachShader(ShaderProgram, ShaderObj);
}
static void CompilerShaders()
{
//Create Shaders
GLuint ShaderProgram = glCreateProgram();
//Check yes or not success
if (ShaderProgram == 0) {
fprintf(stderr, "Error creating shader program\n");
exit(1);
}
//the buffer of shader texts
string vs, fs;
//read the text of shader texts to buffer
if (!ReadFile(pVSFileName, vs)) {
exit(1);
}
if (!ReadFile(pFSFileName, fs)) {
exit(1);
}
//add vertex shader and fragment shader
AddShader(ShaderProgram, vs.c_str(), GL_VERTEX_SHADER);
AddShader(ShaderProgram, fs.c_str(), GL_FRAGMENT_SHADER);
//Link the shader program, and check the error
GLint Success = 0;
GLchar ErrorLog[1024] = { 0 };
glLinkProgram(ShaderProgram);
glGetProgramiv(ShaderProgram,GL_LINK_STATUS,&Success);
if (Success == 0) {
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog);
exit(1);
}
//check if it can be execute
glValidateProgram(ShaderProgram);
glGetProgramiv(ShaderProgram, GL_VALIDATE_STATUS, &Success);
if (!Success) {
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Invalid shader program: '%s'\n", ErrorLog);
exit(1);
}
//use program
glUseProgram(ShaderProgram);
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(1024, 768);
glutInitWindowPosition(10, 10);
glutCreateWindow("Shader");
InitializeGlutCallbacks();
GLenum res = glewInit();
if (res != GLEW_OK) {
fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
return 1;
}
printf("GL version: %s\n", glGetString(GL_VERSION));
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
CreateVertexBuffer();
CompilerShaders();
glutMainLoop();
return 0;
}
CompilerShaders();
main()函数里多了一个CompilerShaders()函数,这个函数是我们自定义的,用来完成着色器的编译工作
我们主要任务是创建GLSL着色器对象,编译和链接来生成可执行着色器程序。下图给出了具体过程
GLuint ShaderObj = glCreateShader(ShaderType);
if (ShaderObj == 0) {
fprintf(stderr, "Error creating shader type %d\n", ShaderType);
exit(0);
}
GLuint glCreateShader(GLenum type)用来创建一个着色器对象,type必须是
type | 说明 |
---|---|
GL_VERTEX_SHADER | 顶点着色器 |
GL_FRAGMENT_SHADER | 片元着色器 |
GL_TESS_CONTROL_SHADER | 细分控制着色器 |
GL_TESS_EVALUATION_SHADER | 细分赋值着色器 |
GL_GEOMETRY_SHADER | 几何着色器 |
GL_COMPUTE_SHADER | 计算着色器 |
const GLchar* p[1];
p[0] = pShaderText;
GLint Lengths[1];
Lengths[0] = strlen(pShaderText);
glShaderSource(ShaderObj, 1, p, Lengths);
glCompileShader(ShaderObj);
glShaderSource((GLuint shader , GLsizei count ,
const GLchar **string ,const GLint *length) //将着色器源代码关联到一个着色器对象上
glCompileShader(ShaderObj);
GLint success;
glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
if (!success) {
GLchar InfoLog[1024];
glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog);
fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
exit(1);
}
GLuint ShaderProgram = glCreateProgram()
glCreateProgram创建一个空的着色器程序,返回值是一个非零的整数,如果为0则说明发生了错误
glAttachShader(ShaderProgram, ShaderObj);
将着色器对象Shaderobj关联到着色器程序program上
glLinkProgram(ShaderProgram);
处理所有与ShaderProgram关联的着色器对象来生成一个完整的着色器程序
glGetProgramiv(ShaderProgram,GL_LINK_STATUS,&Success);
if (!Success) {
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog);
exit(1);
}
glValidateProgram(ShaderProgram);
glGetProgramiv(ShaderProgram, GL_VALIDATE_STATUS, &Success);
if (!Success) {
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Invalid shader program: '%s'\n", ErrorLog);
exit(1);
}
glUseProgram(ShaderProgram);
glEnableVertexAttribArray(0);
...
glDisableVertexAttribArray(0);
bool ReadFile(const char* pFileName, string &outFile)
{
ifstream f(pFileName);
bool ret = false;
if (f.is_open()) {
string line;
while (getline(f, line)) {
outFile.append(line);
outFile.append("\n");
}
f.close();
ret = true;
}
else {
fprintf(stderr, "%s:%d: unable to open file `%s`\n", __FILE__,__LINE__,pFileName);
}
return ret;
}