OPenGL--GLSL基础

着色器与OpenGL

现代OpenGL渲染管线严重依赖着色器来处理出入的数据,如果不使用着色器,那么OpenGL可以处理的事情可能只有清除窗口了,可见着色器对OpenGL的重要性。在3.0版本(含3.0)以前,如果用到了兼容模式环境,OpenGL还包含一个固定渲染管线,可以在不使用着色器的情况下处理几何与像素数据。自从3.1版本开始,固定渲染管线从核心模式中去除,因此必须使用着色器来完成工作。

 

GLSL概述

GLSL是一种专门为图形开发设计的语言,你会发现它与“C”语言非常类似,当然也具备C++和java的许多特性,它也被OpenGL中所有阶段所支持。

 

构建GLSL着色器

基本的着色器

一个着色器程序和C语言类似,都是从main()函数开始的,一个典型的着色器是这样的

#version 330 core

Void main()

{

//编写代码

}

1.在程序的起始位置需使用#version类声明当前使用的版本。

2.Main函数不需要返回值,返回为void。

3.可以用// 单行注释也可以用/* */ 多行注释。

变量声明

GLSL是一种强类型语言,所有的变量必须事先声明,并给出变量的类型,语法与C语言相似。下表为GLSL的基本数据类型。

类型

描述

float

32位浮点值

double

32位浮点值

int

有符号的32位整数

uint

无符号的32位整数

bool

布尔值

以上的基本数据类型是都是透明的,他们的内部形式都是暴露出来的,与之对于的是不透明类型,他们的内部形式没有暴露出来,例如采样器(sampler),图像(image),以及原子计数器(atomic)。他们声明的变量相当于一个不透明的句柄。

变量的作用域

在任何函数定义之外声明的变量都拥有全局作用域,对着色器中所有函数都是可见的。

在一组大括号之内声明的变量只能在大括号范围内使用

循环的迭代自变量只能在循环内起最作用。

变量的初始化

所有的变量必须在声明的同时进行初始化,例如

int i = 100‘

foat f = 2.0;

bool b = true;

 

聚合类型

向量与矩阵类型

基本类型

2D向量

3D向量

4D向量

矩阵类型

 

float

 

vec2

 

vec3

 

vec4

mat2  mat3  mat4

mat2*2  mat2*3  mat2*4

Mat3*2  mat3*3  mat3*4

Mat4*2  mat4*3  mat4*4

 

double

 

dvec2

 

dvec3

 

dvec4

dmat2  dmat3  dmat4

dmat2*2  dmat2*3  dmat2*4

Mat3*2  dmat3*3  dmat3*4

Mat4*2  dmat4*3  dmat4*4

int

ivec2

ivec3

ivec4

--

uint

uvec2

uvec3

uvec4

--

bool

bvec2

bvec3

bvec4

--

1.矩阵类型需要给出两个纬度的信息,例如mat4*3,其中一个值表示列数,一个值表示行数。

2.向量的初始化与标量类似

Vec3 v = vec3(0.0, 4.0, 1.0);

类型间可以互相转化

ivec3 steps = ivec3(v);

3.矩阵的构建方式与此相同,例如

通过指定每一个元素来初始化

mat3 M = mat3(1, 2, 3,

4, 5, 6,

7, 8, 9)

通过向量来初始化

vec3 v1 = vec3(1,2,3);

vec3 v2 = vec3(4,5,6);

vec3 v3 = vec3(7,8,9);

mat3 M = mat3(v1,v2,v3);

还可以

vec2 v1 = vec2 (1,2);

vec2 v2 = vec2 (4,5);

vec2 v3 = vec2 (7,8);

mat3 M = mat3(v1, 3, v2, 6, v3, 9);

4,访问向量和矩阵中的元素

向量可以使用分量的名称和下表来访问元素

vec3 color = vec3(1,2,3);

float rad = color.r;或 float rad = color[0];

矩阵元素的访问可以使用数组标记访问

