It's free to join Gamasutra!|Have a question? Want to know who runs this site? Here you go.|Targeting the game development market with your product or service? Get info on advertising here.||For altering your contact information or changing email subscription preferences.
Registered members can log in here.Back to the home page.

Search articles, jobs, buyers guide, and more.

By Ron Fosner
[Author's Bio]

Gamasutra
May 14, 2003

Introduction

HLSL with RenderMonkey

Oren-Nayar Diffuse Diffuse Shading

Printer Friendly Version
   

 

Change Login/Pwd
Post A Job
Post A Project
Post Resume
Post An Event
Post A Contractor
Post A Product
Write An Article
Get In Art Gallery
Submit News

 


 


Latest Letters to the Editor:
Perpetual Layoffs by Alexander Brandon [09.21.2007]

Casual friendliness in MMO's by Colby Poulson [09.20.2007]

Scrum deals and 'What is Scrum?' by Tom Plunket [08.29.2007]


[Submit Letter]

[View All...]
  



Upcoming Events:
SPARK Animation Festival
Vancouver, Canada
09.10.08

Women In Games Conference
Coventry, United Kingdom
09.10.08

3rd ACM International Conference on Digital Interactive Media in Entertainment and Arts - DIMEA 2008
Athens, Greece
09.10.08

GDC Austin
Austin, United States
09.15.08

Game Career Seminar
Austin, United States
09.17.08

[Submit Event]
[View All...]

 


[Enter Forums...]

Note: Discussion forums for Gamasutra are hosted by the IGDA, which is free to join.
 

 

 


Features

Implementing Modular HLSL with RenderMonkey

HLSL with RenderMonkey

When you first open RenderMonkey, you'll be greeted with a blank workspace. The first thing to do is create an Effect Group. To do this, right-click on the Effect Workspace item in the RenderMonkey Workspace view and select Add Effect Group. This will add a basic Effect Group that will contain editable effects elements. If you have the same capabilities as the default group (currently a RADEON 8500, GeForceFX or better) then you'll see a red teapot. If you're running on older hardware (like a GeForce3) then you'll have to edit the pixel shader version in the default effect from ps 1.4 to ps 1.1.

RenderMonkey automatically creates a vertex stream mapping for the positional data of the model, places the view/projection matrix in a shader constant for you, and creates the high level vertex and pixel shaders for you. The default vertex shader is shown in below:


The default vertex shader in RenderMonkey.

Both the high-level vertex and pixel shader editor windows have three areas. The top area lets you manage the interface between "external" parameters (either RenderMonkey supplied or user-created variables) and the shader and lets you pick the target shader version. The middle area is a read-only area that shows the parameter declaration block used by the HLSL. When you add a parameter to an effect, it will become available as an external parameter, and the parameter declaration block lets you see the association between these parameters and the shader registers. The bottom area contains the actual shader code that you edit directly. In Figure 1, you can see that the RenderMonkey supplied view/projection matrix is mapped to shader constant c0 (c0 though c3 is implied by the float4x4 mapping), and this name is used in the actual vertex shader. These variables can be considered global declarations. The input variables from the vertex stream show up as the parameters to the entry point function, typically called main.

As you can see in the Figure 1, RenderMonkey has provided the minimal shader as the default. The default vertex shader transforms the incoming vertex position by the view/projection matrix while the default pixel shader (not shown) sets the outgoing pixel color to red. You can edit the shader code in the lower window till you get the shader you want. To see what the shader looks like, click on the Commit Changes button on the main toolbar (or press F7) to internally save and compile the shader. If the shader has any errors, there will be an informative message displayed in the output pane at the bottom of the RenderMonkey window. If the shader compiled successfully, then you'll immediately see the shader results in the preview window.

And that's about all you need to know to edit shaders in RenderMonkey! The interface is very intuitive - just about everything can be activated or edited by double-clicking. You can insert nodes to add textures, set render state, or add additional passes with just a few clicks. The documentation for RenderMonkey comes with the RenderMonkey download and is also available on this page, along with a number of documents on using RenderMonkey.

Finally, you'll need to know some internal variables that are available to RenderMonkey, shown in Figure 2. If you add the RenderMonkey names (case sensitive) as variables they'll be connected to the internal RenderMonkey variables. The time-based values are vectors, but all elements are the same value. You can use these to vary values programmatically instead of connecting a variable to a slider.

Writing Modular Code in HLSL

If you've been writing low-level shader code, you probably haven't been thinking about writing modular code. It's tough to think modularly when you don't have any support in the language for any type of control statements. And surprisingly, there's still no actual support for modular code. A shader written in HLSL still compiles to a monolithic assembly shader. However, the HLSL compiler does hide a lot of the details and does let you write like we can write a modular shader. I mention this because it's easy to get lulled into thinking that you're working with a mature language, not one that's less than a year old. You should be aware of these limitations. There's no support (yet) for recursion. All functions are inlined. Function parameters are passed by value. Statements are always evaluated entirely - there's no short-circuited evaluation as in a C program.

Even with those limitations, it's surprisingly easy to write modular code. In Wolfgang Engel's article, he discussed the lighting equation for computing the intensity of the light at a surface as the contribution of the ambient light, the diffuse light and the specular light.

I've made a slight change by adding in a term for the light color and intensity, which multiplies the contributions from the diffuse and specular terms and by using I for intensity and C for color. Note that the color values are RGBA vectors, so there are actually four color elements that will get computed by this equation. HLSL will automatically do the vector multiplication for us. Wolfgang also created a HLSL shader for this basic lighting equation, so if you're new to HLSL you might want to review what he wrote, since I'm going to build on his example.

Let's rewrite the basic shader, setting things up so that we can modularize our lighting functions. If I add a color element to the output structure (calling it Color1), we can edit the main function to add in the vertex normal as a parameter from the input stream and write the output color. Insert two scalar variables, Iamb for ambient intensity and Camb for ambient color (correspond the above equation) in the RenderMonkey workspace. This will allow us to manipulate these variables from RenderMonkey's variable interface. RenderMonkey has a very nice interface that supports vectors, scalars, and colors quite intuitively. To implement the lighting equation we'll need to compute the lighting vector and the view vector, so I added these calculations for later use. The ambient lighting values and light properties (position and color) need to be provided to RenderMonkey by assigning them to variables. The basic vertex shader computing the output color from the product of the ambient intensity and the ambient color looks like this.

Note that vector is a HLSL native type for an array of four floats, it's the same as writing float4. Also note the use of swizzles when calculating the normalized vectors - this leaves the vector's w parameter out of the calculation. I also modified the default pixel shader to simply pass along the color created in the vertex shader as shown below. This simple pixel shader simply returns the (interpolated) color provided by the vertex shader.

Functions in HLSL

So let's start off by making the ambient calculation a function just to see how it's done in HLSL. Making the ambient calculation a function is pretty simple.

The static inline attributes are optional at this point, but I've placed them there to emphasize that currently all functions are inlined, so creating and using a function like this adds no overhead to the shader. This Ambient() function just computes the ambient color and returns it.

Creating the Diffuse function requires that we pass in the lighting vector and the normal vector. In addition to the argument type description you'd expect to see in a C program, HLSL allows you to specify if a value is strictly input, output or both through the in, out and inout attributes. A parameter that is specified as out or inout will be copied back to the calling code, allowing functions another way to return values. If not specified, in is assumed. Since this diffuse equation is an implementation of what's called a Lambertian diffuse, I've named it as such. The LambertianDiffuse() function looks like this.

Note the use of the HLSL intrinsic dot product function. The specular equation is taken from Phong's lighting equation and requires calculation of the reflection vector. The reflection vector is calculated from the normalized normal and light vectors.

The dot product of the reflection vector and the view vector is raised to a power that is inversely proportional to the roughness of the surface. This is a more intuitive value than letting a user specify a specular power value. To limit the specular contribution to only the times when the angle between these vectors is less than 90 degrees, we limit the dot product to only positive values. The specular color contribution becomes;

Implementing this in HLSL looks like the following:

Note the use of the intrinsic saturate function to limit the range from the dot product to [0,1]. Roughness is added to the RenderMonkey Effect Workspace and added in the shader editor as a parameter.

Using these functions we can now implement our main shader function as follows:

The three functions that we added are either placed above the main function or below, in which case you'd need to add a function prototype. As you can see, it's fairly easy to write functional modules in HLSL code.

Finally, Modular Code

The real utility of this comes when we create modules that can replace other modules. For example, suppose that you wanted to duplicate the original functionality of the fixed-function-pipeline, which implemented a particular type of specular called Blinn-Phong. This particular specular lighting equation is similar to Phong's but uses something called the half-angle vector instead of the reflection vector. An implementation of it looks like this:

To change our shader to use Blinn-Phong, all we need to do is change the function we call in main. The color computation would look like this;

Since all of these functions are inlined, any unused code is optimized out from the shader. As long as there's no reference to a function from main or any of the functions that are called from main, then we can pick which implementation we want in our shader code simply by selecting the functions we want, and we don't have to worry about unused code since it's not included in the compiled shader.

As we get more real-time programmability it becomes easier to implement features that have been in the artist's domain for years. Suppose your art lead creates some really cool scenes that look great in Maya or 3DS Max, but don't look right because the Lambertian diffuse in your engine makes everything look like plastic? Why can't you just render with the same shading options that Maya has? Well, now you can! If your artist really has to have gentler diffuse tones provided by Oren-Nayar diffuse shading, then you can now implement it.


______________________________________________________


join | contact us | advertise | write | my profile
news | features | companies | jobs | resumes | education | product guide | projects | store



Copyright © 2003 CMP Media LLC

privacy policy
| terms of service