第九章:颜色

来源:互联网 发布:软件测试转正心得体会 编辑:程序博客网 时间:2024/04/30 08:23

Chapter 9: Color

Overview

In the last chapter, we rendered the objects in the scene using lines to form wireframe meshes. In this chapter, we learn how to render solid objects with color.

本章我们来学渲染有颜色的实心物体

Objectives:

·         To discover how colors are described in Direct3D.

·         To find out what changes need to be made to our vertex and pixel shaders to support color.

·         To continue to strengthen our understanding of how vertex and pixel shaders work.

·         To learn how to animate and color geometry in a vertex shader.

 

9.1 Color Representation颜色的表示

Figure 9.1 shows a common color dialog box you can make with the Win32 API function ChooseColor. As you can see, one way to specify a color is by specifying its red, green, and blue color components (RGB triplet); the additive mixing of these three components determines the final color. Each of these components can be in a range 0 to 255 (the range an 8-bit byte can store), and thus, with this setup, 256×256×256 = 16,777,216 distinct colors can be represented. We can think of the colors red, green, and blue as our "basis" colors from which we can express all other colors as a linear combination.

 

扯颜色的定义

 

Figure 9.1: Typical color chooser dialog box.

Tip 

If you are not experienced with describing colors as RGB values, then you might want to spend some time in your favorite 2D computer paint program experimenting with a dialog likeFigure 9.1 to build an intuition of the colors formed by different RGB values.

9.1.1 D3DCOLOR

We use two different types of structures to hold the RGB data. The first is the D3DCOLOR type, which is actually typedefed as a DWORD and is 32 bits. The bits in the D3DCOLOR type are divided into four 8-bit sections, where each section stores the intensity of a color component. Figure 9.2 shows the distribution.

两种定义颜色的方式,第一种是D3DCOLOR类型,DWORD双字节,32位。4*8.

Figure 9.2: A 32-bit color, where a byte is allocated for each color component red, green, and blue. A fourth byte is allocated for the alphacomponent.

Because each ARGB ("A" is included now since there is also an alpha) color component gets a byte of memory, the intensity of the color can range from 0 to 255.

For an arbitrary integer n in the range 0 to 255, we have x = n/255 · 100% in the range from 0% to 100%. Thus, n implies a percentage of intensity of the color

component from 0% (black) to 100% (maximum brightness of the color component). For example, suppose n represents the red color component; a value of n = 64,

n = 128, and n = 191 means roughly 25%, 50%, and 75% intensity of red, respectively.

ARGB,0到255

 

Note 

Do not worry about the alpha component now; it is used for alpha blending, which is the topic ofChapter 12. For now just set it to 255 (0xff).

透明值A暂时无视,取255就好

Specifying each component and then inserting it into the proper position in the D3DCOLOR type will require some bit operations or familiarity with hex (e.g., pure green can be specified by 0xff00ff00). Direct3D provides a macro called D3DCOLOR ARGB, which provides a simpler interface for setting a color. There is one parameter for each color component and the alpha component. Each parameter must be in the range 0 to 255 and is used like so:

D3DCOLOR_ARGB宏,这样写:

D3DCOLOR brightRed = D3DCOLOR_ARGB(255, 255, 0, 0);

D3DCOLOR someColor = D3DCOLOR_ARGB(255, 144, 87, 201);

Alternatively, we can use the D3DCOLOR_XRGB macro, which is similar but does not take the alpha parameter; instead it sets the alpha to 0xff (255) automatically:

也可以用D3DCOLOR_XRGB宏,原型声明这样的:

#define D3DCOLOR_XRGB(r,g,b) D3DCOLOR_ARGB(0xff,r,g,b)
 
 

9.1.2 D3DCOLORVALUE结构体

Another way to store a color in Direct3D is with the D3DCOLORVALUE structure. With this structure, we use a floating-point value to measure the intensity of each component. The range measures from 0 to 1 — 0 being no intensity and 1 being full intensity. Essentially, we are specifying the color component intensity percent directly; that is, [0, 1] · 100% = [0%, 100%]. For example, supposex represents the red color component; a value ofx = 0.25,x = 0.5, and x = 0.75 means 25%, 50%, and 75% intensity of red, respectively.

另一种存储颜色的方式是D3DCOLORVALUE结构体

Also note that with this color representation, we can store more colors since we are no longer limited to 256 step sizes per color component. Of course, the D3DCOLORVALUE structure uses 128 bits versus the D3DCOLOR value's 32 bits.

颜色比0到255更丰富,因为取值为0.0到1.0.

Typedef struct _D3DCOLORVALUE {
    float r; // the red component, range 0.0-1.0
    float g; // the green component, range 0.0-1.0
    float b; // the blue component, range 0.0-1.0
    float a; // the alpha component, range 0.0-1.0
} D3DCOLORVALUE;
 

 

9.1.3 D3DXCOLOR结构体

Alternatively, we can use the D3DXCOLOR structure, which contains the same data members as D3DCOLORVALUE but provides useful constructors and overloaded operators, making color manipulations easy. In addition, because they contain the same data members, we can cast back and forth between the two. D3DXCOLOR is defined as:

D3DXCOLOR是比D3DCOLORVALUE更牛逼的结构体,里面有构造函数和运算符的重载,这样颜色相乘就容易了。定义如下:

typedef struct D3DXCOLOR
{
#ifdef _cplusplus
public:
    D3DXCOLOR() {}
    D3DXCOLOR( DWORD argb );
    D3DXCOLOR( CONST FLOAT * );
    D3DXCOLOR( CONST D3DXFLOAT16 * );
    D3DXCOLOR( CONST D3DCOLORVALUE& );
    D3DXCOLOR( FLOAT r, FLOAT g, FLOAT b, FLOAT a );
 
    // casting
    operator DWORD () const;
 
    operator FLOAT* ();
    operator CONST FLOAT* () const;
 
    operator D3DCOLORVALUE* ();
    operator CONST D3DCOLORVALUE* () const;
 
    operator D3DCOLORVALUE& ();
    operator CONST D3DCOLORVALUE& () const;
 
    // assignment operators
    D3DXCOLOR& operator += ( CONST D3DXCOLOR& );
    D3DXCOLOR& operator -= ( CONST D3DXCOLOR& );
    D3DXCOLOR& operator *= ( FLOAT );
    D3DXCOLOR& operator /=  ( FLOAT );
 
    // unary operators
    D3DXCOLOR operator + () const;
    D3DXCOLOR operator - () const;
 
    // binary operators
    D3DXCOLOR operator + ( CONST D3DXCOLOR& ) const;
    D3DXCOLOR operator - ( CONST D3DXCOLOR& ) const;
    D3DXCOLOR operator * ( FLOAT) const;
    D3DXCOLOR operator / ( FLOAT) const;
 
    friend D3DXCOLOR operator * (FLOAT, CONST D3DXCOLOR& );
 
    BOOL operator == ( CONST D3DXCOLOR&) const;
    BOOL operator != ( CONST D3DXCOLOR&) const;
 
#endif //_cplusplus
    FLOAT r, g, b, a;
} D3DXCOLOR, *LPD3DXCOLOR;
 
 

Observe that both the D3DCOLORVALUE and the D3DXCOLOR structures have four floating-point components. This leads to the common notation of treating a color as a 4D vector (r,g,b,a). Color vectors are added, subtracted, and scaled just like regular vectors. On the other hand, dot and cross products donot make sense for color vectors, but component-wise multiplicationdoes make sense for colors. The D3DX library provides the D3DXColorModulate function for performing component-wise multiplication, which is prototyped as:

显然D3DCOLORVALUE 和 D3DXCOLOR都有4个浮点组成,这样我们就可以把颜色当做是4D向量(r,g,b,a)。颜色向量和具有和普通向量一样的特性,点乘和叉乘无意义。组件相乘却有意义。D3DX库提供了D3DXColorModulate函数来进行颜色的组件相乘。

定义如下:

D3DXCOLOR* D3DXColormodulate(
      D3DXCOLOR* pOut,       // Returns (C1, C2, C3, C4)  (k1 k2 k3, k4)
      CONST D3DXCOLOR* pC1,  // (c1, c2, c3, c4)
      CONST D3DXCOLOR* pC2); // (k1, k2, k3, k4)

The symbol denotes component-wise multiplication and it is defined as: (c1,c2, c3, c4) (k1,k2, k3, k4)= (c1k1, c2k2,c3k3,c4k4).

 

9.2 Vertex Colors顶点颜色

Recall that our vertex structures are flexible and we can include additional data; in particular, we can couple a color to each vertex. Then, in the rasterization stage of the rendering pipeline, these vertex colors are interpolated across the face of the triangle to generate pixel colors for the interior and boundary of the triangle. Thus, to add color, all we need to do is modify our vertex structure and the vertex and pixel shader slightly.

回想一下,我们的顶点结构是灵活的,我们可以包含其他数据,特别是,我们可以结合每个顶点的颜色。然后,在光栅化阶段的渲染管线,这些顶点的颜色在三角形面上进行插值运算。因此,要增添颜色,我们需要做的就是稍微修改下顶点结构体,顶点着色器和像素着色器。

 

Our new vertex structure, and its corresponding vertex declaration, looks like this:

我们修改后的顶点声明是这样的:

struct VertexCol
{
      VertexCol():pos(0.0f, 0.0f, 0.0f),col(0x00000000) {}
      VertexCol (float x, float y, float z, D3DCOLOR c)
                 pos(x,y,z), col(c){}
      VertexCol(const D3DXVECTOR3& v, D3DCOLOR c):pos(v),col(c){}
 
      D3DXVECTOR3 pos;
      D3DCOLOR    col;
      static IDirect3DVertexDeclaration9* Decl;
};
 
D3DVERTEXELEMENT9 VertexColElements[] =
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,
                           D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,
                              D3DDECLUSAGE_COLOR, 0},
D3DDECL_END()
};
HR(gd3dDevice->CreateVertexDeclaration(VertexColElements,
                                       &VertexCol::Decl));
 

The new properties are in bold, and we have also updatedVertex.h/.cpp.

新加的属性加粗了,我们也修改了Vertex.h/.cpp

 

Now our vertices have an extra color component; therefore, our vertex shader has an extra input parameter:

现在我们的顶点就有了额外的颜色组件。因此,我们的顶点着色器就有了额外的输入参数了。

uniform extern float4x4 gWVP;
 
struct OutputVS
{
    float4 posH  : POSITION0;
    float4 color : COLORO;
};
OutputVS ColorVS(float3 posL : POSITION0, float4 c : COLOR0)
{
      // Zero out our output.
      OutputVS outVS = (OutputVS)0;
 
      // Transform to homogeneous clip space.
      outVS.posH = mul(float4(posL, 1.0f), gWVP);
 
      // Just pass the vertex color into the pixel shader.
      outVS.color = c;
 
      // Done--return the output.
      return outVS;
}

Note that even though we use a D3DCOLOR value in our vertex structure, it is automatically converted to a D3DXCOLOR type format (i.e., a float4 4D color vector) for the vertex shader. Furthermore, observe that the vertex shader does not do anything with the color — it merely passes it on to an output color register, where it will be used for interpolation during rasterization (§6.4.8).

小贴士:尽管D3DCOLOR在我们的顶点结构体里面了,它自动为顶点着色器转化到了D3DXCOLOR类型的格式(一个float4 4D颜色向量)。另外,记住顶点着色器和颜色没有任何关系,它看到有颜色传进来了,一边进一边出,传到颜色寄存器中,在颜色寄存器中进行插值。

The new pixel shader is trivial — we set the color of the pixel fragment to be the interpolated input color, so no real work needs to be done.

新的像素着色器改动不多——我们设置像素片的颜色为插值输入颜色,所以没有真正的工作要做。

float4 ColorPS(float4 c : COLORO) : COLOR
{
      return c;
}

Finally, the technique looks like this:

最终,手法写法就是这样;

technique ColorTech
{
      pass P0
      {
            // Specify the vertex and pixel shader associated
            // with this pass.
            vertexShader = compile vs_2_0 ColorVS();
            pixel Shader = compile ps_2_0 ColorPS();
      }
}
 

Note 

The .fx file all this is in is calledcolor.fx.

 

 

 

 

 

 