mat4 m = mat4(2.0);

vec4 zVec = m[2];

float yScale = m[1][1];

结构体

struct particle{

float lifetime;

vec3 position;

vec3 velocity;

};

数组

GLSL还支持任意类型的数组,包括结构体数组

GLSL4.2和更早的版本不允许创建数组类型的数组。

float coeff[3];//有3个float元素的数组

float[3] coeff;//有3个float元素的数组

int indices[];//为定义数组,可以从新声明它的纬数

float coeff[3] = float[3]2.38, 3.14, 42.0();//用构造函数构造数组

coeff.lenhth();//返回元素的个数

 

存储限制符

类型修饰符

类型修饰符

描述

const

将变量定义为只读形式

in

设置这个变量为着色阶段的输入变量

out

设置这个变量为着色阶段的输出变量

uniform

设置这个变量为用户应用程序传递给着色器的数据,对于图元而言它是个常量

buffer

设置应用程序共享的一块可读写的内存

shared

设置变量是本地工作组中共享的。它只能用于计算着色器中

in存储限制符

用于修饰用于定义着色器的输入变量,这类输入可以是顶点属性,也可以是前一个着色器的输出变量

out存储限制符

用于修饰用于定义着色器的输出变量,例如顶点着色器输出坐标,或者片段着色器输出最终的片段颜色

uniform存储限制符

uniform修饰符可以设置一个全局变量,该变量数据是单向传递的,用户应用程序可以修改该变量,着色器只能读取该变量,无法写入到该变量,也无法改变它的值。

在用户程序中想要写入数据到uniform变量,通常需要两个函数

GLint  glGetUniformLocation(GLuint program,  const char* name);

返回值:返回着色器程序中uniform变量对应的索引值。

参  数:program着色器程序Id

name uniform变量名

 

void  glUniform1f (GLint location, GLfloat value );

返回值:空。

参  数:location色器程序中uniform变量对应的索引值

value写入的值

使用方法通常是这样的

1.在着色器程序中定义一个uniform变量

uniform float temp;

2.在用户程序中修改f的值,首先获取该uniform变量在着色器中对应的索引值,

GLint id = glGetUniformLocation(1, "temp");

3.接着修改它的值

glUniform1f(id, 4);

 

buffer存储限制符

如果需要在应用程序中共享一大块缓冲给着色器,那么buffer变量是一个好的选择,这块缓冲对于着色器来说是可读可写的。

shared存储限制符只能用于计算着色器中,它可以建立本地工作组内共享的内存。

 

参数限制符

在GLSL中函数可以在运行后返回和修改数据,但它与"C" 不同,并没有指针或引用的概念,不过函数的参数可以指定一个参数限制符,来表明如何处理数据。

函数参数的访问修饰符

访问修饰符

描述

int

将数据拷贝到函数中(如果没有指定,则默认这种形式)

const in

将只读数据拷贝到函数中

out

从函数中获取数值(输入函数的值是为定义的)

inout

将数据拷贝到函数中,并且返回函数中修改的数据

 

 

数据块接口

uniform块

着色器与应用程序之间或者着色器个阶段之间共享的变量可以组织为变量块的形式,uniform可以使用uniform块,in, out, buffer可以使用各自对应的块。例如

uniform b{//限定符可以用in, out, buffer

vec3 v;//变量列表

bool b;

}

注:uniform块中只能包含透明类型的变量,而且uniform块必须在全局作用域内声明。

 

uniform块的布局控制

在uniform块中可以使用不同的限制符来设置变量的布局方式。限制符如下表

uniform的布局限制符

布局限制符

描述

shared

在多个程序间共享(默认的布局方式)

packed

占用最小的内存空间,但会禁止程序间共享这个块

std140

使用标准布局方式来设置uniform块或着色器存储的buffer块

std430

使用标准布局方式来设置buffer块

row_major

使用行主序的方式来存储uniform块中的矩阵

column_major

