Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2003-205
$eventId
ID of event: wwdc2003
$eventContentId
ID of session without event part: 205
$eventShortId
Shortened ID of event: wwdc03
$year
Year of session: 2003
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC03 • Session 205

Vertex Programming with OpenGL

Graphics and Imaging • 53:15

A must-see for developers who are interested in unlocking the maximum performance from the latest generations of graphics hardware, this session covers techniques for increasing 3D performance by transforming geometry using the display card's Graphics Processing Unit (GPU).

Speaker: Michael Larson

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it may have transcription errors.

Good afternoon everyone. My name is Travis Brown. I'm the graphics and imaging evangelist. And it's my pleasure to welcome you to Vertex Programming with OpenGL, Session 205. And sort of the theme that we have at this year's WWC is the aspect of leveraging the GPU. And in the graphics and imaging overview yesterday, we showed a lot of examples of using programmability to do interesting things, particularly interesting visual effects. Those were fragment programs, but there's also sort of a a companion technology or another way to program the GPU called vertex programming, which instead of touching fragments and therefore pixels, it's actually going to be using the GPU to do very high speed geometry calculations. And the two go hand in hand in terms of the ways you can really offload the burden of doing the graphics from the CPU and allow the GPU to do what it does best, which is work with geometry and then obviously draw that geometry. So it's my pleasure to welcome our speaker today, Mike Larson, up to the stage so we can take it to the presentation.

Thanks, Travis. Today we're here to talk about vertex programming with OpenGL. It's been around for a couple years, starting with the ATI RV200 in that generation. So we'll talk about it a little bit. Let's start out. We're going to go through an introduction to vertex programs. We're going to talk about vertex programs in the OpenGL pipeline, the computation model used by the vertex programs, the program syntax for the R vertex program, and go through a number of examples and starting from simple ones using Shader Builder and some more complicated ones using the Project Builder. So starting out, why use vertex programs? Well, I use vertex programs Say you're using fragment programs, a lot of the inputs that go into a fragment program come out of the vertex program. You might have texture coordinates, you might have some varying position coordinates that come through, or just in general, you'll probably end up using vertex programs if you're using fragment programs. Say your application doesn't fit into the standard OpenGL lighting pipeline, you've got an existing lighting model that you want to implement in Vertex programs, and you don't want to change that to the OpenGL one, you can use a Vertex program for that.

Say your application is pre-processing vertex arrays, or every time you draw an object, you're touching the data. One way to get rid of that, touching the data, and see view time is actually moving up to the vertex program, isolate that part of your program that actually does that and move it up. Examples would be surfaces and vertex tweening. Thank you. In-betweening is from keyframe animation. It's an old term. You know, you had the general cartoonists and the people who drew everything in between, so it's called "tweening."

So the benefits of vertex programs, essentially you're going to be offloading functionality from the CPU to the GPU. You have more flexible per vertex geometric operations, higher performance, and lower memory usage. So let's talk about Vertex programs in the OpenGL pipeline. So here's the old fixed model. You start out with the OpenGL vertices. You have your standard model view of transformation, the color materials and the lighting effects, and your perspective division.

And that's been replaced with vertex programs. Essentially, your vertex program replaces a lot of the same functionality that was, quote, a fixed functionality. A lot of that was actually implemented in microcode. You're just essentially allowing you to use that as microcode. The SGI machines for years used microcode for all these transforms. So what changes?

Your program, if you're using Vertex program, you're now responsible for doing all the transformation, the color materials and lighting, and a whole slew of other issues if you're using the OpenGL color lighting model. And if you're using them and you disable them, Here's a scenario. Say you're switching out of a vertex program and you're expecting the standard open gel lighting model to be there. You have to disable them or you'll get everything run through the vertex program regardless. So let's talk about the computation model.

So what invokes a vertex program? Well, there's a number of standard methods to find the ARB spec. Say that the -- you're issuing a GL vertex command, which usually kicks off some kind of operation indirectly through OpenGL vertex draw elements or draw arrays commands. The current raster position is changed. And essentially a vertex program terminates at the end of reaching the end of the program.

