天行健 君子当自强而不息

D3D中的地形绘制基础(2)

新建网页 1

13.2.1 计算顶点

在图13.4中,计算三角形网格上的顶点,我们只是在开始产生顶点的地方,一行一行的生成顶点数据,直到结束为止。单元格的顶点与顶点之间有一块空白区域,这会让我们取得x、z坐标,但y坐标是什么呢?得到y坐标很容易,当读取高度图数据结构时会找到对应的入口。

注意:这个操作使用一个巨大的顶点缓存去保存所有地形上的所有顶点。这可能会引起硬件局限性的问题。例如:3D设备都预设了最大图元数和最大顶点索引值。检查D3DCAPS9结构的MaxPrimitiveCount和MaxVertexlndex成员来获得你的设备的限定值。

计算纹理坐标,看图13.5,给我们一个简单的设定,允许我们用(u, v)纹理坐标去对应地形顶点坐标。

最后,用代码生成顶点:

    bool cTerrain::generate_vertices()
    {
        
if(FAILED(m_device->CreateVertexBuffer(m_num_vertices * sizeof(cTerrainVertex), D3DUSAGE_WRITEONLY,
            TERRAIN_VERTEX_FVF, D3DPOOL_MANAGED, &m_vertex_buffer, NULL)))
        {
            
return false;
        }
    
        
// coordinates to start generating vertices at
    
    int start_x = -m_width / 2;
        
int start_z =  m_depth / 2;
    
        
// coordinates to end generating vertices at
    
    int end_x =  m_width / 2;
        
int end_z = -m_depth / 2;
    
        
// compute the increment size of the texture coordinates from one vertex to the next
    
    float u_coord_increment_size = 1.0f / m_num_cells_per_row;
        
float v_coord_increment_size = 1.0f / m_num_cells_per_col;
    
        cTerrainVertex* v;
    
        m_vertex_buffer->Lock(0, 0, (
void**)&v, 0);
    
        
int row = 0;
    
        
for(int z = start_z; z >= end_z; z -= m_cell_spacing)
        {
            
int column = 0;
    
            
for(int x = start_x; x <= end_x; x += m_cell_spacing)
            {
                
int index = row * m_num_verts_per_row + column;
    
                v[index] = cTerrainVertex(x, m_height_map[index], z,
                                          column * u_coord_increment_size, row * v_coord_increment_size);
    
                column++;
            }
    
            row++;
        }
    
    
        m_vertex_buffer->Unlock();
        
        
return true;
    }

13.2.2 计算索引-定义三角形

计算三角形网格的索引,只需要循环访问每一个格子,从左上到右下,如图13.4,并且计算组成格子的2个三角形。

代码生成索引:

    bool cTerrain::generate_indices()
    {
        
if(FAILED(m_device->CreateIndexBuffer(
            m_num_triangles * 3 * 
sizeof(WORD),    // 3 indices per triangle
    
            D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &m_index_buffer, NULL)))
        {
            
return false;
        }
    
        WORD* indices;
    
        m_index_buffer->Lock(0, 0, (
void**)&indices, 0);
    
        
// index to start of a group of 6 indices that describe the two triangles that make up a quad
    
    int base_index = 0;
    
        
// loop through and compute the triangles of each quad
    
    for(int row = 0; row < m_num_cells_per_col; row++)
        {
            
for(int col = 0; col < m_num_cells_per_row; col++)
            {
                indices[base_index]        = row      * m_num_verts_per_row + col;
                indices[base_index + 1] = row      * m_num_verts_per_row + (col+1);
                indices[base_index + 2] = (row+1) * m_num_verts_per_row + col;
    
                indices[base_index + 3]    = (row+1) * m_num_verts_per_row + col;
                indices[base_index + 4] = row      * m_num_verts_per_row + (col+1);
                indices[base_index + 5] = (row+1) * m_num_verts_per_row + (col+1);
    
                base_index += 6;    
// next quad
    
        }
        }
    
        m_index_buffer->Unlock();
    
        
return true;
    }

13.3纹理

