游戏图形引擎中 Shader System 的设计(转)

仅为开发爱好者提供一些浅见,转载请注明出处。

  Introduction

  Shader,又称着色器,是可编程的,相比过去在显卡上通过硬件hardcode流水线的方式,Shader提供了更灵活有效的解决方案,为新一代的显卡所支持,而在图形引擎中弃用固定流水线编程方式是大势所趋。对于刚刚接触Shader的同学,如何接受这种编程方式的迁移呢?回忆一下主板上BIOS程序,当我们要设置主机是从光盘优先启动还是硬盘优先启动的时候要用到它,配置好的启动信息是保存在主板的CMOS上的。固定管道编程就类似于此,只是对显卡上烧制好的一些算法做一些配置,而Shader的方式则可以使用自己Shader Program去替换部分流水线,把更大图形渲染能力释放出来了。

  Shader Program是载入到显卡中执行的一段汇编指令(assemble instruction),使用固定管线的时候,不需要知道它的算法只需要做一些配置就行了;而使用Shader之后,像光照模型这样的简单公式都得自己写了,所以说能力越大,责任也越大。在渲染系统(render system)中Shader Progrom应被做为一种资源(resource)管理和使用。类似于图片(image)的地位一样,一张图片能为多个物体贴图,一个shader program同样也可以被多个渲染效果(effect)所分享。

  在进行图形渲染时,如果显卡中已经载入了对应的shader,便可以直接使用,比如说在DirectX中,就意味着获得了一个有效的IDirect3DVertexShader9或者IDirect3DPixelShader9的COM接口。如果没有的话,检查内存中是不是已经从磁盘载入。如果也没有,则在磁盘中检查是否有Shader Progrom的文件,如果磁盘中也没有,就提供一个默认的shader。

  Shader System,作为图形引擎的渲染系统(render system)一部分的,需要实现对Shader Program在磁盘(disk), 内存(System memory), 显存(VRAM) 的3个存储层级的管理,从而为渲染提供一个高效简洁的接口。

  Design Decisions

  1. Shader Progarm的资源类型

  在Disk上的Shader Program可以是使用Shader language高级语言像Cg, HLSL写的源程序,也可以是预编译好的汇编代码。从资源的角度来看,汇编代码的使用效率更高,做到一段小的代码实现一段独立的shader,同时为了考虑以后实现Shader Program的自动拼接,我目前选择的是存储汇编代码。

  2. 词法分析和语法分析的方式

  从汇编代码中可以看到,它用到了很多已经设置好了值的寄存器。是的,这些寄存器的值是在调用代码前,就已经设置好了。比如说顶点Shader的位置变换矩阵,每一次摄像机的移动都会引起变化。设置Shader外部变量用到的寄存器的值,必须对汇编代码进行词法分析和语法分析,才能知道各个寄存器应该存储什么变量。最易扩展的方式是使用编译工具进行分析,也可以简单写一个字符串处理程序进行语法分析。我目前选择的是自己实现字符串处理。

  Class Overview

  通过下面的类图简要地介绍下类设计框架。从左边起,Effect表示一个完整的渲染效果,一个效果可能需要几次的流水线(pass),所以它包括了多对的VertexShader和PixelShader,其中每对实现一个pass。

  往右看,每个Shader都要用到一段Shader Program,这是渲染系统中的核心资源。Program上面是Catalogs,在Introduction中说过Program是一种可被共享的资源。如果共享呢?通过Catalogs以Program的名字为索引,来找到这个Program,如果找不到则试图从磁盘载入。

  在Program的下方,描述了一个Program的属性,有许多的ShaderExternals,每个表示需要填入的寄存器的位置和内容,有两个Attributes,分别存储这个Program的输入和输出,另外ResourceID表示对应显卡中Shader的资源接口,如果为空,则试图重新创建。通过这样管理磁盘,内存和显卡三处的资源。

  Figure 1 Class Overview of Shader System

  

  Component Desgin

  通过下面的VertexShader使用的序列图(已作简化)说明一下Shader的应用。从Render的角度看,分为两个阶段:Load和Enable。第一,Load的过程首先是将Program的Assemeble text进行处理后载入到显卡中,在D3D中调用Device的CreateVertexShader接口。这里如果通过Catalog的Find方法发现Shader已经装载到了VRAM中,这时候这一帧就不需要再去装载Shader了。第二,Enable的过程是首先是为指定渲染用到的shader,然后设置寄存器的值,在Shader的Asssemble text中已经指定了哪些寄存器应该存储什么内容,比如说世界变换矩阵,比如说Material。在D3D中通过Device的SetVertexShaderConstantF来指定。这样Shader就应用到渲染顶点上了。

  Figure 2 Load and Enable Vertex Shader

posted @ 2011-11-07 20:37  ☆A希亿  阅读(1456)  评论(0编辑  收藏  举报