So the computation model is you have independent execution on each vertex. There can be, and there likely are, two, four vertex units in each GPU. Temporary data, so when you write a program, you kind of feel there's gonna be data left over, but Vertix program, you run the program, you're done, and it's all gone. So the input-output model, you have your vertex program, You have vertical state inputs, which be your position, color, textures, coordinates, and those kinds of values. Environment parameters.

OpenGL state. Let me talk about environment parameters real quick. Environment parameters are values that you can load up to your vertex program through GL or across all vertex programs, or for a single one. And OpenGL state information, which is the current lighting model and a number of matrices available from OpenGL.

In addition, you have a number of temporary registers. I need some water. available for use while you're executing the Vertex program. And then once your program completes computation for each value, say the color, the position, and everything else, you spit it to the Vertex data output. And once you reach the end of the Vertex program, you spit out everything to your output registers, you hit an end, Vertex program stops and it kicks it off to the rest of the engine. So what's available to vertex programs? Standard attributes, position, color, textures, coordinates, and additional programmable attributes that you'd set. OpenGL Transformer Lighting State.

So the parameter information is read only by the program. It's defined in the spec. Essentially constant values from the... from the sense of the program. From outside you can program them up using environment commands from OpenGL. There's two types. There's global parameters, which say you have multiple vertex programs, you can load up across these environment parameters across all vertex programs. Or you have local parameters, which only affect the current vertex program running.

The instruction set is limited to about 27 instructions. They're focused on transformer lighting. Remember, this is kind of like a microcode implementation exposed out, so they're very limited. There's no loops, no branches. You have limited program length. Some instructions are macros, which means you have instructions that are complicated that might do multiple things in one operation. They might actually be two instructions, or two or three instructions, and they are defined by the spec. So not all instructions can be, will require two instructions or more.

Oops, went ahead. There's data operands. There's scalar operands, which are standard floating point values, and SIMD operations, which are Altevec-type values, four floating point values indexed by X, Y, Z, and W. regardless of the color, texture, or what, they're always indexed by the same thing. All data and all operations is represented as SIMD float 4.

So regardless of what you think you're loading up a single value, it's always represented as 4 values. Thank you. So data component selection, you have a normal SIMD operation like an Altivec instruction. A equals B. You've got some input parameter, goes through some operation, gets flushed out at the bottom, and they all get set.

You have a swizzle SIMD selection, which allows you to move data between components. So it's on the input select. It's not on the output select. So if you have a two-source component, you might have source A might be XYZ, and source B might be ZZZ. You can select them all at the same one. You have a scalar mass operation, which is, these are the scalar operations. You can select which source operation or source component you want for the operation. So if you're doing like a reciprocal divide and you want to see the reciprocal of Y, you essentially select Y.

The instruction set has a number of standard scalar and SIMD instructions, like you see in Altivec. You have your standard add, subtract, multiply, and add. Multiply and add is a very important instruction. It's not a macro. It's a one instruction. You can do a lot with multiple adds, and you'll see in the examples I'll show you. You have absolute value, minimum and maximum, dot product, disinfectors, so real lighting. You can see the lighting in here, and reciprocal functions. Math functions are pretty limited. You have the exponent base two, floor fraction, and log base two.

And then the more complex instructions you have, component selection, lighting, conditional set on compare, doesn't operate like you think it does, but I'll show you how it works, and indirect registers load from parameter space. So you can't modify your temporary registers. You can't index them through indirect addressing. Only parameters can be indirect values. Thank you.

So let's look at the constraints. These are the base requirements for all Vertex program implementations. So if you write a Vertex program, the base implementation will have this many values. It has 96 program environments, 96 local values, eight program matrices, one address register, 128 instructions, and 12 temporary registers. Now, spec allows for anything more than that, but these are the base requirements that you can look to have in all implementations.

