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和视 ...
随机推荐
- php实现从尾到头打印列表
php实现从尾到头打印列表 一.总结 4.数组倒序:array_reverse() 5.函数肯定要return,而不是echo 二.php实现从尾到头打印列表 输入一个链表,从尾到头打印链表每个节点的 ...
- 【30.93%】【codeforces 558E】A Simple Task
time limit per test5 seconds memory limit per test512 megabytes inputstandard input outputstandard o ...
- [CSS] Dynamically Size Elements with Pure CSS
Learn how to size elements based on the dimensions of the viewport, even when the browser is resized ...
- Android OkHttp网络连接封装工具类
package com.lidong.demo.utils; import android.os.Handler; import android.os.Looper; import com.googl ...
- 第一次使用docker for windows 遇到的坑
原文:第一次使用docker for windows 遇到的坑 1. 目前win10安装docker, 不需要安装其他工具,可直接到官网下载 2. 此版本的docker可同时运行Windows con ...
- 文本处理之可视化wordcloud
什么是词云 词云又叫文字云,是对文本数据中出现频率较高的“关键词”在视觉上的突出呈现,形成关键词的渲染形成类似云一样的彩色图片,从而一眼就可以领略文本数据的主要表达意思. 准备工作: python开发 ...
- OpenCV编译步骤
作者:朱金灿 来源:http://blog.csdn.net/clever101 1. 从网上下载OpenCV安装包,然后安装. 2. 打开CMake 2.8设置源码路径和生成的VS工程文件路径.(首 ...
- 基于 Android NDK 的学习之旅-----环境搭建
工欲善其事 必先利其器 , 下面介绍下 Eclipse SDK NDK Cygwin CDT 集成开发环境的搭建. 1.Android 开发环境搭建 Android开发环境搭建不是重点,相信看此文章的 ...
- 【u028】数列的整除性
Time Limit: 1 second Memory Limit: 128 MB [问题描述] 对于任意一个整数数列,我们可以在每两个整数中间任意放一个符号'+'或'-',这样就可以构成一个表达式, ...
- 【hdu 4315】Climbing the Hill
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s) ...