虽然所知甚少,但康大的《GPU编程与Cg编程之阳春白雪下里巴人》确实带我入了shader的门,在里面我第一次清晰地知道了“语义”的意思,非常感谢。

入门shader,我觉得可以先读3本书:《GPU编程与Cg编程之阳春白雪下里巴人》=》《cg教程》=》《Real-Time Rendering 3rd》(在读,最近忙,搁下了),打下理论基础。

下面是《cg教程》的读书笔记。

1.基本cg函数

  1)数学函数:abs,acos反余弦,all(x)x的所有分量都不为0则为true,any(x)x有分量不为0则为true,ceil/floor,clamp(x, a,b),cross(A,B)/dot(A,B)叉乘与点乘,degrees/radians弧度角度互转,exp(x)e的x次方,exp2(x)2的x次方,fmod(x, y) x/y的余数(符号同x),frac取小数部分,lerp(a, b, f)f=0取a,f=1取b,否则权重混合,log/log2/log10基于e/2/10的对数,pow,round,sign(x)x>0返回1,否则0,saturate(x)把x限制到[0,1]之间,smoothstep(min, max, x)x<=min返回0,x>=max返回1,否则返回一个(0,1)的平滑值,step(a, b)a<=b返回1,否则0,mul矩阵相乘/向量矩阵相乘,sincos,transpose转置,noise噪声函数,lit(NdotL, NdotH, m)计算环境光系数x,漫反射系数y,高光系数z, w=1

  2)几何函数:distance点和点的欧氏距离,length向量的欧氏长度,normalize归1化,reflect(I, N)反射向量,可以根据pt-eyepos得到入射反向,然后算出反射向量,然后采样环境贴图,混合当前颜色和环境颜色,就可以在物体表面反射环境了,refract(I, N, eta)折射向量,可以根据pt-eyepos得到入射反向,然后算出折射向量,然后采用环境贴图并根据透明度插值物体颜色和环境颜色,然后就可以“透过物体”看到周围景色了

  3)纹理采样函数:1维tex1D,2维tex2D,3维tex3D和立方贴图采样texCUBE

2.cg程序

    opengl/dx的渲染管线和通常说的gpu渲染管线一样,我猜测其中的关系是:gpu里嵌入了opengl/dx库,来完成其渲染管线,所以其实是一个东西。

    那cg程序呢,cg程序一开始是文本形式,应用程序通过cg编译器把它翻译成中间形式,然后通过cg核心运行库把中间形式搞成最终形式,这个最终形式被运行库送入gpu中执行。cg也是这样嵌入应用程序的:应用程序使用cg编译器编译cg程序,然后调用cg核心运行库和cgGL/cgDXD库把程序送给驱动程序即可,不用和gpu直接打交道。

    下面是使用opengl接口的cg程序文本到gpu执行过程:

    

  cg程序是动态编译的,即应用程序可以动态运行上图 流程,更新在gpu里的cg最终形式,gpu会对每个输入的顶点执行上面的最终形式程序,即并行计算,片段类似。

  cg程序编译需要cg程序外,还需指定profile(顶点程序指定一个,片段程序指定一个),一个Cg profile定义了一个“被特定图形硬件或API所支持的Cg语言子集”,profile决定了你的cg程序能使用哪些cg功能,如果使用了不支持的功能就编译不通过,也决定了编译后的程序能在哪些硬件上运行,硬件不支持则不能运行。

  cg编译错误包括传统的错误和依赖profile的错误,前者就是语法错误,后者是cg程序使用了profile不支持的功能,后者或者可以考虑使用更高级的profile解决。

3.顶点着色器

Attributes:由 vertext array 提供的顶点数据,如空间位置,法向量,纹理坐标以及顶点颜色,它是针对每一个顶点的数据。属性只在顶点着色器中才有,片元着色器中没有属性。属性可以理解为针对每一个顶点的输入数据。OpenGL ES 2.0 规定了所有实现应该支持的最大属性个数不能少于 8 个。

Uniforms:uniforms保存由应用程序传递给着色器的只读常量数据。在顶点着色器中,这些数据通常是变换矩阵,光照参数,颜色等。由 uniform 修饰符修饰的变量属于全局变量,该全局性对顶点着色器与片元着色器均可见,也就是说,这两个着色器如果被连接到同一个应用程序中,它们共享同一份 uniform 全局变量集。因此如果在这两个着色器中都声明了同名的 uniform 变量,要保证这对同名变量完全相同:同名+同类型,因为它们实际是同一个变量。此外,uniform 变量存储在常量存储区,因此限制了 uniform 变量的个数,OpenGL ES 2.0 也规定了所有实现应该支持的最大顶点着色器 uniform 变量个数不能少于 128 个,最大的片元着色器 uniform 变量个数不能少于 16 个。