So how do you find out what your new piece of hardware has, your R300 or whatever you put in there? use essentially GL program arb and an integer value and query the interface and what's there. There's a whole bunch of enums that you can ask for and see what's out there. So let's talk about syntax.

All programs must start with the version signature of our vertex program 1.0. Must terminate with an end statement. And just about anything in between is okay. It has loose temporary and parameter variable definitions. You don't have to find all your temps at top. You can define them in line just as long, you know, before you use them, just like C++. plus.

So, what is a syntax? It's effectively a GPU independent assembly language. It's not targeting any specific GPU. It's independent of all types. It's not C, and it's not pure assembly. There's no fixed registry allocation. You don't, it's independent. Like I said, it's GPU independent. They're loaded as strings, they're runtime compiled by the driver, ATI, NVIDIA, whoever, and there's no fixed allocation of GPU registers.

Three kinds of parameters. You have the constant parameters, which are not programmable from your program. You have the environment parameters, which are programmable across all Vertex programs. And you have local parameters, which are specific to the currently bound Vertex program. You have temporary registers. Essentially, they start out with a temp, and you can name off up to 12. You'd be surprised how few you actually have to use. The key thing about using temporary registers is you get a mindset from Altivec or whatever you're used to programming on of creating a whole bunch of data and then having a result and storing them away. The key thing about using the temporary registers for vertex programs is compute your result and stick it in the result as fast as you can and reuse that temporary register.

And then a number of urinary, binary, and ternary instructions. You know, reciprocals and maths and adds and multiplies, those things. So, just to look at the instruction set real quick, this comes out of the ARB spec. You see the instruction on the, you have a defined set of inputs. Everything, just pretty much everything takes a vector input. Might have two sources. The output, that's the important part by looking at the spec is you might have, it might be a scalar function or it might be a vector, a SIMD operation.

You can see the flavor of the instructions and the syntax that's used. and more additional instructions. You can look at some of the math functions, the power, the reciprocal, the reciprocal square root. All those instructions are scalar outputs. So you have to select a particular, if you want the square root of some value, like the square root of the y value, it's going to be spread across all the outputs. And that's what it shows in this example.

So, where do you get more information on our Vertix programs? Well, the shader builder and the development tools comes for free, and it's a great place to start. It has active syntax checking. You're not gonna, you don't have to build a framework and build a program to make it work. You can just turn it on, start dialing away, and things work.

So let's talk about loading programs and controlling the execution environment for Vertex programs. So vertex programs are loaded as strings. Like I said, they're runtime compiled by the driver. Essentially, there's only one way to do it today. They've left ways to do it different ways in the future, but strings are the only way today. It has active syntax checking. So after you try to load a vertex program, you can test the GL get error, and if you call a GL get string with this new, it'll actually come back with a string reporting the actual position in the code where the error was found.

which is very useful. You'll find out. So loading, like I said, there's parameters. You have environment parameters. There's a specific call for loading environment parameters. It's program env parameter, and they're all loaded up SIMD values. There's no way to load a scalar value. Everything gets loaded as SIMD.

Also, there's an equivalent call for local parameters, GL, local parameters, and they also get loaded as SIMD. So let's go through a basic vertex program. What it's going to do, it's effectively the shader build example, maybe a little bit simplified. We're going to do a model view perspective transform of an input vertex and set the result and move it on.

So here we have Shader Builder. Let me bring it up a little bit. Let's look around here a little bit. You have essentially your code input here. This is actually loaded as a vertex program. You have to load it before any begin end statement, by the way. You can't change it while you're doing a begin end. Well, draw arrays and draw elements have implied begin ends, but effectively, before a begin statement, you better be doing it. You have to have this loaded as a program.

So let's go through this again. You have your editor interface right here. You can enable, disable the Vertex program. You have a number of objects available to draw your, test your vertex program out on. You have a sphere, teapot, a hedron, plane. I think it's actually called that. I don't know.

Everybody draws teapots. And you have your GL parameters. And you can dial up your color, you can change the color on the fly, go to Chrome, uh... you can also select uh... number texture units available for input uh... you can load them up and select them through this interface but today we're just going to use uh... Texture unit zero. And you can enable it or disable it. Turns things on. So.

