代码改变世界

Silverlight 5 3d游戏开发(1)绘制简单图形

2011-05-30 09:25  老咩  阅读(4309)  评论(13编辑  收藏  举报

Silverlight 5 beta 版本的公布, 其支持3d 效果到底如何。本系列文章将和大家一起学习研究。

Silverlight 5 目前处于Beta版本,如果是 Visual Studio 2010,需要先安装Visual Studio SP1,然后才能安装 Silverlight 5 开发工具包。由于Silverlight 5 在测试,可能得不到版本支持------即浏览一个Silverlight 5 网站,只能手动安装 Silverlight 开发版本来浏览。

地址:http://www.silverlight.net/getstarted/silverlight-5-beta/

 

在Silverlight 5 帮助文档里,给出了3d渲染的基本步骤:

 

 1.开启GPU加速,即设置EnableGPUAcceleration true

2.添加DrawSurface控件同时定义Draw事件

 3.生成Draw事件处理函数

4.定义一个存储顶点数据的结构体。(包括VertexDeclartion)

 5.为3D图形定义 顶点数据

6.定义VertexBuffer并使用SetData(T)来添加顶点数据

7.定义PixelShader和VertexShader对象

8.如果你的顶点着色器需要矩阵,定义world-view,Camera view, Projection space矩阵

9.在Draw事件里

a.调用Clear方法清空GraphicsDevice

 b.使用SetVertexBuffer将VertexBuffer设置到图形设备上

 c.使用SetVertexShader将VertexShader设置到图形设备上

 使用SetVertexShaderConstantFloat4(T)传递一个矩阵参数到顶点着色器

 d.使用SetPixelShadeer将PixelShader设置到图形设备上

 e.调用DrawPrimitives

 f.可以通过调用InvalidateSurface触发另一个绘图事件

10.在图形线程无效的表面上调用Invalidate;

 

Silverlight 5 依靠DrawingSurface 这个新控件来渲染3d 图形。并且增加了一些新类来支持3d通过引进一个轻量级的XNA Graphics Framework 4.0提供对3D的支持。这个特性可以开发更丰富的游戏和用户体验。

以下是Silverlight 命名空间中 3d 相关的类。

Namespace

Notes

Microsoft.Xna.Framework

包含类 ColorRectangle 结构被Silverlight 3D 图形类使用.

注意:这2个结构和 System.Windows.Media.Color 以及 System.Windows.Shapes.Rectangle 类是不一样的。

Microsoft.Xna.Framework.Graphics

包含了大多数 XNA 3D 图形类。

Microsoft.Xna.Framework.Silverlight

包含了Silverlight 特有的3d 类.

System.Windows.Controls

包含了 DrawingSurfaceDrawEventArgs.

System.Windows.Media.Imaging

BitmapSource 类被BitmapSourceExtensions 扩展了一些 CopyTo 方法用来从位图图片中创建 Texture

 

Silverlight 只支持 Reach profile 模式,这个模式需要显卡支持Shader model2.0+以及DirectX 9+,关于该模式一些参数,可以查看下表(该表取至Xna):

Shader model

(着色器 版本)

2.0 

Max texture size

(最大纹理)

2048

Max cubemap size

(最大多面体)

512

Max volume texture size

(最大体积纹理)

不支持

Non power of two textures

(无二次幂限制的纹理)

解释:OpenGL仅支持分辨率为2mx2n的纹理

限制: 不能用于环绕

寻址模式, 纹理链, 或 DXT 压缩尺寸不是二次幂

Non power of two cubemaps

(无二次幂限制的多面体)

不支持

Non power of two volume textures

(无二次幂限制的体积纹理)

体积纹理不支持

Max primitives per draw call

(最大每秒调用绘制)

65535

Index buffer formats

(缓冲索引格式)

16 bit

Vertex element formats

(顶点元素格式)

Color, Byte4, Single, Vector2, Vector3, Vector4, Short2, Short4, NormalizedShort2, NormalizedShort4

Texture formats

(纹理格式) 

Color, Bgr565, Bgra5551, Bgra4444, NormalizedByte2, NormalizedByte4, Dxt1, Dxt3, Dxt5

Vertex texture formats

(顶点纹理格式) 

Vertex texturing is not supported

Render target formats

(渲染目标格式) 

部分支持 (见下)

Multiple render targets

(多次渲染目标) 

不支持

Occlusion queries

(遮挡测试) 

不支持

Separate alpha blend

不支持

Blend.SourceAlphaSaturation

只支持SourceBlend, 不支持 DestinationBlend

Max vertex streams

16

Max stream stride

255

下面来开始一个简单的3d 例子:

首先创建一个Silverlight 工程项目。

第一步:将 <param name="EnableGPUAcceleration" value="true" /> 添加到网页的Silverlight 插件对象设置中来开启硬件加速。

 

第二步:在主控件的xaml 里添加 DrawingSurface,并定义了Draw事件处理函数DrawingSurface_Draw。

 



<Grid x:Name="LayoutRoot" Background="Black">

<DrawingSurface Draw="DrawingSurface_Draw"

Width
="500"

Height
="500" />

</Grid>

 

第三步: 在没有编写Draw事件处理函数DrawingSurface_Draw 的内容之前,在xmal里添加一个TextBlock来显示程序的fps情况

 
 
<TextBlock x:Name="txt" VerticalAlignment="Top" HorizontalAlignment="Left" Foreground="White" />
然后在DrawingSurface_Draw函数里添加以下代码:
 
private void DrawingSurface_Draw(object sender, DrawEventArgs e)

{

txt.Dispatcher.BeginInvoke(() =>

{

txt.Text = string.Format("刷新频率: {0}ms\t 总运行时间: {1}", e.DeltaTime.TotalMilliseconds, e.TotalTime);

});

}

 

运行后,刷新频率没有值因为Draw事件仅仅被命中一次,DrawEventArgs使用InvalidateSurface 方法来强制刷新DrawingSurface控件同时再次命中Draw事件。

修改成以下代码再运行:

 

private void DrawingSurface_Draw(object sender, DrawEventArgs e)

{

txt.Dispatcher.BeginInvoke(()
=>

{

txt.Text
= string.Format("刷新频率: {0}ms\t 总运行时间: {1}", e.DeltaTime.TotalMilliseconds, e.TotalTime);

});

e.InvalidateSurface();

}

 

我的机器上大概是 17ms ,也就是fps 在60 左右。

再添加一个TextBlock来显示程序的硬件加速情况,然后在MainPage 的构造函数里添加以下代码:

GraphicsDeviceManager deviceManager = GraphicsDeviceManager.Current;

txt1.Text
= string.Format("渲染模式: {0}\t 渲染结果: {1}", deviceManager.RenderMode, deviceManager.RenderModeReason);
GraphicsDeviceManagerl类管理当前图形设备并且给出该设备支持3D渲染的属性信息。该类使用RenderMode和RenderModeReason属性来通知户用为什么3D渲染没有开启。

RenderMode 只有2个参数,未定义模式和硬件模式。注意:Silverlight 3D 不支持软件渲染模式,这里的 Unavailable 不代表软件模式。

RenderModeReason 有好几个理由,分别是正常,GPU加速未开启,GPU加速可能在浏览器中禁用,硬件不支持3d,设备暂时不可用。

 

第四步:3d中一个图形绘制分为顶点渲染和像素渲染(也叫顶点着色和像素着色),顶点渲染主要负责描绘图形,也就是建立模形。像素渲染主要负责把顶点绘出的图形填上填色。然后再加上纹理贴图单元贴上纹理。

绘制的第一步就是为显卡提供顶点数据。顶点数据存储在结构体里,可以自定义格式,但是必须遵循一定的规则。一个顶点数据的结构保存了顶点的数据(空间位置,颜色,贴图座标等等信息)和一个VertexDeclaration对象来告诉设备数据的大小和如何格式化数据。

 

下面来定义一个顶点数据结构,Silverlight 类库中没有提供 Vector3 等辅助类,需要去以下地址下载Microsoft.Xna.Framework.Math.dll

 

地址:http://code.msdn.microsoft.com/XNA-Math-Helper-DLL-d4d1f7d4

 

引用到项目里,构建顶点数据结构如下:

 

public struct VertexPositionColor

{

public Vector3 Position;

public Microsoft.Xna.Framework.Color Color;



public VertexPositionColor(Vector3 position, Microsoft.Xna.Framework.Color color)

{

Position
= position; Color = color;

}



public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration(

new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),

new VertexElement(12, VertexElementFormat.Color, VertexElementUsage.Color, 0));

}
 

