OPENGL ES 2.0 知识串讲 (3)——SHADER的功能GLSL语法(I)

news2024/9/26 5:23:58

更多图形知识请关注我的公众号:

在这里插入图片描述

在第一节中,我们介绍过 OpenGL ES 与 GLSL 的主要功能,就是往绘制 buffer 上绘制图片。其中虽然 GLSL 制作的 shader 是穿插在 OpenGL ES 中使用,但是我们在流程中可以看出来,两大 shader(vertex shader 和 fragment shader)相对于 OpenGL ES 其他模块还是比较独立的。这两个 shader 就好比两个函数一样,有输入,有输出。从 OpenGL ES 传入一些参数,在 shader 中进行运算,然后再传出给 GPU 的其他模块。这一节,我们来仔细研究一下 shader 的功能。然后从这一节开始,我们将一共用四节的内容,来说一下 GLSL 的语法和如何使用 GLSL 语言 书写 shader。

Shader 的功能

OpenGL ES 2.0 对应的 Shader 有两种,vertex shader 和 fragment shader。

Vertex shader 的输入为顶点相关的信息数据,输出分为两部分,必须输出的部分是该顶点最终显示在屏幕上的坐标信息,可选输出的是该顶点对应的其他信息(比如颜色、纹理坐标等)。Vertex shader 一次只能操作一个顶点。

Fragment shader 的输入为屏幕上像素点的信息(坐标以及坐标对应像素从 Vertex Shader 传过来的颜色、纹理坐标等信息)。Fragment shader 不能修改一个像素的位置,在操作一个像素点的时候,也不能访问旁边的像素点。Fragment Shader 通过对顶点信息进行操作,输出每个像素点的颜色。输出值用于更新绘制 buffer 中的 color buffer 或者其他目标 buffer。

Shader 文件看上去其实和一个普通的.c 或者.cpp 文件很像。有预处理,会定义一些变量,有 main 函数,会根据变量进行一些运算,并得到一些结果。

在 Vertex shader 中 main 函数中最终得到的结果是顶点坐标 gl_Position。

Fragment shader 结构和 VS 基本相同。在 main 函数中计算得到的最终结果是像素点的颜色值 gl_FragColor。

VS 和 PS 相似,但是又存在一些不同之处,里面存在着一些我们从未见过的关键词。从现在开始的四节内容,我们将来学习 GLSL 语法,以及如何使用 GLSL 语言编写 Shader。

GLSL 预处理

首先我们来看预处理。

Shader 中预处理的语法与我们熟悉的语法类似。Shader 支持如下这些预处理的方式。

如果一行中只有一个#,这种用法是支持的,但是这一行会被忽略掉。#前只能放置空格或者 tab,而在其后面如果有内容的话,则是指令。

#define 和#undef 与 c/c++中类似,都是用于定义宏定义。后面选择可以跟或不跟宏参数。

#if、#ifdef、#ifndef、#else、#elif、#endif 这些条件判断宏与 c++中也类似, 但是稍微有一些区别,在这些宏的后面只能跟随数字运算或者 define 定义的宏定义。未被 define 定义的识别符不会被默认为 0,如果使用它们会导致 error。不支持字母常量。

在操作符中,不支持连字符##或者 sizeof 等。

我们也稍微举几个和 c++一致的例子,比如&&与操作符,只有在左边不为 0 的时候才计算右边的运算。再比如||或操作符,只有在左边为 0 的时候,才进行右边的运算。没有参加运算的地方,如果使用到了一个未定义的标识符,不会报错。

预处理中的运算是在编译的时候进行。

#error 会将错误信息放到 shader 的 log 中,我们可以通过 OpenGL ES 的 API 获取 shader 的 log。#error 后面的所有信息,一直到新的一行的开始为止,都会 出现在 shader 的 log 中。存在#error 的 shader,我们会认为是一个错误的 shader。

#pragma 是用于通过它后面的参数,控制编译。比如它后面写的是 STDGL, 是用于限定指令不能以 STDGL 开头,因为这些已经被预留了。再比如它后面可以跟 optimize(on)或者 optimize(off),这个是用于在开发和调试 shader 的时候关闭 和开启优化使用的,必须用在 shader 的函数之外,默认情况下所有的 shader 的优化都是打开的。