And then over here you have a bunch of debugging information. I'm not going to go through it. You know, it essentially allows you to step through a Vertex program and watch the values change. Thank you. In addition, Shader Builder has an instruction reference that comes up over here. So you're not digging around the ARB spec. It's 64 pages long and it's about, it's really detailed, you don't wanna read it, so. Read it when you get there, you know, okay. But you can essentially have online documentation of all the instructions and what they're supposed to do. So let's go back. Turn that off.

Start the program out, the R vertex program 1.0. I have an offset, a scale, and a zero value. This is a constant thrown in here. Once you program enough, you start throwing in your standard cut and paste, and I have a whole bunch of standard values I use all the time. Here's a temporary value, the vertex position. Let's show you an example of the runtime compile.

Sorry, inline error checking. So, see, it comes up with an error saying there's something wrong here. And if you look at the bottom, it says line nine. Actually, if you did the DL error check and actually pulled that string out like I showed in that previous example, it would return this same value.

So as you work into project builder examples and you start doing it by yourself, just about all your Vertex programs will pull that string out just to help you out. So, let's start out. Right here I'm just adding a, here's the vertex position right here. That's an input value that comes from the program. Here's a temporary called VFOS and a zero value. So I can inline, it can actually add an offset. and it actually automatically adds it to the program. I can change that offset real time. These are great tools, by the way.

One thing I do a lot is I actually get a program up and running here, cut and paste it, stick it in a file, and add it to my project builder. And then if I develop errors in the program, I always come back and paste it in here, see where the error's at. Um... And right here, so I'm taking the vertex position, the vertex from the input, assigning it to a temporary by doing an add of an offset, and then I'm multiplying by some scale value. And you can comment all this stuff out.

Just like that. It's pretty easy. Moving on. Then we do the transform of the value we've been messing around with. We set the result color. You can set these to anything you want to. I don't want to spend too much time here. and then also set the text coordinate. So that's an example of a basic vertex program within Shader Builder. It's pretty simple, it's probably 10 instructions, five instructions long. So, let's go back.

So we're going to go through next, we're going to go through a lighting vertex program. That's another major use for vertex programs for, like I said, if you don't have a standard GL lighting model that you want to use. Thank you. What we're going to do is we're going to use Shader Builder. It's essentially the same example that comes out of your development example area. We're going to model view the transfer of an input vertex, perform some lighting computations, and then move on. Let's do the demo. A little more complex.

So I wanted to remove some functionality before I moved on too much. I'll leave that up there. So you can look at this in stages, this thing being applied in stages. A trib, effectively it's a pound to find. It really is. So if you want to use nice names for standard input values, you can call them a tribs. Now I have two parameters here. And if you notice, in this parameter right here, I'm sharing a whole bunch of values. Rather than creating multiple parameter variables, I'm jamming three scalar values into one SIMD value.

You also save parameter space. And here's your light position. So what we go through here, we do our model view perspective transform. We start computing the lighting value. based on the light position. Then we start issuing a light instruction. And then we add that to the current value. So this is standard shading stuff. And then here we add a little Fong shading to it.

So the nice thing about this tool is you can do things right in line and test them as you go. As you develop, say you would develop a bug through here, you can comment it out real time and actually see what part of the program is actually messing up. And then we go through and we move the result to the output position and the color.

So those are two simple examples of how to use vertex programs in the Shader Builder. We're not going to talk about Shader Builder too much. We're going to move on to some new ideas. So as far as Shader Builder goes, it's a great place to start. It's a great tool. You load up textures. You don't have to build a framework. You can get started today. It also provides active syntax checking for, you know, of your Vertex program as they develop. In addition, as you start out, it has online instruction information.