这个是一个帮助文档里示例的简单顶点数据结构,包括了一个位置,颜色,和VertexDeclaration对象。

第5步:绘制一个3角形,用前面的定义的顶点数据结构来构造,并填充到顶点数据缓冲VertexBuffer里。

private VertexBuffer CreateTriangle()

{

VertexPositionColor[] vertices
= new VertexPositionColor[3];

Microsoft.Xna.Framework.Color color
= new Microsoft.Xna.Framework.Color(255, 255, 255, 255);



vertices[
0].Position = new Vector3(-1, -1, 0); // left

vertices[
1].Position = new Vector3(0, 1, 0); // top

vertices[
2].Position = new Vector3(1, -1, 0); // right



vertices[
0].Color = color;

vertices[
1].Color = color;

vertices[
2].Color = color;



VertexBuffer vb
= new VertexBuffer(

GraphicsDeviceManager.Current.GraphicsDevice,

VertexPositionColor.VertexDeclaration,

vertices.Length,

BufferUsage.WriteOnly);



vb.SetData(
0, vertices, 0, vertices.Length, 0);



return vb;

}

第6步:需要创建PixelShader和VertexShader对象,安装 最新的 DirectX SDK,然后可以使用以下命令编译你的 hlsl 文件。

Fxc.exe /T vs_2_0 /O3 /Zpr /Fo Triangle.vs Triangle.vs.hlsl

