第五十九章 试着制作3D背景吧(3)

>>点此回到教程目录

虽然之前大家已经看过了,不过首先我们还是看一下运行结果。


运行结果


可以看到墙壁连续地在滚动(Scroll)呢。

然而,我们要对这个墙壁一个个地登录、独立起来计算、进行淡入淡出计算,看起来实在是麻烦啊。

因此,

通过进行

“把这个图像像墙壁一样给我表示在这里!”

“把这个图像作为地面给我表示在这这里!”

这样子的初始化方式,在恰到好处的间隔下一个一个地设定多边形,而登录计算都自动地进行的话就相当方便了。

在这一章中,我们来试着考虑只需要写一行初始化函数,墙壁和的地面要多少都被追加多少的程序吧。

一开始虽然很麻烦,不过从后面开始就很轻松了。

首先,由于我们要把许多多边形都统一管理,因此我们有必要定义一个进一步来统括前面的多边形信息的结构体的结构体。

统括许多多边形的结构体我们编写为Object_t。

而每一个多边形作为Object_t的孩子,在这个意义下我们将其编写为ObChild_t。

结构体和之前的结构体比起来并没有什么太大的变化,它像下面那样:

//每一个纹理相关的结构体
typedef struct{
    float x,y,z;//中心点
    VERTEX_3D Vertex[6] ;        //绘制用的6个顶点
} ObChild_t;

//大量的纹理集中在一起的信息
//比如在左边不断地连续显示的许多墙壁将在这里管理许多ObChild_t。
//Obchild_t的集合
typedef struct{
    int Type;    // 0:平行于画面、1:垂直于画面
    int Img;    //图像
    int ImgSize;
    int ImgX1,ImgX2,ImgY1,ImgY2;
    float LargeX,LargeY;//横纵的大小(Type为1的时候LargeX扮演LargeZ的职能)
    float Zhaba;        //深度的幅度
    float FromZ,ToZ;    //是否设定从哪儿到哪儿z深度上的移动
float FadeFromZ,FadeToZ;    //是否设定从哪儿到哪儿进行淡入淡出(消失的瞬间淡出,显示的瞬间淡入)
    int ObchindMax;
    ObChild_t ObChild[ OBCHILD_MAX ];
} Object_t;

这里新出现一个Zhaba的变量。它表示“深度Z的幅度”。(译者注:这里的幅度可以理解为范围)

要说这是什么的话,

像这样,由于要把多张纹理连续地贴上去,如果在执行“在FromZ~ToZ之间,给我显示五张纹理”这样子的命令的时候,可以自动地计算纹理结构中的Zhaba然后计算出纹理的深度的话会很方便,因此我们增加了这个变量。

另外,如果说“在FromZ~ToZ之间,给我显示5张纹理”的话,那么同时表示的纹理需要6张,这一点请注意。

要说为什么的话,这是因为前进方向的反方向端假设没有纹理的话,如果考虑只有正当中的5张纹理向自己靠近过来,那么就会发现“FromZ”的位置就没有用于绘制的纹理了。

为此,像下面的图像中一样,后面还需要一张纹理。

图像的张数我们保存在ObchildMax这个变量中。

另外,还有一个重要的地方。那就是“必须要先绘制深处的东西”这个事情。

要是我们先绘制靠近外面的东西,之后 再绘制靠近立面的东西的话,显示出来就会很奇怪。

因此,一般而言我们需要用Z来进行排序。

由于在范例中只在同一个对象里面排序,因此如果不同的对象之间出现重叠的时候有必要对全体进行排序这一点请您牢记。

我们注意着这一点然后来看看程序吧。

首先是结构体的初始化。由于初始化所需要的信息变得相当地多了,因此看起来会很困难,大家努力地看吧。(汗)