So, as far as program possibilities, you know, a lot of people, You look at the instruction set, it's rather small, lighting focused, the code space is pretty small, we're all used to having as much code as we want to, and the number of temporary registers is actually pretty small, but you'd be surprised what you can actually do with that. You can do surfaces, you can do active displacements, you can do real-time displacements, you can do advanced lighting effects, keyframe animation, and visual computation.

So let's go some real quick tips from an application standpoint. Best way to use vertex programs is using vertex arrays combined with vertex array ranges. Key thing about this is you're moving all the CPU from the issuing and fetching of all the vertex arrays. So you're keeping the CPU out of the equation.

Use compound selection to save parameter space. So rather than having multiple parameters defined, like one, two, the runtime compiler is not going to be able to figure this out. Stick them together. In addition, use compound parameters. Save yourself reciprocal multiply. Save it by actually pre-computing these fixed values and combining them in one array. So here I have factorial 6, 720, negative 720, the inverse of that and the negative of that.

Additional programming tips. These are things I've had to figure out how they went. Only one value for some instructions can be selected. And at some point, you might want to merge all those values back into the same vector, or same SIMD value, and start computing again, rather than generating a single scalar value, and then doing a whole bunch of work on it, generating another SIMD scalar value, doing a whole bunch of work on it. You might want to merge all them back together and just compute them like you would using Altivec. So you can use the select parameter and a multiply add instruction to merge everything back together. This shows how you're using, you're doing a reciprocal on three values, and then you're starting to merge them back together by doing a multiply add back into one value. This actually is quite helpful.

Conditional selection. There is no, you know, branches, so how do you do conditional selection? Well, the only way to really do it is through multiplies. So, you have a value, say you want to do with a greater than or equal to b, then assign c equals a or something like that.

you have to do, you know, set greater than equal, which actually sets, it doesn't actually, what it does, it sets the, if it's true, it sets the output to ones. So you're going to use that ones value to multiply against the input value, so it's either going to be one or zero, and a mat instruction in between that merges the two together like a conditional would. So I've got it down to four instructions, and there's no branches. So let's talk about vertex programs for surfaces.

So a lot of the lighting functionality for vertex programs is actually moved down to fragment programs. You can get per-pixel lighting. So what else can you do with this? This is a pretty powerful little tool you have here. So rather than tweaking with your vertices every time you want to move somebody's position or you want to define a new shape, just use UV mesh. Essentially two-dimensional mesh that you create once and you never touch it again. So the vertex program is going to compute the x, y, z position of all the values. You can be surprised what you can do with it. You can do quadratic surfaces, implicit surfaces, parametric surfaces. You can do bilinear interpolation, Bezier, B-splines, and NURBS, all vertex programs. So what's a UV mesh? Well, a UV mesh is a 2D mesh. It's bounded by UNV. UNV can be bounded between any kind of fixed value.

Parametric surface, which is base area surfaces, or nerves, or B-splines, they're bound between zero and one, implicitly, for the whole surface. So, you know, why would you want to use a vertex program for a surface? Well, you have to define one mesh. One mesh for all your objects. And by using that, you never have to touch your vertices again, you can load them up in VRAM, and you'll never see them again. You can animate surfaces using control parameters. We'll show an example of that. And you're offloading the work from the CPU to the GPU. So let's talk about an implicit surface vertex program.

It's a cool math thingy, picked it off a webpage. You know, that's three sign values. I got a U and V for X and Y, sign of U and V for X and Y, and then four control parameters combined with U and V for the final Z value.

So we're going to build a UV mesh as a vertex array, and we're going to bound it between negative pi and pi and have some number of steps in between. And then we're going to fill in the position with the UV mesh, U and V. I see Z is zero is actually right here. The U and V are just input parameters to the vertex program. Thank you.

So we're gonna create our program, debug it through Shader Builder, and load it as a program string. Then we're gonna load the shape control parameters, the A, B, C, and D, you've seen in the previous equation, as local parameters. And then we're gonna submit a number of quad strips as a, for drawing.

