天行健 君子当自强而不息

顶点着色器入门(3)

新建网页 1

作为第二个顶点着色器的例子,让我们编写两个顶点着色器,它们以卡通风格的绘画方式对网格着色(shade轮廓勾勒(outline)。图17.2展示了这一点:

 

                       
               

注意:卡通渲染是一种特定类型的非写实渲染(non-photorealistic rendering),有时被称作风格化渲染(stylistic rendering)。

虽然卡通渲染不适用于所有游戏,例如激烈的第一人称射击游戏,但是它仍然可以增强一些希望表现卡通感觉类型游戏的气氛。此外,卡通渲染是漂亮的,并易于实现。让我们好好的演示一个顶点着色器。

我们将卡通渲染分为两步:

1.       卡通绘画的特点是:在一个顶点到下一个顶点的强烈转换时,有少量的阴影强度级别;我们看一下这个卡通阴影(cartoon shading)。在图17.2a)中,我们看到网络着色使用了三种阴影强度(亮、中、暗),而且其间的过渡是不平滑的——不像图17.2c),其明暗过渡是平滑的。

2.       卡通绘图的主要特点是:在其外框上勾画轮廓,如图17.2b)所示。

这两个步骤都需要其各自的顶点着色器。

 

17.5.1 卡通着色

要实现卡通着色,我们采用Lander20003月发表在Game Developer Magazine的文章“Shades of Disney: Opaquing a 3D World”中所描述的方法。我们创建一个带强度级别的灰度纹理,它包含我们需要的不同的着色强度。图17.3显示了我们在样例程序中使用的这个纹理。

然后在顶点着色器中,我们执行标准漫反射光运算(standard diffuse calculation dot product)来确定顶点法线N和光线向量L之间角度的余弦,用以确定顶点接收到多少光线:s=L·N

如果s0,就表示光线向量和顶点法线之间的角度大于90度,即该表面接收不到光线。因此,如果s0,我们就让s0。所以s [0, 1]

现在,在通常的散射光照模型中,我们使用s来标记颜色向量。这样,顶点颜色的明暗取决于接收到的光照的数量:diffuseColor = s(r, g, b, a)

但是,这将会导致从亮到暗之间平滑的着色。这是与我们期望的卡通着色相反的。我们想要一种在几个不同着色器间突然转换颜色的效果(对卡通渲染来说,在24种着色器工作起来还是挺不错的)。

不使用s来标记颜色向量,我们将使用s作为早先提到的强度纹理的u纹理坐标——如图17.3

