Learn OpenGL(七)——OpenGL中使用着色器的基本步骤及GLSL渲染简单示例
OpenGL着色语言(OpenGL Shading Language,GLSL)是用来在OpenGL中着色编程的语言,是一种具有C/C++风格的高级过程语言,同样也以main函数开始,只不过执行过程是在GPU上。GLSL使用类型限定符而不是通过读取和写入操作来管理输入和输出。着色器主要分为顶点着色器(Vertex Shader)和片段着色器(Fragment Shader)两部分。
顶点着色器的主要功能是:
- 顶点法线变换及单位化
- 纹理坐标变换
- 光照参数生成
顶点着色器的输入内容包括:
- 着色器源代码
- attribute变量
- uniform变量
顶点着色器的输出内容包括:
- varying变量
- 内置的特殊变量,如gl_Position、gl_FrontFacing、gl_PointSize
片元着色器的主要功能:
- 在差值得到的值上进行操作
- 访问纹理
- 应用纹理
- 雾化
- 颜色融合
片元着色器的输入内容包括:
- 着色器源代码
- 用户自定义的varying变量
- uniform变量
- 采样器(Sampler)
- 一些内置的特殊变量(gl_PointCoord、gl_FragCoord、gl_FrontFacing等)
片元着色器的输出:
- 内置的特殊变量gl_FragColor
在OpenGL程序中使用着色器的初始化一般需要依次执行以下步骤:
- 1、顶点着色程序的源代码和片段着色程序的源代码分别写入到一个文件里(或字符数组)里面,一般顶点着色器源码文件后缀为.vert,片段着色器源码文件后缀为.frag;
- 2、使用glCreateshader()分别创建一个顶点着色器对象和一个片段着色器对象;
- 3、使用glShaderSource()分别将顶点/片段着色程序的源代码字符数组绑定到顶点/片段着色器对象上;
- 4、使用glCompileShader()分别编译顶点着色器和片段着色器对象(最好检查一下编译的成功与否);
- 5、使用glCreaterProgram()创建一个着色程序对象;
- 6、使用glAttachShader()将顶点和片段着色器对象附件到需要着色的程序对象上;
- 7、使用glLinkProgram()分别将顶点和片段着色器和着色程序执行链接生成一个可执行程序(最好检查一下链接的成功与否);
- 8、使用glUseProgram()将OpenGL渲染管道切换到着色器模式,并使用当前的着色器进行渲染;
以下是一个功能简单但流程完整的使用顶点着色器和片段着色器渲染的矩形图形。项目一共包含5个文件。2个资源文件(VertexShader.vert和FragmentShader.frag,分别是顶点着色器源码文件和片段着色器源码文件),2个cpp文件(Hello GLSL.cpp和Textfile.cpp),1个头文件Textfile.h。
VertexShader.vert文件内容:
//定义GLSL版本
#version 440 in vec4 VertexPosition;
in vec4 VertexColor;out vec4 Color; void main()
{ Color =VertexColor; gl_Position = VertexPosition;
}
FragmentShader.frag文件内容:
#version 440 in vec4 Color; //汉字用于测试汉字是否可用,有报着色器源码注释含汉字运行报错的out vec4 FragColor; void main() { FragColor = Color; }
Hello GLSL.cpp文件内容:
#include <GL/glew.h>
#include "Textfile.h"
#include <GL/freeglut.h>
#include <iostream>#pragma comment(lib,"glew32.lib") using namespace std;GLuint vShader, fShader;//顶点/片段着色器对象
GLuint vaoHandle;// VAO对象//顶点位置数组
float positionData[] = {-0.5f,-0.5f,0.0f,1.0f,0.5f,-0.5f,0.0f,1.0f,0.5f,0.5f,0.0f,1.0f,-0.5f,0.5f,0.0f,1.0f
};
//顶点颜色数组
float colorData[] = {1.0f, 0.0f, 0.0f,1.0f,0.0f, 1.0f, 0.0f,1.0f,0.0f, 0.0f, 1.0f,1.0f,1.0f,1.0f,0.0f,1.0f
};void initShader(const char *VShaderFile, const char *FShaderFile)
{//1、查看显卡、GLSL和OpenGL的信息 const GLubyte *vendor = glGetString(GL_VENDOR);const GLubyte *renderer = glGetString(GL_RENDERER);const GLubyte *version = glGetString(GL_VERSION);const GLubyte *glslVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);cout << "显卡供应商 : " << vendor << endl;cout << "显卡型号 : " << renderer << endl;cout << "OpenGL版本 : " << version << endl;cout << "GLSL版本 : " << glslVersion << endl;//2、编译着色器 //创建着色器对象:顶点着色器 vShader = glCreateShader(GL_VERTEX_SHADER);//错误检测 if (0 == vShader){cerr << "ERROR : Create vertex shader failed" << endl;exit(1);}//把着色器源代码和着色器对象相关联 const GLchar *vShaderCode = textFileRead(VShaderFile);const GLchar *vCodeArray[1] = { vShaderCode };//将字符数组绑定到对应的着色器对象上glShaderSource(vShader, 1, vCodeArray, NULL);//编译着色器对象 glCompileShader(vShader);//检查编译是否成功 GLint compileResult;glGetShaderiv(vShader, GL_COMPILE_STATUS, &compileResult);if (GL_FALSE == compileResult){GLint logLen;//得到编译日志长度 glGetShaderiv(vShader, GL_INFO_LOG_LENGTH, &logLen);if (logLen > 0){char *log = (char *)malloc(logLen);GLsizei written;//得到日志信息并输出 glGetShaderInfoLog(vShader, logLen, &written, log);cerr << "vertex shader compile log : " << endl;cerr << log << endl;free(log);//释放空间 }}//创建着色器对象:片断着色器 fShader = glCreateShader(GL_FRAGMENT_SHADER);//错误检测 if (0 == fShader){cerr << "ERROR : Create fragment shader failed" << endl;exit(1);}//把着色器源代码和着色器对象相关联 const GLchar *fShaderCode = textFileRead(FShaderFile);const GLchar *fCodeArray[1] = { fShaderCode };glShaderSource(fShader, 1, fCodeArray, NULL);//编译着色器对象 glCompileShader(fShader);//检查编译是否成功 glGetShaderiv(fShader, GL_COMPILE_STATUS, &compileResult);if (GL_FALSE == compileResult){GLint logLen;//得到编译日志长度 glGetShaderiv(fShader, GL_INFO_LOG_LENGTH, &logLen);if (logLen > 0){char *log = (char *)malloc(logLen);GLsizei written;//得到日志信息并输出 glGetShaderInfoLog(fShader, logLen, &written, log);cerr << "fragment shader compile log : " << endl;cerr << log << endl;free(log);//释放空间 }}//3、链接着色器对象 //创建着色器程序 GLuint programHandle = glCreateProgram();if (!programHandle){cerr << "ERROR : create program failed" << endl;exit(1);}//将着色器程序链接到所创建的程序中 glAttachShader(programHandle, vShader);glAttachShader(programHandle, fShader);//将这些对象链接成一个可执行程序 glLinkProgram(programHandle);//查询链接的结果 GLint linkStatus;glGetProgramiv(programHandle, GL_LINK_STATUS, &linkStatus);if (GL_FALSE == linkStatus){cerr << "ERROR : link shader program failed" << endl;GLint logLen;glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH,&logLen);if (logLen > 0){char *log = (char *)malloc(logLen);GLsizei written;glGetProgramInfoLog(programHandle, logLen,&written, log);cerr << "Program log : " << endl;cerr << log << endl;}}else//链接成功,在OpenGL管线中使用渲染程序 {glUseProgram(programHandle);}
}void initVBO()
{//绑定VAOglGenVertexArrays(1, &vaoHandle);glBindVertexArray(vaoHandle);// Create and populate the buffer objects GLuint vboHandles[2];glGenBuffers(2, vboHandles);GLuint positionBufferHandle = vboHandles[0];GLuint colorBufferHandle = vboHandles[1];//绑定VBO以供使用 glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);//加载数据到VBO glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float),positionData, GL_STATIC_DRAW);//绑定VBO以供使用 glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);//加载数据到VBO glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float),colorData, GL_STATIC_DRAW);glEnableVertexAttribArray(0);//顶点坐标 glEnableVertexAttribArray(1);//顶点颜色 //调用glVertexAttribPointer之前需要进行绑定操作 glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, (GLubyte *)NULL);glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (GLubyte *)NULL);
}void init()
{//初始化glew扩展库 GLenum err = glewInit();if (GLEW_OK != err){cout << "Error initializing GLEW: " << glewGetErrorString(err) << endl;}//加载顶点和片段着色器对象并链接到一个程序对象上initShader("VertexShader.vert","FragmentShader.frag");//绑定并加载VAO,VBOinitVBO();glClearColor(0.0, 0.0, 0.0, 0.0);
}void display()
{glClear(GL_COLOR_BUFFER_BIT);//使用VAO、VBO绘制 glBindVertexArray(vaoHandle);glDrawArrays(GL_TRIANGLE_FAN, 0, 4);glBindVertexArray(0);glutSwapBuffers();
}//ESC键用于退出使用着色器
void keyboard(unsigned char key, int x, int y)
{switch (key){case 27:glDeleteShader(vShader);glUseProgram(0);glutPostRedisplay(); //刷新显示break;}
}int main(int argc, char** argv)
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);glutInitWindowSize(600, 600);glutInitWindowPosition(100, 100);glutCreateWindow("Hello GLSL");init();glutDisplayFunc(display);glutKeyboardFunc(keyboard);glutMainLoop();return 0;
}
Textfile.cpp文件内容:
// textfile.cpp // simple reading and writing for text files
#include "Textfile.h"unsigned char * readDataFromFile(char *fn)
{FILE *fp;unsigned char *content = NULL;int count = 0;if (fn != NULL) {fp = fopen(fn, "rb");if (fp != NULL) {fseek(fp, 0, SEEK_END);count = ftell(fp);rewind(fp);if (count > 0) {content = (unsigned char *)malloc(sizeof(unsigned char) * (count + 1));count = fread(content, sizeof(unsigned char), count, fp);content[count] = '\0';}fclose(fp);}}return content;
}//读入字符流
char *textFileRead(const char *fn)
{FILE *fp;char *content = NULL;int count = 0;if (fn != NULL){fp = fopen(fn, "rt");if (fp != NULL){fseek(fp, 0, SEEK_END);count = ftell(fp);rewind(fp);if (count > 0){content = (char *)malloc(sizeof(char) * (count + 1));count = fread(content, sizeof(char), count, fp);content[count] = '\0';}fclose(fp);}}return content;
}int textFileWrite(char *fn, char *s)
{FILE *fp;int status = 0;if (fn != NULL) {fp = fopen(fn, "w");if (fp != NULL) {if (fwrite(s, sizeof(char), strlen(s), fp) == strlen(s))status = 1;fclose(fp);}}return(status);
}
Textfile.h文件内容:
// textfile.h: interface for reading and writing text files
#ifndef TEXTFILE_H
#define TEXTFILE_H#include <stdio.h>
#include <stdlib.h>
#include <string.h>char *textFileRead(const char *fn);
int textFileWrite(char *fn, char *s);
unsigned char *readDataFromFile(char *fn);#endif
运行结果:
总结一下以上程序的执行过程:
1. 传统的初始化,创建窗口
2. 调用glewInit初始化glew库
3. 使用glGetString查询显卡和OpenGL以及GLSL等信息
4. 使用glCreateShader创建顶点/片段着色器对象
5. fread读入顶点/片段着色器的源码字符流
6. 使用glShaderSource将字符数组绑定到对应的着色器对象上
7. glCompileShader编译着色器对象
8. glCreateprogram创建着色器程序
9. glAttachShader将着色器程序链接到所创建的程序中
10.glLinkProgram将顶点/片段着色器、程序对象链接成一个可执行程序。
11.glUseProgram启用着色器渲染程序
程序:
1. glGenVertexArrays生成VAO,glBindVertexArray绑定VAO
2. glGenBuffers分别生成顶点位置VBO和颜色VBO
3. glBindBuffer绑定VBO
4. glBufferData加载实际数据到VBO
5. glEnableVertexAttribArray启用顶点/颜色VBO
6. glVertexAttribPointer对顶点/颜色数值内容进行解释(定义)
显示部分:
1. glBindVertexArray绑定VAO
2. glDrawArrays绘制图像
3. glBindVertexArray(0)解除VAO绑定
Learn OpenGL(七)——OpenGL中使用着色器的基本步骤及GLSL渲染简单示例相关推荐
- Learn OpenGL (三):着色器
GLSL 着色器是使用一种叫GLSL的类C语言写成的.GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性. 着色器的开头总是要声明版本,接着是输入和输出变量.uniform和mai ...
- Learn OpenGL(四)——片段着色器(Fragment Shader)
片段着色器(Fragment Shader) 片段着色器是第二个也是最终我们打算创建的用于渲染三角形的着色器. 片段着色器的全部, 都是用来计算你的像素的最后颜色输出. 为了让事情比较简单, 我们的片 ...
- Learn OpenGL(三)——顶点着色器(Vertext Shader)
顶点着色器是几个着色器中的一个, 它是可编程的. 现代OpenGL需要我们至少设置一个顶点着色器和一个片段着色器, 如果我们打算做渲染的话. 我们会简要介绍一下着色器以及配置两个非常简单的着色器来绘制 ...
- 【OPENGL】第三篇 着色器基础(一)
在这一章,我们会学习什么是着色器(Shader),什么是着色器语言(OpenGL Shading Language-GLSL),以及着色器怎么和OpenGL程序交互. 首先我们先来看看什么叫着色器. ...
- OpenGL从入门到精通--着色器的使用
着色器 github源码仓库 opengl环境准备 opengl编程从入门到精通-hello,window OpenGL从入门到精通–你好三角形 OpenGL从入门到精通–着色器的使用 着色器(Sha ...
- OpenGL ES (三)着色器和程序
OpenGL ES学习系列文章: 上一篇:OpenGL ES (二)EGL介绍和使用 下一篇:OpenGL ES (二)EGL介绍和使用 着色器和程序 前言 1.创建Shader 2.加载Shader ...
- opengl顶点数据传送和着色器处理(vao,vbo)
OpenGL学习脚印: 顶点数据传送和着色器处理1 写在前面 本节内容翻译和整理自<Learning Modern 3D Graphics Programming>Chapter1内容.作 ...
- 【OpenGL】笔记三、着色器
1. 流程 1.1 图形颜色随时间变化 接上回生成矩形,本次我们要对矩形颜色做一定改变,让它在渲染过程中随时间而变化,但是目前来说我们好像无法在着色器源码中获取时间,那么剩下的方法应该就是在程序中对片 ...
- OpenGL ES3.1使用计算着色器(Compute Shader)
OpenGL ES3.1使用计算着色器(Compute Shader) 1.基本介绍 OpenGL ES从3.1版本开始支持计算着色器 工作模型有全局工作组和本地工作组,全局工作组包含 ...
最新文章
- 有关GetPrivateProfileString的使用方法
- Java多个jdk安装切换
- Dom方法,解析XML文件
- 查看ubuntu磁盘空间占用及占用空间大的文件
- Python第五、六种数据类型——Set(集合) and Dictionary(字典)
- 其他类型的链表和线性表的总结(一级)
- 线段树之线段操作之陈老师的福利
- 在nodejs中操作mongodb
- 涡轮流量计说明书_实际应用中超声波流量计与电磁流量计哪个更好用
- 玩转Docker镜像
- mysql乱码加的一段代码_mysql乱码的解决方法
- C++继承机制(一)——基本语法、三种继承方式、继承哪些数据
- $ajax 获取返回值object,来自.ajax()调用的数据的jQuery .find()返回“ [object Object]”,而不是di...
- js、jQuery实现自定义弹出框效果
- Dell’Oro 5年期数据中心报告预测25G/100G端口速率市场快速上升
- 翻译:生产中的机器学习:为什么你应该关心数据和概念漂移
- xmind 拖拽_GitHub - xdsnet/jsxMind: 基于jsmind 改写的jsxmind插件
- 【ArcGIS】Packaging succeeded,but publishing failed
- conv2d() received an invalid combination of arguments问题解决
- 计算机设计大赛感言,平面设计大赛获奖感言
热门文章
- sql SELECT时的with(nolock)选项说明
- 【数据结构笔记04】线性结构:线性表及其实现
- 如何在 Swift 中进行错误处理
- Git分支简介与使用——Git的学习与使用(五)
- DW8里面的HTML面板在哪里,打开Dreamweaver8窗口后,如果没有出现属性面板,可执行()菜单中的 - 问答库...
- mysql 存储过程 风险_删除/恢复SQL server危险的存储过程方法
- 三维点云学习(4)6-ransac 地面分割
- SQL NOTE-VARIABLE
- zybo上运行linux,Zybo开发板linux作业系统移植
- 路由协议之间的经典对比