使用列主序的方式来存储uniform块中的矩阵(默认的顺序)

如果需要共享一个uniform块,并且使用行主序的方式来存储数据,如下所示:

layout (shared, row_major) uniform{       };

如果需要对所有的uniform块布局,可以使用下面的语句

layout (shared, row_major) uniform;

 

应用程序访问uniform块

1.在着色器中定义uniform块

uniform Uniforms{

vec3 trans;

float scale;

vec4 rotation;

bool enabled;

}

2.在应用程序中据获取Uniforms的缓冲索引,并判断其大小,使用函数glGetUniformBlockIndex和glGetActiveUniformBlockiv。

函数原型:

GLuint glGetUniformBlockIndex(GLuint program, const GLchar *uniformBlockName);

返回值:返回Uniforms的缓冲索引

参  数:program  着色器程序ID

        uniformBlockName  Uniforms块名

 

void glGetActiveUniformBlockiv(GLuint program, GLuint uniformBlockIndex,

GLenum pname, GLint *params)

返回值:void

参  数:program           着色器程序ID

        uniformBlockIndex   Uniforms的缓冲索引

        pname             输出的类型

        params            输出参数,输出Uniforms块的大小

 

GLvoid* buffer;//缓冲区

GLint uboSize;//Uniforms块大小

GLint uboIndex;//Uniforms缓冲索引

uboIndex = glGetUniformBlockIndex(ID , "Uniforms");

glGetActiveUniformBlockiv(ID,  uboIndex , "Uniforms", &uboSize);

buffer = malloc(uboSize);

到了这一步我们已经开辟了一片和Uniforms块大小相同的缓冲区,下面就是给他填充数据

 

3.给应用程序中的buffer赋值

//准备buffer中的值

vec3 trans[] = {1.0, 2.0, 3.0}

float scale = 0.5;

vec4 rotation = {1.0, 2.0, 3.0, 4.0}

bool enabled = True;

 

//查询对应的属性判断向缓冲中写入数值的位置

GLuint indices[4];

GLuint size[4];

GLuint offset[4];

GLuint type[4];

 

void glGetUniformIndices(GLuint program,GLsizeiUniformCount, const char**UniformNames, GLuint * uniformIndices);

返回值:void

参  数:program       着色器程序ID

UniformCount   Uniforms块的变量个数

UniformNames   Uniforms块中的变量名

uniformIndices  Uniforms的缓冲索引

 

void glGetActiveUniformsiv(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params)

返回值:void

参  数:program      着色器程序ID

uniformCount  Uniforms块的变量个数

uniformIndices  Uniforms的缓冲索引

pname        输出类型

params        输出函参数

 

4.将变量值拷贝到buffer中

memcpy(buffer + offset[Scale],  &scale,  size[Scale] * TypeSize(type[Scale]));

memcpy(buffer + offset[trans],  &scale,  size[trans] * TypeSize(type[trans]));

memcpy(buffer + offset[rotation],  &scale,  size[rotation] * TypeSize(type[rotation]));

memcpy(buffer + offset[enabled],  &scale,  size[enabled] * TypeSize(type[enabled]));

注:现在只是把数值按Uniforms块中的格式存储到buffer,数据还在应用程序内存中,下一不就要把数据放到着色器中了

 

5.将buffer放入到着色器中

//创建uniform缓存对象

GLuint ubo;

glGenBuffers(1, &ubo);

glBindBuffer(GL_UNIFORM, uboSize, buffer, GL_STATIC_RAW);

//将缓存对象与块关联

void  glBindBufferBase(GLenum target, GLuint index, GLuint buffer)

返回值:void

参  数:target  关联的目标

index   Uniforms的缓冲索引

buffer  ubo缓存对象

glBindBufferBase(GL_UNIFORM_BUFFER, uboIndex, ubo);

 

参见:《OpenGL编程指南》第八版第2章

你可能感兴趣的:(OpenGL笔记,OpenGL笔记)