/*
int ImgHandle   : 图像句柄
int ImgSize     : 图像尺寸
int ImgX1       : 图像使用部分的左上角坐标
int ImgY1       : 图像使用部分的左上角坐标
int ImgX2       : 图像使用部分的右下角坐标
int ImgY2       : 图像使用部分的右下角坐标
float LargeX    : 绘制的大小(横向)
float LargeY    : 绘制的大小(纵向)
int Type        : 绘制的种类 0:与画面一个方向 1:与画面垂直(墙壁) 2:与画面垂直(地面)
float FromZ     : 绘制开始的深度
float FadeFromZ : 淡入开始的深度
float FadeToZ   : 淡出开始的深度
float ToZ       : 绘制种子的深度
float GraphX    : 绘制的中心点
float GraphY    : 绘制的中心点
int ObchildMax  : 只在type为0的时候,决定同时显示多少个
*/

初始化所需要的信息就有这么多。IniObj函数的参数除了一开始的*Ob意外,其他的都按照上面的顺序来接受,请参考一下。

void IniObj(Object_t *Ob, int ImgHandle, int ImgSize, int ImgX1, int ImgY1, int ImgX2, int ImgY2, float LargeX, float LargeY,
              int Type, float FromZ, float FadeFromZ, float FadeToZ, float ToZ, float GraphX, float GraphY, int ObchildMax){
    int i,s;

统括大量纹理的结构体Object在范例中最大可以登录3个。 如果您有余力的话,可以试着对其进行动态登录。(译者注:这里可以用malloc来进行动态处理,如果是C++的话就更方便了)

    if( ObjectNum >= OBJECT_NUM_MAX-1 ){
        printfDx("Object登录溢出\n");
        return;
    }
    ObjectNum++;//Object登录数量增加

后面就都是代入的事情了。

    Ob->Img     = ImgHandle;//图像句柄
    Ob->ImgSize = ImgSize;//图像尺寸
    Ob->ImgX1   = ImgX1;
    Ob->ImgY1   = ImgY1;
    Ob->ImgX2   = ImgX2;
    Ob->ImgY2   = ImgY2;
    Ob->LargeX  = LargeX;//总之我们适当地设定绘制的大小。横纵比和素材一样。
    Ob->LargeY  = LargeY;
    Ob->Type    = Type;//将Type设定为垂直
    Ob->FromZ      =  FromZ;//绘制开始位置
    Ob->FadeFromZ  =  FadeFromZ;//绘制淡入开始位置
    Ob->FadeToZ    =  FadeToZ;//绘制淡出开始位置
    Ob->ToZ        =  ToZ;//绘制结束位置
    Ob->ObchindMax = OBCHILD_MAX;

Type为0的时候,表示垂直于画面。

也就是说,在范例中对应于樱花的显示。

我们让垂直于画面的物体能够被指定绘制的个数。

至于除此之外的Type,在范例中我们让其使用预定义的个数。

    if( Ob->Type == 0 ){
        Ob->ObchindMax = ObchildMax;
    }
    if( Ob->ObchindMax - 1 <= 0 ){
        printfDx("显示数量的设定异常\n");
        return;
    }
        //Z的幅度计算
    Ob->Zhaba = (Ob->FromZ - Ob->ToZ) / (Ob->ObchindMax-1);

至于为什么这里是-1,正如上面说明的那样,这是因为允许绘制的最大数-1章纹理将成为在.FromZ~.ToZ之间的纹理数。(译者注:多余的一张纹理用于追加显示)

计算Z的幅度,然后自动计算从开始绘制的位置开始到结束绘制的位置为止这一范围内的多边形的位置并完成对它们的一系列设置。

    float ou1 = (float)Ob->ImgX1 / Ob->ImgSize, ou2 = (float)(Ob->ImgX2 - Ob->ImgX1) / Ob->ImgSize;
    float ov1 = (float)Ob->ImgY1 / Ob->ImgSize, ov2 = (float)(Ob->ImgY2 - Ob->ImgY1) / Ob->ImgSize;
    for(s=0; s<Ob->ObchindMax; s++){
        Ob->ObChild[s].x = GraphX;
        Ob->ObChild[s].y = GraphY;
        Ob->ObChild[s].z = Ob->ToZ - Ob->Zhaba + Ob->Zhaba * s;;
        for(i=0; i<6; i++){
            Ob->ObChild[s].Vertex[i].r = Ob->ObChild[s].Vertex[i].g = Ob->ObChild[s].Vertex[i].b = Ob->ObChild[s].Vertex[i].a = 255;
            Ob->ObChild[s].Vertex[i].u = ou1 + ou2 * VtPm[i].u;
            Ob->ObChild[s].Vertex[i].v = ov1 + ov2 * VtPm[i].v;
        }
    }
}

