cg语言学习&&阳春白雪GPU编程入门学习

虽然所知甚少,但康大的《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渲染。默认好像是后者,存疑。

时间: 2024-11-05 18:35:51

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

OPENSSL编程入门学习

相关学习资料 http://bbs.pediy.com/showthread.php?t=92649 https://www.openssl.org https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=0CDoQFjAD&url=http%3a%2f%2fidning-ebook%2egooglecode%2ecom%2fsvn%2ftrunk%2fopenss

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

博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识.鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程. 有志同道合的小伙伴,欢迎一起交流和学习.我的邮箱: [email protected] .使用的是自己的老古董笔记本上面的 Geforce 103m 显卡,尽管显卡相对于如今主流的系列已经很的弱,可是对于学习来说.还是能够用的.本系列博文也遵从由简单到复杂,记录自己学习的过程. 0. 文件夹 GPU 编程入门到精通

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

博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程.有志同道合的小伙伴.欢迎一起交流和学习,我的邮箱: [email protected] . 使用的是自己的老古董笔记本上面的 Geforce 103m 显卡,尽管显卡相对于如今主流的系列已经很的弱.可是对于学习来说,还是能够用的.本系列博文也遵从由简单到复杂.记录自己学习的过程. 0. 文件夹 GPU 编程入门到精通

Python编程入门学习:最常见加密方式和Python实现

前言 我们所说的加密方式,都是对二进制编码的格式进行加密的,对应到Python中,则是我们的Bytes. 所以当我们在Python中进行加密操作的时候,要确保我们操作的是Bytes,否则就会报错. 将字符串和Bytes互相转换可以使用encode()和decode()方法.如下所示: # 方法中不传参数则是以默认的utf-8编码进行转换In [1]: '南北'.encode()Out[1]: b'\xe5\x8d\x97\xe5\x8c\x97'In [2]: b'\xe5\x8d\x97\xe

[书籍推荐] 编程入门学习必备

编程入门书籍推荐 (均为中文版,如果你不喜欢教材那种言而又止.厌倦不透彻的学习,请看以下书籍吧) 只推荐我看过的... Ps. 如果你想让 孩子或自己 边玩边学编程,请下载这个谷歌教育团队开发的免费游戏 https://store.steampowered.com/app/929860/Game_Builder/(同样适用于中学生模拟机器人竞赛选手,还能顺便学点计算机相关的英语) 1.  C语言 C primer plus 中文版 非常详细的入门书,偏重于代码和思想方面.是一种极度推崇的入门书,

Spring阶段性学习总结(十)AOP编程入门学习之动态代理实现代码的优化

1 public interface Calculator { 2 int add(int i, int j); 3 int sub(int i, int j); 4 int mul(int i, int j); 5 int div(int i, int j); 6 } 1 public class CalculatorImp implements Calculator{ 2 /* 3 * 如果在每个方法中添加想赢的输出日志,那么维护起来相当困难,而且, 4 * 代码结构也破坏了,掺杂了多余的非

Lua编程入门-学习笔记1

第1章:起点 Chunks: 语句块 每个语句结尾的分号是可选的,如果同一行有多个语句最好使用分号分隔: dofile("lib1.lua")  -- 执行lua文件 全局变量:局部变量用local修饰,否则就是全局变量 保留字: if then else elseif end and or not fuction return end true false nil while ... do .. end    break repeat ... until ... for ... in

如何选择编程入门语言?一个让很多人纠结的问题

用心分享,共同成长 没有什么比你每天进步一点点更实在了 本文已经收录至我的github,欢迎大家踊跃star 和 issues. https://github.com/midou-tech/articles 点关注,不迷路!!! 为什么会出这样一篇文章? ?本来想写点最近学习的东西,但是最近好多粉丝再问我一个问题. image-20200105164345384 于是我决定把我这个过来人的一些建议给到你们,希望大家少一些不必要的迷茫,多花点时间去学习该学习的东西,去做一些重要的事情. 刚开始学习

【python】入门学习(十)

#入门学习系列的内容均是在学习<Python编程入门(第3版)>时的学习笔记 统计一个文本文档的信息,并输出出现频率最高的10个单词 #text.py #保留的字符 keep = {'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',' ','-',"'"} #将文本规范化 def normalize(s): "