So here's our object in line format. This is actually a two-dimensional UV mesh being input and it's finely tessellated and all the positions are being computed by the Vertex program. Right here are our control parameters. We can dial in anything we want, change the shape. Tear it apart. So these are the A, Bs, and Cs. Everybody who sees this wants to go out and do knots and stuff like that. Good luck. But you can do a lot of cool stuff. So let's do a little demo here.

Right now we're computing the shape on the fly. It's been loaded once in the VRAM. It's not coming across AGP at all. The texture coordinates are being computed by the Vertex program, real time. The shape is being perturbed by the equalizer values. I'm fetching them. This is an OpenGL context right here. I'm pulling the input from QuickTime. And here's the volume indicated. It's kind of fun.

What's that example? It's fun. So what did you see there? You've seen a vertex program that computed the object shape and color and the texture coordinates. The control values, A, B, and C, D, were fed up as control parameters. So one SIMD value per frame was loaded up and it computed everything else based on that one value. There's no sine function. So how did I compute that?

I used a McLaren power series. You can do a lookup, but it's just about as much work as doing McLaren because you're going to do indirect address register loads and all that stuff. And then once I had the values computed, I computed all three sign values the same times using an ultimate similar code. So let's take a look at the program. It's rather small up here. You guys see that? and lights maybe. So there's an input parameter, ABCD, four temporary values, not a whole lot.

So the surface is described by two fixed UV values for X and Y, and then the control parameters for the shape are in the Z. So I had to compute those on the vertex program rather than stuffing the vertex array every time with new values. So that's about six instructions there. That's the example of the syntax. It's very assembly-like. Then I'm cranking on and I'm starting to compute the sign values through a number of terms. So if you notice, I'm doing a multiply and a mat instruction, and I'm continually moving on.

And then we do a few more terms. We set the output position. So once I have the output position, I jam it away, 'cause I'm gonna use that temporary value again. And then I set the texture coordinates to some input value, which is actually get time. I jammed it in the EQ0123x value. And then I add it to the vertex coordinate, and that value's been normalized between 0 and 1. And that's it.

So where do you go from here? Try doing waves. Waves are actually pretty simple. Very similar. Use cosine functions. You can have essentially multiple emitters. If you think about things bobbing up in the water, you have three, four of them bobbing in the water. And for each point on your water, you just have to compute how far you are away from that cosine emitter and compute your Z value on that and then sum across all the emitters in the whole thing. There's a demo tomorrow that shows this. It's pretty cool.

So let's go through NURBS. Actually, I had bilinear surfaces, B-splines, and everything else figured out. NURBS took a while to figure out how to do them. So why use a vertex program for NURBS? Most people who use NURBS in OpenGL have already figured out that NURBS is slow. You have to do a multiply, accumulate across a number of control points, and they've either rolled their own NURBS evaluators. You can store multiple patches as a single vertex array and just load up control points for each patch.

You can do automatic level of detail based on how close the patch is to the user or to the screen by selecting multiple different UV meshes. Thank you. So what's a NIRB? Actually, I was going through the airport, and my book dropped out, and the guy comes up and goes, what's a NIRB? And I got to about non-uniform rational, and his eyes glazed over, and he walked away. So most people say that. Nobody really understands NIRBs. Actually, that's what it means. So it's a surface defined by interpolating a number of control points.

It's used a lot in CAD. It's dominant in CAD for, 'cause it can do sphericals and a number of things you can't do with Bayes-Zier surfaces. If you want more information on NURBS, I'm not gonna explain how they work here. Go search for "Introduction to NURBS on the Web" or Rogers has a pretty good book on it and it's very simple to read.

So, we're gonna do an outline of how to draw NURBS using a vertex program. We're gonna compute the B-spline basis functions for that particular NURB on the host. It's a recursive evaluator. That's the biggest problem I had with figuring out how to do it on the vertex shaders. It's a recursive function.

There's no branches, there's no calls, there's no jumps. So it had to be computed on the host. That's actually a pretty good thing 'cause you only compute it once and you never use it again. So you're going to load the control points as local program parameters. And then you can do the same thing for your position, your normal, your texture coordinates. They just get loaded as control parameters. They don't get loaded as a new mesh. It's essentially just control point parameters. So you're only loading 16 points for each parameter that you want to load in this example.

