|
楼主 |
发表于 2005-8-8 12:07:13
|
显示全部楼层
Texture Coordinates 纹理坐标
Note that it is stretched/squashed to fit the rectangle. You can adjust that by adjusting the texture coordinates. For example, if you change the lines where u = 1.0 to u = 0.5, then only half of the texture is used and the remaining part will not be squashed. So, if you had a 640x480 image that you wanted to place on a 640x480 window, you could place the 640x480 image in a 1024x512 texture and specify 0.625, 0.9375 for the texture coordinates. You could use the remaining parts of the texture to hold other sub images that are mapped to other panels (through the appropriate texture coordinates). In general, you want to optimize the way textures are used because they are eating up graphics memory and/or moving across the bus. This may seem like a lot of work for a blit, but it has a lot to do with the way new cards are optimized for 3D (like it or not). Besides, putting some thought into how you are moving large chunks of memory around the system is never a bad idea. But I'll get off my soapbox. 注意纹理会被拉伸和缩短以适合矩形。你可以通过调整纹理的坐标来调整这些。例如,如果你把u=1.0那行改为u=0.5,那么只有一半的纹理被使用,而剩下的另一半不会被压缩。所以,如果你有一个640X480的图片,你想把它放到一个640X480的窗口中去,你应该将640X480大小的图片放到一个1024X512大小的纹理中去然后指定纹理坐标为0.625,0.9375。你可以使用纹理中剩余的部分来放置那些会被映射到其他的面板中去的子图片(通过相应的纹理坐标)。通常,你会想优化纹理被使用的方式,因为它们吃光了图片内存并且在总线中移动。这看上去很象blit中的大量的工作,但是很多都会被新式的为3D进行优化的显卡处理掉了。此外,多多考虑如何在系统中移动大块的内存决不是一个坏的想法。但是我还是开始我的演说吧。
Let's see where we are so far. At one level, we've written a lot of code to blit a simple bitmap. But, hopefully you can see some of the benefit and the opportunities for tweaking. For instance, the texture coordinates automatically scale the image to the area we've defined by the geometry. There are lots of things this does for us, but consider the following. If we had set up our ortho matrix to use a percentage based mapping, and we specified a panel as occupying the lower quarter of the screen (for a UI, let's say), and we specified a texture with the correct texture coordinates, then our UI would automagically be drawn correctly for any chosen window/screen size. Not exactly cold fusion, but it's one of many examples. Now that we have the texture working well, we have to get back to talking about transparency. 让我们来看看我们走了多远。首先,我们写了很多代码来blit一个简单的位图。但是,希望你能够看到一些好处和机会。例如,纹理坐标自动缩放图片以适应我们的几何定义。这为我们做了很多工作,但是考虑到后面。如果我们设置使用一个基于百分比映射的垂直矩阵,并且,我们指定一个占据屏幕底部四分之一位置的面板(让我们说它是UI吧),而且我们也用正确的纹理坐标来指定它的纹理,这样,我们的UI在任何选定的窗口/屏幕大小下都会被自动的正确绘制出来。(Not exactly cold fusion),但这只是很多例子中的一个。现在我们已经让纹理可以工作的很好了,我们回过头来谈论一下透明。
Transparency 透明
As I said before, one easy way of adding transparency is to specify a color key value in the call to D3DXCreateTextureFromFileEx. Another is to use an image that actually has an alpha channel. Either way, specify a texture with some transparency (either with an alpha channel or a color key value) and run the app. You should see no difference. This is because alpha blending is not enabled. To enable alpha blending, add these lines to PostInitialize: 象我以前所说的,加入透明的一个简单的方法就是在调用D3DXCreateTextureFromFileEX函数里指定一个ColorKey值。另一个办法是使用一个实际带alpha通道的图片。无论使用哪种方法为纹理指定透明(使用alpha通道,或者ColorKey),然后运行,你都会看不到有什么区别。这是因为alpha混合还没有被允许。在PostInitialize中加入这些行以允许alpha混合:
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
The first line enables blending. The next two specify how the blending works. There are many possibilities, but this is the most basic type. The last line sets things up so that changing the alpha component of the vertex colors will fade the entire panel by scaling the texture values. For a more in depth discussion of the available settings, see the SDK. Once these lines are in place, you should see the proper transparency. Try changing the colors of the vertices to see how they affect the panel. 第一行允许了混合。下两行指定混合如何工作。这会有很多的可能性,不过这是最基本的类型。最后一行进行一些设置以致当改变顶点颜色的alpha成分时会缩放纹理值来减弱整个面板。关于可使用设置的更深层讨论,请参见SDK。一旦这些行被加入进来,你会看到正确的透明。试着改变顶点的颜色来看看它将如何影响面板。
Moving the Panel 移动面板
By now our panel has many of the visual properties we need, but it's still stuck in the center of our viewport. For a game, you probably want things to move. One obvious way is to relock the vertices and change their positions. DO NOT do this!! Locking is very expensive, involves moving data around, and is unnecessary. A better way is to specify a world transformation matrix to move the points. For many people, matrices may seem a bit scary, but there are a host of D3DX functions that make matrices very easy. For example, to move the panel, add the following code to the beginning of Render2D: 现在我们的面板已经有了很多我们需要的视觉属性,但它还只是粘在我们的视口中央。在游戏中,你可以想让一些东西移动起来。一个显而易见的方法是重新锁定顶点,然后改变它们的位置。千万不要这样做!锁定是很昂贵的操作,它包括数据的移动,并且这是不必要的。一个更好的方法是指定世界变换矩阵来移动这些点。对于很多人来说,矩阵看上去是有一点吓人的,但是在D3DX中有一大群函数让矩阵使用起来非常简单。例如,为了移动面板,在Render2D函数的开头加入下面的代码:
D3DXMATRIX Position; D3DXMatrixTranslation(&osition, 50.0f, 0.0f, 0.0f); g_pd3dDevice->SetTransform(D3DTS_WORLD, &osition);
This creates a matrix that moves the panel 50 pixels in the X direction and tells the device to apply that transform. This could be wrapped into a function like MoveTo(X, Y), which I won't actually give the code for. Earlier I said to remember the fact that the vertex code specified the vertices around the origin. Because we did that, translating (moving) the position moves the center of the panel. If you are more comfortable with moving the upper left or some other corner, change the way the vertices are specified. You could also create different coordinate systems by correcting the parameters sent to the MoveTo function. For example, our viewport currently goes from -100 to +100. If I wanted to use MoveTo as if it were going from 0 to 200, I could simply correct for it in my call to D3DXMatrixTranslation by subtracting 100 from the X position. There are many ways to quickly change this to meet your specific needs, but this will provide a good basis for experimentation. 这里创建了一个可以在X方向移动面板50个象素的矩阵,然后告诉设备应用这个移动。这可以被装封到一个象MoveTo(X,Y)的函数里去,不过我没有实际给出这样的代码。前面,我说过要记住顶点是相对于原点来定义的。因为我们是这样做的,所以,平移(移动)移动了面板的中心位置。如果你认为移动左上角或是其他的角会更加适合,请改变顶点定义的方式。你也可以通过传递正确的参数到MoveTo函数来创建不同的坐标系统。例如,我们的视口当前是从-100到100。如果我想将视口认为是从0到200那样来使用MoveTo函数,我可以简单的在我调用D3DXMatrixTranslation时从X坐标中减去100来进行更正。有很多方法可以很快的改变以使你能看到你所想要的效果,但是,作为实验这将提供一个好的基础。
Other Matrix Operations 其他的矩阵操作
There are many other matrix operations that will affect the panel. Perhaps the most interesting are scaling and rotation. There are D3DX functions for creating these matrices as well. I'll leave the experimenting up to you, but here are a few hints. Rotation about the Z-axis will create rotation on the screen. Rotating about X and Y will look like you are shrinking Y and X. Also, the way you apply multiple operations is through multiplication and then sending the resulting matrix to the device: 有很多其他的矩阵操作可以影响面板。最有趣的可能是缩放和旋转了。有一些D3DX函数可以很好的创建这些矩阵。我将把这些实验留给你来做,不过这儿有一些提示。关于Z轴的旋转将会在屏幕上旋转。而关于X和Y轴的旋转将看上去象是Y轴和X轴在收缩。另外,应用多个操作的方法是通过乘法,然后将结果矩阵送给设备:
D3DXMATRIX M = M1 * M2 * M3 * M4; g_pd3dDevice->SetTransform(D3DTS_WORLD, &M);
But, remember that the product of matrix multiplication is dependent on the order of the operands. For instance, Rotation * Position will move the panel and then rotate it. Position * Rotation will cause an orbiting effect. If you string together several matrices and get unexpected results, look closely at the order. 不过,记住矩阵乘法的结果是依赖于操作数的顺序的。例如,Rotation*Position将移动面板然后旋转它。Position*Rotation将导致一个沿轨道而行的效果。如果你排列了几个矩阵在一起,但是得到了并不期待的结果,请仔细的看看排列的顺序。
As you become more comfortable, you may want to experiment with things like the texture matrix, which will allow you to transform the texture coordinates. You could also move the view matrix to affect your coordinate system. One thing to remember: locks are very costly, always look to things like matrices before locking your vertex buffers. 当你变得更加轻松时,你可以想去试试象纹理矩阵这样的东西,它将允许你移动纹理坐标。你也可以移动观察矩阵来影响你的坐标系统。记得一点:锁定是非常昂贵的,在你锁定你的顶点缓冲之前,总是先看看象矩阵这样的东西。
Wrapping Up 装封
Looking at all the code listed here, this is really a long, drawn out way to do a blit, but the nice thing is that most of this can be wrapped into tidy functions or classes that make all this a one time cost for long term benefit. Please remember that this is presented in a very bare bones, unoptimized way. There are many ways to package this to get maximum benefit. This method should be the optimal way to create 2D applications on current and coming hardware and also will pay off in terms of the effects that you can implement very easily on top of it. This approach will also help you blend 2D with 3D because, aside from some matrices, the two are the same. The code was easily adapted from 2D work that I did in OpenGL, so you could even write abstract wrappers around the approach to support both APIs. My hope is that this will get people started using DX8 for 2D work. Perhaps in future articles I will talk about more tricks and effects. 看看这儿列出的所有的代码,为了进行blit我们走了很长的一段路,但是好事是很多都可以被装封到一些小的函数或是类中去,这样我们就可以一劳永逸了。请注意,这儿是使用一种非常梗概的而且没有优化的方法来表示的。有很多方法可以将这些包装起来以获得最大的收益。这可能是在当前的和以后的硬件上创建2D应用程序的最佳方法,而且你也可以获得在硬件上可以很简单的实现那些效果的好处。这种方法也可以帮助你在3D中混合进2D元素,因为在矩阵面前,它们是一样的。这些代码也可以简单的适合在OpenGL中进行2D工作,所以你甚至可以写一个抽象装封来支持两种API。我的希望是这可以让人们使用DX8来做2D工作。可能在以后的文章中我会讨论更多的技巧和效果。
|
|