pragma 后面还可以跟 debug(on)或者 debug(off),用于开启 debug 信息。必须用在 shader 的函数之外,默认情况下所有的 shader 的 debug 都是关闭的。

由于在 GLSL 预处理的时候就会进行一些检查,如果需要引入一些 extension, 那么就需要在早一些被引入。在预处理中,引入 extension 是通过了#extension 来引入,引入的时候#extension 后面跟上 extension 的名字或者跟上 all,all 的意思是编译器支持的所有 extension。然后,再在后面跟上冒号和 behavior。

当 behavior 为 required 的时候,就是指当前 shader 需要用到某个 extension, 如果指定的 extension 不支持,返回 error。

当 behavior 为 enable 的时候,就是指当前 shader 会打开某个 extension 的语法。比 require 的需求稍微弱一点,所以如果指定的 extension 不支持,返回 warn。

使用#extension all required 和#extension all enable 都会返回 error。

当 behavior 为 warn 的时候,也是打开了某个 extension 的语法,如果该 extension 定义了一些 warn 的情况,那么除非是在另外一个已经被 enable 或者 require 的 extension 支持,否则一旦遇到这些 warn 的情况,就会报 warn。如果 #extension 后面跟的是 all 的话,所有 extension 中定义的 warn 一旦遇到,就都会报 warn。如果指定的 extension 不支持,返回 warn。

当 behavior 为 disable 的时候,指定的 extension 就不被支持。如果指定的 extension 不支持,则返回 warn。如果#extension 后面跟的是 all 的话,那么所有 的 extension 规定的语法就都被 disable 了,仅使用 main spec 中的语法。

编译器的初始状态相当于执行了指令:#extension all:disable。指定当前 shader 按照 spec 规定的语法规则,并没有引入任何 extension。

#extension 是从底层控制 extension 的方式,一般指定哪些 extension 组合需要被支持。从顺序上说,后面的#extension 会覆盖前面的#extension。而#extension all 会覆盖前面所有的。不过只有 warn 和 disable 支持 all。

extension 的定义必须在所有非预处理语法之前,extension 的定义是有使用范围的,如果没有特殊指明,那么使用范围就是当前 shader。如果必要的话,linker可以提高 extension 的使用范围,从而扩展到所有包含的 shader 中。

每个 extension 都有对应的宏定义,该宏被定义在 extension 的实现中。所有在 shader 中,可以使用#ifdef 判断某个宏是否被定义了。如果被定义了,说明该宏对应的 extension 被引入了,那么就可以在代码中使用 extension 引入的语法, 如果没有被定义,则不能使用该 extension 引入的新语法。

#version 用于定义该 shader 使用语言的版本号。如果使用 GLSL100,那么这里就要在#version 后面写个 100。这个数值如果写小于 100,或者大于最新的 GLSL 的版本都不对。所有的 shader 中理论上都应该存在该字段,但是在 GLSL100 中, #version 100 这个预处理并不是必须的,因为默认 GLSL 的版本号就是 100。所以如果一个 shader 中没有写#version,那么就默认为是#version 100。

#version 必须写在一个 shader 的最前面,前面只能有注释或者空格。

#line 后面会跟一个常数,比如#line 10。执行了这个指令后,紧随其后的一行代码则被认为是 line 10。

#line 后面也可以跟两个常数,比如#line 10 100。执行了这个指令后,紧随其后的一行代码则被认为是 line 10。而且该行的第一个 string 的 number 会被认 为是 100。然后从该行往下,行号和 string number 都会递加,一直到下一个#line 指令。

上述所说的这些指令中,假如在一个 shader 中使用到了两个冲突的指令, 那么结果就是未定义。编译器有可能报错,也可能不报错,这个要看编译器的具体实现。

在 shader 中存在一些预定义的宏定义,比如 LINE,这个宏定义代表着其当前所在行的行数+1。

再比如 FILE,代表着当前文件的文件名。