Fxc.exe /T ps_2_0 /O3 /Zpr /Fo Triangle.ps Triangle.ps.hlsl

这里,要了解的是着色程序是由图形设备执行,由于Shader指令集非常复杂,因此出现了hlsl这样的面向过程的Shader语言,来简化程序员的工作。 hlsl 编写完后需要被编译成显卡的GPU指令序列来让显卡进行顶点处理过程和像素处理过程。所以,编写hlsl时应该尽量简洁高效。

关于更详细的hlsl 请自行寻找相关资料,这里我直接使用了帮助文档里的hlsl.

为了更好的效率,我把创建3角形顶点数据和构建着色对象放在了DrawingSurface 的加载事件中。

private void DrawingSurface_Loaded(object sender, RoutedEventArgs e)

{

GraphicsDevice resourceDevice
= GraphicsDeviceManager.Current.GraphicsDevice;

vertexBuffer
= CreateTriangle();



Stream shaderStream
= Application.GetResourceStream(new Uri(@"Silverlight3DGame1;component/Triangle.vs", UriKind.Relative)).Stream;

vertexShader
= VertexShader.FromStream(resourceDevice, shaderStream);



shaderStream
= Application.GetResourceStream(new Uri(@"Silverlight3DGame1;component/Triangle.ps", UriKind.Relative)).Stream;

pixelShader
= PixelShader.FromStream(resourceDevice, shaderStream);

}
 

第7步:设置相机和投影矩阵。(以后继续学习)

private void SetMatrix()

{

Vector3 cameraPosition
= new Vector3(0, 0, 5.0f); // 相机坐标

Vector3 cameraTarget
= Vector3.Zero; // 相机朝向 (朝向世界坐标原点)



// 相机的视口

view
= Microsoft.Xna.Framework.Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up);

projection
= Microsoft.Xna.Framework.Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 1.667f, 1.0f, 10.0f);

}

第8步:绘制,这里的代码完全按照帮助文档里3d渲染的基本步骤的第九项。

 

private void Draw(GraphicsDevice graphicsDevice)

{

graphicsDevice.Clear(ClearOptions.Target
| ClearOptions.DepthBuffer, Microsoft.Xna.Framework.Color.Transparent, 1.0f, 0);



graphicsDevice.SetVertexBuffer(vertexBuffer);

graphicsDevice.SetVertexShader(vertexShader);

Microsoft.Xna.Framework.Matrix viewProjection
= view * projection;

graphicsDevice.SetVertexShaderConstantFloat4(
0, ref viewProjection);



graphicsDevice.SetPixelShader(pixelShader);



graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList,
0, 2);

}

 

 

最后,即可在屏幕上画出一个白色三角形。在代码里还有一个绘制矩形的函数,大家可以更换一下,查看下效果,并自己尝试绘制其它图形。

源码下载