小贴士

;上面写的这些.fx代码在一个叫color.fx的文件里

 

9.3 Color Cube Demo

Figure 9.3 shows a screenshot for this demo, which is a colored cube. The source code for this demo is almost the same as the Cube demo fromChapter 7, except we usecolor.fx and the color vertex structure.

这个demo和前面那个cube demo非常相似,唯一的区别是color.fx和颜色顶点结构体


Figure 9.3: Screenshot of the Color Cube demo.

First we define the following constants:

首先我们定义下这些颜色常量:

const D3DCOLOR WHITE   = D3DCOLOR_XRGB(255, 255, 255); // 0xffffffff
const D3DCOLOR BLACK   = D3DCOLOR_XRGB(0, 0, 0);       // 0xff000000
const D3DCOLOR RED     = D3DCOLOR_XRGB(255, 0, 0);     // 0xffff0000
const D3DCOLOR GREEN   = D3DCOLOR_XRGB(0, 255, 0);     // 0xff00ff00
const D3DCOLOR BLUE    = D3DCOLOR_XRGB(0, 0, 255);     // 0xff0000ff
const D3DCOLOR YELLOW  = D3DCOLOR_XRGB(255, 255, 0);   // 0xffffff00
const D3DCOLOR CYAN    = D3DCOLOR_XRGB(0, 255, 255);   // 0xff00ffff
const D3DCOLOR MAGENTA = D3DCOLOR XRGB(255, 0, 255);   // 0xffff00ff

Observe that we have commented the hexadecimal representation of the D3DCOLORs above; if you are not already, it is a good idea to become comfortable with the hexadecimal and binary number systems.

上面把16进制表示法个注释了,按需求使用

When we construct the cube's vertices, we just pass in one of the above colors as the color component for each vertex:

我们架构立方体的顶点,用这些颜色宏就可以了:

VertexCol* v = 0;
HR(mVB->Lock(0, 0, (void**)&v, 0));
 
v[0] = VertexCol(-1.0f, -1.0f, -1.0f, WHITE);
v[1] = VertexCol(-1.0f,  1.0f, -1.0f, BLACK);
v[2] = VertexCol( 1.0f,  1.0f, -1.0f, RED);
v[3] = VertexCol( 1.0f, -1.0f, -1.0f, GREEN);
v[4] = VertexCol(-1.0f, -1.0f,  1.0f, BLUE);
v[5] = VertexCol(-1.0f,  1.0f,  1.0f, YELLOW);
v[6] = VertexCol( 1.0f,  1.0f,  1.0f, CYAN);
v[7] = VertexCol( 1.0f, -1.0f,  1.0f, MAGENTA);
 
HR(mVB->Unlock());
 

Then, other than small changes, like passing a different fX filename and specifying a different technique name, the code is the same as the Cube demo fromChapter 7.

另外的小的改变,比如fx文件名字改了,不同的手法名,其他的代码都和第七章的那个cube demo差不多的。

附ColoredCubeDemo.cpp源代码:

//=============================================================================// ColoredCubeDemo.cpp by Frank Luna (C) 2005 All Rights Reserved.//// Demonstrates how to color triangles.//// Controls: Use mouse to orbit and zoom; use the 'W' and 'S' keys to //           alter the height of the camera.//=============================================================================#include "d3dApp.h"#include "DirectInput.h"#include <crtdbg.h>#include "GfxStats.h"#include <list>#include "Vertex.h"const D3DCOLOR WHITE   = D3DCOLOR_XRGB(255, 255, 255); // 0xffffffffconst D3DCOLOR BLACK   = D3DCOLOR_XRGB(0, 0, 0);       // 0xff000000const D3DCOLOR RED     = D3DCOLOR_XRGB(255, 0, 0);     // 0xffff0000const D3DCOLOR GREEN   = D3DCOLOR_XRGB(0, 255, 0);     // 0xff00ff00const D3DCOLOR BLUE    = D3DCOLOR_XRGB(0, 0, 255);     // 0xff0000ffconst D3DCOLOR YELLOW  = D3DCOLOR_XRGB(255, 255, 0);   // 0xffffff00const D3DCOLOR CYAN    = D3DCOLOR_XRGB(0, 255, 255);   // 0xff00ffffconst D3DCOLOR MAGENTA = D3DCOLOR_XRGB(255, 0, 255);   // 0xffff00ffclass ColoredCubeDemo : public D3DApp{public:ColoredCubeDemo(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP);~ColoredCubeDemo();bool checkDeviceCaps();void onLostDevice();void onResetDevice();void updateScene(float dt);void drawScene();// Helper methodsvoid buildVertexBuffer();void buildIndexBuffer();void buildFX();void buildViewMtx();void buildProjMtx();private:GfxStats* mGfxStats;IDirect3DVertexBuffer9* mVB;IDirect3DIndexBuffer9*  mIB;ID3DXEffect*            mFX;D3DXHANDLE              mhTech;D3DXHANDLE              mhWVP;float mCameraRotationY;float mCameraRadius;float mCameraHeight;D3DXMATRIX mView;D3DXMATRIX mProj;};int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,   PSTR cmdLine, int showCmd){// Enable run-time memory check for debug builds.#if defined(DEBUG) | defined(_DEBUG)_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );#endifColoredCubeDemo app(hInstance, "Colored Cube Demo", D3DDEVTYPE_HAL, D3DCREATE_SOFTWARE_VERTEXPROCESSING);gd3dApp = &app;DirectInput di(DISCL_NONEXCLUSIVE|DISCL_FOREGROUND, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND);gDInput = &di;    return gd3dApp->run();}ColoredCubeDemo::ColoredCubeDemo(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP): D3DApp(hInstance, winCaption, devType, requestedVP){if(!checkDeviceCaps()){MessageBox(0, "checkDeviceCaps() Failed", 0, 0);PostQuitMessage(0);}mGfxStats = new GfxStats();mCameraRadius    = 10.0f;mCameraRotationY = 1.2 * D3DX_PI;mCameraHeight    = 5.0f;buildVertexBuffer();buildIndexBuffer();buildFX();onResetDevice();InitAllVertexDeclarations();}ColoredCubeDemo::~ColoredCubeDemo(){delete mGfxStats;ReleaseCOM(mVB);ReleaseCOM(mIB);ReleaseCOM(mFX);DestroyAllVertexDeclarations();}bool ColoredCubeDemo::checkDeviceCaps(){D3DCAPS9 caps;HR(gd3dDevice->GetDeviceCaps(&caps));// Check for vertex shader version 2.0 support.if( caps.VertexShaderVersion < D3DVS_VERSION(2, 0) )return false;// Check for pixel shader version 2.0 support.if( caps.PixelShaderVersion < D3DPS_VERSION(2, 0) )return false;return true;}void ColoredCubeDemo::onLostDevice(){mGfxStats->onLostDevice();HR(mFX->OnLostDevice());}void ColoredCubeDemo::onResetDevice(){mGfxStats->onResetDevice();HR(mFX->OnResetDevice());// The aspect ratio depends on the backbuffer dimensions, which can // possibly change after a reset.  So rebuild the projection matrix.buildProjMtx();}void ColoredCubeDemo::updateScene(float dt){// One cube has 8 vertice and 12 triangles.mGfxStats->setVertexCount(8);mGfxStats->setTriCount(12);mGfxStats->update(dt);// Get snapshot of input devices.gDInput->poll();// Check input.if( gDInput->keyDown(DIK_W) ) mCameraHeight   += 25.0f * dt;if( gDInput->keyDown(DIK_S) ) mCameraHeight   -= 25.0f * dt;// Divide by 50 to make mouse less sensitive. mCameraRotationY += gDInput->mouseDX() / 100.0f;mCameraRadius    += gDInput->mouseDY() / 25.0f;// If we rotate over 360 degrees, just roll back to 0if( fabsf(mCameraRotationY) >= 2.0f * D3DX_PI ) mCameraRotationY = 0.0f;// Don't let radius get too small.if( mCameraRadius < 5.0f )mCameraRadius = 5.0f;// The camera position/orientation relative to world space can // change every frame based on input, so we need to rebuild the// view matrix every frame with the latest changes.buildViewMtx();}void ColoredCubeDemo::drawScene(){// Clear the backbuffer and depth buffer.HR(gd3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffeeeeee, 1.0f, 0));HR(gd3dDevice->BeginScene());// Let Direct3D know the vertex buffer, index buffer and vertex // declaration we are using.HR(gd3dDevice->SetStreamSource(0, mVB, 0, sizeof(VertexCol)));HR(gd3dDevice->SetIndices(mIB));HR(gd3dDevice->SetVertexDeclaration(VertexCol::Decl));// Setup the rendering FXHR(mFX->SetTechnique(mhTech));HR(mFX->SetMatrix(mhWVP, &(mView*mProj)));// Begin passes.UINT numPasses = 0;HR(mFX->Begin(&numPasses, 0));for(UINT i = 0; i < numPasses; ++i){HR(mFX->BeginPass(i));HR(gd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12));HR(mFX->EndPass());}HR(mFX->End());mGfxStats->display();HR(gd3dDevice->EndScene());// Present the backbuffer.HR(gd3dDevice->Present(0, 0, 0, 0));}void ColoredCubeDemo::buildVertexBuffer(){// Obtain a pointer to a new vertex buffer.HR(gd3dDevice->CreateVertexBuffer(8 * sizeof(VertexCol), D3DUSAGE_WRITEONLY,0, D3DPOOL_MANAGED, &mVB, 0));// Now lock it to obtain a pointer to its internal data, and write the// cube's vertex data.VertexCol* v = 0;HR(mVB->Lock(0, 0, (void**)&v, 0));v[0] = VertexCol(-1.0f, -1.0f, -1.0f, WHITE);v[1] = VertexCol(-1.0f,  1.0f, -1.0f, BLACK);v[2] = VertexCol( 1.0f,  1.0f, -1.0f, RED);v[3] = VertexCol( 1.0f, -1.0f, -1.0f, GREEN);v[4] = VertexCol(-1.0f, -1.0f,  1.0f, BLUE);v[5] = VertexCol(-1.0f,  1.0f,  1.0f, YELLOW);v[6] = VertexCol( 1.0f,  1.0f,  1.0f, CYAN);v[7] = VertexCol( 1.0f, -1.0f,  1.0f, MAGENTA);HR(mVB->Unlock());}void ColoredCubeDemo::buildIndexBuffer(){// Obtain a pointer to a new index buffer.HR(gd3dDevice->CreateIndexBuffer(36 * sizeof(WORD), D3DUSAGE_WRITEONLY,D3DFMT_INDEX16, D3DPOOL_MANAGED, &mIB, 0));// Now lock it to obtain a pointer to its internal data, and write the// cube's index data.WORD* k = 0;HR(mIB->Lock(0, 0, (void**)&k, 0));// Front face.k[0] = 0; k[1] = 1; k[2] = 2;k[3] = 0; k[4] = 2; k[5] = 3;// Back face.k[6] = 4; k[7]  = 6; k[8]  = 5;k[9] = 4; k[10] = 7; k[11] = 6;// Left face.k[12] = 4; k[13] = 5; k[14] = 1;k[15] = 4; k[16] = 1; k[17] = 0;// Right face.k[18] = 3; k[19] = 2; k[20] = 6;k[21] = 3; k[22] = 6; k[23] = 7;// Top face.k[24] = 1; k[25] = 5; k[26] = 6;k[27] = 1; k[28] = 6; k[29] = 2;// Bottom face.k[30] = 4; k[31] = 0; k[32] = 3;k[33] = 4; k[34] = 3; k[35] = 7;HR(mIB->Unlock());}void ColoredCubeDemo::buildFX(){// Create the FX from a .fx file.ID3DXBuffer* errors = 0;HR(D3DXCreateEffectFromFile(gd3dDevice, "color.fx", 0, 0, D3DXSHADER_DEBUG, 0, &mFX, &errors));if( errors )MessageBox(0, (char*)errors->GetBufferPointer(), 0, 0);// Obtain handles.mhTech = mFX->GetTechniqueByName("ColorTech");mhWVP  = mFX->GetParameterByName(0, "gWVP");}void ColoredCubeDemo::buildViewMtx(){float x = mCameraRadius * cosf(mCameraRotationY);float z = mCameraRadius * sinf(mCameraRotationY);D3DXVECTOR3 pos(x, mCameraHeight, z);D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);D3DXMatrixLookAtLH(&mView, &pos, &target, &up);}void ColoredCubeDemo::buildProjMtx(){float w = (float)md3dPP.BackBufferWidth;float h = (float)md3dPP.BackBufferHeight;D3DXMatrixPerspectiveFovLH(&mProj, D3DX_PI * 0.25f, w/h, 1.0f, 5000.0f);}