So this example is a four by four controlled mesh. The position information is interpolated by the vertex program. Real simple example. On the implementation side, so you have four by four control mesh. If you don't know anything about NURBS, that means you're going to have 16 control points, 16 basis functions per UV value, and that's a lot of storage. So what I do is I actually post-multiply the basis functions in terms of rows and columns on the vertex program as I go along, and that essentially reduces the requirements down to eight floats. So if you look at the little formula right here, you see the UV point is the sum of your number of control points, which is 16, then you have two sets of basis functions, one for the rows and one for the columns. Let's do a demo.

So here we have our surface, I put a texture on it, just to show, you know, There's a UV values coming in there. It's actually quite a few number of points. It's 10,000 points. It's 100 by 100 grid. There's a number of the control points right here. You can grab the control points.

You can move the surface real time. This is all being computed on the Vertex program. Then loaded as a mesh, a UV mesh in VRAM. It never goes across the AGP bus more than once. And you can do a lot of fun things with it. NURBS are actually a very flexible surface.

So there we go. So that's cool. Thank you. So, let's talk about what we've seen. Mesh was 100 by 100, it's 10,000 points. If you do the math, just for the position only, that's 96,000, 960,000 flops that the host didn't have to do. A full screen application running at 1280 by 1K, 75 frames per second, blah, blah, blah, blah, is about a gigaflop, just for the position only. So if you wanna add textures and normals to that, you can add on essentially three gigaflops. And the GPUs can do it.

So let's talk about the program real quick. Hopefully you're not straining your eyes. I have two inputs, a U basis and a V basis. There's four floating point SIMD values there. Bring them in through the vertex position and the color. And I have a number of control points as program locals.

I didn't show them all, there's a number of 'em, 16. So here I am, I'm pre-computing, post-multiplying the basis functions to come up with the required basis functions I need for each row-column combination. So I multiply, post-multiply, and then I start my multiply-accumulate on all the control points as I go along for each point.

For this example, it's a four by four, so there's four rows, so there's four sets of these things. I go along, continually computing the new basis functions, and I finally end up with a final position. do a transform, I have a result position, and then I start, then I just move the texture coordinate along. The texture was normalized between zero and one, so it fit across the whole surface.

So where do you go from here? Pretty cool stuff. Well, you can import the position and normals and the color information, texture information, from a modeler program directly into this. You can design vertex programs for different size meshes, you know, 3x5s, anything in that value. If you look at the number of instructions it takes, once you figure out how it works, you know that you're bound to a certain size because you can't interpolate 10x10 control meshes. There's just not enough space in the vertex program to do that. And then you can actually do, I haven't figured out how, but I know you can do it, subdivision surfaces. Subdivision surfaces, people have always said that they're algorithmic. They're always, like, similar to B-splines. Um... But if you look for it, there's an exact solution for subdivision surfaces in SIGGRAPH98 in the back end of it. Somebody actually sat down and figured it out. So that was one of the biggest problems of implementing subdivision surfaces. You know, if you want to do very simple demos, bilinear surfaces are easy. Basiar surfaces, you compute the basis functions on the vertex program. And NURBS get a little more complicated. Like I said, it uses recursion, but busy errors are simple. So let's do a wrap up.

So vertex program, they're a powerful tool for shading and lighting. You can use custom lighting models outside the OpenGL lighting model. You can add additional complexity to your geometries in vertex programs. And also, you can actually go to a lot finer level of detail. I mean, if you look at some of the games out there today, the level of detail is horrific.

You can use UV meshes to define most surfaces. And once the UV mesh is defined, you can load it in VRAM, and it'll never go across the AGP bus again. Essentially, you can lower the CPU load for most applications, and we're seeing up to 100 times faster performance than having the host compute those kinds of values.

