码迷,mamicode.com
首页 > 其他好文 > 详细

龙书学习笔记(二)

时间:2014-08-22 20:57:59      阅读:204      评论:0      收藏:0      [点我收藏+]

标签:des   style   blog   http   color   os   使用   io   文件   

补线代之余抽空把第四章上色学了,之所以说之余,是因为这一章内容确实不怎么多,不过为了巩固知识,便结合刚学的上色又做了一个小程序。

首先进行回顾,这一章学到的一共有四点:

一、Direct3D中颜色用RGB(Red、Green、Blue)三元组表示,用两种结构来保存

  1. D3DCOLOR,即unsigned long,共32位,分成4个8位项,分别保存Alpha(这玩意的作用会在第七章学到)、红、绿、蓝,均在0x00~0xff之间取值(就是0~255)
  2. 通过结构体来保存(D3DXCOLOR和D3DCOLORVALUE),用float r、float g、float b,float a四个float数据成员来替代第一种结构的存储方式。其中,每个数据成员的亮度区间均为0~1。另外,由于D3DXCOLOR包含了一系列的运算符重载和构造函数,而D3DCOLORVALUE只包含4个数据成员,所以通常使用前者而非后者。

二、颜色可通过D3DCOLOR_ARGB宏和D3DCOLOR_XRGB宏来取值,其中后者只是前者将Alpha设置为0xff后的简化版:

D3DCOLOR  brightRed = D3DCOLOR_ARGB(255, 255, 0, 0);
D3DXCOLOR brightRed = D3DCOLOR_XRGB(255, 0, 0);
#define D3DCOLOR_XRGB(r, g, b) D3DCOLOR_ARGB(0xff, r, g, b)

三、为了添加颜色,需要在顶点结构体中添加相应的数据成员,而FVF也要改变相应设置:

struct ColorVertex
{
    ColorVertex(){}

    ColorVertex(float x, float y, float z, D3DCOLOR c)
    {
        _x = x;     _y = y;  _z = z;  _color = c;
    }

    float _x, _y, _z;
    D3DCOLOR _color;

    static const DWORD FVF;
};
const DWORD ColorVertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;

四、到龙书第四章为止,给出的着色方式有两种,分别为平面找色和Gouraud着色(也称平滑着色)。前者将每个图元的每个像素一致赋予第一个顶点做指定的颜色,后者则不会忽略另外两个顶点的颜色,而是会在各种颜色之间进行平滑的过渡,设置方式分别为:

Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); // 平面着色
Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); // 平滑着色

接下来便是详细解释自己完成的小程序,这玩意由两个旋转的蓝色水晶(想不出其它名字了......)组成,每个水晶由两个底面重合的四棱锥构成,采用默认的面填充方式,着色模式分别为平面着色和平滑着色。

首先需要指明的是,这段程序要在龙书提供的d3dUtility.h头文件中添加下列全局颜色常量:

namespace d3d{
    .
    .
    .
    const D3DXCOLOR WHITE  (D3DCOLOR_XRGB(255, 255, 255));
    const D3DXCOLOR BLACK  (D3DCOLOR_XRGB(  0,   0,   0));
    const D3DXCOLOR RED    (D3DCOLOR_XRGB(255,   0,   0));
    const D3DXCOLOR GREEN  (D3DCOLOR_XRGB(  0, 255,   0));
    const D3DXCOLOR BLUE   (D3DCOLOR_XRGB(  0,   0, 255));
    const D3DXCOLOR YELLOW (D3DCOLOR_XRGB(255, 255,   0));
    const D3DXCOLOR CYAN   (D3DCOLOR_XRGB(  0, 255, 255));
    const D3DXCOLOR MAGENTA(D3DCOLOR_XRGB(255,   0, 255));
}

然后开始主要的源代码,依旧是从全局变量Device、屏幕高度Height和屏幕宽度Width的定义开始:

/*
 * File: crystal.cpp
 *
 * Author: EnoWang
 *
 * Desc: 绘制了两个水晶,每个水晶由两个底面重合的蓝色四棱锥构成,
 *       分别采用平面着色模式和平滑着色模式。
 *
 */
#include "d3dUtility.h"

IDirect3DDevice9* Device;

const int Width  = 640;
const int Height = 480;

之后定义全局变量WorldMatrix,即世界变换矩阵,在这个简单的程序中,它的作用是在世界坐标系中设置某个水晶的中心点坐标。

下一步是顶点缓存和索引缓存的定义:

// 世界变换矩阵
D3DXMATRIX WorldMatrix;

IDirect3DVertexBuffer9* verb = 0;
IDirect3DIndexBuffer9 * indb = 0;

