Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Introduction to 3D Game Programming with DirectX.9.0 - F. D. Luna

.pdf
Скачиваний:
243
Добавлен:
24.05.2014
Размер:
6.94 Mб
Скачать

264 Chapter 15

float b = 2.0f * D3DXVec3Dot(&ray->_direction, &v);

float c = D3DXVec3Dot(&v, &v) – (sphere->_radius * sphere-> _radius);

// find the discriminant

float discriminant = (b * b) - (4.0f * c);

// test for imaginary number if( discriminant < 0.0f )

return false;

 

 

 

 

Y

 

discriminant = sqrtf(discriminant);

 

float s0 = (-b + discriminant) / 2.0f;

 

 

 

F

 

float s1 = (-b - discriminant) / 2.0f;

 

 

 

M

 

 

// if a solution is >= 0, then we intersected the sphere

 

if( s0 >= 0.0f || s1 >= 0.0f )

 

 

return true;

L

 

return false;

A

 

}

E

 

 

T

 

 

Of course, we have seen BoundingSphere already, but for convenience we show its definition again here:

struct BoundingSphere

{

BoundingSphere();

D3DXVECTOR3 _center; float _radius;

};

15.5 Sample Application: Picking

Figure 15.5: Screen shot of this chapter’s sample

Team-Fly®

Picking 265

Figure 15.5 shows a screen shot of the sample application for this chapter. The teapot moves around the screen, and you can try to click on it with the mouse. If you click on the bounding sphere of the teapot, a message box will pop up indicating that you hit it. We handle the mouse click event by testing for a WM_LBUTTONDOWN message:

case WM_LBUTTONDOWN:

//compute the ray in view space given the clicked screen point d3d::Ray ray = CalcPickingRay(LOWORD(lParam), HIWORD(lParam));

//transform the ray to world space

D3DXMATRIX view;

Device->GetTransform(D3DTS_VIEW, &view);

D3DXMATRIX viewInverse;

D3DXMatrixInverse(&viewInverse, 0, &view);

TransformRay(&ray, &viewInverse);

// test for a hit

if( RaySphereIntTest(&ray, &BSphere) ) ::MessageBox(0, "Hit!", "HIT", 0);

break;

15.6 Summary

Picking is the technique used to determine the 3D object that cor-

 

responds to the 2D projected object displayed on the screen that

I

the user clicked on with the mouse.

II

art

The picking ray is found by shooting a ray, originating at the origin

of the view space, through the point on the projection window that

P

 

corresponds to the clicked screen point.

 

 

We can transform a ray r(t) = p0 + tu by transforming its origin p0

 

and direction u by a transformation matrix. Note that the origin is

 

transformed as a point (w = 1) and the direction is treated as a vec-

 

tor (w = 0).

 

To test if the ray has intersected an object, we can test if the ray

 

intersected a triangle that composes the object or test if the ray

 

intersects a bounding volume of the object, such as a bounding

 

sphere.

 

This page intentionally left blank.

Part IV

Shaders and Effects

Thus far, we have achieved a desired effect by altering the configuration of device states such as transforms, lights, textures, and render states. Although the various supported configurations provide us with some flexibility, we are still limited to predefined fixed operations (hence the name “fixed function pipeline”).

The primary theme of this part is vertex and pixel shaders, which replace sections of the fixed function pipeline with a custom program that we implement, called a shader. Shaders are completely programmable and allow us to implement techniques that are not defined in the fixed function pipeline. Consequently, the number of techniques that we have available at our disposal has greatly increased. The programmable sections of the rendering pipeline are commonly referred to as the programmable pipeline. A brief description of the chapters in this part follows.

Chapter 16, “Introduction to the High-Level Shading Language”— In this chapter we explore the High-Level Shading Language (HLSL), which is the language we use to write vertex and pixel shader programs in this book.