uniforms变量只是初始值和其他变量不同,它是允许被改变的(除非被const修饰),和其他变量一样。在unity的shader中,除了上述数据算uniforms外,开放出来的属性应该也算uniforms,比如经常有的纹理贴图等

Samplers:一种特殊的 uniform,用于呈现纹理。sampler 可用于顶点着色器和片元着色器。

顶点着色器的输出:

Varying:varying 变量用于存储顶点着色器的输出数据,当然也存储片元着色器的输入数据,varying 变量最终会在光栅化处理阶段被线性插值。顶点着色器如果声明了 varying 变量,它必须被传递到片元着色器中才能进一步传递到下一阶段,因此顶点着色器中声明的 varying 变量都应在片元着色器中重新声明同名同类型的 varying 变量。OpenGL ES 2.0 也规定了所有实现应该支持的最大 varying 变量个数不能少于 8 个。

在顶点着色器阶段至少应输出位置信息-即内建变量:gl_Position,其它两个可选的变量为:gl_FrontFacing 和 gl_PointSize。

4.片段着色器,类似顶点着色器。

5.顶点变换

投影变换是把眼空间变换到裁剪空间=》平截体四个角对应正方体四个角,各个点都右乘此矩阵,不在正方体内的剔除(-w<x<w,yz同),此时正方体不是单位正方体;

接着,透视除法,映射到单位正方体中,然后根据各个视口尺寸(相机尺寸)映射到视口对应位置【最后这个变换了解甚少,存疑】,这里还有个问题,裁剪是在裁剪空间搞的,还是在单位正方体中搞的?其实这个问题的答案未必重要,因为可能只是不同做法而已。我在一个前辈实现的光栅器代码中看到,他是在-w,w,w中剪切的,切完后进行透视除法,然后窗口变换,完成顶点变换。

6.应用程序传入的数据,也即shader能读到的数据(如果模型提供,比如法线),unityshader中应该有对应的全局变量或顶点分别数据

7.粒子系统,所谓把顶点视作粒子,然后控制粒子的位置、大小和颜色,可是正常而已,顶点是木有大小的,书中这点木有给出具体做法;粒子图是什么?是怎么使用的?疑惑。

8.顶点混合,和蒙皮一起理解,顶点就是皮肤网格上的点,矩阵就是骨头集,我们有基本姿势:基本姿势的矩阵集(全是单位矩阵),顶点集,矩阵集中各个矩阵对各个顶点的影响权重(一般不超过4个矩阵影响一个顶点)和 其他姿势信息:对应基本姿势矩阵集的新矩阵集,这个矩阵集很可能大部分和基本姿势对应的一样---单位矩阵。这样我们就能够表达多种动作了,矩阵集里的矩阵物理意义是变换:从基础姿势的位置变换到其他姿势的位置的变换。书中的那一段代码值得研究,有2个点需要思考:

  

为何model是3x4,即为何不是正常变换的4x4?估计:model在这里是用来计算顶点变换后的位置的,只需要xyz,而model的第4行只影响pos的第4个变量,所以可以略去,其实说白了,本来变换搞成4x4也是为了方便矩阵,这里抛弃无用信息而已。

为何只取model得一个角3x3去变换法线向量?首先因为是仿射变换所以用model而不是其逆的转置,再者,4x4的矩阵变换3维向量是可以直接用其3x3部分左乘向量的,因为:

| a1, a2, a3, a4|

| b1, b2, b3, b4|

| c1, c2, c3, c4|

| d1, d2,d3, d4|

向量normal扩展成4维后为(normal, 0),结果为4x1向量,此向量的前3维只受左上角3x3*normal影响,而第4维只是辅助计算,直接干掉,证毕。此结论可用于变换法线向量或其他向量。记住不要混淆了为什么选model和为什么选model的左上角的3x3!前者是选变换矩阵,后者是删繁就简直取本质。

环境映射

