GLSL-语法基础

3.1 字符集

GLSL用到的字符集是ASCII码的子集,主要包含下面的几部分:

  1. 字母 a-z,A-Z,以及下划线“_”。
  2. 数字 0-9
  3. 符号 .+-/*%<>[]{}()|$~=!:;,?
  4. 预处理器专用符号 #
  5. 空白符,包括各种回车、换行、TAB等等

该字符集不包含反斜杠**,也不包含任何字符或字符串。 一般来说,GLSL是大小写敏感的。

3.2 Source Strings

不管是Vertex Shader还是Fragment Shader,其实都有一段对应的代码,称为Source Strings,它包含多个string,每个string一行。

对于本版本的GLSL来说,每个Shader对应着一个可编程单元,一个Vertex Shader对应着一个Fragment Shader,二者连接起来组成一个program

3.3 编译的逻辑阶段

编译程序使用C++标准的一个子集。 Vertex Shader和Fragment Shader在各自编译之后连接到一起。

编译的步骤如下:

  1. .glsl文件中得到Source String
  2. Source Strings被转成一系列的预处理Token,这些Token包括预处理行号、预处理标识、预处理操作。注释被替换成空格。保留换行。
  3. 预处理器执行。执行命令,执行宏扩展。
  4. 预处理Token被转换成Token
  5. 空白符、换行符被丢弃。
  6. 根据GLSL语法进行语法分析。
  7. 根据GLSL语义规则进行语义检查。
  8. Vertex Shader和Fragment Shader连接到一起,丢弃没有被二者同时使用的varying变量。
  9. 生成二进制文件。

3.4 预处理器

作为编译的其中一个步骤,预处理器会处理source strings。

预处理指令以#开头,#号之前不能有除了空白字符之外的任何字符。每一个指令独占一行。 预处理指令只能使用上面列出的指令,使用其他未定义指令会报错。

完整的预处理器指令如下:

#
#define
#undef
#if
#ifdef
#ifndef
#else
#elif
#endif
#error
#pragma
#extension
#version
#line

下面的这个操作符也是合法的:

defined

#define and #undef

这两个和C++中的用法完全一样,不做过多说明

#if, #ifdef, #ifndef, #else, #elif, and #endif

这几个也基本和C++的用法一样,不过有下面几点差别。

1. #if和#elif只能判断int类型,0为false,非0为true
2. 不支持string类型

#pragma

编译指示。用来控制编译器的一些行为。

#pragma optimize(on)
#pragma optimize(off)

在开发和调试时可以设置为off,默认设为on。

#pragma debug(on)
#pragma debug(off)

在开发和调试时可以打开debug选项,以便获取更多的调试信息。默认设为off。

#version

每一个编译单元都要指定GLSL的版本,如下:

#version number

一般默认即可。这个命令必须放到编译单元的最前面,其前面只能有注释或空白,不能有其他字符。

#extension

如果想使用GLGL默认不支持的操作,则必须启用对应的扩展,启用一个扩展可以使用下面的命令:

#extension : behavior
#extension all : behavior

其中,extension_name是扩展的名称,all是指所有的编译器支持的扩展。 behavior是指对该扩展的具体操作。比如启用、禁用等等。详情如下:

behavior

作用

require

启用该扩展。如果不支持,则报错。

enable

启用该扩展。如果不支持,则会警告。extension_name是all的时候会报错。

warn

启用该扩展。但是会检测到所有使用该扩展的地方,提出警告。

disable

禁用该扩展。如果该扩展不被支持,则提出警告。

默认是不会启用任何扩展,就像是执行了下面的命令:

#extension all : disable

对于每一个被支持的扩展,都有一个对应的宏定义,我们可以用它来判断编译器是否支持该扩展。

#ifdef OES_extension_name
#extension OES_extension_name : enable
    // code that requires the extension
#else
    // alternative code
#endif

一些预定义的变量

除此之外,还预定义了一些变量:

  • __LINE__ :int类型,当前的行号,也就是在Source String中是第一行
  • __FILE__ :int类型,当前Source String的唯一ID标识
  • __VERSION__ :int类型,GLGL的版本
  • GL_ES :对于嵌入式系统(Embed System,简称 ES),它的值为1,否则为0

所有的以两个下划线__开头的变量都是系统保留的,不允许私自定义和篡改。

操作符

优先级

类型

操作符

结合性

1

括号

()

2

一元运算符

defined + - ~ !

| 从右往左 | | 3 | 乘除法 | * / % | 从左往右 | | 4 | 加减法 | + - | 从左往右 | | 5 | 位运算 移位 | << >> | 从左往右 | | 6 | 大小关系 | < > <= >= | 从左往右 | | 7 | 相等性判断 | == != | 从左往右 | | 8 | 位运算 与 | & | 从左往右 | | 9 | 位或算 非 | ^ | 从左往右 | | 10 | 位或算 或 | | | 从左往右 | | 11 | 逻辑与 | && | 从左往右 | | 12 | 逻辑或 | || | 从左往右 |

defined操作符一般有下面两种使用方式:

defined identifier
defined ( identifier )

注释

和Java里的注释没啥区别。主要有下面两种注释方法。

// 我是注释
/* 我是注释 */

