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()
{
//绑定VAO
glGenVertexArrays(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,VBO
initVBO();
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绑定
OpenGl中使用着色器的基本步骤及GLSL渲染简单示例的更多相关文章
- OpenGL官方教程——着色器语言概述
OpenGL官方教程——着色器语言概述 OpenGL官方教程——着色器语言概述 可编程图形硬件管线(流水线) 可编程顶点处理器 可编程几何处理器 可编程片元处理器 语言 可编程图形硬件管线(流水线) ...
- 在CG/HLSL中访问着色器属性(Properties)
在CG/HLSL中访问着色器属性 Shader在Properties块中访问材质属性.如果你想在一个着色程序中访问一些属性,你需要声明一个Cg/HLSL具有相同的名称和一个匹配的类型的变量. Prop ...
- OpenGL之shader着色器的应用,三色渐变的三角形
学习自: https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/#_7 首先放一张效果图: 本次教程,将着色器单独定 ...
- three中的着色器示例
其实在3D引擎/库的帮助下,我们做webgl开发的难度已经很大大地降低了,熟悉相关API的话,开发一个简单的3D程序可以说是很轻松的事情. 在我看来,webgl的核心就是着色器(顶点着色器.片元着色器 ...
- 在CG/HLSL中访问着色器的内容
着色器在Properties代码块中声明 材质球的各种特性.如果你想要在着色器程序中使用这些特性,你需要在CG/HLSL中声明一个变量,这个变量需要与你要使用的特性拥有同样的名字和对的上号的类型.比如 ...
- 编写Unity3D着色器的三种方式
不管你会不会写Unity3D的shader,估计你会知道,Unity3D编写shader有三种方式,这篇东西主要就是说一下这三种东西有什么区别,和大概是怎样用的. 先来列一下这三种方式: fixed ...
- [GEiv]第七章:着色器 高效GPU渲染方案
第七章:着色器 高效GPU渲染方案 本章介绍着色器的基本知识以及Geiv下对其提供的支持接口.并以"渐变高斯模糊"为线索进行实例的演示解说. [背景信息] [计算机中央处理器的局限 ...
- 在Android中使用OpenGL ES开发第(五)节:GLSL基础语法
一.前期基础储备笔者之前的四篇文综述了Android中使用OpenGL ES绘制基本图形和实现了简单的相机预览,初次接触OpenGL ES开发的读者可能对其中新的概念比较迷惑,尤其是其中的顶点着色器( ...
- OpenGL学习脚印: uniform blocks在着色器中的使用 转自https://blog.csdn.net/wangdingqiaoit/article/details/52717963
写在前面 目前,我们在着色器中要传递多个uniform变量时,总是使用多个uniform,然后在主程序中设置这些变量的值:同时如果要在多个shader之间共享变量,例如投影矩阵projection和视 ...
随机推荐
- Nginx和Nginx+的比較(下)
Nginx和Nginx+的比較(下) 作者:chszs.未经博主同意不得转载.经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 内容紧接上一篇<Nginx和 ...
- [Err] 1136 - Column count doesn't match value count at row 1
1 错误描写叙述 [Err] 1136 - Column count doesn't match value count at row 1 Procedure execution failed 113 ...
- js json简介(json的本质也是字符串)(用于服务器和客户端通信)
js json简介(json的本质也是字符串)(用于服务器和客户端通信) 一.总结 1.json的语法和js的语法非常像,只是json的键和值都是双引号,因为json的本质也是字符串 2.json是一 ...
- HDU 1425 sort hash+加速输入
http://acm.hdu.edu.cn/showproblem.php?pid=1425 题目大意: 给你n个整数,请按从大到小的顺序输出其中前m大的数. 其中n和m都是位于[-500000,50 ...
- STATUS CODE: 91, occurs when trying to move media from one volume pool to another.
Overview:Symantec NetBackup (tm) will not allow a tape with active images to be moved from one volum ...
- 26、驱动调试之根据oops信息和堆栈确定出错的代码
a.驱动作为模块:1. 根据pc值确定该指令属于内核还是外加的模块pc=0xbf000018 它属于什么的地址?是内核还是通过insmod加载的驱动程序?先判断是否属于内核的地址: 看System.m ...
- Bash Shell 的管道命令
1.cut: 命令选取 cut -d'分隔字符' -f fields -d :后面接分隔字符.用-f一起使用 -f: 根据-d的分隔字符将一段信息分割成为数段 -c:以字符的单位取出固定字符区间 Eg ...
- ios开发零散知识点总结
1:当有导航栏的时候,子视图为UIScrollView,或是继承于UIScrollView的控件如UITableView,UICollectionView等,控制器会自动调用 self.automat ...
- 深度理解IIS下部署ASP.NET Core2.1 Web应用拓扑图
原文:深度理解IIS下部署ASP.NET Core2.1 Web应用拓扑图 IIS部署ASP.NET Core2.1 应用拓扑图 我们看到相比Asp.Net, 出现了3个新的组件:ASP.NET Co ...
- 【C++竞赛 F】yyy的三角形
时间限制:2s 内存限制:32MB 问题描述 yyy对三角形非常感兴趣,他有n个木棍,他正在用这些木棍组成三角形.这时xxx拿了两根木棍过来,xxx希望yyy能给他一根木棍,使得xxx可以组成一个三角 ...