Learning XNA 3.0 phần 7 pptx

50 414 0
Learning XNA 3.0 phần 7 pptx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

280 | Chapter 13: HLSL Basics Don’t be too scared by this code. It looks a bit different than what you’re used to in XNA, but we’ll explore each section of the code here so you can figure out what it’s doing. First, let’s look at the first three lines of the file: float4x4 World; float4x4 View; float4x4 Projection; These three lines represent global variables in this file. All three variables are 4 × 4 matrices; they correspond to the world, the camera view, and the camera projection. These terms should be pretty familiar to you by now because they are the same world, view, and projection that you’ve used in previous examples in this book when drawing in 3D. Variables that are in this global space can be set from your XNA code. (You’ll see more about how to do that later, when you apply this code to an actual project.) When using an HLSL file in XNA, one of the first things to do is look for what vari- ables need to be set from the XNA code. Look Familiar? Remember where you’ve used the world, view, and projection variables in the past? You used them to set the World, View, and Projection properties of the BasicEffect class. The following code sets those variables, and it should be similar to what you have in the Draw method of your BasicModel class in the source code you completed in Chapter 12: foreach (BasicEffect be in mesh.Effects) { be.EnableDefaultLighting( ); be.Projection = camera.Projection; be.View = camera.View; be.World = GetWorld( ) * Mesh.ParentBone.Transform; } You’re seeing the same World, Projection, and View variables in this HLSL file. It makes sense when you think about it. Remember that when drawing in 3D, everything in XNA uses HLSL. Until now, you’ve been using the BasicEffect class, which provides you with a way to draw, well, basic effects without having to know the underlying workings of HLSL. Behind the scenes, though, even the BasicEffect class is using HLSL. Now that you’re looking at branching out and using HLSL instead of the BasicEffect class, it probably won’t surprise you to see some of the same properties you’ve used in the BasicEffect class show up in the HLSL file itself. Dissecting a Sample HLSL Effect File | 281 Just like when you’re programming in C or C++, in HLSL, variables and functions need to be defined before they are used. This means that a typical flow for an HLSL file will have variables at the top, followed by structs, functions, and finally the shader calls themselves. When reading an HLSL file, it typically makes sense to read it in the order in which it will be executed. So, let’s next skip to the bottom of the code in the sample file. Here’s the section of the file that uses the technique keyword: technique Technique1 { pass Pass1 { VertexShader = compile vs_1_1 VertexShaderFunction( ); PixelShader = compile ps_1_1 PixelShaderFunction( ); } } Each HLSL effect file will have one or more techniques. In your XNA code, you’ll specify which technique to run when applying an HLSL file by referring to the name of the technique, which in this case is Technique1. Inside the technique section, notice that there is a subsection called a pass. Each technique has one or more passes. When you draw in XNA using a custom HLSL effect file, you’ll set the technique as mentioned earlier and then loop through all the passes to draw each of them. This particular technique has only one pass ( Pass1). Each pass can contain a vertex and/or pixel shader. To implement a shader, your HLSL file must set the value of the VertexShader and/or PixelShader object(s) equal to a compiled vertex or pixel shader function. Let’s look at those two lines in more depth: VertexShader = compile vs_1_1 VertexShaderFunction( ); PixelShader = compile ps_1_1 PixelShaderFunction( ); The VertexShader and PixelShader objects are HLSL objects whose names must be spelled exactly that way and are case-sensitive. The keyword compile tells XNA to compile the method that follows using a certain version of vertex shader or pixel shader (in this case, you’re compiling using vertex shader version 1.1, as specified by vs_1_1, and pixel shader version 1.1, as specified by ps_1_1). The functions ( VertexShaderFunction( ) and PixelShaderFunction( )) were defined earlier in the file. The first of those to be called is the vertex shader, so next we’ll look at the vertex shader function. The vertex shader function is listed in the file as: VertexShaderOutput VertexShaderFunction(VertexShaderInput input) { VertexShaderOutput output; float4 worldPosition = mul(input.Position, World); 282 | Chapter 13: HLSL Basics float4 viewPosition = mul(worldPosition, View); output.Position = mul(viewPosition, Projection); return output; } The first thing you’ll probably notice is that as an input type the function accepts a struct of type VertexShaderInput (which was defined earlier in the file). The return type is also a struct and is of type VertexShaderOutput (which was also defined ear- lier in the file). Let’s take another look at these two structs: struct VertexShaderInput { float4 Position : POSITION0; }; struct VertexShaderOutput { float4 Position : POSITION0; }; There’s really nothing unusual about these structs, other than one thing that you may not have seen before: an HLSL semantic. The POSITION0 code that appears after each of the variable definitions is an HLSL semantic. HLSL code uses semantics to link data from the XNA game that is using the HLSL code. When a semantic is specified on a variable, HLSL will automatically assign a value to that variable based on the semantic given. This is essentially a way to con- nect variables in HLSL with certain data to which the XNA game has access. In this case, the vertex shader method accepts a parameter of type VertexShaderInput, which contains a variable with a semantic of POSITION0. Specify- ing the POSITION0 semantic on the Position variable causes HLSL to automatically set that variable to a position value that is provide by the XNA game. What position value will be automatically assigned? Remember that a vertex shader runs once for every visible vertex, so the position given via the POSITION0 semantic in a vertex shader is the position of the vertex. There are several other possible vertex shader input semantics, in addition to POSITION0. These are listed in Table 13-1. Table 13-1. Valid input semantics for vertex shaders Semantic Description Type BINORMAL[n] Binormal float4 BLENDINDICES[n] Blend indices uint BLENDWEIGHT[n] Blend weights float Dissecting a Sample HLSL Effect File | 283 You probably noticed that the vertex shader returns a struct as well, and that that struct also has a semantic of POSITION0 associated with it. While the semantic is the same, the meaning is different in this case because it is used as a vertex shader out- put rather than an input. The meaning of different semantics varies depending on whether the variable using a particular semantic is being used for a pixel shader or a vertex shader and whether it is being specified as input or output in that shader. For example, the semantics that can be applied to vertex shader output are different than those that can be applied to vertex shader input. Vertex shader output seman- tics are listed in Table 13-2. So, when the POSITION0 semantic is applied to an input parameter in the vertex shader, HLSL automatically assigns the vertex position to that parameter, but in con- trast, a semantic specified on an output parameter is used to flag a variable as containing specific data. Earlier, you read that the minimum job of any vertex shader is to set the positions of all vertices in the scene, which is done by specifying an output parameter with the POSITION[n] semantic. Essentially, your vertex output needs to set the position of each vertex. But how do you do this, given that there’s no vertex.position variable or anything like that to set? That’s where the output semantic comes in. By returning COLOR[n] Diffuse and specular color float4 NORMAL[n] Normal vector float4 POSITION[n] Vertex position in object space float4 POSITIONT Transformed vertex position float4 PSIZE[n] Point size float TANGENT[n] Tangent float4 TEXCOORD[n] Texture coordinates float4 Table 13-2. Valid output semantics for vertex shaders Semantic Descrption Type COLOR[n] Diffuse or specular color. float4 FOG Vertex fog. float POSITION[n] Position of a vertex in homogenous space. Compute position in screen- space by dividing (x, y, z) by w. Every vertex shader must write out a parameter with this semantic. float4 PSIZE Point size. float TESSFACTOR[n] Tessellation factor. float TEXCOORD[n] Texture coordinate. float4 Table 13-1. Valid input semantics for vertex shaders (continued) Semantic Description Type 284 | Chapter 13: HLSL Basics a value in a variable that has a semantic of POSITION0, you’re specifying a return value that denotes a position. Freaking out about this? Don’t worry. Let’s take a look at the actual vertex shader function. For your convenience, the function and the structs it uses are shown again here: struct VertexShaderInput { float4 Position : POSITION0; }; struct VertexShaderOutput { float4 Position : POSITION0; }; VertexShaderOutput VertexShaderFunction(VertexShaderInput input) { VertexShaderOutput output; float4 worldPosition = mul(input.Position, World); float4 viewPosition = mul(worldPosition, View); output.Position = mul(viewPosition, Projection); return output; } The first thing this code does is create a variable of type VertexShaderOutput so that it can return that datatype at the end of the function. Notice that that struct has a vari- able called Position that has a semantic of POSITION0. Again, the basic responsibility of a vertex shader is to set the position of each vertex. You can set the position by returning a value tied to a semantic of POSITION0. So, by returning an object of type VertexShaderOutput, you’ll be setting the position of the vertex, assuming that you set the Position member of that VertexShaderOutput struct. Next, the vertex shader function creates a variable called worldPosition and sets the value of that variable to the result of the mul function. What is the mul function? This is what’s called an intrinsic function in HLSL: it’s a function that HLSL provides for developers to manipulate data. A full list of intrinsic functions will be provided later in this chapter. This particular function ( mul) multiplies two matrices together. First, the vertex shader function multiplies the position of the vertex ( input.Position) by the World matrix. How do you know that input.Position represents the position of the vertex? Well, once again, this comes back to semantics. The input variable is of type VertexShaderInput, which is a struct defined prior to the definition of the ver- tex shader function. The VertexShaderInput struct has one member: Position. The Position member of that struct has a semantic of POSITION0. When the POSITION0 Dissecting a Sample HLSL Effect File | 285 semantic is tied to a variable used for vertex shader input, the position of the vertex is automatically assigned to that variable. Next, the vertex shader function multiplies the resulting matrix by the camera view. It then multiplies that resulting matrix by the camera projection and assigns that value to the output parameter, which it returns. All of that matrix multiplication sets the position of the vertex to the correct place, given the position and direction of the camera. I know what you’re thinking: “Whoa hold on a second. You’re tell- ing me that if you multiply the position of a vertex by the object’s world matrix, then multiply that by the camera’s view matrix, and then multiply that by the camera’s projection matrix, it will magically set the position of the vertex to the correct place?” Well, yes. And “magic” is really a good term for it. You don’t really need to understand how all of this works, and such a discussion would go way deeper into matrix multiplication than we want to in this book. But yes, multiplying those matrices together in that order will yield the correct position for the vertex. That just goes to show how fascinating and how powerful matrix multiplication can be. You might also be wondering how the code sets the position of each vertex in the scene when only one position variable is returned from the function. Remember that the vertex shader will be run for every vertex in the scene. For example, if you drew a triangle and then ran this HLSL file to render that triangle, your vertex shader would run once for each vertex (there are vertices in a triangle, so the vertex shader would run three times). The parameter passed into the vertex shader has a semantic of POSITION0, which will cause that variable to be automatically filled with the value representing the position of the vertex. So, if the vertices of the triangle you draw are (0, 1, 0), (1, 0, 0), and (–1, 0, 0), your vertex shader will run three times: the first time the input parameter value will be (0, 1, 0), the second time the input parameter value will be (1, 0, 0), and the third time the value will be (–1, 0, 0). This may seem like a lot of confusing code, semantics, shaders, and so forth. Your head may be spinning. But that’s OK. Here are the key points to remember so far: • Global variables that aren’t given semantics or initial values need to be set from XNA code. • The name of the technique to run in your HLSL file needs to be set from XNA code. • An input parameter with a semantic will automatically receive the data repre- sented by that semantic when the HLSL effect is executed (for example, an input parameter in a vertex shader with a semantic of POSITION0 will automatically be set to a value representing the position of the vertex). 286 | Chapter 13: HLSL Basics • An output variable with a semantic flags that variable as having a specific type of data for processing in the effect. This is how you “set” data in HLSL. If you need to “set” the position of a vertex in a vertex shader, you return a variable with a semantic of POSITION0. • A vertex shader has different semantics for input and output. • At a minimum, a vertex shader needs to specify a POSITION[n] semantic for output. Once the vertex shader finishes, the pixel shader will run. Let’s look at the pixel shader that was created in the effect file: float4 PixelShaderFunction( ) : COLOR0 { return float4(1, 0, 0, 1); } First, notice that there is no input parameter. While vertex shader output will even- tually make it to pixel shader input, the datatypes used in each situation don’t neces- sarily have to be exactly the same. In fact, because vertex shader output semantics are different than pixel shader input semantics, often they cannot be the same type. And sometimes (as in this case), you don’t need to specify any input at all. Table 13-3 shows a list of valid pixel shader input semantics. Finally, notice the strange semantic on the function itself: float4 PixelShaderFunction( ) : COLOR0 This is another way of specifying a semantic for a return type. You can also specify an input semantic in the parameter list rather than using a struct. For example, the following method returns a color and accepts one as well: float myShader(float COLOR0) : COLOR0 Valid output semantics for pixel shaders are shown in Table 13-4. Table 13-3. Valid input semantics for pixel shaders Semantic Description Type COLOR[n] Diffuse or specular color. float4 TEXCOORD[n] Texture coordinates. float4 VFACE Floating-point scalar that indicates a back-facing primitive. A negative value faces backward, while a positive value faces the camera. float VPOS Contains the current pixel (x, y) location. float2 Table 13-4. Valid output semantics for pixel shaders Semantic Description Type COLOR[n] Output color float4 DEPTH[n] Output depth float Applying an HLSL Effect in C# | 287 As mentioned earlier, the minimum job of a pixel shader is to set the color for each individual pixel. In this case, the pixel shader function is returning a float with the COLOR0 semantic to accomplish that. The function contains only one line of code, and with that line, returns the color red: return float4(1, 0, 0, 1); Because the function itself has a semantic of COLOR0, returning a float4 (which can represent color) will “set” the color for that pixel. You may be wondering how this function sets the color of each pixel in the scene. Remember that a pixel shader is run for each pixel in the scene. If you drew a triangle and then used this HLSL effect file to render that triangle, this pixel shader would run for every pixel that are com- posing that part of the triangle. Depending on the size of the triangle and the size of the screen, that could be 10 times, 100 times, 1,000 times, or more. Each time the pixel shader returns a value, it returns that value with a semantic of COLOR0, indicat- ing that the value being returned represents the new color of that particular pixel. In this example, the return value is always red (RGBA 1, 0, 0, 1), so every pixel will be colored red. You could change the color depending on the position of the pixel or based on some texture file or whatever else you might want to do, and that would let you color every pixel in the scene differently. After running the pixel shader, the pass is finished. Because there was only one pass in this code, the HLSL file is now finished, and the data will be sent to the screen. The vertex shader set the positions of all the vertices in the world and the pixel shader colored all the pixels red, so applying this effect file to a scene should result in everything in the scene being colored red. In the next section, we’ll apply the file to some primitives to see if that is actually the case. Applying an HLSL Effect in C# In this section, we’ll be using the source code for the textured rectangle project from Chapter 9. You might remember this project as the one that created the cool rectangle with a tree image texture. Running the project will result in the familiar tree rectan- gle that you saw back when you first started the 3D section (see Figure 13-2). Currently, the rectangle is drawn using the BasicEffect class. You’re going to change that so it uses an effect created from an HLSL file that you generate. The first thing to do is create a subfolder under the Content node in your project by right-clicking the Content node in Solution Explorer and selecting Add ➝ New Folder. Name the new folder Effects. Next, right-click the Content\Effects folder in Solution Explorer and select Add ➝ New Item Select Effect File as the template on the right and name the file Red.fx, as shown in Figure 13-3. 288 | Chapter 13: HLSL Basics Figure 13-2. Pretty, pretty rectangle ahhhhh Figure 13-3. Creating a new effect file Applying an HLSL Effect in C# | 289 Odds are that your sample effect file is the same as the code listed previously, but just to be safe, make sure that your effect file contains the following code: float4x4 World; float4x4 View; float4x4 Projection; struct VertexShaderInput { float4 Position : POSITION0; }; struct VertexShaderOutput { float4 Position : POSITION0; }; VertexShaderOutput VertexShaderFunction(VertexShaderInput input) { VertexShaderOutput output; float4 worldPosition = mul(input.Position, World); float4 viewPosition = mul(worldPosition, View); output.Position = mul(viewPosition, Projection); return output; } float4 PixelShaderFunction( ) : COLOR0 { return float4(1, 0, 0, 1); } technique Technique1 { pass Pass1 { VertexShader = compile vs_1_1 VertexShaderFunction( ); PixelShader = compile ps_1_1 PixelShaderFunction( ); } } You can verify that your effect code will compile by compiling your solution. If you get no compilation errors, you know that your code is at least syntactically correct (that’s a good sign). To use your effect in code, you need to create a variable of type Effect to store the effect in memory. Create a class-level Effect variable in your Game1 class: Effect effect; [...]... System.Collections.Generic; System.Linq; Microsoft .Xna. Framework; Microsoft .Xna. Framework.Audio; Microsoft .Xna. Framework.Content; Microsoft .Xna. Framework.GamerServices; Microsoft .Xna. Framework.Graphics; Microsoft .Xna. Framework.Input; Microsoft .Xna. Framework.Media; Microsoft .Xna. Framework.Net; Microsoft .Xna. Framework.Storage; namespace _3D_Madness { public class Game1 : Microsoft .Xna. Framework.Game { GraphicsDeviceManager... variable, called xWorldViewProjection In your XNA code, you’ll need to multiply the world, view, and projection matrices together and assign the resulting value to this variable Because they are all multiplied together to set the vertex position, it doesn’t matter if you do that in the HLSL file or in XNA code The advantage of doing the multiplication in XNA code rather than in the HLSL file is that... semantics to the XNA code itself In this case, that first element, which corresponds to the position member of the Particle struct, is given the VertexElementUsage Position This tells XNA that it needs to send the data that you put in the position member of this struct to the HLSL code and that it basically needs to slap a label on the data calling it a position That’s the push side of the data XNA pushes... negative image being drawn: float4 PixelShader(VertexOut input) : COLOR0 { float4 output = 1-tex2D(ColoredTextureSampler, input.textureCoordinates); return output; } The effect is shown in Figure 13 -7 Figure 13 -7 Negative image drawn using HLSL HLSL Effects: Creating a Negative | 303 HLSL Effects: Blur Another very simple effect is blurring the image To do this, you grab the color of each pixel in the texture... Creating a Custom Vertex | 309 Figure 14-1 Creating a blank code file called Particle.cs Once your blank code file is open and ready, add the following code to the file: using Microsoft .Xna. Framework; using Microsoft .Xna. Framework.Graphics; namespace _3D_Game { struct Particle { public Vector3 position; public Vector3 direction; public Vector2 textureCoordinate; public float pointSize; public static... still use that data, but you instead assign it to the global variables mentioned in your HLSL effect file by using the effect.Parameters[].SetValue method Passing Data from XNA to HLSL There are two different ways to get data from XNA to your HLSL effect file One way is by using semantics As discussed earlier in this chapter, if you create a variable that is used as an input variable to a vertex shader... throw it together Create a new class within your project called ParticleExplosion Make sure that you have the following namespaces at the top of the file: using System; using Microsoft .Xna. Framework; using Microsoft .Xna. Framework.Graphics; Next, add the following class-level variables: // Particles Particle[] particles; // Position Vector3 position; // Life int lifeLeft; // Rounds and particle counts... need to change the code in your Draw method to use your new custom effect rather than the red custom effect used previously Do you remember what data you need to set in order to use an HLSL effect in XNA? You’ll need to set the name of the effect to run, and you’ll need to set all global variables in the effect file Currently, you have the following code in the Draw method of your Game1 class to do... vertex shader or a pixel shader and assign a semantic to that variable, the appropriate data will automatically be set to that variable when the function is run The other way to get data to HLSL from XNA is to manually set parameters As you saw earlier, only certain types of data can be passed via semantics (things like positions, colors, texture coordinates, and so on) However, you can pass anything... vertex and pixel shader versions to use? 4 What does HLSL do for you that you can’t accomplish without it? 5 How do you multiply two matrices together in HLSL? 6 What is the role of a semantic in HLSL? 7 What kind of bear is best? Test Your Knowledge: Exercise 1 Take the code you built in this chapter and draw a six-sided cube using the trees image provided as the texture for each side of the cube On . divide tex3D(s, t) 3D texture lookup tex3Dbias(s, t) 3D texture lookup with bias tex3Dgrad(s, t, ddx, ddy) 3D texture lookup with a gradient tex3Dlod(s, t) 3D texture lookup with LOD tex3Dproj(s, t) 3D. VertexPositionTexture[4]; verts [0] = new VertexPositionTexture( new Vector3(-1, 1, 0) , new Vector2 (0, 0) ); verts[1] = new VertexPositionTexture( new Vector3(1, 1, 0) , new Vector2(1, 0) ); verts[2] = new. VertexPositionTexture( new Vector3(-1, -1, 0) , new Vector2 (0, 1)); verts [3] = new VertexPositionTexture( new Vector3(1, -1, 0) , new Vector2(1, 1)); //Load effect Applying an HLSL Effect in C# | 2 93 effect = Content.Load<Effect>(@"effects ed");

Ngày đăng: 12/08/2014, 20:22

Mục lục

  • HLSL Basics

    • Applying an HLSL Effect in C#

    • Applying HLSL Using Textures

    • HLSL Effects: Creating a Negative

    • HLSL Effects: Blur

    • HLSL Effects: Grayscale

    • What You Just Did

    • Summary

    • Test Your Knowledge: Quiz

    • Test Your Knowledge: Exercise

    • Particle Systems

      • Creating a Custom Vertex

      • Creating a Particle Engine

      • Adding a Particle Effect File

      • Adding Your Particle Engine to Your Game

      • Adding a Starfield

Tài liệu cùng người dùng

Tài liệu liên quan