着色系列基础:XNA 中的可编程管线基础 (基于微软官方教程)

面向读者:

没有写过着色器(shader)或者效果(effect),但想要学习着色程序编写的用户。

翻译BY:baesky

背景

历史

在1999年,微软对DirectX7引入了一个新特性,其被称作"hardware transformation and lighting”(硬件变换与光照)或者"hardware T&L”。这种新技术将昂贵的顶点变换与光照计算的开销从CPU转移到GPU。DirectX7 API 提供一套复杂的状态值供绘画函数使用。

但是因为这个功能是写死在GPU上的,所以那个时候的游戏看起来都差不多。到了2002年,第一个民用级别的可编程GPU诞生。游戏开发者们迫不及待的想要使用新的GPU,随后一些开发者用可编程GPU渲染除了经典的水波纹效果。

DirectX8是微软最初支持可编程着色技术的图形接口,最初,所有的着色程序需要用汇编码写出。随着着色硬件越来越复杂,高级着色语言诞生了。如今,微软的HLSL(高级着色语言)是所有微软3D接口都支持的基本语言,包括XNA框架。这种语言编译成被GPU执行的字节码。

着色继续已经进化了N年了。最初着色模型只有极其有限的操作,现在则丰富而复杂的多。很多XNA程序最少要求Shader Model 2.0,XBOX360支持它特殊版本的3.0模型。

高级着色语言(HLSL)

HLSL的编程是类C风格的,DirectX SDK有很多关于HLSL各方面的信息,一个非常完备的HLSL文档集可以从这里找到:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/HLSL_Shaders.asp