初始化函数在这里。只要为对象的初始化函数iniObj传入必要的信息,那么“墙壁”和“地面”地面就被增加进去了。

在这里需要注意的,是同一张图像没有多次读入这一点。

在3D函数中,允许进行“将图像从这里到这里进行使用”这样子的指定方式。

因此,在一个图像文件中经常出现里面放入了多张图像的情况。在范例中就是这样。

这个时候,请只生成一次图像句柄。如果两次载入的话,那么程序会吃掉2倍的内存量。

因为要传给函数的信息是相当的多,每写一个参数就换一行并写上注释的话我认为会比较容易阅读(因为在范例中为了减少行数我省了这个过程)。

void ini (){
    int ImgHandle;
    ObjectNum = 0;
    ImgHandle = LoadGraph( "mydat/img/tex.png" );
    IniObj(&Object[0], ImgHandle, 512,  0,  0, 256, 128, 250, 50, 2, 1000, 400, -200, -400, 320, 240-90, OBCHILD_MAX);
    IniObj(&Object[1], ImgHandle, 512, 60,270, 405, 512, 180,125, 0, 1000, 400, -200, -400, 470, 275, 6);
    ImgHandle = LoadGraph( "mydat/img/kabe.png" );
    IniObj(&Object[2], ImgHandle, 512,  0,  0, 390, 512,  73, 90, 1, 1000, 400, -200, -400, 170, 240, OBCHILD_MAX);
}

接下来进行对象的计算。

首先,按照Type的不同来计算坐标。感觉上和前面一章很相似呢。