注意:标量(scalars必定是一个有效的纹理坐标,因为s [0, 1],这是通常的纹理坐标区间。

按这种方式,顶点不会被平滑着色,而是间断的。例如,强度纹理可能被分成3种着色,如图17.4所示:

由图17.4可以看出:若s [0, 0.33]则使用shader0着色,s [ 0.330.66]则使用shader1着色,s [0.66,1]则使用shader2着色。当然,从这些着色的一种到另一种的过渡是不平滑的,这就赋予了我们期望的效果。

注意:我们还为卡通着色关闭了纹理过滤,因为这种过滤会试图使着色过渡变平滑。这对于我们要求的不连续过渡是多余的。

17.5.2 卡通着色的顶点着色器代码

我们现在介绍卡通着色的顶点着色器。这个着色器的主要任务只是根据s=L·N计算并设置纹理坐标。注意观察输出结构,我们已经增加了一个数据成员来存储已被计算过的纹理坐标。同时还需注意,我们仍然输出顶点颜色,虽然我们不修改它,不过当颜色与强度纹理组合起来的时候,便呈现出着色的效果。

     /*****************************************************************************************
      Vertex shader that lights geometry such it appears to be drawn in a cartoon style.
     *****************************************************************************************/

   
    matrix g_world_view;
    matrix g_world_view_proj;
   
    vector g_color;
    vector g_dir_to_light;
   
   
struct sVertexInput
    {
        vector position : POSITION;
        vector normal    : NORMAL;
    };
   
   
struct sVertexOutput
    {
        vector position : POSITION;
        float2 uv        : TEXCOORD;
        vector diffuse  : COLOR;
    };
   
   
    ////////////////////////////////////////////////////////////////////////////////////////////////
   

    sVertexOutput main(sVertexInput input)
    {
        sVertexOutput output = (sVertexOutput) 0;
   
        
// transforma vertex position to homogenous clip space
   
        output.position = mul(input.position, g_world_view_proj);
   
        
// Transform lights and normals to view space, set w components to zero since we're transforming vectors.
        // Assume there are no scalings in the world matrix as well.
   

        g_dir_to_light.w = 0.0f;
        input.normal.w   = 0.0f;
   
        g_dir_to_light = mul(g_dir_to_light, g_world_view);
        input.normal   = mul(input.normal,   g_world_view);
   
        
// compute the 1D texture coordinate for catoon rendering
   

        
float u = dot(g_dir_to_light, input.normal);
   
        
// Clamp to zero if u is negative because u negative implies the angle between the light and normal 
        // is greater than 90 degrees.  And if that is true then the surface receives no light.
   
    if(u < 0.0f)
            u = 0.0f;
   
        
float v = 0.5f;    // set other texture coordinate to middle
   

        output.uv.x = u;
        output.uv.y = v;
   
        output.diffuse = g_color;    
// save color
   
    
        
return output;
    }

两点注解:

我们假设世界矩阵没有执行任何缩放。因为如果它执行,它就会弄乱乘以它的顶点的长度和方向。

我们总是设置v纹理坐标为纹理的中点。这意味着我们仅使用纹理中一条单一的线,那就是说我们可以使用1D强度纹理来代替2D的那个纹理。不管怎样,1D2D纹理都能工作。本例中,我们使用了2D纹理而不是1D纹理,这是没有什么特别的原因的。

执行程序:

      /**************************************************************************************************
      Demonstrates cartoon rendering using a vertex shader.  You will have to switch to 
      the REF device to run this sample if your hardware does not support shaders.  
      Or you can use software vertex processing: D3DCREATE_SOFTWARE_VERTEXPROCESSING. 
     **************************************************************************************************/

   
    #include "d3dUtility.h"
   
    #pragma warning(disable : 4100)
   
   
#define MESH_TEAPOT        0
   
#define MESH_SPHERE        1
   
#define MESH_TORUS        2
   
#define MESH_CYLINDER    3
   
   
const int WIDTH  = 640;
   
const int HEIGHT = 480;
   
    IDirect3DDevice9*        g_device;
    IDirect3DVertexShader9* g_vertex_shader;
    ID3DXConstantTable*        g_constant_table;
    IDirect3DTexture9*        g_shade_texture;
   
    ID3DXMesh*                g_meshes[4];
    D3DXMATRIX                g_world_matrices[4];
    D3DXVECTOR4                g_mesh_colors[4];
   
    D3DXMATRIX                g_proj_matrix;
   
    D3DXHANDLE                g_world_view_handle;
    D3DXHANDLE                g_world_view_proj_handle;
    D3DXHANDLE                g_color_handle;
    D3DXHANDLE                g_dir_to_light_handle;
   
   
    ////////////////////////////////////////////////////////////////////////////////////////////////////
   

   
bool setup()
    {    
        D3DXCreateTeapot(g_device, &g_meshes[MESH_TEAPOT], NULL);
        D3DXCreateSphere(g_device, 1.0f, 20, 20, &g_meshes[MESH_SPHERE], NULL);
        D3DXCreateTorus(g_device, 0.5f, 1.0f, 20, 20, &g_meshes[MESH_TORUS], NULL);
        D3DXCreateCylinder(g_device, 0.5f, 0.5f, 2.0f, 20, 20, &g_meshes[MESH_CYLINDER], NULL);
   
        D3DXMatrixTranslation(&g_world_matrices[MESH_TEAPOT],   0.0f,   2.0f, 0.0f);
        D3DXMatrixTranslation(&g_world_matrices[MESH_SPHERE],   0.0f,  -2.0f, 0.0f);
        D3DXMatrixTranslation(&g_world_matrices[MESH_TORUS],    -3.0f,  0.0f, 0.0f);
        D3DXMatrixTranslation(&g_world_matrices[MESH_CYLINDER],  3.0f,  0.0f, 0.0f);
   
        g_mesh_colors[MESH_TEAPOT]   = D3DXVECTOR4(1.0f, 0.0f, 0.0f, 1.0f);
        g_mesh_colors[MESH_SPHERE]   = D3DXVECTOR4(0.0f, 1.0f, 0.0f, 1.0f);
        g_mesh_colors[MESH_TORUS]    = D3DXVECTOR4(0.0f, 0.0f, 1.0f, 1.0f);
        g_mesh_colors[MESH_CYLINDER] = D3DXVECTOR4(1.0f, 1.0f, 0.0f, 1.0f);
   
        
// compile shader
   

        ID3DXBuffer*    shader_buffer;
        ID3DXBuffer*    error_buffer;
   
        HRESULT hr = D3DXCompileShaderFromFile("ToonShader.cxx", NULL, NULL, "main", "vs_1_1",
                                               D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY,
                                               &shader_buffer, &error_buffer, &g_constant_table);
   
        
// output any error messages
   
    if(error_buffer)
        {
            MessageBox(NULL, (
char*)error_buffer->GetBufferPointer(), "ERROR", MB_OK);
            safe_release<ID3DXBuffer*>(error_buffer);
        }
   
        
if(FAILED(hr))
        {
            MessageBox(NULL, "D3DXCreateEffectFromFile() - FAILED", "ERROR", MB_OK);
            
return false;
        }
   
        hr = g_device->CreateVertexShader((DWORD*) shader_buffer->GetBufferPointer(), &g_vertex_shader);
   
        
if(FAILED(hr))
        {
            MessageBox(NULL, "CreateVertexShader - FAILED", "ERROR", MB_OK);
            
return false;
        }
   
        safe_release<ID3DXBuffer*>(shader_buffer);
   
        
// load textures
   
        D3DXCreateTextureFromFile(g_device, "toonshade.bmp", &g_shade_texture);
   
        g_device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
        g_device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
        g_device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);        
// disable mipmap
   
        // get handles
   
        g_world_view_handle            = g_constant_table->GetConstantByName(NULL, "g_world_view");
        g_world_view_proj_handle    = g_constant_table->GetConstantByName(NULL, "g_world_view_proj");
        g_color_handle                = g_constant_table->GetConstantByName(NULL, "g_color");        
        g_dir_to_light_handle        = g_constant_table->GetConstantByName(NULL, "g_dir_to_light");
   
        
// set shader constants
   
        D3DXVECTOR4 dir_to_light(-0.57f, 0.57f, -0.57f, 0.0f);
        g_constant_table->SetVector(g_device, g_dir_to_light_handle, &dir_to_light);
   
        g_constant_table->SetDefaults(g_device);
   
        
// set the projection matrix
   
        D3DXMatrixPerspectiveFovLH(&g_proj_matrix, D3DX_PI/4.0f, (float)WIDTH/HEIGHT, 1.0f, 1000.0f);
        
        
//g_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
   
    
        
return true;
    }
   
   
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
   

   
void cleanup()
    {    
        
for(int i = 0; i < 4; i++)
            safe_release<ID3DXMesh*>(g_meshes[i]);
   
        safe_release<IDirect3DTexture9*>(g_shade_texture);
        safe_release<IDirect3DVertexShader9*>(g_vertex_shader);
        safe_release<ID3DXConstantTable*>(g_constant_table);    
    }
   
   
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
   

   
bool display(float time_delta)
    {    
        
static float angle  = (3.0f * D3DX_PI) / 2.0f;
        
static float height = 5.0f;
   
        
if(GetAsyncKeyState(VK_LEFT) & 0x8000f)
            angle -= 0.5f * time_delta;
   
        
if(GetAsyncKeyState(VK_RIGHT) & 0x8000f)
            angle += 0.5f * time_delta;
   
        
if(GetAsyncKeyState(VK_UP) & 0x8000f)
            height += 5.0f * time_delta;
   
        
if(GetAsyncKeyState(VK_DOWN) & 0x8000f)
            height -= 5.0f * time_delta;
   
        D3DXVECTOR3 position(cosf(angle) * 7.0f, height, sinf(angle) * 7.0f);
        D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
        D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
   
        D3DXMATRIX view_matrix;
        D3DXMatrixLookAtLH(&view_matrix, &position, &target, &up);    
        
        
// render now
   

        g_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFF333333, 1.0f, 0);
   
        g_device->BeginScene();
   
        g_device->SetVertexShader(g_vertex_shader);
        g_device->SetTexture(0, g_shade_texture);
   
        D3DXMATRIX world_view, world_view_proj;
   
        