接下来开始定义顶点结构体:

// 创建顶点结构体
struct Vertex{
    Vertex() { }
    Vertex(float x, float y, float z, D3DCOLOR color)
    {
        _x = x, _y = y, _z = z, _color = color;
    }

    float _x, _y, _z;
    D3DCOLOR _color;

    static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;

正如之前所说,因为这次需要在绘制的图形上添加颜色,所以结构体的构成和FVF也发生了变化。

首先,相比上一个程序,结构体中多出了一个D3DCOLOR类型的颜色数据成员。同时书中强调,这里无法使用D3DCOLORVALUE结构,原因是,Direct3D希望用一个32位的值来描述顶点的颜色,而非一个结构体。

而相应的,作为顶点格式的标记,FVF的值也需要做出改变,在这里,将单纯描述空间的D3DFVF_XYZ修改成了同时描述空间和颜色的D3DFVF_XYZ | D3DFVF_DIFFUSE。

现在开始框架函数的定义,由于相比前一个程序的改变并非十分明显,所以依旧分为创建顶点缓存和索引缓存、锁定并对缓存写入数据、设置摄像机、实施投影变换和设置渲染模式五个步骤,首先进行第一步:

// 框架函数
bool Setup()
{
    // 创建顶点缓存和索引缓存
    Device->CreateVertexBuffer(
        6 * sizeof(Vertex), // 每个水晶包含6个顶点
        D3DUSAGE_WRITEONLY,
        Vertex::FVF,
        D3DPOOL_MANAGED,
        &verb,
        0);
    Device->CreateIndexBuffer(
        24 * sizeof(WORD), // 每个水晶包含八个三角形面
        D3DUSAGE_WRITEONLY,
        D3DFMT_INDEX16,
        D3DPOOL_MANAGED,
        &indb,
        0);

接下来锁定并向缓存中写入数据:

    // 将数据写入缓存
    Vertex* vertics;

    verb->Lock(0, 0, (void**)&vertics, 0);

    vertics[0] = Vertex(-1.0f, 0.0f,-1.0f, d3d::BLUE);
    vertics[1] = Vertex( 1.0f, 0.0f,-1.0f, d3d::BLUE);
    vertics[2] = Vertex( 1.0f, 0.0f, 1.0f, d3d::BLUE);
    vertics[3] = Vertex(-1.0f, 0.0f, 1.0f, d3d::BLUE);
    vertics[4] = Vertex( 0.0f, 2.0f, 0.0f, d3d::WHITE);
    vertics[5] = Vertex( 0.0f,-2.0f, 0.0f, d3d::WHITE);

    verb->Unlock();

    WORD* Indics;
    indb->Lock(0, 0, (void**)&Indics, 0);

    Indics[0]  = 0; Indics[1]  = 1; Indics[2]  = 4;
    Indics[3]  = 0; Indics[4]  = 1; Indics[5]  = 5;
    Indics[6]  = 1; Indics[7]  = 2; Indics[8]  = 4;
    Indics[9]  = 1; Indics[10] = 2; Indics[11] = 5;
    Indics[12] = 2; Indics[13] = 3; Indics[14] = 4;
    Indics[15] = 2; Indics[16] = 3; Indics[17] = 5;
    Indics[18] = 3; Indics[19] = 0; Indics[20] = 4;
    Indics[21] = 3; Indics[22] = 0; Indics[23] = 5;

    indb->Unlock();

虽然我需要的是两个水晶,以便进行对比,但考虑到它们只有着色模式不同,所以只需要在每一帧中,于不同的两个世界坐标将水晶各绘制一遍即可。

之后设置摄影机、投影矩阵和渲染模式:

    // 设置摄影机位置
    D3DXVECTOR3 position(0.0f, 0.0f, -4.0f);
    D3DXVECTOR3      target(0.0f, 0.0f,  0.0f);
    D3DXVECTOR3       up(0.0f, 1.0f,  0.0f);

    D3DXMATRIX Camera;

    D3DXMatrixLookAtLH(&Camera, &position, &target, &up);

    Device->SetTransform(D3DTS_VIEW, &Camera);

    // 设置投影矩阵
    D3DXMATRIX proj;
    D3DXMatrixPerspectiveFovLH(
        &proj,
        D3DX_PI * 0.5f,
        (float)Width / (float)Height,
        1.0f,
        1000.0f);
    Device->SetTransform(D3DTS_PROJECTION, &proj);

    // 关闭光照
    Device->SetRenderState(D3DRS_LIGHTING, false);

    return true;
}

首先,由于这次程序所采用的多边形填充模式是面填充,D3DFILL_SOILD——默认的填充模式,所以也不需要像前一个程序一样进行强调,省略即可。相应的,如果将FILL_MODE修改为D3DFILL_WIREFRAME或D3DFILL_POINT,则会生成着色后的线框或顶点。

其次,考虑到已经给物体本身设置了颜色,所以就不需要光照给与颜色了,因此便将D3DRS_LIGHTING设置为false。

void Cleanup()
{
    d3d::Release<IDirect3DVertexBuffer9*>(verb);
    d3d::Release<IDirect3DIndexBuffer9 *>(indb);
}

接下来是绘制函数的定义,首先设置旋转角度:

bool Display(float timeDelta)
{
    if (Device) {
        D3DXMATRIX Rx, Ry;
        D3DXMatrixRotationX(&Rx, 0.5f);

        static float y = 0.0f;
        D3DXMatrixRotationY(&Ry, y);
        y += 2 * timeDelta;

        if (y > 6.28f)
            y = 0.0f;

之后开始绘制图像,这一次,我既想改变物体的世界坐标,又想改变物体的角度,并让物体产生旋转的效果,而改变绘制状态的SetTransform函数只能接受一个矩阵。

首先尝试着用了两次SetTransform,但是编译后的程序在运行后,窗口中的图像只有后一个SetTransform的效果。之后又思索半响,便参考之前Rx,Ry两个矩阵的相乘,把WorldMatrix也放入了矩阵相乘的队列里,居然成功的让结果产生了既旋转也改变世界坐标的效果。

于是便感觉自己大致明白了矩阵相乘在D3D中的一部分用法(当然,数学原理是什么我依旧不太明白,继续补线代吧......)

        // 开始绘制
        Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, d3d::WHITE, 1.0f, 0);
        Device->BeginScene();

        // 将图形设置信息写入数据流
        Device->SetStreamSource(0, verb, 0, sizeof(Vertex));
        Device->SetIndices(indb);
        Device->SetFVF(Vertex::FVF);
        
        // 第一个水晶,采用平滑着色(Gouraud着色)模式
        // 设置世界坐标系中水晶的中心点坐标
        D3DXMatrixTranslation(&WorldMatrix, -2.0f, 0.0f, 0.0f);

        D3DXMATRIX Res = Rx * Ry * WorldMatrix;
        Device->SetTransform(D3DTS_WORLD, &Res);
        Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); // 设置为平滑着色
        Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 6, 0, 8);
        
        // 第二个水晶,采用平面着色模式
        D3DXMatrixTranslation(&WorldMatrix,  2.0f, 0.0f, 0.0f);
        // 前一个水晶沿顺时针方向旋转,这一个水晶沿逆时针
        Res = Rx * Ry * WorldMatrix;
        Device->SetTransform(D3DTS_WORLD, &Res);
        Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); // 设置为平面着色
        Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 6, 0, 8);
        
        Device->EndScene(); // 结束绘制
        Device->Present(0, 0, 0, 0); // 提交后台缓存
    }
    return true;
}