cTerrain类提供2个方法去处理地形的纹理。最简单的方法是读取一个已经制作好的纹理文件并使用它:
    bool cTerrain::load_texture(const string& filename)
    {
        
if(FAILED(D3DXCreateTextureFromFile(m_device, filename.c_str(), &m_texture)))
            
return false;
    
        
return true;
    }

13.3.1 程序上的处理方法

一个可选择的方法是用程序计算地形的纹理,就是说,我们创建一个空纹理,根据定义的参数用代码计算每一个部分的颜色,在例子中,参数是地形的高度。

我们用cTerrain::generate_texture方法去生成纹理,首先用D3DXCreateTexture方法创建一个空的纹理,锁定高度级别(top level,纹理图的一个成员,有多个级别),不断的循环每一个texel图素)并给它上色,texel的颜色取决于与方格对应的高度(近似高度)。我们的想法是:地形中较低的地方是沙滩色,中间的地方像是绿色的小山丘,较高的地方颜色好像雪山。我们定义的高度是方格中左上角的近似高度。

一旦每个texel都有了颜色,我们想让每一个texel变暗或是变亮,这基于光打在格子中对应的texel上的角度,由Terrain::lightTerrain方法实现。
    bool cTerrain::generate_texture(D3DXVECTOR3* dir_to_light)
    {
        
// Method fills the top surface of a texture procedurally, then lights the top surface.
        // Finally, it fills the other mipmap surfaces based on the top surface data using D3DXFilterTexture.
    
        // texel for each quad cell
    
    int texture_width  = m_num_cells_per_row;
        
int texture_height = m_num_cells_per_col;
    
        
// create an empty texture with a complete mipmap chain
    
    if(FAILED(D3DXCreateTexture(m_device, texture_width, texture_height, 0, 0, D3DFMT_X8R8G8B8,
                                    D3DPOOL_MANAGED, &m_texture)))
        {
            
return false;
        }
    
        D3DSURFACE_DESC texture_desc;
        m_texture->GetLevelDesc(0, &texture_desc);
    
        
// make sure we got the requested format because our code that fills the texture is
        // hard coded to a 32 bit pixel depth.
    
    if(texture_desc.Format != D3DFMT_X8R8G8B8)
            
return false;
    
        
// lock top entire texture surface
    
    D3DLOCKED_RECT locked_rect;
        m_texture->LockRect(0, &locked_rect, NULL, 0);
    
        DWORD* image_data = (DWORD*) locked_rect.pBits;
    
        
for(int row = 0; row < texture_height; row++)
        {
            
for(int col = 0; col < texture_width; col++)
            {
                D3DXCOLOR color;
    
                
// get height of upper left vertex of quad
    
            float height = get_height_map_entry(row, col) / m_height_scale;
    
                
if(height < 42.5f)          color = BEACH_SAND;
                
else if(height < 85.0f)  color = LIGHT_YELLOW_GREEN;
                
else if(height < 127.5f) color = PUREGREEN;
                
else if(height < 170.0f) color = DARK_YELLOW_GREEN;
                
else if(height < 212.5f) color = DARKBROWN;
                
else                     color = WHITE;
    
                
// fill locked data, note we divide the pitch by four because the pitch is given in bytes
                // and there are 4 bytes per DWORD.
    
                image_data[row * (locked_rect.Pitch / 4) + col] = (D3DCOLOR) color;
            }
        }
    
        m_texture->UnlockRect(0);
    
        
if(! light_terrain(dir_to_light))
        {
            MessageBox(NULL, "light_terrain() - FAILED", "ERROR", MB_OK);
            
return false;
        }
    
        
if(FAILED(D3DXFilterTexture(m_texture, NULL, 0, D3DX_DEFAULT)))
        {
            MessageBox(NULL, "D3DXFilterTexture() - FAILED", "ERROR", MB_OK);
            
return false;
        }
    
        
return true;
    }

posted on 2008-04-02 19:41 lovedday 阅读(1894) 评论(0)  编辑 收藏 引用


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


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论