Chapter 17, “Introduction to Vertex Shaders”—This chapter explains what vertex shaders are and how to create and use them in Direct3D. The chapter illustrates vertex shaders by explaining the implementation of a cartoon styled shading technique.

Chapter 18, “Introduction to Pixel Shaders”—This chapter explains what pixel shaders are and how to create and use them in Direct3D. The chapter concludes by showing how to implement multitexturing using a pixel shader.

Chapter 19, “The Effects Framework”—In this chapter, we discuss the Direct3D effects framework. The chapter describes the purpose of the effects framework, the structure and syntax of effect files, how to create effect files, and how to use effect files in Direct3D applications.

267

This page intentionally left blank.

Chapter 16

Introduction to the High-Level Shading Language

In this chapter we describe the High-Level Shading Language (HLSL), which we use to program vertex and pixel shaders over the next three chapters. Briefly, vertex and pixel shaders are small custom programs we write, executed on the graphics card’s GPU (graphics processing unit), that replace a portion of the fixed function pipeline. By replacing a section of the fixed function pipeline with our own custom shader program, we obtain a huge amount of flexibility in the graphical effects that we can achieve. We are no longer limited to predefined “fixed” operations.

In order to write shader programs, we need a language to write them in. In DirectX 8.x, shaders were written in a low-level shader assembly language. Fortunately, we no longer have to write shaders in assembly language, as DirectX 9 has supplied a High-Level Shading Language that we can use to write shaders. Using HLSL over assembly language to write shader programs has the same advantages as using a high-level language, like C++, over assembly language to write applications, namely:

Increased productivity—Writing programs in a high-level language is faster and easier than writing them in a low-level language. We can spend more time focusing on algorithms rather than coding.

Improved readability—Programs in a high-level language are easier to read, which implies programs written in a high-level language are easier to debug and maintain.

The compilers, more often than not, generate more efficient assembly code than hand-written assembly code.

269

270Chapter 16

Using the HLSL compiler, we can compile our code to any available shader version. Using the assembly language, we would have to port the code for each desired version.

HLSL is also very similar to C and C++ syntax, thus there is a very short learning curve.

Finally, you will need to switch to the REF device for the shader samples if your graphics card does not support vertex and pixel shaders. Using the REF device means the shader samples run very slowly, but they still display the correct results, allowing us to verify that our code is correct.

Note: Vertex shaders can be emulated in software with software vertex processing — D3DCREATE_SOFTWARE_VERTEXPROCESSING.

Objectives

To learn how to write and compile an HLSL shader program

To learn how to communicate data from the application to the shader program

To become familiar with the syntax, types, and built-in functions of HLSL

16.1Writing an HLSL Shader

We can write the code to our HLSL shaders directly into our application source files as a long character string. However, it is more convenient and modular to separate the shader code from the application code. For this reason, we write our shaders in Notepad and save them as regular ASCII text files. Then we use the D3DXCompileShaderFromFile function (section 16.2.2) to compile our shaders.

As an introduction, the following is a simple vertex shader written in HLSL that was saved to a text file generated in Notepad called Transform.txt. The complete project can be found in the companion files under the title Transform. This vertex shader transforms the vertices by a combined view and projection matrix and sets the diffuse color component of the vertex to blue.

Note: This sample uses a vertex shader as an example, but do not worry about what a vertex shader is supposed to do yet, as they are covered in the next chapter. For now, the objective is to familiarize yourself with the syntax and format of an HLSL program.

Introduction to the High-Level Shading Language 271

/////////////////////////////////////////////////////////////////////

//

//File: transform.txt

//Author: Frank D. Luna (C) All Rights Reserved

//System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP,

//MSVC++ 7.0

//

//Desc: Vertex shader that transforms a vertex by the view and

//projection transformation, and sets the vertex color to blue.

/////////////////////////////////////////////////////////////////////

//

//Globals

//Global variable to store a combined view and projection

//transformation matrix. We initialize this variable

//from the application.