Tokens

Source String会被转成一系列的Tokens。可以这么理解,代码中的每一个单词都属于某一种Token。比如关键词、数字、变量等等。GLSL中,Token主要有下面几种:

Token

解释

举例

keyword

关键词

if elif ...

identifier

标识符

自定义的函数名、变量名 ...

integer-constant

int常量

1 2 3 ...

float-constant

float常量

1.1 1.2 1.3 ...

operator

操作符

+ - * / ...

关键词

列举一下GLSL中的关键词,这些全部是系统保留的,不可私自篡改。

attribute const uniform varying
break continue do for while
if else
in out inout
float int void bool true false
lowp mediump highp precision invariant
discard return
mat2 mat3 mat4
vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4
sampler2D samplerCube
struct

下面的这些也被系统预留了,使用它们会报错。

asm
class union enum typedef template this packed
goto switch default
inline noinline volatile public static extern external interface flat
long short double half fixed unsigned superp
input output
hvec2 hvec3 hvec4 dvec2 dvec3 dvec4 fvec2 fvec3 fvec4
sampler1D sampler3D
sampler1DShadow sampler2DShadow
sampler2DRect sampler3DRect sampler2DRectShadow
sizeof cast
namespace using

除此之外,所有的以"__"开头的变量全部是预留的,自定义的变量不能以“__”开头。

标识符

标识符其实就是指用户自定义的变量名、函数名、结构体名等等。

变量名可以由下面的字符组成:

_ a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9

用户可以随便定义标识符,但是不能以gl_开头,因为以gl_开头的都是系统预留的。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Porschev[钟慰]的专栏

Nodejs学习笔记(十四)— Mongoose介绍和入门

简介   Mongoose是在node.js异步环境下对mongodb进行便捷操作的对象模型工具   那么要使用它,首先你得装上node.js和mongodb,...

3616
来自专栏丑胖侠

《Drools7.0.0.Final规则引擎教程》第3章 3.2 KIE概念&FACT对象

3.2.1 什么是KIE KIE(Knowledge Is Everything),知识就是一切的简称。JBoss一系列项目的总称,在《Drools使用概述》章...

2185
来自专栏逆向技术

异常处理第二讲,结构化异常(微软未公开)

            异常处理第二讲,结构化异常(微软未公开) 讲解之前,请熟悉WinDbg的使用,工具使用的博客链接 一丶认识段寄存器FS的内容,以及作用 ...

1867
来自专栏魂祭心

原 Data Access Compone

3366
来自专栏JetpropelledSnake

Python面试题之Python面试题汇总

(1)与java相比:在很多方面,Python比Java要简单,比如java中所有变量必须声明才能使用,而Python不需要声明,用少量的代码构建出很多功能;...

1.5K3
来自专栏程序员的SOD蜜

AdoHelper使用MySQL存储过程示例

AdoHelper是MS DAAB中的一个抽象数据访问类,由它派生出的SqlHelper使用很广泛,PDF.NET数据开发框架内部按照AdoHelper的接口做...

2095
来自专栏Golang语言社区

一个go语言实现的短链接服务

一个go语言实现的短链接服务 首先,博客转移到 wusay.org 了,去年没交费,博客都没了。。。刚刚恢复过来,以前的文章没了,慢慢补吧。 什么是短链接服务 ...

45210
来自专栏mukekeheart的iOS之旅

iOS学习——@class和#import的区别

  在iOS开发过程中,我们在一些源码中经常会看到导包的时候有的用#import进行导包,但是有的的时候也会看到用@class进行导包,那么这两种方式有什么区别...

30511
来自专栏张学林的专栏

手 Q 红包工程师过去一年踩过的坑

工作中遇到的一切,几乎都是从0开始,故难免会走很多弯路,也曾踩过无数的坑,欢迎各位解答。若有异议,欢迎拍砖。

2970
来自专栏LinXunFeng的专栏

iOS - 揭露Block的内部实现原理

1255

扫码关注云+社区