So where do you start? Start with Shader Builder. Start with simple transforms. Play with the scale, play with the position, move those things around, change the color, get a feeling for how the language works. Move on to lighting models. I actually skipped the lighting models, I went straight to services, but that's just the way I am. And then move on to simple UV meshes. Try implicit surface models, come up with your crazy cool math thingies, and then start exploring B-splines and NURBS and everything else.

So more information on the Arb Vertex program. You can read the spec. It's 60 pages long and very detailed. Lots of information in there. And it gives you kind of a guideline of what things are supposed to do. Or you can use Shader Builder. I'm gonna try and have these posted in the next couple weeks, hopefully. - Not the same, just show this post. - Yeah, yeah. And then, or through the Mac OpenGL mailing list.

Now, like what I want to do is just do a quick, you know, twirl through the roadmap here. We have planned for the remaining sessions talking about OpenGL and also the graphics technology Mac OS X. I think we're actually-- on Wednesday, obviously. So what we have remaining for you tomorrow in the OpenGL track is essentially fragment programming with OpenGL. This is the sort of more pixel-specific companion to programmability. It's also gonna be a really cool session, lots of interesting demos. So if you're interested in programmability, unlocking the power of the GPU, you should also attend that session. We also have, obviously we're gonna talk tomorrow about our course-to-D technology, which is also relevant for OpenGL developers, 'cause one of the things we announced in our graphics and imaging overview session yesterday is the ability to use 2D drawing API to draw directly into an OpenGL context. That can also be very exciting for OpenGL developers, 'cause it can make things like doing high quality text in OpenGL very easy.

Then we have a great session that if you're doing any OpenGL development, you have to attend session 209, which is the OpenGL optimizations. One thing that I've realized in working with a lot of developers who are using OpenGL is in many cases they're leaving performance on the floor on the platform. And it's in many cases we have a great tool set, and this session will provide a lot of information for you to learn how to take a look at your applications and figure out how to unlock the true performance potential of both the GPUs and also the Macintosh as a platform and Mac OS X. And then another thing that might be interesting is session 211, which is going to be on Friday, which is introduction to Quartz services. Now, this is a non-drawing API in Quartz, but one of the things it does that's very important is it manages the displays. So a lot of OpenGL developers need to find out what the configuration of the display is, what display modes available on the particular display are. And this is the core APIs that we really want developers to adopt and use in their applications to do things such as screen capture and display reconfiguration. So you should attend that. And then also we have our hardware partners, ATI, are here this year. And then session 212, which is also on Friday, they're gonna sort of push the envelope and show what you can do with their latest generation GPU hardware and also programmable features because their demo team is gonna essentially show what they do to create their incredibly captivating demonstrations that they do to support their announcements of their hardware. So it's gonna be a really cool session and it's gonna leverage a lot of what you've learned today and tomorrow in fragment programming, with fragment programming session and Vertex programming.

And then obviously we have a feedback forum on Friday, our traditional spot, the last feedback forum. But we urge you to come back, come to the feedback forum and let us know what sort of things you want to see in Mac OS X from a technology perspective. Because a lot of the feedback we get at WWC is what we take back and start working from to figure out what goes in the next major release of Mac OS X. So I think we have a QA, or we have contact information.

You can, if you have questions about what you've learned, you can contact Mike Larson, who is the presenter today. And if you have other questions, Jeff Stahl is very active with developers directly. And you can also use him as a contact reference point. I'd also like to add my name to this list. If you had questions about the session or any of the graphics technologies on Mac OS X, you can feel free to contact me, Travis Brown, at [email protected].

So we have some additional information. These are essentially where the R Vertex programming spec can be found. And I'll leave this up here for a little bit. And what I'm actually going to do is, why don't I go ahead and invite the Q&A team up, the OpenGL engineering team up to the stage. And we'll actually engage in a question and answer. And I'll leave this up here.

So if you want to copy information down off this, you'll have plenty of time. Okay, so we have some microphones in the center right here. If you have any questions about what you've seen today, please feel free to go to microphone. Basically announce your name and your company, and we'll field your question.