9.立方贴图纹理

  新的GPU支持立方贴图纹理(texCUBE可采样之),一个立方贴图是由6幅纹理组成的立方体,对其采样就像从中心发射的光线,穿透立方体的点即为采样所得,相当于在中心向外看所看到的颜色,一般用于环境映射。

  生成立方贴图:在场景中心放一个摄像机,朝左、右、上、下、前、后拍一张照片即可,摄像机在每个朝向的放置都是左右成90度,上下成90度,如下图:

  

  这样,6幅图紧凑在一起就能完全展示四周环境数据了。

  R = reflect(N, I),I是指向顶点的入射光线,返回值是从顶点发出的反射光线,N需要归一化,但I不需要,并且返回值R去查询纹理时也不需要归一化,因为I的长度不影响reflect返回R的方向,而R的长度不影响采样结果,并且,R在片段程序中采样之前先被光栅器插值,不归一化的R更能准确插值(有点像球面插值和椭球体面插值,为什么后者更准确就没研究了)。

  环境映射只依赖方向而不依赖位置,所以方向相同位置不同得到的采样结果是一样的,要想展现其斑驳之美,最好用于曲面。

  

  环境贴图通常需要在用世界坐标系的向量来采样,所以我们必须把N和I转到世界坐标系后reflect,【当然也可以先在模型坐标系reflect,再把reflect的结果转到世界坐标系,未测试】,向量变换的方法参考上面第8点。

  

  为什么用3x3去左乘,看上面第8点的证明就好。另外,这里我们假设position.w = 1(这也是正常情况下的取值),如果其w分量被特殊处理过导致不为1,我们需要先把其所有分量都除以w,来使得position.w=1,方便后面只进行3维的计算(默认w=1)

10.控制贴图

  用纹理去控制可变参数,这种想法被此书所推崇,不知是因其新颖还是确实是好,没研究过shader采样纹理和多纹理采样的性能。不过这种做法对美术而言确实好一些,比如环境贴图的例子,加一个控制纹理,对每个片段都给一个“反射系数”,然后就可以控制各点反射的效果了,这些系数就存在控制贴图中。其实法线贴图也算一种控制贴图:控制法向量。

11.折射,透过物体看环境:从视点到顶点发出光线,经物体折射后(只算进入物体的折射),采样环境贴图,done。

  refract(I, N, etaRatio);类似reflect,N需要归一化I不需要。

12.菲涅尔效果:进入眼睛的有反射光和折射光,书中给出了计算2者权重的近似公式:

  

  最终顶点的色值是:

  

  基本只是提供了一个更加准确的混合参数,比控制贴图、全局变量等更方便,但如果想应用开来,比如看水里的鱼等真实折射呢?书中木有提,难道要用光线跟踪,看折射的光线被什么反射了?疑惑。

 带颜色色散的菲涅尔效果:在顶点程序中,计算反射向量,计算菲涅尔系数,分别计算RGB3个分量对应的折射向量(因为折射系数不同),在片段程序中先得到反射的环境色值,然后得到RGB3个折射向量的色值,把这3个色值分别取RGB分量构成最终的折射色值,然后用菲涅尔系数混合反射色值和折射色值,done.

13.凹凸贴图。

  已成此文:深入理解法线贴图

14.性能方面,散入此文:写shader的一些小细节

15.附录

  技巧(Technique),“每个技巧描述一种实现效果,一个cg程序中可以有多个技巧,每个技巧针对一种级别的图形处理器”,所以这里的技巧类同于Unity shader的subshader,一般地unity shader都是一个或多个subshader+一个fallback,2+个技巧,以适应不同硬件。执行shader时,从第一个subshader开始,硬件不符合就执行下一个subshader,直到符合。

  过程(Pass),“一个技巧可以包括一个或多个过程,一个过程代表一个单独的渲染过程,其包括一组渲染状态(一些额外设置)和一组着色器,例如过程0可以只设置深度值,以便后面过程使用等”,这里的过程就是unity shader里的pass了,执行subshader时,有时候只用其中一个pass渲染,有时按顺序逐个应用各个pass渲染。默认好像是后者,存疑。

