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渲染简单示例的更多相关文章

  1. OpenGL官方教程——着色器语言概述

    OpenGL官方教程——着色器语言概述 OpenGL官方教程——着色器语言概述 可编程图形硬件管线(流水线) 可编程顶点处理器 可编程几何处理器 可编程片元处理器 语言 可编程图形硬件管线(流水线) ...

  2. 在CG/HLSL中访问着色器属性(Properties)

    在CG/HLSL中访问着色器属性 Shader在Properties块中访问材质属性.如果你想在一个着色程序中访问一些属性,你需要声明一个Cg/HLSL具有相同的名称和一个匹配的类型的变量. Prop ...

  3. OpenGL之shader着色器的应用,三色渐变的三角形

    学习自: https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/#_7 首先放一张效果图: 本次教程,将着色器单独定 ...

  4. three中的着色器示例

    其实在3D引擎/库的帮助下,我们做webgl开发的难度已经很大大地降低了,熟悉相关API的话,开发一个简单的3D程序可以说是很轻松的事情. 在我看来,webgl的核心就是着色器(顶点着色器.片元着色器 ...

  5. 在CG/HLSL中访问着色器的内容

    着色器在Properties代码块中声明 材质球的各种特性.如果你想要在着色器程序中使用这些特性,你需要在CG/HLSL中声明一个变量,这个变量需要与你要使用的特性拥有同样的名字和对的上号的类型.比如 ...

  6. 编写Unity3D着色器的三种方式

    不管你会不会写Unity3D的shader,估计你会知道,Unity3D编写shader有三种方式,这篇东西主要就是说一下这三种东西有什么区别,和大概是怎样用的. 先来列一下这三种方式: fixed ...

  7. [GEiv]第七章:着色器 高效GPU渲染方案

    第七章:着色器 高效GPU渲染方案 本章介绍着色器的基本知识以及Geiv下对其提供的支持接口.并以"渐变高斯模糊"为线索进行实例的演示解说. [背景信息] [计算机中央处理器的局限 ...

  8. 在Android中使用OpenGL ES开发第(五)节:GLSL基础语法

    一.前期基础储备笔者之前的四篇文综述了Android中使用OpenGL ES绘制基本图形和实现了简单的相机预览,初次接触OpenGL ES开发的读者可能对其中新的概念比较迷惑,尤其是其中的顶点着色器( ...

  9. OpenGL学习脚印: uniform blocks在着色器中的使用 转自https://blog.csdn.net/wangdingqiaoit/article/details/52717963

    写在前面 目前,我们在着色器中要传递多个uniform变量时,总是使用多个uniform,然后在主程序中设置这些变量的值:同时如果要在多个shader之间共享变量,例如投影矩阵projection和视 ...

随机推荐

  1. [Maven实战](6)仓库(本地仓库,远程仓库,镜像)

    1. 简单介绍 maven能够在某个位置统一存储全部maven项目共享的构件,这个统一的位置就是仓库.实际的Maven项目将不会各自存储其依赖文件,它们仅仅须要声明这些依赖的坐标,在须要的时候(比如. ...

  2. kernel-char设备的建立

    kernel下的设备分成了一些类,char block char.. 这里就贴出来一个样例能够建立一个char设备 ,抛砖引玉吧 这是kernel中的 drivers/char/msm_smd_pkt ...

  3. Android自定义组件系列【5】——进阶实践(2)

    上一篇<Android自定义组件系列[5]--进阶实践(1)>中对任老师的<可下拉的PinnedHeaderExpandableListView的实现>前一部分进行了实现,这一 ...

  4. [RxJS] Split an RxJS observable with window

    Mapping the values of an observable to many inner observables is not the only way to create a higher ...

  5. 使用JSONP解决跨域问题-代码示例

    前段时间用JSONP解决了跨域问题,现在不用了,把代码思路记下来,今后说不定还用得上. JS代码 //查询公告数据 function recentpost(){ $.getJSON(cmsUrl+&q ...

  6. 【z06】观光公交

    描述 风景迷人的小城Y市,拥有n个美丽的景点.由于慕名而来的游客越来越多,Y市特意安排了一辆观光公交车,为游客提供更便捷的交通服务.观光公交车在第0分钟出现在1号景点,随后依次前往2.3.4--n号景 ...

  7. 【codeforces 760B】Frodo and pillows

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  8. Go语言学习(十)bytes包处理字节切片

    bytes包提供了对字节切片进行读写操作的一系列函数 字节切片处理的函数比較多,分为基本处理函数,比較函数,后缀检查函数,索引函数,切割函数, 大写和小写处理函数和子切片处理函数等. 1.字节切片基本 ...

  9. OSGi开发环境的建立

    1 OSGi开发环境的建立 1.1 Equinox是什么 从代码角度来看,Equinox其实就是OSGi核心标准的完整实现,并且还在这个基础上增加了一些额外的功能(比如为框架增加了命令行和程序执行的入 ...

  10. XMPP之ios即时通讯客户端开发-mac上搭建openfire服务器(二)

    come from:http://www.cnblogs.com/xiaodao/archive/2013/04/05/3000554.html 一.下载并安装openfire 1.到http://w ...