美文网首页
着色器基础

着色器基础

作者: Dragon_boy | 来源:发表于2020-08-08 15:39 被阅读0次

GLSL

变量类型与声明

GLSL常见的基本数据类型为:

类型 描述
float IEEE32位浮点数
double IEEE64位浮点数
int 有符号二进制补码的32位整数
uint 无符号的32位整数
bool 布尔值

表格中的类型都是透明类型,即内部形式是暴露出来的,与之对应的是不透明类型,包括采样器(sampler)、图像(image)、原子计数器(atomic counter)。

注意,GLSL中的所有变量都必须在声明时就进行初始化。

GLSL中,变量隐式强制转换是很严格的,通常能直接进行强制转换的类型为:

所需类型 可以从这些类型隐式转换
uint int
float int、uint
double int、uint、float

不过可以通过转换构造函数来显示强制转换,如int()、float()、double()、uint()、bool()以及它们的矢量vec和矩阵mat。

对于复合类型,GLSL的支持为:

基本类型 2D矢量 3D矢量 4D矢量 矩阵类型
float vec2 vec3 vec4 mat(2,3,4)...mat(4x2,4x3,4x4)
double dvec2 dvec3 dvec4 上行加前缀d
int ivec2 ivec3 ivec4
uint uvec2 uvec3 uvec4
bool bvec2 bvec3 bvec4

对于复合类型,GLSL可以使用分量访问,支持分量:

符号访问符 符号描述
x、y、z、w 位置相关
r、g、b、a 颜色相关
s、t、p、q 纹理坐标相关

也可以使用数组形式访问,使用[ ]和[ ][ ]。

GLSL可以声明结构体:

struct Particle{
    float lifetime;
    vec3 position;
    vec3 velocity;
};

用法和C语言类似。

GLSL还支持任意类型的数组,其用法和C语言类似。比如:

float coeff[3];
float[3] coeff;
int indices[];

还可以使用构造函数:

float coeff[3] = float[3](2.38, 3.14, 42.0)

数组有一个内置方法length(),即数组长度,该方法矢量和矩阵也可以使用,分别对应矢量分量数和矩阵列数。

对于变量,GLSL有一些限制符:

类型修饰符 描述
const 变量定义为只读形式
in 设置为输入变量
out 设置为输出变量
uniform 设置为统一变量,通过应用程序传递给着色器
buffer 设置应用程序共享的一块可读写的内存,这块内存也可作为着色器中的存储缓存
shared 设置变量是本地工作组中共享的,只能用于计算着色器

对于uniform变量,在应用程序中使用下面的方法传递值:

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

该方法返回程序中着色器变量name对应的索引值。

然后使用下面的方法根据位置值传递变量:

void glUniform{1234}{fdi ui}(GLint location, TYPE value);
void glUniform{1234}{fdi ui}(GLint location, GLsizei count, const TYPE* values);
void glUniformMatrix{234}{fd}v(GLint location, GLsizei count, GLboolean tranpose,
const GLfloat* values);
void glUniformMatrix{2x3, 2x4, 3x2,3x4,4x2,4x3}{fd}v(GLint location, GLsizei count, 
GLboolean tranpose, const GLfloat* values);

流控制与循环

GLSL可以使用if-else和switch,这点和C语言一致。

GLSL支持C语言形式的for、while和do..while循环。

除此之外,GLSL还支持一些控制语句:

  • break
  • continue
  • return
  • discard:丢弃当前的片元,终止着色器的执行,该语句只在片元着色器中有效。

函数

GLSL的函数语法和C语言类似:

returnType functionName([accessMidifier] type1 variable1,
                        [accessMidifier] type2 variable2,
                        ...){... return returnValue;}

函数名可以是任何名字,但不能使用数字、连续下滑线和gl_开头。

注意,使用函数前,必须声明函数原型或者直接给出函数体。

函数的参数可以使用下面的访问修饰符:

访问修饰符 描述
in 将数据拷贝到函数中,这是默认的
const in 将只读数据拷贝到函数中
out 从函数中获取数值
inout 将数据拷贝到函数中,并且返回函数中修改的数据

计算不变性

GLSL无法保证在不同的着色器中,两个完全相同的计算式会得到完全一样的结果。GLSL有两种方法来确保着色器之的间计算不变性,即invariant或precise关键字。

invariant限制符可以设置任何着色器的输出变量,可以确保如果两个着色器的输出变量使用了同样的表达式,并且表达式中的变量也是相同值。调试过程中,还可以将所有变量都设置为invariant,可在顶点着色器中这么写:

#pragma STDGL invariant(all)

precise限制符可以设置任何计算中的变量或者函数的返回值,它可以增加计算的可复用型,这通常在细分着色器中使用。

预处理命令

GLSL的预处理命令包括:

预处理命令 描述
#define,#undef 控制常量和宏的定义,与C语言类似
#if,#ifdef,#ifndef,#else,#elif,#endif 代码的条件编译,与C语言类似。只能使用整数表达式或者#define定义的值
#error text 强制编译器将text内容(直到第一个换行符)插入到着色器的信息日志中
#pragma options 控制编译器的特定选项
#extension options 设置编译器支持特定GLSL扩展功能
#version number 设置当前使用的GLSL版本名称
#line options 设置诊断行号

对于宏定义,GLSL和C语言类似,只是不支持字符串资环以及预编译连接符,可以定义单一的值,也可以带参数:

#define NUM_ELEMENTS 10
#define LPos(n) gl_LightSource[(n)].position

GLSL还有一些预先定义好的宏,可以使用#error输出:

  • __LINE__:行号,默认为已处理的换行符个数加一
  • __FILE__:当前处理的源字符串编号
  • __VERSION__:OpenGL着色语言版本的整数表示形式

#pragma可以定义一些编译器选项:

#pragma optimize(on)\(off)  //编译器优化选项 ,默认开启
#pragma debug(on)\(off)  //编译器调试选项,默认关闭

数据块

类似于结构体声明,GLSL可以将uniform,in,out等变量声明在一个块中:

uniform Matrices{
    mat4 Model;
    mat4 View;
    mat4 Projection;
}(name);

注意,只能在uniform块中声明透明数据类型。

uniform块可以使用不同的布局方式,有以下布局限制符:

  • binding = N:设置缓存的绑定方式
  • shared:设置uniform块是多个程序间共享的,这是默认方式
  • packed:设置uniform块占用最小的内存空间,禁止程序间共享
  • std140:使用标准布局方式来设置uniform块或buffer块
  • std430:使用标准布局方式来设置uniform块
  • offset=N:强制设置成员变量位于缓存的N字节偏移处
  • align = N:强制设置成员变量的偏移位置是N的倍数
  • row_major:使用行主序的方式来存储uniform块中的矩阵
  • column_major:使用列主序的方式来存储uniform块中的矩阵,这是默认地

可以这么使用限制符声明块:

layout(shared, row_major) uniform{...};

多个限制符用逗号隔开。

对于uniform块,应用程序可以这么与变量进行关联:

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

该方法会返回某一uniform块的索引值。

如果要初始化uniform块对应的缓存对象,那么就需要使用glBindBuffer()将缓存对象绑定到GL_UNIFORM_BUFFER上。接着使用glGetActiceUniformBlockiv(),将参数设为GL_UNIFORM_BLOCK_DATA_SIZE来得到编译器分配的块的大小。接着可以用下面的方法将缓存对象与索引对应的块相关联:

void glBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, 
GLsizeiptr size);
void glBindBufferBase(GLenum target, GLuint index, GLuint buffer);

我们还可以在调用glLinkProgram前调用下面的函数来显式地控制一个uniform块的绑定方式:

GLuint glUniformBlockBinding(GLuint program, GLuint uniformBlockIndex,
GLuint uniformBlockBinding);

buffer块声明如下:

buffer BufferObject{
    int mode;
    vec4 points[];
};

它与uniform块类似。不过,着色器可以写入buffer块,修改其中的内容并呈现给其它的着色器调用或者应用程序本身。其次,可以在渲染之前再决定它的大小,而不是编译链接的时候。注意,只支持std430布局。

in、out块类似:

out Lighting{
    
};
in Lighting{

};

名称在着色器间必须匹配。

着色器编译

着色器对象创建和编译的过程为:

  • 创建一个着色器对象
  • 将着色器对象关联到着色器程序
  • 链接着色器程序
  • 判断着色器的链接过程是否成功完成
  • 使用着色器来处理顶点和片元

创建着色器对象:

GLuint glCreateShader(GLenum type);

type为几种shader中的一种。

接着将着色器源代码关联到对象上:

void glShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length);

string用来表示源代码字符串。

编译着色器源代码:

void glCompileShader(GLuint shader);

然后可以使用glGetShaderiv(),参数设为GL_COMPILE_STATUS来判断编译是否正确。然后可以使用下面的方法来获得日志信息:

void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, char* infoLog);

创建完必要的着色器对象后,通常是一个顶点+一个片元,就创建程序:

GLuint glCreateProgram(void);

然后关联到着色器对象上:

void glAttachShader(GLuint program, GLuint shader);

想要移除着色器对象,使用:

void glDetachShader(GLuint program, GLuint shader);

之后可以连接对象生成程序:

void glLingProgram(GLuint program);

可以使用glGetProgramiv(),参数设为GL_LINK_STATUS来判断链接是否成功,接着可以使用下面的方法获得日志信息:

void glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei* length, char* infoLog);

最后调用该程序来运行着色器代码:

void glUseProgram(GLuint program);

在着色器对象完成任务后,可以删除它:

void glDeleteShader(GLuint shader);

不再使用程序后,也可以删除:

void glDeleteProgram(GLuint program);

我们可以使用下面的方法来判断着色器对象和程序是否存在:

GLboolean gllsShader(GLuint shader);
GLboolean gllsProgram(GLuint program);

相关文章

  • WebGL编程指南入门基础篇

    WebGL编程指南入门基础篇 着色器介绍 着色器分为顶点着色器 跟 片元着色器 顶点着色器描述顶点特性,包括位置,...

  • OpenGL ES基础知识回顾

    OpenGL ES基础知识回顾 GLSL着色器语言 语言基础 绘制方式 纹理映射 一、GLSL着色器语言 渲染管线...

  • OpenGL03 — 金字塔、六边形、圆环的绘制

    01:基础了解 投影方式:正投影2D,透视投影3D 存储着色器分类: 单元着色器、平面着色器 、上着色器、默认光...

  • Unity Shader技术入门(1)

    一、着色器基础1.什么是着色器,简介着色器(Shader),是一款运行在GPU->Graphic Processo...

  • Godot官方文档Shader部分原创翻译

    着色器基础,原文地址Docs » Shading » Shading reference » Shaders Go...

  • GLSL语言

    什么是GLSLGLSL(OpenGL Shading Language)OpenGL着色器语言。 GLSL基础类型...

  • OpenGLES开发基础(二、创建和使用着色器)

    在上一节的文章中,我们对着色器的基础有一些基础的认识。学习到这里,下面我们挽起裤管就是干,看看着色器在开发中的应用...

  • 着色器基础

    1. OpenGL着色器语言变量存储限定符 普通本地变量,外部不可以访问,外部不可见。const ...

  • 着色器基础

    GLSL 变量类型与声明 GLSL常见的基本数据类型为: 类型描述floatIEEE32位浮点数doubleIEE...

  • LibGDX图形模块之着色器

    如果你想使用OpenGL ES 2.0,你应该知道一些着色器的基础知识。 Libgdx附带了一个标准着色器,它将通...

网友评论

      本文标题:着色器基础

      本文链接:https://www.haomeiwen.com/subject/ueecdktx.html