color.fx的源代码

 

//=============================================================================// color.fx by Frank Luna (C) 2004 All Rights Reserved.//// Basic FX that simply transforms geometry from local space to // homogeneous clip space, and draws the geometry in solid color.//=============================================================================uniform extern float4x4 gWVP;struct OutputVS{    float4 posH  : POSITION0;    float4 color : COLOR0;};OutputVS ColorVS(float3 posL : POSITION0, float4 c : COLOR0){    // Zero out our output.OutputVS outVS = (OutputVS)0;// Transform to homogeneous clip space.outVS.posH = mul(float4(posL, 1.0f), gWVP);// Just pass the vertex color into the pixel shader.outVS.color = c; // Done--return the output.    return outVS;}float4 ColorPS(float4 c : COLOR0) : COLOR{    return c;}technique ColorTech{    pass P0    {        // Specify the vertex and pixel shader associated with this pass.        vertexShader = compile vs_2_0 ColorVS();        pixelShader  = compile ps_2_0 ColorPS();    }}


 

9.4 Digression: Traveling Sine Waves题外话:移动的正弦波

The next demo utilizes sine waves and so we go over the basics of them here. (We'll keep it simple here and refer you to any introductory physics textbook for a more detailed look at traveling waves; see, for example, [Halliday0l].) A general sine wave may be described by the following function (seeFigure 9.4):

后面要写个相关的demo,这节来讲一下这方面知识。

y =a · sin(kx)

The constant a is called theamplitude of the wave and it essentially scales the sine function. (Recall that sin(x) evaluates to a real number in the range [-1, 1]; thus,a · sin(x) evaluates to a real number in the range [-a,a], and we have scaled the range of the sine function.)

常数a为幅度。


Figure 9.4: The amplitude a is the maximum displacement of the wave from the origin on the positivey-axis. The wavelengthλ is the distance from peak to peak of the wave.

The constant k is called theangular wave number and it implies the wavelength λ of the wave. Recall that sin(x) repeats every multiple of 2π radians; thus, by default, it has a wavelength of 2π. We like to control the wavelength, just as we like to control the amplitude to customize our sine wave. To do this, we introduce the angular wave numberk, which can be thought of as a scaling factor forx. That is, for 0 ≤k < 1, k scales x down, and thus it takes a largerx forkx to reach a multiple of 2,π and repeat, hence lengthening the wavelength (i.e., it takes a longer distance ofx for the wave to repeat itself — we have lengthened the wavelength). On the other hand, for 1 <k, k scalesx up, and thus it takes a smallerx forkx to reach a multiple of 2π and repeat, hence shortening the wavelength (i.e., it takes a shorter distance ofx for the wave to repeat itself — we have shortened the wavelength).Figure 9.5 illustrates these results.


Figure 9.5: For sin(2x),k = 2 and the wavelength isπ; for sin(x),k = 1 and the wavelength is 2π; for sin(x/2),k= 1/2 and the wavelength is 4π.

So far, we can control the amplitude and wavelength of our wave, but it just sits there as a static wave. What we want to do is oscillate the points on the wave up and down with respect to time to give a traveling waveform motion. To do this, we introduce a new time variablet:y =a · sin(kx -t). (The negative sign makes the waveform move in the direction of the +x-axis, whereas a plus sign would make the waveform move in the direction of the -x-axis.) Note thatt does not stand still — it represents the time elapsed, so it is increasing over time. For afixed x0, the point (x0,y) = (x0,a · sin(kx0-t)) moves up and down, and makes a complete oscillation every 2π seconds. As with the wavelength, we want to control how long it takes for a point on the wave to move through a complete oscillation. To do this, we introduce theangular frequency ω:

y = a · sin(kx -ωt)

The angular frequency (ω) works the same way as the angular wave number (k), but it controls how fast/slow it takes an arbitrary point on the wave to make one oscillation.

Finally, at t = 0 the waveform looks likey =a · sin (kx). We might not always want the initial waveform to look like this; specifically, we may want the wave to be initially shifted. Thus, we add a phase shift parameterφ so that we can control the initial waveform:

y = a · sin(kx -ωt +φ)

Thus, at t = 0 the waveform looks likey =a · sin (kx +φ).

9.4.1 Summing Waves

Suppose that you want to use sine waves to loosely model the motion of water waves. If you observe water waves, you will notice large, broad waves that move up and down slowly compared to small, sharp choppy waves that move up and down quickly. So how do we capture both the broad waves and the choppy waves? Well, we can make several sine waves, some to model broad waves and some to model choppy waves, and then we can sum the waves to produce a net wave; seeFigure 9.6.


Figure 9.6: Combining different waves together to produce a net wave.

9.4.2 Circular Waves

Suppose that we want waves to generate circular waves originating from a source point. That is, all points on the circle centered aboutwith radius r have the samey-coordinate, as shown in Figure 9.7. For an arbitrary point= (px, 0,pz) on the xz-plane, the radius of the circle on whichlies is given by(or justifis the origin). Now, the vertical displacement of all points on the circle centered aboutwith radiusr is given byy = a · sin(kr - ωt + φ).


Figure 9.7: To form circular waves, we need all points on a circle with center pointand radiusr to have the same height value. Thus, we compute the height of a point as a function of the radius of the circle on which the point lies; that is,y =a · sin(kr -ωt +φ). Consequently, because all points on the circle have the same radius, they also have the same height, which is what we want.

9.4.3 Directional Waves

Suppose that we want waves to move in the direction of a unit vector= (ux, 0,uz) on thexz-plane. In other words, we want wavefronts to move in thedirection, where, in this particular case, a wavefront is a line orthogonal tosuch that all the points on the line have the samey-coordinate (see Figure 9.8). To describe this mathematically, observe that all points on a wavefront have the same projection onto . Thus, the vertical displacement of a point can be given byy = a · sin(kx - ωt + φ), where. In this way, all points on the same wavefront will have the samey -coordinate because they will have the same projection.


Figure 9.8: Wavefronts; projecting points onto.

 

9.5 Colored Waves Demo颜色波demo

We now examine a demo that employs a less trivial vertex shader. The program generates circular waves by summing sine waves together, and it generates a color for each vertex based on the vertical displacement of the vertex (i.e., distance of the y-coordinate from 0). Figure 9.9 shows a screenshot of the animation.


Figure 9.9: Screenshot of the Colored Waves demo.

The vertex shader is given below:

顶点着色器代码如下:

OutputVS ColorVS(float3 posL : POSITIONO)
{
      // Zero out our output.
      OutputVS outVS = (OutputVS)0;
 
      // Get the height of the vertex--the height is given by
      // summing sine waves.
      posL.y = SumOfRadialSineUaves(posL.x, posL.z);
 
      // Generate the vertex color based on its height.
      outVS.color = GetColorFromHeight(posL.y);
 
      // Transform to homogeneous clip space.
      outVS.posH = mul(float4(posL, 1.0f), gWVP);
 
      // Done--return the output.
      return outVS;
}

We have a couple of quick comments before analyzing this vertex shader. First, this shader expects the input vertices to be on a grid on thexz-plane, as it overwrites the inputy-coordinate based on the position of the vertex and current time. Overwriting they-coordinate in this way for an arbitrary mesh would probably not make sense. Second, observe that the shader does not input a color component; we input only a vertex position and the vertex shader will generate a color based on the vertical displacement of the vertex.

首先,此着色器预计的输入顶点是在xz平面上的网格上,作为覆盖y坐标的基础上的顶点的位置和当前时间的输入。以这种方式任意网格覆盖的y坐标可能会没有意义的。其次,观察到着色器没有输入的颜色分量;我们只输入顶点位置和顶点着色器将生成一个基于颜色的垂直位移的顶点。

As said, this vertex shader does two things: It animates the vertices based on moving circular waves and it generates a color for each vertex based on the vertical displacement of the vertex. These two tasks are encapsulated in the SumOfRadialSineWaves and GetColorFromHeight functions, respectively. (Observe then that we can write our own functions in an effect file that can be invoked in a vertex/pixel shader.) The SumOfRadialSineWaves function is implemented as follows:

 

顶点着色器做了两件事,第一它驱动顶点基于环形波来运动,以及为每个顶点更新一个颜色。这两件事分别封装在了SumOfRadialSineWaves和GetColorFromHeight方法中了。SumOfRadialSineWaves实现代码如下:

 

// Amplitudes
static float a[2] = {0.8f, 0.2f};
 
// Angular wave numbers.
static float k[2] = {1.0, 8.0f};
 
// Angular frequency.
static float w[2] = {1.0f, 8.0f};
 
// Phase shifts.
static float p[2] = {0.0f0.0f, 1.0f};
 
float SumOfRadialSineWaves (float x, float z)
 
{
     // Distance of vertex from source of waves (which we set
     // as the origin of the local space).
     float d = sqrt(x*x + z*z);
 
     // Sum the waves.
     float sum = 0.0f;
     for(int i = 0; i < 2; ++i)
           sum += a[i]*sin(k[i]*d - gTime*w[i] + p[i]);
     return sum;
}

Essentially, all this code does is sum two different kinds of waves in order to get a general broad wave motion along with some choppy chaotic wave motion (§9.4). You can experiment with the amplitude, angular wave number, angular frequency, and phase shifts to see the different results. (The static variables just mean the variables are local to the effect and not external; we put them outside the function because they do not need to be instantiated per-vertex.) The variable gTime is an effect parameter declared like so:

从本质上来将,上面这段代码就是把两种不同的波进行相加,来得到一个一般的断断续续的波动。

gTime变量定义如下:

 

uniform extern float gTime;

In the application, we update this variable every frame based on the time that has elapsed:

在应用程序中,我们根据时间的变化,每帧都更新这个变量:

mTime += dt;
HR(mFX->SetFloat(mhTime, mTime));

In the application constructor, the variable mTime is set to zero. In this way, it stores the time elapsed, in units of seconds, since the start of the program. Note that we do not really need to worry about time elapsing such that mTime accumulates to a value it cannot store; we can represent days in units of seconds with a float without any problem.

在应用程序的构造函数中,这个mTime变量设为0了,他就可以表示程序运行了多久。

Now a few comments on the HLSL code. Observe that for-loops in the HLSL have the same syntax as in C++. Also observe that array syntax is the same as in C++. To compute the sine of an angle in radians, we use the HLSL intrinsic sin function, and to compute the square root of a number in the HLSL we use the intrinsic sqrt function.

下面讲一讲HLSL代码,显然HLSL中的循环和C++中的如出一辙。数组的语法也一样。

The GetColorFromHeight function is defined below:

GetColorFromHeight函数定义如下:

float4 GetColorFromHeight(float y)
{
      if( abs(y) <= 0.2f)          // black
            return float4(0.0f, 0.0f, 0.0f, 1.0f);
      else if(abs(y) <= 0.4f )     // blue
            return float4(0.0f, 0.0f, 1.0f, 1.0f);
      else if(abs(y) <= 0.6f )     // green
            return float4(0.0f, 1.0f, 0.0f, 1.0f);
      else if(abs(y) <= 0.8f )     // red
            return float4(1.0f, 0.0f, 0.0f, 1.0f);
 
     else                          // yellow
            return float4(1.0f, 1.0f, 0.0f, 1.0f);
}

这个函数根据不同的y的绝对值来设置不同的颜色。

This function checks the displacement of they-coordinate from 0, and returns a color based on the range in which that height lies. In this way, we procedurally generate a color for the vertex in the vertex shader program — the color was not an input into the vertex shader! Also observe that conditional statements (i.e., if/else statements) in the HLSL are just like the conditional statements in C++, and to compute the absolute value, we use the HLSL intrinsic abs function.

 

附源代码:

 

//=============================================================================// ColoredWavesDemo.cpp by Frank Luna (C) 2005 All Rights Reserved.//// Shows how to animate vertices in a vertex shader and how to color// vertices in a vertex shader procedurally based on the height of // the vertex.//// Controls: Use mouse to orbit and zoom; use the 'W' and 'S' keys to //           alter the height of the camera.//=============================================================================#include "d3dApp.h"#include "DirectInput.h"#include <crtdbg.h>#include "GfxStats.h"#include <list>#include "Vertex.h"class ColoredWavesDemo : public D3DApp{public:ColoredWavesDemo(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP);~ColoredWavesDemo();bool checkDeviceCaps();void onLostDevice();void onResetDevice();void updateScene(float dt);void drawScene();// Helper methodsvoid buildGeoBuffers();void buildFX();void buildViewMtx();void buildProjMtx();private:GfxStats* mGfxStats;DWORD mNumVertices;DWORD mNumTriangles;IDirect3DVertexBuffer9* mVB;IDirect3DIndexBuffer9*  mIB;ID3DXEffect*            mFX;D3DXHANDLE              mhTech;D3DXHANDLE              mhWVP;D3DXHANDLE              mhTime;float mTime;float mCameraRotationY;float mCameraRadius;float mCameraHeight;D3DXMATRIX mView;D3DXMATRIX mProj;};int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,   PSTR cmdLine, int showCmd){// Enable run-time memory check for debug builds.#if defined(DEBUG) | defined(_DEBUG)_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );#endifColoredWavesDemo app(hInstance, "Colored Waves Demo", D3DDEVTYPE_HAL, D3DCREATE_HARDWARE_VERTEXPROCESSING);gd3dApp = &app;DirectInput di(DISCL_NONEXCLUSIVE|DISCL_FOREGROUND, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND);gDInput = &di;    return gd3dApp->run();}ColoredWavesDemo::ColoredWavesDemo(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP): D3DApp(hInstance, winCaption, devType, requestedVP){if(!checkDeviceCaps()){MessageBox(0, "checkDeviceCaps() Failed", 0, 0);PostQuitMessage(0);}mGfxStats = new GfxStats();mCameraRadius    = 25.0f;mCameraRotationY = 1.2 * D3DX_PI;mCameraHeight    = 15.0f;mTime            = 0.0f;buildGeoBuffers();buildFX();onResetDevice();InitAllVertexDeclarations();}ColoredWavesDemo::~ColoredWavesDemo(){delete mGfxStats;ReleaseCOM(mVB);ReleaseCOM(mIB);ReleaseCOM(mFX);DestroyAllVertexDeclarations();}bool ColoredWavesDemo::checkDeviceCaps(){D3DCAPS9 caps;HR(gd3dDevice->GetDeviceCaps(&caps));// Check for vertex shader version 2.0 support.if( caps.VertexShaderVersion < D3DVS_VERSION(2, 0) )return false;// Check for pixel shader version 2.0 support.if( caps.PixelShaderVersion < D3DPS_VERSION(2, 0) )return false;return true;}void ColoredWavesDemo::onLostDevice(){mGfxStats->onLostDevice();HR(mFX->OnLostDevice());}void ColoredWavesDemo::onResetDevice(){mGfxStats->onResetDevice();HR(mFX->OnResetDevice());// The aspect ratio depends on the backbuffer dimensions, which can // possibly change after a reset.  So rebuild the projection matrix.buildProjMtx();}void ColoredWavesDemo::updateScene(float dt){mGfxStats->setVertexCount(mNumVertices);mGfxStats->setTriCount(mNumTriangles);mGfxStats->update(dt);// Get snapshot of input devices.gDInput->poll();// Check input.if( gDInput->keyDown(DIK_W) ) mCameraHeight   += 25.0f * dt;if( gDInput->keyDown(DIK_S) ) mCameraHeight   -= 25.0f * dt;// Divide to make mouse less sensitive. mCameraRotationY += gDInput->mouseDX() / 100.0f;mCameraRadius    += gDInput->mouseDY() / 25.0f;// If we rotate over 360 degrees, just roll back to 0if( fabsf(mCameraRotationY) >= 2.0f * D3DX_PI ) mCameraRotationY = 0.0f;// Don't let radius get too small.if( mCameraRadius < 5.0f )mCameraRadius = 5.0f;// Accumulate time for simulation.  mTime += dt;// The camera position/orientation relative to world space can // change every frame based on input, so we need to rebuild the// view matrix every frame with the latest changes.buildViewMtx();}void ColoredWavesDemo::drawScene(){// Clear the backbuffer and depth buffer.HR(gd3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0));HR(gd3dDevice->BeginScene());// Let Direct3D know the vertex buffer, index buffer and vertex // declaration we are using.HR(gd3dDevice->SetStreamSource(0, mVB, 0, sizeof(VertexPos)));HR(gd3dDevice->SetIndices(mIB));HR(gd3dDevice->SetVertexDeclaration(VertexPos::Decl));// Setup the rendering FXHR(mFX->SetTechnique(mhTech));HR(mFX->SetMatrix(mhWVP, &(mView*mProj)));HR(mFX->SetFloat(mhTime, mTime));// Begin passes.UINT numPasses = 0;HR(mFX->Begin(&numPasses, 0));for(UINT i = 0; i < numPasses; ++i){HR(mFX->BeginPass(i));HR(gd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mNumVertices, 0, mNumTriangles));HR(mFX->EndPass());}HR(mFX->End());mGfxStats->display();HR(gd3dDevice->EndScene());// Present the backbuffer.HR(gd3dDevice->Present(0, 0, 0, 0));} void ColoredWavesDemo::buildGeoBuffers(){std::vector<D3DXVECTOR3> verts;std::vector<DWORD> indices;GenTriGrid(100, 100, 0.5f, 0.5f, D3DXVECTOR3(0.0f, 0.0f, 0.0f), verts, indices);// Save vertex count and triangle count for DrawIndexedPrimitive arguments.mNumVertices  = 100*100;mNumTriangles = 99*99*2;// Obtain a pointer to a new vertex buffer.HR(gd3dDevice->CreateVertexBuffer(mNumVertices * sizeof(VertexPos), D3DUSAGE_WRITEONLY,0, D3DPOOL_MANAGED, &mVB, 0));// Now lock it to obtain a pointer to its internal data, and write the// grid's vertex data.VertexPos* v = 0;HR(mVB->Lock(0, 0, (void**)&v, 0));for(DWORD i = 0; i < mNumVertices; ++i)v[i] = verts[i];HR(mVB->Unlock());// Obtain a pointer to a new index buffer.HR(gd3dDevice->CreateIndexBuffer(mNumTriangles*3*sizeof(WORD), D3DUSAGE_WRITEONLY,D3DFMT_INDEX16, D3DPOOL_MANAGED, &mIB, 0));// Now lock it to obtain a pointer to its internal data, and write the// grid's index data.WORD* k = 0;HR(mIB->Lock(0, 0, (void**)&k, 0));for(DWORD i = 0; i < mNumTriangles*3; ++i)k[i] = (WORD)indices[i];HR(mIB->Unlock());}void ColoredWavesDemo::buildFX(){// Create the FX from a .fx file.ID3DXBuffer* errors = 0;HR(D3DXCreateEffectFromFile(gd3dDevice, "heightcolor.fx", 0, 0, D3DXSHADER_DEBUG, 0, &mFX, &errors));if( errors )MessageBox(0, (char*)errors->GetBufferPointer(), 0, 0);// Obtain handles.mhTech = mFX->GetTechniqueByName("HeightColorTech");mhWVP  = mFX->GetParameterByName(0, "gWVP");mhTime = mFX->GetParameterByName(0, "gTime");}void ColoredWavesDemo::buildViewMtx(){float x = mCameraRadius * cosf(mCameraRotationY);float z = mCameraRadius * sinf(mCameraRotationY);D3DXVECTOR3 pos(x, mCameraHeight, z);D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);D3DXMatrixLookAtLH(&mView, &pos, &target, &up);}void ColoredWavesDemo::buildProjMtx(){float w = (float)md3dPP.BackBufferWidth;float h = (float)md3dPP.BackBufferHeight;D3DXMatrixPerspectiveFovLH(&mProj, D3DX_PI * 0.25f, w/h, 1.0f, 5000.0f);}


 

heightcolor.fx源代码:

//=============================================================================// heightcolor.fx by Frank Luna (C) 2004 All Rights Reserved.//// Animates vertices in a wave motion and generates vertex colors // based on their height.//=============================================================================uniform extern float4x4 gWVP;uniform extern float gTime;struct OutputVS{    float4 posH  : POSITION0;    float4 color : COLOR0;};// Amplitudesstatic float a[2] = {0.8f, 0.2f};// Angular wave numbers.static float k[2] = {1.0, 8.0f};// Angular frequency.static float w[2] = {1.0f, 8.0f};// Phase shifts.static float p[2] = {0.0f, 1.0f};float SumOfRadialSineWaves(float x, float z){// Distance of vertex from source of waves (which we set// as the origin of the local space).float d = sqrt(x*x + z*z);// Sum the waves.float sum = 0.0f;for(int i = 0; i < 2; ++i)sum += a[i]*sin(k[i]*d - gTime*w[i] + p[i]);return sum;}float4 GetColorFromHeight(float y){if( abs(y) <= 0.2f ) // blackreturn float4(0.0f, 0.0f, 0.0f, 1.0f);else if(abs(y) <= 0.5f ) // bluereturn float4(0.0f, 0.0f, 1.0f, 1.0f);else if(abs(y) <= 0.8f ) // greenreturn float4(0.0f, 1.0f, 0.0f, 1.0f);else if(abs(y) <= 1.0f ) // redreturn float4(1.0f, 0.0f, 0.0f, 1.0f);else // yellowreturn float4(1.0f, 1.0f, 0.0f, 1.0f);}OutputVS ColorVS(float3 posL : POSITION0){    // Zero out our output.OutputVS outVS = (OutputVS)0;// Get the height of the vertex--the height is given by// summing sine waves.posL.y = SumOfRadialSineWaves(posL.x, posL.z);// Generate the vertex color based on its height.outVS.color = GetColorFromHeight(posL.y);// Transform to homogeneous clip space.outVS.posH = mul(float4(posL, 1.0f), gWVP); // Done--return the output.    return outVS;}float4 ColorPS(float4 c : COLOR0) : COLOR{    return c;}technique HeightColorTech{    pass P0    {        // Specify the vertex and pixel shader associated with this pass.        vertexShader = compile vs_2_0 ColorVS();        pixelShader  = compile ps_2_0 ColorPS();// Specify the render/device states associated with this pass.FillMode = WireFrame;    }}


 

 

9.6 Summary总结

·         Colors are described by specifying an intensity of red, green, and blue. The additive mixing of these three colors at different intensities allows us to describe millions of colors. In Direct3D, we can use the D3DCOLOR, D3DCOLORVALUE, or D3DXCOLOR type to describe a color in code.

·         颜色通过指定红色,绿色,和蓝色的强度来描述。在Direct3D中,我们可以使用D3DCOLOR,D3DCOLORVALUE,或D3DXCOLOR的类型来描述颜色。

·         We sometimes treat a color as a 4D vector (r, g, b, a). Color vectors are added, subtracted, and scaled just like regular vectors. On the other hand, dot and cross products donot make sense for color vectors, but component-wise multiplication does make sense for colors. The symbol denotes component-wise multiplication and it is defined as: (c1, c2, c3,c4) (k1,k2, k3,k4) = (c1k1,c2k2, c3k3, c4k4).

·         我们常常把颜色看着4D向量(r, g, b, a),颜色向量的加法,减法和缩放都和常规的向量一致,但点乘和叉乘无意义。组件相乘有意义。

·         We specify a color for each vertex and then Direct3D. During the rasterization process, vertex colors are interpolated across the face of a primitive. These interpolated color values serve as input for the pixel shader.

·         我们为每一个顶点指定颜色,在光栅化过程中,顶点颜色做插值运算加到几何体表面,插值的值作为像素着色器的输入。

 

原创粉丝点击