void ClacObject(){
    int t,s,i;
    for(t=0; t<ObjectNum; t++){
        for(s=0; s<Object[t].ObchindMax; s++){
            Object[t].ObChild[s].z-=3;
            for(i=0;i<6;i++){
                switch(Object[t].Type){
                    case 0://与画面平行
                        Object[t].ObChild[s].Vertex[i].pos.x = Object[t].ObChild[s].x + Object[t].LargeX * VtPm[i].x ;    
                        Object[t].ObChild[s].Vertex[i].pos.y = Object[t].ObChild[s].y + Object[t].LargeY * VtPm[i].y ;
                        Object[t].ObChild[s].Vertex[i].pos.z = Object[t].ObChild[s].z ;
                        break;
                    case 1://与画面垂直(墙壁)
                        Object[t].ObChild[s].Vertex[i].pos.x = Object[t].ObChild[s].x;    
                        Object[t].ObChild[s].Vertex[i].pos.y = Object[t].ObChild[s].y + Object[t].LargeY * VtPm[i].y ;
                        Object[t].ObChild[s].Vertex[i].pos.z = Object[t].ObChild[s].z + Object[t].Zhaba/2* VtPm[i].x ;
                        break;
                    case 2://与画面锤子(地板)
                        Object[t].ObChild[s].Vertex[i].pos.x = Object[t].ObChild[s].x + Object[t].LargeX * VtPm[i].x;    
                        Object[t].ObChild[s].Vertex[i].pos.y = Object[t].ObChild[s].y ;
                        Object[t].ObChild[s].Vertex[i].pos.z = Object[t].ObChild[s].z + Object[t].Zhaba/2* VtPm[i].y;
                        break;
                }
            }
        }

接下来是淡入淡出。这个和前面也是一样的。

        if( Object[t].FromZ - Object[t].FadeFromZ <= 0 ){
            printfDx("Object[%d].From的设定有问题\n",t);
        }
        else if( Object[t].FadeToZ - Object[t].ToZ <= 0 ){
            printfDx("Object[%d].To的设定有问题\n",t);
        }
        else{
            for(s=0; s<Object[t].ObchindMax; s++){
                for(i=0; i<6; i++){
                    float z = Object[t].ObChild[s].Vertex[i].pos.z;
            //当前位置如果比绘制范围还要远的话设置透明度为0
                    if (z < Object[t].ToZ){
                        Object[t].ObChild[s].Vertex[i].a = 0;
                    }
            //(靠近的时候)如果在淡入的位置的话
                    else if(Object[t].ToZ < z && z <=Object[t].FadeToZ){
                        Object[t].ObChild[s].Vertex[i].a = (unsigned char)(255.0f / (Object[t].FadeToZ - Object[t].ToZ) * (z - Object[t].ToZ)) ;
                    }
            //如果在通常绘制的位置的话
                    else if(Object[t].FadeToZ <= z && z <= Object[t].FadeFromZ){
                        Object[t].ObChild[s].Vertex[i].a = 255;
                    }
            //(靠近的时候)如果在淡出的位置的话
                    else if(Object[t].FadeFromZ <= z && z < Object[t].FromZ){
                        Object[t].ObChild[s].Vertex[i].a = (unsigned char)(255.0f / (Object[t].FromZ - Object[t].FadeFromZ) * (Object[t].FromZ - z)) ; 
                    }
                    //如果比绘制范围还要近的话设置透明度为0
                    else if(Object[t].FromZ < z){
                        Object[t].ObChild[s].Vertex[i].a = 0;
                    }
                }

在这里,我们进行循环图像的处理。

当图像来到最靠前的时候,又把它移动到最深处,循环滚动(Loop Scroll),我们像这样子进行处理。

        //靠近以致看不见的话
                if(Object[t].ObChild[s].z < Object[t].ToZ - Object[t].Zhaba*0.5f){
        //把它移动到最靠近那边的那一侧
                    float sub = (Object[t].ToZ - Object[t].Zhaba*0.5f)- Object[t].ObChild[s].z;
                    Object[t].ObChild[s].z = Object[t].FromZ + Object[t].Zhaba*0.5f - sub;
                }
        //远离以致看不见了的话
                else if(Object[t].ObChild[s].z > Object[t].FromZ + Object[t].Zhaba*0.5f){
        //将它移动到最靠近这边的这一侧
                    float sub = Object[t].ObChild[s].z - (Object[t].FromZ + Object[t].Zhaba*0.5f);
                    Object[t].ObChild[s].z = Object[t].ToZ - Object[t].Zhaba*0.5f + sub;
                }
            }
        }
    }
}

接下来,我们通过Z来对纹理排序。我们用最简单易懂的简单选择排序算法来进行排序。

排序相当简单,就是简单选择的排序。

void SwapObChild(ObChild_t *Ob1,ObChild_t *Ob2){
    ObChild_t t = *Ob1;
    *Ob1 = *Ob2;
    *Ob2 = t;
}

void SortObject(){
    int i,j,t;
    for(t=0; t<ObjectNum; t++){
        for (i = 0; i < Object[t].ObchindMax ; i++) {
            for (j = i + 1; j < Object[t].ObchindMax ; j++) {
                if ( Object[t].ObChild[i].z < Object[t].ObChild[j].z ) {
                    SwapObChild( &Object[t].ObChild[i],  &Object[t].ObChild[j] );
                }
            }
        }
    }
}

那么我们来试着看看整个程序吧。

— main.cpp —

#include "../../../include/DxLib.h"

//不能够太少。因此我们要-1后使用。
#define OBCHILD_MAX 11
#define OBJECT_NUM_MAX 10

//用于用2个三角形多边形来绘制四边形而设定的数值。因为数值是固定的,因此没有必要记住。
typedef struct{
    float x,y;
    float u,v;
}VtPm_t;
VtPm_t VtPm[6]={{-1,1,0,0},{1,1,1,0},{-1,-1,0,1},{1,-1,1,1},{-1,-1,0,1},{1,1,1,0}};

//每一个纹理相关的结构体
typedef struct{
    float x,y,z;//中心点
    VERTEX_3D Vertex[6] ;        //绘制用的6个顶点
} ObChild_t;