VERSION 代表当前 GLSL 的版本,常用的就是 GLSL1.0 和 1.3,对应的 VERSION 就是 100 和 130。

GL_ES 在 ES 系统中会被定义成 1,这个主要是用于判断当前 shader 是否运 行在 ES 的系统中。

所有的以__开头或者以 GL_开头的宏定义都已经被保留了,也就是说开发者不可以定义这种规则的宏定义。

GLSL 注释

Shader 的注释语法,与我们熟知的 c、c++的语法完全一样。都是使用/**/ 和//来指明注释的。如果一个完整的注释完全表达在一行中,那么在编译器眼中, 它就是一个空格。

Shader 中除了预处理之外,还有变量,还有 main 函数等,这些语法我们将在之后的文章中进行详细讲解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/97505.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大学毕业生就业信息管理平台

开发工具(eclipse/idea/vscode等): 数据库(sqlite/mysql/sqlserver等): 功能模块(请用文字描述,至少200字): 系统在功能设计充分利用信息化技术和互联网的优势,建立一个以浏览器为用户工作界面,实现跨 平台…

Hive电子商务消费行为分析项目

文章目录数据说明环境准备项目代码上传数据文件并创建数据表数据清洗数据可视化客户分析交易分析门店分析评价分析数据说明 某零售企业的门店最近一年收集的数据 customer_details.csv:客户信息 transaction_details.csv:交易信息 store_details.csv:门店信息 store_review.c…

第1章 基础知识简介

🌞欢迎来到C语言的世界 🌈博客主页:卿云阁 💌欢迎关注🎉点赞👍收藏⭐️留言📝 🌟本文由卿云阁原创! 🌠本阶段属于练气阶段,希望各位仙友顺利完成…

【机器码】原码、反码、补码的学习

目录 让我们看看这三个码是什么 原码、反码、补码各自的范围 补码的加减运算 根据自己学习做的笔记来记录一下 原码、反码、补码,巩固自己的学习成果。 有符号数是由机器数和真值组合而成 真值:数值数据的实际值,带有-符号 …

RL 实践(3)—— 悬崖漫步【QLearning Sarsa 各种变体】

本文介绍如何用 QLeaning 系列和 Sarsa 系列表格方法解经典的悬崖漫步 (Cliff Walking) 问题完整代码下载:4_[Gym Custom] Cliff Walking (Q-Learning series and Sarsa series) 文章目录1. 悬崖漫步环境 (Cliff Walking)2. 使用 TD 方法求解2.1 Sarsa2.1.1 Sarsa 原…

kali 安装AWVS [赠附件]

前言 1.AWVS简介 AWVS(Acunetix Web Vulnerability Scanner)是一款知名的网络漏洞扫描工具,通过网络爬虫测试网站安全,检测流行的Web应用攻击,如跨站脚本、sql 注入等。据统计,75% 的互联网攻击目标是基于…

项目中遇到的错误

项目中遇到的错误swagger2 和 swagger3swagger 文档的注解springboot 版本问题SQL 关键字异常Apifox 的使用集中版本管理swagger2 和 swagger3 swagger2和 swagger3 需要导入的依赖 <dependency><groupId>io.springfox</groupId><artifactId>springfo…

LabVIEW FPGA中可重入和非可重入子VI的区别

LabVIEW FPGA中可重入和非可重入子VI的区别 LabVIEW FPGAVI默认是可重入的。如果多次调用重入VI&#xff0c;则每个实例会占用FPGA器件的单独硬件资源。如果使用非重入VI&#xff0c;无论是并行多次调用还是仅调用一次&#xff0c;都只会创建一个硬件实例并将其用于该VI。 ​…

最常用的 9 个JavaScript 函数与示例

输出内容才能更好的理解输入的知识 前言&#x1f380; 如果你想深入图形、可视化等领域&#xff0c;那么肯定离不开 canvas、webgl、three.js 等一系列技术。在这众多技术中&#xff0c;选择canvas2d为基础来入门是一个不错的选择。 canvas在动画、可视化、图片处理等领域有着…

物联网通信技术原理 第5章