(PS:此页面我是进不去,自己搜了个可用的:http://msdn.microsoft.com/en-us/library/bb944006(VS.85).aspx

其他着色语言

HLSL是微软创造的用于编写着色程序的语言,但是世界上还有很多其他着色语言如GLSL和N卡出的Cg。

 

效果(effect)

顶点着色介绍

顶点着色程序基于每一个顶点运行一次,他们的功能是将原始顶点数据变换到可被其他图形管线使用的顶点数据。顶点着色的数据来源于顶点缓冲中未处理的数据。每个顶点着色程序都最少要求输出一个顶点的位置。

像素着色介绍

像素着色相当于对传统的固定管线添加了一个新的控制阶段。想要了解像素着色,你需要知道当顶点着色运行完毕后接着发生了什么操作。顶点着色处理过的顶点数据用于装配三角形。像素着色的输入就是顶点着色的输出。所以如果一个顶点着色器输出一个颜色值,那么像素着色的输入可能包含这个颜色,这个数据一般是一个插值。每个像素着色程序至少输出一个颜色。

(PS:总结下,顶点着色是处理顶点的位置,像素着色是处理顶点在所在位置处应该呈现的颜色。)

效果介绍

Effect把顶点着色程序,像素着色程序和图形设备状态合并到了一个文件格式中。XNA支持effect,所以它是我们在XNA中编写着色程序的核心。一个effect文件就是一个包含HLSL代码的文本文件。

一个effect包含一个或更多的technique,一个technique至少包含一个pass。每个pass一般包含一个顶点着色函数,一个像素着色函数,或者还有一定数目的渲染状态和采样状态设置。下面就来看看一个effect文件:

———————————————————————————————————————————————

float4x4 mWorldViewProj;  // World * View * Projection 变换


float4 Vertex_Shader_Transform( 
    in float4 vPosition : POSITION ) : POSITION
{
    float4 TransformedPosition;

    // 将顶点变换到投影空间. 
    TransformedPosition = mul( vPosition, mWorldViewProj );
    
    return TransformedPosition;
}

float4 Pixel_Shader() : COLOR0
{
    return float4(1,1,1,1);
}

technique ColorShaded
{
    pass P0
    {          
        VertexShader = compile vs_1_1 Vertex_Shader_Transform();
        PixelShader  = compile ps_1_4 Pixel_Shader(); 
    }
}

———————————————————————————————————————————————

这个effect是最简单的效果之一。这个作色程序会通过一个世界-视域-投影矩阵来处理顶点位置,并渲染出来一个白色的几何体。下面来详细分析下:

HLSL语义

熟悉pascal的朋友会注意到某些类似与变量声明的语法:

in float4 vPosition : POSITION

POSITION关键字称作一个语义,这个语义告诉着色程序如果把这个变量关联到着色程序的输入或者输出中。在这个例子中,顶点的位置数据被映射到vPosition中。而这个vPosition数据包含的就是顶点缓冲中的顶点位置信息。

HLSL类型

float4 TransformedPosition;

一个float4X4表示一个4行4列的矩阵,矩阵中每一个值都是一个floatleix,关于类型更详细的信息可查阅:

http://msdn2.microsoft.com/en-us/library/bb206325.aspx

(PS:同样,这个连接我打开后发现过期了,可从前面给的连接里找关于HLSL类型的信息)

下面是一些HLSL与.NET和XNA里类型的对应信息

HLSL Type

XNA or .NET Framework Type

Float

Float

float2

Vector2

float3

Vector3

float4

Vector4, Quaternion, Color

float4x4

Matrix

Int

Int32

效果参数

效果参数是在处理顶点和像素时保持不变的数据。

float4x4 mWorldViewProj; // World * View * Projection 变换

在前面的示例中只有一个参数。这个例子中,表示一个保存变换的矩阵。这个为初始化的变量在这里一点用也没有。程序必须为其提供数据才能是着色程序利用。XNA框架API通过EffectParameter类型将参数关联到这种变量中。下面是一个C#代码段:

———————————————————————————————————————————————

//初始化参数
Effect exampleEffect = content.Load<Effect>("ExampleEffect");
EffectParameter worldViewProjParameter =
   exampleEffect.Parameters["mWorldViewProj"];

Matrix worldViewProj = Matrix.Identity *  //世界变换
   Matrix.CreateLookAt(                   //视域变换
      new Vector3(0f, 0f, -10f),
      Vector3.Zero,
      Vector3.Up) *
   Matrix.CreatePerspectiveFieldOfView(   //投影变换
      MathHelper.PiOver4,
      1.333f,
      0.1f,
      100f);  

//设置变换矩阵
worldViewProjParameter.SetValue(worldViewProj);
————————————————————————————————————————————
Uniform和Varying类型输入
着色函数有两种类型的输入:Uniform和Varying(恒定与变化)。(刚才上面那个变换矩阵就是Uniform类型的。)Varying数据是哪些每次执行着色函数都不同的。在顶点着色程序中,就是顶点缓冲中的数据。在像素着色程序中,就是每个被着色的像素数据。
Uniform数据就好像是常量。开发者可以使用Effect API设置uniform类型的值,前面的例子中,一个float4x4的常量mWorldViewProj,在XNA框架API中,开发者可以查找这个变量并给他设值。
这个例子中的变换矩阵基本每个顶点着色程序都要用到。
  
顶点着色函数
float4 Vertex_Shader_Transform( in float4 vPosition : POSITION ) : POSITION
这个例子中,vPosition关联到POSITION语义,所以他代表顶点数据的位置信息。第2个POSITION是个输出语义,表示顶点着色程序的输出:一个float4类型的值代表的是顶点的位置。
 
变换管线
固定功能管线实际上对开发者隐藏了顶点的变换操作。在可编程管线中,着色程序的灵活性体现在有开发者决定是否应用变换(有时候顶点是不需要变换位置的)。
前面的例子中,顶点着色程序负责变换输入的顶点数据。这需要着色程序通过一个变换矩阵来计算顶点的新位置。
TransformedPosition = mul( vPosition, mWorldViewProj );
这个操作将顶点位置变换到投影空间中。这个被变换的位置用于在Direct3D管线中,将几何体的位置正确映射到屏幕中。在XNA中可用Vector4.Transform(vPosition,mWorldViewProj)函数完成。
 
从顶点着色程序中返回值
return TransformedPosition;
跟C++或C#函数的返回值写法一样。
 
像素顶点着色函数
float4 Pixel_Shader( ) : COLOR0
首先表明顶点着色程序返回一个float4类型的值。这个值表示在随后绘画调用中像素的颜色。
很多像素着色器值返回一个RGBA的颜色。在大多数着色程序中,颜色表示为浮点值,0.0表示完全黑暗,1.0表示最亮。图形硬件把这个值转换为可在当前上下文中的后备缓冲可显示的格式。
return float4(1,1,1,1);
上例像素着色只是简单的返回一个单色值。
 
状态设定
effect文件的最后是设定图形设备的状态,它告诉设备用什么函数进行着色处理:
technique ColorShaded
{
    pass P0
    {          
        VertexShader = compile vs_1_0 Vertex_Shader_Transform();
        PixelShader  = compile ps_1_4 Pixel_Shader(); 
    }
}
本文完。
本文翻译自XNA官方网站的指导教程,原文在:http://create.msdn.com/en-US/education/catalog/article/shader_primer,翻译不好,仅供参考,欢迎各位朋友的批评与指正:)

posted on 2010-10-16 14:17  Meta.Grfx  阅读(904)  评论(2编辑  收藏  举报

导航