//大量的纹理集中在一起的信息
//比如在左边不断地连续显示的许多墙壁将在这里管理许多ObChild_t。
//Obchild_t的集合
typedef struct{
    int Type;    // 0:平行于画面、1:垂直于画面
    int Img;    //图像
    int ImgSize;
    int ImgX1,ImgX2,ImgY1,ImgY2;
    float LargeX,LargeY;//横纵的大小(Type为1的时候LargeX扮演LargeZ的职能)
    float Zhaba;        //深度的幅度
    float FromZ,ToZ;    //是否设定从哪儿到哪儿z深度上的移动
float FadeFromZ,FadeToZ;    //是否设定从哪儿到哪儿进行淡入淡出(消失的瞬间淡出,显示的瞬间淡入)
    int ObchindMax;
    ObChild_t ObChild[ OBCHILD_MAX ];
} Object_t;

int ObjectNum;
Object_t Object[OBJECT_NUM_MAX];
/*
int ImgHandle   : 图像句柄
int ImgSize     : 图像尺寸
int ImgX1       : 图像使用部分的左上角坐标
int ImgY1       : 图像使用部分的左上角坐标
int ImgX2       : 图像使用部分的右下角坐标
int ImgY2       : 图像使用部分的右下角坐标
float LargeX    : 绘制的大小(横向)
float LargeY    : 绘制的大小(纵向)
int Type        : 绘制的种类 0:与画面一个方向 1:与画面垂直(墙壁) 2:与画面垂直(地面)
float FromZ     : 绘制开始的深度
float FadeFromZ : 淡入开始的深度
float FadeToZ   : 淡出开始的深度
float ToZ       : 绘制种子的深度
float GraphX    : 绘制的中心点
float GraphY    : 绘制的中心点
int ObchildMax  : 只在type为0的时候,决定同时显示多少个
*/
void IniObj(Object_t *Ob, int ImgHandle, int ImgSize, int ImgX1, int ImgY1, int ImgX2, int ImgY2, float LargeX, float LargeY,
              int Type, float FromZ, float FadeFromZ, float FadeToZ, float ToZ, float GraphX, float GraphY, int ObchildMax){
    int i,s;

    if( ObjectNum >= OBJECT_NUM_MAX-1 ){
        printfDx("Object登录溢出\n");
        return ;
    }
    ObjectNum++;//Object登录数量增加

    Ob->Img     = ImgHandle;//图像句柄
    Ob->ImgSize = ImgSize;//图像尺寸
    Ob->ImgX1   = ImgX1;
    Ob->ImgY1   = ImgY1;
    Ob->ImgX2   = ImgX2;
    Ob->ImgY2   = ImgY2;
    Ob->LargeX  = LargeX;//总之我们适当地设定绘制的大小。横纵比和素材一样。
    Ob->LargeY  = LargeY;
    Ob->Type    = Type;//将Type设定为垂直
    Ob->FromZ      =  FromZ;//绘制开始位置
    Ob->FadeFromZ  =  FadeFromZ;//绘制淡入开始位置
    Ob->FadeToZ    =  FadeToZ;//绘制淡出开始位置
    Ob->ToZ        =  ToZ;//绘制结束位置
    Ob->ObchindMax = OBCHILD_MAX;
    if( Ob->Type == 0 ){
        Ob->ObchindMax = ObchildMax;
    }
    if( Ob->ObchindMax - 1 <= 0 ){
        printfDx("显示数量的设定异常\n");
        return ;
    }
        //Z的幅度计算
    Ob->Zhaba = (Ob->FromZ - Ob->ToZ) / (Ob->ObchindMax-1);

    float ou1 = (float)Ob->ImgX1 / Ob->ImgSize, ou2 = (float)(Ob->ImgX2 - Ob->ImgX1) / Ob->ImgSize;
    float ov1 = (float)Ob->ImgY1 / Ob->ImgSize, ov2 = (float)(Ob->ImgY2 - Ob->ImgY1) / Ob->ImgSize;
    for(s=0; s<Ob->ObchindMax; s++){
        Ob->ObChild[s].x = GraphX;
        Ob->ObChild[s].y = GraphY;
        Ob->ObChild[s].z = Ob->ToZ - Ob->Zhaba + Ob->Zhaba * s;;
        for(i=0; i<6; i++){
            Ob->ObChild[s].Vertex[i].r = Ob->ObChild[s].Vertex[i].g = Ob->ObChild[s].Vertex[i].b = Ob->ObChild[s].Vertex[i].a = 255;
            Ob->ObChild[s].Vertex[i].u = ou1 + ou2 * VtPm[i].u;
            Ob->ObChild[s].Vertex[i].v = ov1 + ov2 * VtPm[i].v;
        }
    }
}