最后完成与上一个程序并无区别的回调函数和Windows主函数:

// 回调函数
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
    case WM_DESTROY:
        ::PostQuitMessage(0);
        break;
    // 按下Esc键退出
    case WM_KEYDOWN:
        if (wParam == VK_ESCAPE)
            ::DestroyWindow(hwnd);
        break;
    }
    return ::DefWindowProc(hwnd, msg, wParam, lParam);
}

// Windows主函数
int WINAPI WinMain(HINSTANCE hinstance,
                   HINSTANCE prevInstance,
                   PSTR cmdLine,
                   int showCmd)
{
    if (!d3d::InitD3D(hinstance,
        Width, Height, true, D3DDEVTYPE_HAL, &Device)) {
        ::MessageBox(0, "InitD3D() - FAILED", 0, 0);
        return 0;
    }

    if (!Setup()) {
        ::MessageBox(0, "Setup() - FAILED", 0, 0);
        return 0;
    }

    d3d::EnterMsgLoop(Display);

    Cleanup();

    Device->Release();

    return 0;
}

编译需要d3dUtility.h和d3dUtility.cpp,头文件的源码可以在这里下载:http://www.d3dcoder.net

然后图形在旋转过程中依旧有几片三角元在我眼前消失又出现,留待以后解决......

截图:

bubuko.com,布布扣

bubuko.com,布布扣

龙书学习笔记(二)

标签:des   style   blog   http   color   os   使用   io   文件   

原文地址:http://www.cnblogs.com/EnoWang/p/3930134.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!