for(int i = 0; i < 4; i++)
        {
            world_view        = g_world_matrices[i] * view_matrix;
            world_view_proj = g_world_matrices[i] * view_matrix * g_proj_matrix;
   
            g_constant_table->SetMatrix(g_device, g_world_view_handle,        &world_view);
            g_constant_table->SetMatrix(g_device, g_world_view_proj_handle, &world_view_proj);
   
            g_constant_table->SetVector(g_device, g_color_handle,            &g_mesh_colors[i]);
   
            g_meshes[i]->DrawSubset(0);
        }    
        
        g_device->EndScene();
   
        g_device->Present(NULL, NULL, NULL, NULL);
   
        
return true;
    }
   
   
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
   

    LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM word_param, LPARAM long_param)
    {
        
switch(msg)
        {
        
case WM_DESTROY:
            PostQuitMessage(0);
            
break;
   
        
case WM_KEYDOWN:
            
if(word_param == VK_ESCAPE)
                DestroyWindow(hwnd);
   
            
break;
        }
   
        
return DefWindowProc(hwnd, msg, word_param, long_param);
    }
   
   
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
   

   
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, PSTR cmd_line, int cmd_show)
    {
        
if(! init_d3d(inst, WIDTH, HEIGHT, true, D3DDEVTYPE_HAL, &g_device))
        {
            MessageBox(NULL, "init_d3d() - failed.", 0, MB_OK);
            
return 0;
        }
   
        
if(! setup())
        {
            MessageBox(NULL, "Steup() - failed.", 0, MB_OK);
            
return 0;
        }
   
        enter_msg_loop(display);
   
        cleanup();
        g_device->Release();
   
        
return 0;
    }

运行截图:

 

下载源程序


posted on 2008-04-08 14:13 lovedday 阅读(1658) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论