void ini (){
    int ImgHandle;
    ObjectNum = 0;
    ImgHandle = LoadGraph( "mydat/img/tex.png" );
    IniObj(&Object[0], ImgHandle, 512,  0,  0, 256, 128, 250, 50, 2, 1000, 400, -200, -400, 320, 240-90, OBCHILD_MAX);
    IniObj(&Object[1], ImgHandle, 512, 60,270, 405, 512, 180,125, 0, 1000, 400, -200, -400, 470, 275, 6);
    ImgHandle = LoadGraph( "mydat/img/kabe.png" );
    IniObj(&Object[2], ImgHandle, 512,  0,  0, 390, 512,  73, 90, 1, 1000, 400, -200, -400, 170, 240, OBCHILD_MAX);
}

void ClacObject(){
    int t,s,i;
    for(t=0; t<ObjectNum; t++){
        for(s=0; s<Object[t].ObchindMax; s++){
            Object[t].ObChild[s].z-=3;
            for(i=0;i<6;i++){
                switch(Object[t].Type){
                    case 0://与画面平行
                        Object[t].ObChild[s].Vertex[i].pos.x = Object[t].ObChild[s].x + Object[t].LargeX * VtPm[i].x ;    
                        Object[t].ObChild[s].Vertex[i].pos.y = Object[t].ObChild[s].y + Object[t].LargeY * VtPm[i].y ;
                        Object[t].ObChild[s].Vertex[i].pos.z = Object[t].ObChild[s].z ;
                        break;
                    case 1://与画面垂直(墙壁)
                        Object[t].ObChild[s].Vertex[i].pos.x = Object[t].ObChild[s].x;    
                        Object[t].ObChild[s].Vertex[i].pos.y = Object[t].ObChild[s].y + Object[t].LargeY * VtPm[i].y ;
                        Object[t].ObChild[s].Vertex[i].pos.z = Object[t].ObChild[s].z + Object[t].Zhaba/2* VtPm[i].x ;
                        break;
                    case 2://与画面锤子(地板)
                        Object[t].ObChild[s].Vertex[i].pos.x = Object[t].ObChild[s].x + Object[t].LargeX * VtPm[i].x;    
                        Object[t].ObChild[s].Vertex[i].pos.y = Object[t].ObChild[s].y ;
                        Object[t].ObChild[s].Vertex[i].pos.z = Object[t].ObChild[s].z + Object[t].Zhaba/2* VtPm[i].y;
                        break;
                }
            }
        }

        if( Object[t].FromZ - Object[t].FadeFromZ <= 0 ){
            printfDx("Object[%d].From的设定有问题\n",t);
        }
        else if( Object[t].FadeToZ - Object[t].ToZ <= 0 ){
            printfDx("Object[%d].To的设定有问题\n",t);
        }
        else{
            for(s=0; s<Object[t].ObchindMax; s++){
                for(i=0; i<6; i++){
                    float z = Object[t].ObChild[s].Vertex[i].pos.z;
            //当前位置如果比绘制范围还要远的话设置透明度为0
                    if     (z < Object[t].ToZ){
                        Object[t].ObChild[s].Vertex[i].a = 0;
                    }
            //(靠近的时候)如果在淡入的位置的话
                    else if(Object[t].ToZ < z && z <=Object[t].FadeToZ){
                        Object[t].ObChild[s].Vertex[i].a = (unsigned char)(255.0f / (Object[t].FadeToZ - Object[t].ToZ) * (z - Object[t].ToZ)) ;
                    }
            //如果在通常绘制的位置的话
                    else if(Object[t].FadeToZ <= z && z <= Object[t].FadeFromZ){
                        Object[t].ObChild[s].Vertex[i].a = 255;
                    }
            //(靠近的时候)如果在淡出的位置的话
                    else if(Object[t].FadeFromZ <= z && z < Object[t].FromZ){
                        Object[t].ObChild[s].Vertex[i].a = (unsigned char)(255.0f / (Object[t].FromZ - Object[t].FadeFromZ) * (Object[t].FromZ - z)) ; 
                    }
            //如果比绘制范围还要近的话设置透明度为0
                    else if(Object[t].FromZ < z){
                        Object[t].ObChild[s].Vertex[i].a = 0;
                    }
                }
        //靠近以致看不见的话
                if(Object[t].ObChild[s].z < Object[t].ToZ - Object[t].Zhaba*0.5f){
                    //把它移动到最靠近那边的那一侧
                    float sub = (Object[t].ToZ - Object[t].Zhaba*0.5f)- Object[t].ObChild[s].z;
                    Object[t].ObChild[s].z = Object[t].FromZ + Object[t].Zhaba*0.5f - sub;
                }
        //远离以致看不见了的话
                else if(Object[t].ObChild[s].z > Object[t].FromZ + Object[t].Zhaba*0.5f){
        //将它移动到最靠近这边的这一侧
                    float sub = Object[t].ObChild[s].z - (Object[t].FromZ + Object[t].Zhaba*0.5f);
                    Object[t].ObChild[s].z = Object[t].ToZ - Object[t].Zhaba*0.5f + sub;
                }
            }
        }
    }
}

