C for Graphic:语言(一)

版权声明: https://blog.csdn.net/yinhun2012/article/details/81977951

       这一篇专栏主要是详细讲解CG语言的,类似市面上那种c# step by step,c++ 从入门到入土,mysql从删库到跑路什么的最基础的讲解,不同于市面上专业书籍的是:在学习理解CG的同时,我会掺杂一些个人见解,主要是为了更加形象的表达。

       首先学习任何知识都是从最基础的开始,比如我学习c#的时候,就是先买了一本叫c# step by step的二手书,大概400面左右,看完后写了很多测试代码,把c#的什么基础数据定义啊,数据结构,面向对象特性啊都用了一遍,接着把什么泛型啊,反射啊,委托lambda啊也用了一遍。接着买了一本叫c#高级编程的书,把什么正则啊,线程啊,unsafe指针啊,gc啊也用了一遍。最后对c#网络编程有点兴趣,顺便买了本visual c#网络编程,了解了c#对tcp udp http的封装,然后把线程同步,异步等简单的服务器客户端实现了一遍,同时测试了多种数据协议传输和自己的二进制加解码传输等。当然学习c/c++的时候也是一样,在学校的时候就是看的c酒红皮书,c++从入门到精通,也是写完那些测试单元,再上c++ primer,不过后面工作就封装库和学ue4的用上了。这里想表达的是,学习一个知识最方便最舒爽的途径就是从最简单的开始,依次提升,不仅轻松,还能激发成就感。假如一开始就从高难度切入,估计一下子就萌币了。比如我看很多开发人员,特别是图形学开发,一开始就是google国外纯英文资料,上opengpu什么的论坛,直接“高端切入”,瞬间感觉茫然了。这里要说一下,首先国内互联网it开发本身发展时间就很短,其次图形学这块发展就更短了,图形学开发人员要是和A股金融市场上那些好高骛远的企业大佬一样,总想着弯道超车,估计很难达到目标。

      so,就让我们从最基本的CG语言开始学起,跟一个呱呱坠地的小娃娃一样,从牙牙学语开始。

      首先说一下CG语言具体是什么,CG语言全称C for Graphic language,是一种运行在图形硬件上的开发语言,不同于我们平时学习的c或c#,虽然CG也是用c去开发,但是编译器和执行器是专门为了图形硬件设计的,也就是说c/c#编译执行在CPU上,CG则是编译执行在GPU上,因为现代PC,图形硬件(显卡)比CPU更加重要,看价格就知道了一目了然,nvidia和ati的显卡比相当档次的cpu贵啊,其实早在几十年前,可编程图形显卡和开发语言就有了(具体可以看之前图形学理论的博客),比如opengl的glsl和directx的hlsl,但是这两大阵营后面都差不多对立了,搞得开发人员还得掌握两种开发语言和API,烦不烦?好在Nvidia公司自己单独创造出能够运行在所有图形硬件上的通用着色器语言CG(当然其竞争对手ATI最后也不得不妥协,显卡硬件中也支持CG),而作为unity开发的我们,刚好能通过CG创建shader,这真的是一件很舒服的事情。 估计CG让图形学开发者感受到了当年fortran对比汇编带来的让人愉快的编程体验。

      前面我们谈到了渲染流程,其中关键的顶点变换和片段着色,这两个过程必须通过特定程序去操作解决,CG就是为了达到这个目的而设计的程序语言(当然CG不仅仅只为了达到这个目的,CG的功能比想象的更强大)。CG语言有一个名叫“profile”的概念,这个profile当初让我困惑过,官方的解释是一个“profile”对应一个特定的图形硬件,编译你的CG代码时必须指定一个profile,后面我的理解就是,因为图形显卡类别实在是太多了,首先就有两大厂商Nvidia和ATI,然后显卡构架GCN/Tahiti/Cape Verde/Hawaii/Tesla/Fermi/Kepler/Maxwell/Volta,最后显卡核心也是一大堆,那么总不可能每次出一种新显卡,就打包一整套支持所有显卡运行的CG API runtime到每个用户吧,所以跟.net或者jvm虚拟机的版本一样,cg runtime跟硬件绑定的版本也是多种多样,除了最顶层接口层必须通用之外,内部的实现比如寄存器指令集肯定会有所区别,最后就变成了,开发shader程序后,发布出去运行指定一个大众用户能满足的运行环境。

     当然Nvidia公司强大的实力保证了未来的profile是现有的profile超集,就跟c++与c的关系一样,那么现有的profile可以直接在未来的超集profile中运行,至少不会经过太大的修改。这就保证了图形开发者不用那么麻烦的去做版本升级了。

     可编程顶点处理器:

     前面我们学习过渲染流水线中顶点变换这个步骤,那么可编程顶点处理器就给予我们使用代码去处理这个步骤的能力,这个处理函数的实际参数就是接收每个顶点的属性(比如位置、颜色、纹理坐标等),然后经过我们的代码处理过后(这些处理包括使用矩阵进行仿射空间变换,或者使用函数比如三角函数,m元n次方程等修改坐标值等),最后将处理过后的数据比如世界空间坐标系中的位置,法向量,顶点颜色等信息传递出去(具体传递到的位置就是片段处理函数)。最后是最关键的一步,虽然cg并不抛出特定函数需要我们代码处理,那就是光栅化过程。为什么如此重要呢?假如我们的顶点元数据就是一个三角形,那么就只有三个顶点经过我们的顶点函数代码处理,但是如果我们的三角形大小覆盖整个显示器屏幕呢?那么就需要经过插值生成顶点与顶点之间所有的“临时顶点”(具体意义参考图形学原理中顶点光栅化的讲解),这个时候才是完完整整的将所有的顶点数据生成并提交给片断处理函数了(其实目的就是为了顶点与片段函数的输入和输出对应)。

    顶点处理函数在我看来,常用的处理目的有两类:

    ①.利用矩阵处理最原始的顶点P到各个仿射空间的变换,比如M矩阵变换到模型空间,接着V矩阵变换到视口空间,P变换到投影空间,同时也可以处理围绕顶点P的其他相关数据比如法向量,毕竟法向量对我们后续操作起到很重要作用,后面我要写光线追踪,法向量是很关键的数据之一。

    ②.使用各种处理函数,比如旋转矩阵处理旋转运动,三角函数sin cos处理周期运动,线性方程处理线性运动,插值函数处理插值运动等。

    可编程片段片段处理器:

    片段这个词最开始也让我困惑,资料上让我们暂时理解为像素(后面综合看了一些资料后,理解到片段其实就是像素的“原材料”,经过一些处理后片段就变成实际显示到屏幕上的像素,这个关系我在图形学原理中讲到过,不清楚或者忘记了的可以返回去看下),那么片段编程处理器也就提供了处理显示屏上每个像素的函数,这个函数的作用就是提供给我们开发者具体的处理像素颜色值。比如我们想让一个三角形显示红色,那么从顶点函数得到的三角形所有栅格化的数据后,将片段函数的返回值color写为(255,0,0,255)即可,那么这个三角形在显示器上显示的所有像素值全为红色(255,0,0,255)。

     这里我要特别提醒,一个很容易让小伙伴们迷惑的一点,一个简单的cg shader文件也就包含一个顶点Vertex函数和一个片段Fragment函数,那为什么会对绑定这个shader的material的model产生“五光十色”的效果呢?难道不应该只是让该model产生一种固定的颜色值么?因为Fragment函数代码就那么几行又没运行时变化。

     其实这个原因是因为输入顶点源数据是时刻变化的,比如一个拥有10万个顶点的模型,它所拥有的顶点Points数组[100000],装载一个material shader,那么shader中vertex和fragment函数会一帧处理顶点Points[0]-[99999]之中所有顶点源数据,那么虽然vertex和fragment函数中代码是一样的,但是产生的效果确实能达到“五光十色”。

     那么紧接着我们就认为这个片段函数依次接收来自顶点函数处理后的模型的所有栅格化顶点数据,并处理像素颜色值,比如颜色值渐变,颜色值随时间变化,颜色值光照计算等等,实现各种各样的颜色值效果。

     片段处理函数在我看来,常用的处理目的就一个:

     ①.根据自己需求处理各种像素颜色值。例如处理光照颜色值,可以查看光照模型三中fragment函数的处理方式。

     好,到这里我们就很通俗的学习完cg语言中重要的两个函数vertex和fragment的意义了,下一篇我们就来通过观察一个简单的cg代码片段,继续学习。

     

      

猜你喜欢

转载自blog.csdn.net/yinhun2012/article/details/81977951