以前一直没搞明白HLSL,最近两天苦下功夫,终于算是学到了HLSL入门了。
这里我将通过对一个矩形模型以BassicEffect贴图和通过HLSL贴图作对比,从而掌握在XNA中使用.fx效果文件,和初步了解HLSL。
下面先分析一下没有使用贴图时的代码:
2 {
3 GraphicsDeviceManager graphics;
4 SpriteBatch spriteBatch;
5
6 //投影矩阵
7 Matrix projection;
8 //观察矩阵
9 Matrix view;
10 //矩形的世界矩阵
11 Matrix world;
12 //矩形模型
13 Model cub;
14
15 //定义贴图纹理和效果(先声明,暂时不使用)
16
17 Texture2D text;
18 Effect effe;
19
20 //是否在暂停状态
21 bool isPause = false;
22
23 KeyboardState preks = Keyboard.GetState();
24
25 public Game1()
26 {
27 graphics = new GraphicsDeviceManager(this);
28 Content.RootDirectory = "Content";
29 }
30
31 protected override void Initialize()
32 {
33 //初始化投影矩阵
34 projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1f, 100000f);
35
36 //初始化观察矩阵
37 view = Matrix.CreateLookAt(Vector3.Forward * 500, Vector3.Zero, Vector3.Up);
38
39 //初始化矩形的世界矩阵
40 world = Matrix.Identity;
41
42 base.Initialize();
43 }
44
45 protected override void LoadContent()
46 {
47 // Create a new SpriteBatch, which can be used to draw textures.
48 spriteBatch = new SpriteBatch(GraphicsDevice);
49
50
51 //载入矩形模型
52 cub = Content.Load<Model>("cub");
53
54 //载入贴图纹理(暂时不使用)
55 text = Content.Load<Texture2D>("rocks");
56
57 //载入效果(暂时不使用)
58 effe = Content.Load<Effect>("effe");
59
60 //设置效果的Technique
61 effe.CurrentTechnique = effe.Techniques["Technique1"];
62
63 }
64
65 protected override void Update(GameTime gameTime)
66 {
67
68 //当前按键状态
69 KeyboardState ks = Keyboard.GetState();
70
71 //按S镜头拉远,W镜头拉近
72 if (ks.IsKeyDown(Keys.S))
73 {
74 view *= Matrix.CreateTranslation(-10 * Vector3.UnitZ);
75 }
76
77 if (ks.IsKeyDown(Keys.W))
78 {
79 view *= Matrix.CreateTranslation(10 * Vector3.UnitZ);
80
81 }
82
83 //通过空格键控制是否暂停
84 if (ks.IsKeyDown(Keys.Space) && preks.IsKeyUp(Keys.Space))
85 {
86 if (isPause)
87 {
88 isPause = false;
89 }
90 else
91 {
92 isPause = true;
93 }
94 }
95
96 //控制FillMode
97 if (ks.IsKeyDown(Keys.A) && preks.IsKeyUp(Keys.A))
98 {
99 GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;
100 }
101
102 if (ks.IsKeyDown(Keys.Q) && preks.IsKeyUp(Keys.Q))
103 {
104 GraphicsDevice.RenderState.FillMode = FillMode.Solid;
105 }
106
107 preks = ks;
108
109 //如果不是暂停状态,自动旋转矩形的世界矩阵,实现旋转效果
110 if (!isPause)
111 {
112 world *= Matrix.CreateFromAxisAngle(new Vector3(1, 1, -1), gameTime.ElapsedGameTime.Ticks * 0.0000001f);
113 }
114 base.Update(gameTime);
115 }
116
117 protected override void Draw(GameTime gameTime)
118 {
119 graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
120
121 foreach (ModelMesh mesh in cub.Meshes)
122 {
123 foreach (BasicEffect effect in mesh.Effects)
124 {
125 effect.World = world;
126 effect.View = view;
127 effect.Projection = projection;
128 effect.EnableDefaultLighting();
129 }
130 mesh.Draw();
131 }
132 base.Draw(gameTime);
133 }
134 }
可以看到程序的高度效果如下:
现在我们为例子加入贴图(即前面声名的Texture),因为是使用的Basiceffect类的方法,所以很简单,只需要在foreach (BasicEffect effect in mesh.Effects)的循环体中插入以下代码,
effect.Texture = text;
再来看一下效果:
上面就是XNA中最简单的纹理贴图的方法,下面就将进入主要的部分——HLSL渲染。
首先看一段很经典的HLSL代码:
2 Float4x4 mWorldViewProjection;
3
4 // 定义一个VertexShader函数,作为入口,并返回值的语义为POSITION
5 float4 vs_main(float4 position:POSITION):POSITION
6 {
7
8 //将传入的坐标顶点与世界观察投影矩阵相乘,一交完成三个顶点的交换
9 return mul(position,mWorldViewProjection);
10}
11
12 // 定义一个PixelShader函数,作为入口,并定义返回语义为COLOR0
13 Float4 ps_main():COLOR0
14 {
15 //固定返回白色作为显示颜色
16 Return float4(1,1,1,1);
17}
18
19 // 开始定义Technique
20 technique technique1
21 {
22 //一个pass
23 pass pass0
24 {
25 //将VertexShader的版本设置为1.1,并将执行函数设置为vs_main
26 VertexShader=compile vs_1_1 vs_main();
27 //将PixelShader的版本设置为1.1,并将执行函数设置为ps_main
28 PixelShader=compile ps_1_1 ps_main();
29}
30
31}
32
33
这段代码中包含了一个全局变量,两个全局函数。这两个全局函数分别用于VertexShader和PixelShader,也直接作为VertexShader和PixelShader的入口函数。而全局变量则用在了VertexShader的计算中。代码中定义了一个technique并且包含了一个pass,在这个pass中VertexShader和PixelShader的版本都是1.1。
使用这段代码去渲染任何一个模型,得到的都是一个白色的物体。
在XNA中使用HLSL的代码如下:
2 {
3 graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
4
5 //设置HLSL参数
6 effe.Parameters["mWorldViewProjection"].SetValue(world * view * projection);
7 effe.Begin();
8 foreach (EffectPass pass in effe.CurrentTechnique.Passes)
9 {
10 pass.Begin();
11 foreach (ModelMesh mesh in cub.Meshes)
12 {
13 foreach (ModelMeshPart part in mesh.MeshParts)
14 {
15 GraphicsDevice.VertexDeclaration = part.VertexDeclaration;
16 GraphicsDevice.Indices = mesh.IndexBuffer;
17 GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StartIndex, part.VertexStride);
18 GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.BaseVertex, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount);
19 }
20 }
21 pass.End();
22 }
23 effe.End();
24
25 base.Draw(gameTime);
26 }
效果如下图:
让我们再近一步,用HLSL为我们的贴上纹理。
修改Draw方法如下:
2 {
3 graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
4
5 effe.Parameters["World"].SetValue(world);
6 effe.Parameters["View"].SetValue(view);
7 effe.Parameters["Projection"].SetValue(projection);
8
9 //下面的参数text类型为Texture
10 effe.Parameters["diffuseTexture"].SetValue(text);
11
12 effe.Begin();
13 foreach (EffectPass pass in effe.CurrentTechnique.Passes)
14 {
15 pass.Begin();
16 foreach (ModelMesh mesh in cub.Meshes)
17 {
18 foreach (ModelMeshPart part in mesh.MeshParts)
19 {
20 GraphicsDevice.VertexDeclaration = part.VertexDeclaration;
21 GraphicsDevice.Indices = mesh.IndexBuffer;
22 GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StartIndex, part.VertexStride);
23 GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.BaseVertex, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount);
24 }
25 }
26 pass.End();
27 }
28 effe.End();
29
30 base.Draw(gameTime);
31 }
HLSL代码如下:
2 float4x4 View;
3 float4x4 Projection;
4 texture diffuseTexture:Diffuse;
5 sampler DiffuseTextureSampler = sampler_state
6 {
7 Texture = <diffuseTexture>;
8 MinFilter=linear;
9 MagFilter=linear;
10 MipFilter=linear;
11} ;
12
13 struct VertexShaderInput
14 {
15 float4 Position : POSITION0;
16 float3 normal:NORMAL;
17 float2 uv:TEXCOORD0;
18} ;
19
20 struct VertexShaderOutput
21 {
22 float4 Position : POSITION0;
23 float2 uv:TEXCOORD0;
24} ;
25
26 VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
27 {
28 VertexShaderOutput output;
29
30 float4 worldPosition = mul(input.Position, World);
31 float4 viewPosition = mul(worldPosition, View);
32 output.Position = mul(viewPosition, Projection);
33 output.uv=input.uv;
34
35 return output;
36}
37
38 float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
39 {
40 return tex2D(DiffuseTextureSampler,input.uv);
41}
42
43 technique Technique1
44 {
45 pass Pass1
46 {
47 VertexShader = compile vs_1_1 VertexShaderFunction();
48 PixelShader = compile ps_1_1 PixelShaderFunction();
49 }
50}
51
52
渲染效果如下图所示:
就此,我也算对HLSL有了更近步的了解,以后在做的就是多多分析高手写好的HSLS代码。