void SwapObChild(ObChild_t *Ob1,ObChild_t *Ob2){
    ObChild_t t = *Ob1;
    *Ob1 = *Ob2;
    *Ob2 = t;
}

//用Z来对纹理排序
void SortObject(){
    int i,j,t;
    for(t=0; t<ObjectNum; t++){
        for (i = 0; i < Object[t].ObchindMax ; i++) {
            for (j = i + 1; j < Object[t].ObchindMax ; j++) {
                if ( Object[t].ObChild[i].z < Object[t].ObChild[j].z ) {
                    SwapObChild( &Object[t].ObChild[i],  &Object[t].ObChild[j] );
                }
            }
        }
    }
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ){
    int s,t;
    ChangeWindowMode(TRUE);//窗口模式
    if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;//初始化于里画面化

    ini();

    while(ProcessMessage()==0 && ClearDrawScreen()==0 && CheckHitKey(KEY_INPUT_ESCAPE)==0){
        ClacObject();
        SortObject();
        SetDrawMode( DX_DRAWMODE_BILINEAR ) ;//为了让多边形看起来平滑我们使用“双线性内插法”的绘制方法
        for(t=0; t<ObjectNum; t++){
            for(s=0; s<Object[t].ObchindMax; s++){
                DrawPolygon3D( Object[t].ObChild[s].Vertex, 2, Object[t].Img, TRUE ) ;
            }
        }
        SetDrawMode(DX_DRAWMODE_NEAREST);//还原绘制方式
        ScreenFlip() ;
    }
    DxLib_End() ;
    return 0 ;
}

在main函数的循环中我们使用了“双线性内插法”(Bi-linear Interpolation)的绘制方法。

这种方法在扩大图像的时候锯齿(译者注:原文为dot,这里意译为锯齿)不会太显眼。

由于对点与点之间进行插值再绘制,因此可以进行平滑的绘制。

至于如果不使用这个方法会怎么样,还请您自行尝试一下。尤其是在樱花扩大的时候您会很容易体会到的。

不过,由于某种原因需要画像不那么清晰,在不需要插值的时候,我们就用标准的绘制方法来绘制吧。

>>点此回到教程目录

results matching ""

    No results matching ""