目录 5.1 移动通信的基本概念及发展历史 5.1.1 移动通信的基本概念 5.1.2 移动通信的发展历史&#xff08;理解&#xff09; 1.第一代移动通信系统(1G) 2.第二代移动通信系统(2G) 3.第三代移动通信系统(3G) 5.1.3 移动通信的发展趋势与展望 5.2 无线传播与移动信道 5.2…

哈希的应用:布隆过滤器(C++实现)

文章目录1. 布隆过滤器1.1 背景1.2 概念1.3 控制误判率2. 实现布隆过滤器2.1 布隆过滤器类2.2 Set2.3 Test2.4 删除3. 优点4. 缺陷4. 缺陷1. 布隆过滤器 1.1 背景 位图&#xff08;bitmap算法&#xff09;告诉我们&#xff0c;想判断一个元素是否存在于某个集合中&#xff0c…

c语言指针 字符 字符串

1、sizeof 某个类型或者某个变量在内存中占用字节数。 例如&#xff1a;sizeof(int) ; sizeof(i)&#xff1b;都可以使用 2、运算符& 获取变量的地址。 int i; scanf("%d",&i); 输入变量时&#xff0c;必须使用&运算符。 &操作符只能应…

机器学习100天(二):002 数据预处理之导入数据集

机器学习 100 天,今天讲的是:数据预处理之导入数据集。 首先,我们打开 spyder。新建一个 load_data.py 脚本。 第一步,导入标准库 机器学习常用的标准库有 3 个: 第一个:numpy,用于数据处理。 第二个:matplotlib.pyplot,用于画图。 第三个:pandas,用于数值分析…

python 爬虫

前言 一、什么是爬虫 爬虫&#xff1a;&#xff08;又称为网页蜘蛛&#xff0c;网络机器人&#xff0c;在 FOAF 社区中间&#xff0c;更经常地称为网页追逐者&#xff09;&#xff1b;它是一种按照一定的规则&#xff0c;自动地从互联网上抓取对于我们有价值的网络信息的程序…

最强大的布局方案——网格Grid布局万字详解

Grid 布局又称网格布局&#xff0c;是W3C提出的一个二维布局系统&#xff0c;它与 Flex 布局有一定的相似性&#xff0c;都可以指定容器内部多个项目的位置。但它们也存在重大区别。Flex 布局是轴线布局&#xff0c;只能指定"项目"针对轴线的位置&#xff0c;可以看作…

jsp+ssm计算机毕业设计大学城二手书交易网站【附源码】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JSPSSM mybatis Maven等等组成&#xff0c;B/S模式 Mave…

绝对神器,今天教你如何识别图片上竖排的日语文字

在文字翻译或者其他的工作中我们经常遇到竖排的日语&#xff0c;有时候我们用普通的日语识别的软件根本无法完成 这个时候我们就需要一款可以识别竖排的日语工具&#xff0c;横排的我们很容易就能找到&#xff0c;但是竖排的就无能为力了 今天我们讲下如何识别竖排日语识别&a…

ZERO-SHOT:多聚焦融合

ZERO-SHOT MULTI-FOCUS IMAGE FUSION &#xff08;零镜头多焦点图像融合&#xff09; 多聚焦图像融合 (Multi-focus image fusion (MFIF)) 是消除成像过程中产生的离焦模糊的有效方法。The difficulties in focus level estimation and the lack of real training set for su…

计算机毕业设计springboot+vue文体用品商城网站

项目介绍 在当今社会的高速发展过程中,产生的劳动力越来越大,提高人们的生活水平和质量,尤其计算机科技的进步,数据和信息以人兴化为本的目的,给人们提供优质的服务,其中网上购买商品尤其突出,使我们的购物方式发生巨大的改变。而线上购物,不仅需要在硬件上为人们提供服务网上购…

ASPICE详细介绍-3.ASPICE有多少能力等级?

目录ASPICE有多少能力等级&#xff1f;9 个过程属性过程属性评定过程能力等级模型ASPICE有多少能力等级&#xff1f; ASPICE能力等级从0级到5级共分为6个层次&#xff0c;必须满足前一级别才可晋级下一个级别的评估。 【0级】Incomplete&#xff0c;未完成。 The process is…