matrix ViewProjMatrix;

// Initialize a global blue color vector. vector Blue = {0.0f, 0.0f, 1.0f, 1.0f};

//

//Structures

//Input structure describes the vertex that is input

//into the shader. Here the input vertex contains

//a position component only.

struct VS_INPUT

{

vector position : POSITION;

};

//Output structure describes the vertex that is

//output from the shader. Here the output

//vertex contains a position and color component. struct VS_OUTPUT

{

vector position : POSITION; vector diffuse : COLOR;

};

//

//Main Entry Point, observe the main function

//receives a copy of the input vertex through

//its parameter and returns a copy of the output

//vertex it computes.

//

VS_OUTPUT Main(VS_INPUT input)

{

// zero out members of output

P a r t I V

272 Chapter 16

VS_OUTPUT output = (VS_OUTPUT)0;

// transform to view space and project

output.position = mul(input.position, ViewProjMatrix);

// set vertex diffuse color to blue output.diffuse = Blue;

//Output the projected and colored vertex. return output;

}

16.1.1 Globals

First we instantiate two global variables:

matrix ViewProjMatrix;

vector Blue = {0.0f, 0.0f, 1.0f, 1.0f};

The first variable, ViewProjMatrix, is of the type matrix, which is a 4 4 matrix type that is built into the HLSL. This variable stores a combined view and projection matrix, such that it describes both transformations. This way we only have to do one vector-matrix multiplication instead of two. Notice that nowhere in the shader source code do we initialize this variable. That is because we set it through the application source code—not the shader. Communication from the application to the shader program is a frequently required operation and is explained in section 16.2.1.

The second variable, Blue, is of the built-in type vector, which is a 4D vector. We simply initialize its components to the color blue, treating it as an RGBA color vector.

16.1.2 Input and Output Structures

After the global variables are declared, we define two special structures, which we call the input and output structures. For vertex shaders, these structures define the vertex data that our shader inputs and outputs, respectively.

struct VS_INPUT

{

vector position : POSITION;

};

struct VS_OUTPUT

{

vector position : POSITION; vector diffuse : COLOR;

};

Note: The input and output structures for pixel shaders define pixel data.

Introduction to the High-Level Shading Language 273

In this sample, the vertex that we input into our vertex shader contains only a position component. The vertex that our shader outputs contains a position component and a color component.

The special colon syntax denotes a semantic, which is used to specify the usage of the variable. This is similar to the flexible vertex format (FVF) of a vertex structure. For example, in VS_INPUT, we have the member:

vector position : POSITION;

The syntax “: POSITION” says that the vector position is used to describe the position of the input vertex. As another example, in

VS_OUTPUT we have:

vector diffuse : COLOR;

Here “: COLOR” says that the vector diffuse is used to describe the color of the output vertex. We talk more about the available usage identifiers in the next two chapters on vertex and pixel shaders.

Note: From a low-level perspective, the semantic syntax associates a variable in the shader with a hardware register. That is, the input variables are associated with the input registers, and the output variables are associated with the output variables. For example, the position member of VS_INPUT is connected to the vertex input position register. Similarly, diffuse is connected with a particular vertex output color register.

16.1.3 Entry Point Function

As with a C++ program, every HLSL program has an entry point. In our sample shader, we call our entry point function Main; however, that name is not mandatory. The shader’s entry point function name can be any valid function name. The entry point function must have an input structure parameter, which is used to pass the input vertex into our shader. The entry point function must also return an output structure instance, which is used to output the manipulated vertex from our shader.

VS_OUTPUT Main(VS_INPUT input)

{

Note: In actuality, it isn’t mandatory to use input and output structures. For example, you will sometimes see syntax similar to the following used, particularly with pixel shaders:

float4 Main(in float2 base : TEXCOORD0, in float2 spot : TEXCOORD1,

in float2 text : TEXCOORD2) : COLOR

{

P a r t I V