cg语言学习&&阳春白雪GPU编程入门学习的更多相关文章

  1. GPU 编程入门到精通(五)之 GPU 程序优化进阶

    博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识.鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程. 有志同道合的小伙 ...

  2. GPU 编程入门到精通(四)之 GPU 程序优化

    博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程.有志同道合的小伙伴 ...

  3. GPU 编程入门到精通(三)之 第一个 GPU 程序

    博主因为工作其中的须要.開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GPU 编程,因此在这里特地学习一下 GPU 上面的编程.有志同道合的小伙伴 ...

  4. 学习《零基础入门学习Python》电子书PDF+笔记+课后题及答案

    初学python入门建议学习<零基础入门学习Python>.适合新手入门,很简单很易懂.前一半将语法,后一半讲了实际的应用. Python3入门必备,小甲鱼手把手教授Python,包含电子 ...

  5. Java开发者必备的10大学习网站,送给入门学习java的你,请收下!

    作为开发者来说,必备的除了对编码的热情还要有自己的一套技巧,另外不可缺少的就是平时学习的网站.以下本人收集的 Java 开发者必备的网站,这些网站可以提供信息.以及一些很棒的讲座 , 还能解答一般问题 ...

  6. C、C++、Java到Python,编程入门学习什么语言好?

    摘要:回顾编程语言几十年来的兴衰起伏,似乎也折射了整个信息产业的变迁消亡,想要在技术的洪流里激流勇进,找准并学精一两门编程语言更加显得至关重要. 最近,TIOBE更新了7月的编程语言榜单,常年霸榜的C ...

  7. 【C/C++编程入门学习】C语言结构体硬核玩法分享,一切皆是数据!

    前言 对于结构体的应用太多了,今天这篇文章我主要为大家总结平时关于结构体的一些独特硬核小技巧,对于结构体更多优秀的编程表现,如果你对结构体的基础知识还不具备的话得回头看一下专栏教程或者自己找本书籍学习 ...

  8. CG标准函数库——数学函数(GPU编程与CG语言之阳春白雪下里巴人)

  9. 【C/C++编程入门学习】同样是数据类型,链表对比数组?哪一个更香?

    说起链表,第一反应:链表是一种数据类型!它可以用来存储同种类型多个批量数据.   有了这种认知,很容易去联想到数组,它也是一种数据类型,也可以用来存储同种类型的批量数据.初学者往往对数组的印象比较好, ...

随机推荐

  1. Spring Joinpoint

    如果用maven管理 则需要 <artifactId> aopalliance </artifactId> <artifactId> spring-aspects ...

  2. Efficient Knowledge Graph Accuracy Evaluation 论文笔记

    前言 这篇论文主要讲的是知识图谱正确率的评估,将知识图谱的正确率定义为知识图谱中三元组表述正确的比例.如果要计算知识图谱的正确率,可以用人力一一标注是否正确,计算比例.但是实际上,知识图谱往往很大,不 ...

  3. 设计实现SAM--无服务器应用模型

    Author:心谭 From:[Serverless]设计实现SAM--无服务器应用模型 Des: 专注算法与 web 开发的技术博客 什么是SAM? sam全称是:Serverless Applic ...

  4. 记Java中有关内存的简单认识

    一.Java内存划分 分为五个部分,可以参考这篇笔记简单认识一下: https://www.cnblogs.com/unleashed/p/13268027.html 栈 堆 方法区 本地方法栈 寄存 ...

  5. Nginx.conf参数配置详解

    Nginx的配置文件nginx.conf配置详解如下: user nginx nginx; #Nginx用户及组:用户 组.window下不指定 worker_processes 8; #工作进程:数 ...

  6. Vue 父子组件之间的互相调用方法

    第一种方法 直接在子组件中通过this.$parent.event来调用父组件的方法 父组件 <template> <div> <child></child& ...

  7. Jenkins(Extended E-mail Notification)邮箱配置正确但是并没有发送邮件

    废话 近期在把之前的接口自动化demo与jenkins集成,昨天发现了邮件配置正确但是没有发送邮件的问题,通过勾选系统设置 - >Extended E-mail Notification -&g ...

  8. 关于setTimeout的用法注意事项

    setTimeout setTimeout的定义:setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式. setTimeout的用法:setTimeout(代码片段,执行代码等待的毫 ...

  9. LeetCode 115.不同的子序列 详解

    题目详情 给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数. 一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串.(例如, ...

  10. 蒲公英 · JELLY技术周刊 Vol.17: 90 行代码实现 React Hooks

    蒲公英 · JELLY技术周刊 Vol.17 React Hooks 相信大家都不陌生,自被设计出以来就备受好评,在很多场景中都有极高的使用率,其中原理更是很多大厂面试中的必考题,很多朋友都能够如数家 ...