Graphics Raycaster中高效的地板/天花板渲染

Graphics Raycaster中高效的地板/天花板渲染,graphics,3d,rendering,raycasting,Graphics,3d,Rendering,Raycasting,我在我的Raycaster引擎上工作了一段时间,我在运行速度较慢的机器上。 我遇到的最具挑战性的问题是高效的地板和天花板铸造 我的问题是: 我还可以使用什么其他方法? (我不确定Doom地板和天花板的渲染方式) 到目前为止,我尝试了两种典型的解决方案: 如众所周知的洛德夫教程所述的垂直和水平铸造: 横向方法当然要快得多,但我还使用固定点变量对其进行了优化 不幸的是,即使是这种方法也是一个性能杀手——即使在速度更快的CPU上,fps也会大幅下降,而速度较慢的CPU则是一个瓶颈 我的其他想法:

我在我的Raycaster引擎上工作了一段时间,我在运行速度较慢的机器上。 我遇到的最具挑战性的问题是高效的地板和天花板铸造

我的问题是: 我还可以使用什么其他方法? (我不确定Doom地板和天花板的渲染方式)

到目前为止,我尝试了两种典型的解决方案:

  • 如众所周知的洛德夫教程所述的垂直和水平铸造:
横向方法当然要快得多,但我还使用固定点变量对其进行了优化

不幸的是,即使是这种方法也是一个性能杀手——即使在速度更快的CPU上,fps也会大幅下降,而速度较慢的CPU则是一个瓶颈

我的其他想法:

  • 我想出了一个算法,将可见的地板/天花板贴图平铺转换为四边形,并将其拆分为两个三角形,然后像常规扫描线光栅化器一样对其进行光栅化。这要快得多——而且我可以按纹理id对瓷砖进行排序,使其更便于缓存。不幸的是,在这种情况下,我进入了“透视校正纹理贴图”,为了解决这个问题,我必须添加一些分割,这将降低性能。。但也可以进行一些优化

  • < P>使用每2个射线(柱、行或两者)的水平铸造-我将填充平均纹理坐标

    的空白空间。
  • 我还可以尝试将我的算法从1点与水平投射相结合——我可以根据ID对纹理进行排序,例如,我认为不会出现纹理扭曲

  • 模式7

我目前的进展:

编辑(1):

地板和天花板重新划线规范(基于水平方法) 但采用定点优化。Ceil计算将镜像到地板

这种方法比垂直方法快,但大量的计算是内循环的,根据纹理像素的随机性会影响性能

inline void RC\u Raycast\u Floor\u天花()
{
//获取RC_级别_纹理的本地副本
sBM_位图*级别_纹理=RC_级别_纹理;
//最左侧光线(x=0)和最右侧光线(x=宽度)的光线方向
float32 r_dx0=RC_pp_dx-RC_pp_nsize_x_d2;
float32 r_dy0=RC_pp_dy-RC_pp_nsize_y_d2;
//float32 r_dx1=RC_pp_dx+RC_pp_nsize_x_d2;
//float32 r_dy1=RC_pp_dy+RC_pp_nsize_y_d2;
//预先计算的性能帮助器
float32 r_dx1_m_dx0_div_width=(RC_pp_nsize_x_d2+RC_pp_nsize_x_d2)*RC_渲染_width_1div_f;
float32 r_dy1_m_dy0_div_width=(RC_pp_nsize_y_d2+RC_pp_nsize_y_d2)*RC_渲染_width_1div_f;
int16射线y=-1;
u_int16光线y_计数器=RC_渲染高度i;
u_int8*墙_缓冲区_ptr=RC_墙_缓冲区;
//铸造地板和天花板-逐行水平-从左到右
while(ray_y_计数器)
{
ray_y++;
ray____计数器--;
//如果当前地板/天花板扫描线不可见,请不要继续
如果(光线y>=RC\U墙\U开始)
{
打破
如果(光线yRC_render_height_uuudiv2_ui;
//与屏幕中心(地平线)相比的当前光线y位置
float32 ry_pos=(float32)(是地板?(光线y-RC_渲染高度uu div2 u i):(光线y-RC_渲染高度u div2 u i-ray_y));
//投影平面的垂直位置,0.5在地板和天花板之间
float32 pp_z=(float32)(是地板?(RC_渲染高度分区2_i):(RC_渲染高度分区2_i));
float32到点的直线距离=pp_z/ry_pos;
//计算我们必须为每个x(平行于摄影机平面)添加的真实世界步长向量
//逐步添加可以避免在内部循环中使用权重进行乘法
float32地板\u台阶\u x=直线距离\u到\u点*r\u dx1\u m\u dx0\u分割宽度;
float32地板\u台阶\u y=直线距离\u到\u点*r\u dy1\u m\u dy0\u分割宽度;
float32 floor_x=RC_player_x+到_point的直线距离*r_dx0;
float32 floor_y=RC_player_y+到_点的直线距离*r_dy0;
//将该值转换为定点
int32楼层x楼层fp=(int32)(楼层x*65536.0f);
int32楼层y*65536.0f=(int32);
int32楼层台阶x楼层台阶fp=(int32)(楼层台阶x*65536.0f);
int32层台阶y层台阶fp=(int32)(层台阶y*65536.0f);
u_int32 ry_m_render_width=光线y*RC_render_width_ui;
u_int32 ry_m_render_width_i_mirror=(RC_render_height_i-ray_y-1)*RC_render_width_i;
int16射线x=-1;
u_int16光线x_计数器=RC_渲染宽度i;
sLV_单元*level_map=RC_level->map;
//从左到右绘制地板和天花板
while(射线计数器)
{
射线x++;
射线计数器--;
地板x_uuufp+=地板步进x_uuufp;
楼层y_uuuFP+=楼层阶梯y_uuFP;
如果(*walls\u buffer\u ptr!=0)
{
walls_buffer__ptr++;
继续;
}
walls_buffer__ptr++;
u_int32输出_像素_索引=光线_x+ry_m_渲染_宽度;
u_int32输出_像素_索引_镜像=光线_x+ry_m_渲染_宽度_i_镜像;
//单元坐标是由floorX和floorY的整数部分得到的
u_int32 curr_cell_x=(floor_x_fp&fp_INTEGER_MASK_16)>>16;
u_int32 curr_cell_y=(floor_y_fp&fp_INTEGER_MASK_16)>>16;
//防止溢出
//当前单元格x&=LV映射大小m1;
//当前单元格y&=LV地图大小
<!-- language: cpp -->

 void Doom3D::draw_scanline(int sx,int sy0,int sy1,int symin,int tx0,int ty0,int tx1,int ty1,BYTE *li)
     {
     // affine texture mapping (front side of walls) sy0>sy1
     union { DWORD dd; BYTE db[4]; } cc;
     int sy,tx,ty,ktx,kty,dtx,dty,ctx,cty,dsy;
            dsy=sy1-sy0; if (dsy<0)                        dsy=-dsy;
     ktx=0; dtx=tx1-tx0; if (dtx>0) ktx=+1; else { ktx=-1; dtx=-dtx; } tx=tx0; ctx=0;
     kty=0; dty=ty1-ty0; if (dty>0) kty=+1; else { kty=-1; dty=-dty; } ty=ty0; cty=0;
     if (dsy) for (sy=sy0;sy>=sy1;sy--)
         {
         if ((sy>=0)&&(sy<scr.ys)&&(sy<=symin))
          if ((tx>=0)&&(tx<ptxr->xs)&&(ty>=0)&&(ty<ptxr->ys))
             {
             cc.dd=ptxr->pyx[ty][tx];
             cc.db[0]=li[cc.db[0]];
             cc.db[1]=li[cc.db[1]];
             cc.db[2]=li[cc.db[2]];
             scr.pyx[sy][sx]=cc.dd;
             }
         for (ctx+=dtx;ctx>=dsy;) { ctx-=dsy; tx+=ktx; }
         for (cty+=dty;cty>=dsy;) { cty-=dsy; ty+=kty; }
         }
     }
 void Doom3D::draw_scanline(int sx,int sy0,int sy1,int sz0,int sz1,int symin,int tx0,int ty0,int tx1,int ty1,BYTE *li)
     {
     // perspective correct mapping (floor, top side of walls, ceiling) sy0>sy1
     union { DWORD dd; BYTE db[4]; } cc;
     int sy,tx,ty,dsy,dtx,dty,n,dn;
     int a,_z0,_z1,_tx;
     const int acc0=16;
     const int acc1=8;
     _tx=tx0-(tx0%ptxr->ys);
     tx0-=_tx;
     tx1-=_tx;
     dsy=sy1-sy0; dn=abs(dsy);
     dtx=tx1-tx0;
     dty=ty1-ty0;
     if (sz0==0) return; _z0=(1<<acc0)/sz0;
     if (sz1==0) return; _z1=(1<<acc0)/sz1;
     if (dn) for (n=0;n<=dn;n++)
         {
         sy=sy0+((n*dsy)/dn);
         a=((n<<acc1)*_z1)/(((dn-n)*_z0)+(n*_z1)); // perspective correction a=<0,1<<acc1> (https://en.wikipedia.org/wiki/Texture_mapping)
         tx=tx0+((a*dtx)>>acc1)+_tx;
         ty=ty0+((a*dty)>>acc1);
         if ((sy>=0)&&(sy<scr.ys)&&(sy<=symin))
          if ((tx>=0)&&(tx<ptxr->xs)&&(ty>=0)&&(ty<ptxr->ys))
             {
             cc.dd=ptxr->pyx[ty][tx];
             cc.db[0]=li[cc.db[0]];
             cc.db[1]=li[cc.db[1]];
             cc.db[2]=li[cc.db[2]];
             scr.pyx[sy][sx]=cc.dd;
             }
         }
     }
 void Doom3D::draw_cell(_ray &p)
     {
     BYTE *li;
     DWORD m;
     int tx0,tx1,ty0,ty1,sy,sy0,sy1,sy2,sy3,sz0,sz1,q;
     int sy4,sy5;
     //sy0>=sy1
     sy0=sys2+divide(double((1.0+2.0*plr.z)*_Doom3D_cell_size)*wall,p.l0);
     sy1=sy0 -divide(double((p.map>>24)<<1                   )*wall,p.l0);
     sy2=sys2+divide(double((1.0+2.0*plr.z)*_Doom3D_cell_size)*wall,p.l1);
     sy3=sy2 -divide(double((p.map>>24)<<1                   )*wall,p.l1);
     sy4=sys2-divide(double((1.0-2.0*plr.z)*_Doom3D_cell_size)*wall,p.l1);
     sy5=sys2-divide(double((1.0-2.0*plr.z)*_Doom3D_cell_size)*wall,p.l0);
     sz0=double(p.l0*_Doom3D_cell_size);
     sz1=double(p.l1*_Doom3D_cell_size);
     // select mipmap resolution
     ty0=divide(double(_Doom3D_cell_size<<1)*wall,p.l0);
     for (q=tm-1;q>=0;q--)
         {
         ptxr=txr+q;
         if (ty0<=ptxr->ys) break;
         }
     if (_no_mipmap) ptxr=txr;
     // mouse select
     if (p.sx==round(keys.mx))
      if (keys.my>=sy3)
       if (keys.my<=sy0)
        if ((keys.my>=map2.ys)||(keys.mx>=map2.xs))
         {
         keys.kx=p.x;
         keys.ky=p.y;
         }
     if ((p.map&0xFF)==0xFF) { sy1=sy0; sy3=sy2; }
     // wall
     if ((sy1<p.sy1)&&((p.map&0xFF)!=0xFF))
         {
         tx0=ptxr->ys*(p.map&0xFF);
         if (p.tp0=='H') { li=liH; tx0+=double(double(ptxr->ys-1)*(p.x0-floor(p.x0))); }
         if (p.tp0=='V') { li=liV; tx0+=double(double(ptxr->ys-1)*(p.y0-floor(p.y0))); }
         draw_scanline(p.sx,sy0,sy1,p.sy1,tx0,0,tx0,((p.map>>24)*(ptxr->ys-1))/_Doom3D_cell_size,li);
         p.sy1=sy1;
         }
     // ceiling
     if ((p.map&0xFF0000)!=0xFF0000)
         {
         q=ptxr->ys*((p.map>>16)&0xFF);
         tx0=double(double(ptxr->ys-1)*(p.x0-double(p.x)))+q;
         ty0=double(double(ptxr->ys-1)*(p.y0-double(p.y)));
         tx1=double(double(ptxr->ys-1)*(p.x1-double(p.x)))+q;
         ty1=double(double(ptxr->ys-1)*(p.y1-double(p.y)));
         draw_scanline(p.sx,sy5,sy4,sz0,sz1,p.sy1,tx0,ty0,tx1,ty1,liF);
         }
     // floor/top side
     if ((sy3<p.sy1)&&((p.map&0xFF00)!=0xFF00))
         {
         q=ptxr->ys*((p.map>>8)&0xFF);
         tx0=double(double(ptxr->ys-1)*(p.x0-double(p.x)))+q;
         ty0=double(double(ptxr->ys-1)*(p.y0-double(p.y)));
         tx1=double(double(ptxr->ys-1)*(p.x1-double(p.x)))+q;
         ty1=double(double(ptxr->ys-1)*(p.y1-double(p.y)));
         draw_scanline(p.sx,sy1,sy3,sz0,sz1,p.sy1,tx0,ty0,tx1,ty1,liF);
         p.sy1=sy3;
         }
     if (sy3<p.sy1) p.sy1=sy3;
     }
 void Doom3D::draw()
     {
     tbeg();
     _ray p;
     DWORD x,y,c,m;
     DWORD mx,mx0,mx1;
     DWORD my,my0,my1;
     double a,a0,da,dx,dy,l;
     double xx0,yy0,dx0,dy0,ll0,dl0;
     double xx1,yy1,dx1,dy1,ll1,dl1;

     // compute diffuse + ambient lighting LUT (light scaled shades of gray)
     c=155.0+fabs(100.0*sin(   plr.a)); for (x=0;x<256;x++) liH[x]=(x*c)>>8; // H wall
     c=155.0+fabs(100.0*cos(   plr.a)); for (x=0;x<256;x++) liV[x]=(x*c)>>8; // V wall
     c=155.0+fabs(100.0*cos(30.0*deg)); for (x=0;x<256;x++) liF[x]=(x*c)>>8; // floor, wall top side

     // [2D map]
     m=_Doom3D_edit_cell_size;
     for (my0=0,my1=m,y=0;y<map.ys;y++,my0=my1,my1+=m)   // map.pyx[][]
      for (mx0=0,mx1=m,x=0;x<map.xs;x++,mx0=mx1,mx1+=m)
         {
         c=0x00010101*((0x40+(0x40*(map.pyx[y][x]>>24)))/_Doom3D_cell_size);
         for (my=my0;my<my1;my++)
          for (mx=mx0;mx<mx1;mx++)
           map2.pyx[my][mx]=c;
         }
     c=0x00202020;                                       // map grid
     for (y=0;y<map2.ys;y+=m) for (x=0;x<map2.xs;x++) map2.pyx[y][x]=c;
     for (x=0;x<map2.xs;x+=m) for (y=0;y<map2.ys;y++) map2.pyx[y][x]=c;
     x=keys.kx*m;                                        // selected cell
     y=keys.ky*m;
     map2.bmp->Canvas->Pen->Color=0x0020FFFF;
     map2.bmp->Canvas->MoveTo(x  ,y  );
     map2.bmp->Canvas->LineTo(x+m,y  );
     map2.bmp->Canvas->LineTo(x+m,y+m);
     map2.bmp->Canvas->LineTo(x  ,y+m);
     map2.bmp->Canvas->LineTo(x  ,y  );
     map2.bmp->Canvas->Pen->Mode=pmMerge;

     // [cast rays]
     a0=plr.a-(0.5*view_ang);
     da=divide(view_ang,scr.xs-1);
     da*=scale_x;
     for (a=a0,x=0;x<scr.xs;x+=scale_x,a+=da)
         {
         // grid V-line hits
         ll0=1.0e20; dl0=0.0; dx0=cos(a); const char tp0='V';
         if (dx0<0.0) { xx0=floor(plr.x); dx0=-1.0; }
         if (dx0>0.0) { xx0=ceil (plr.x); dx0=+1.0; }
         if (fabs(dx0)>1e-6) { dy0=tan(a); yy0=plr.y+((xx0-plr.x)*dy0);             dy0*=dx0; dx=xx0-plr.x; dy=yy0-plr.y; ll0=sqrt((dx*dx)+(dy*dy)); dl0=sqrt((dx0*dx0)+(dy0*dy0)); }
         // grid H-line hits
         ll1=1.0e20; dl1=0.0; dy1=sin(a); const char tp1='H';
         if (dy1<0.0) { yy1=floor(plr.y); dy1=-1.0; }
         if (dy1>0.0) { yy1=ceil (plr.y); dy1=+1.0; }
         if (fabs(dy1)>1e-6) { dx1=divide(1.0,tan(a)); xx1=plr.x+((yy1-plr.y)*dx1); dx1*=dy1; dx=xx1-plr.x; dy=yy1-plr.y; ll1=sqrt((dx*dx)+(dy*dy)); dl1=sqrt((dx1*dx1)+(dy1*dy1)); }
         p.ang=a;
         p.sx =x;
         p.sy0=scr.ys;
         p.sy1=scr.ys;
         // first hit
         if (ll0<ll1){ p.tp0=tp0; p.x0=xx0; p.y0=yy0; p.l0=ll0; xx0+=dx0; yy0+=dy0; ll0+=dl0; }
          else       { p.tp0=tp1; p.x0=xx1; p.y0=yy1; p.l0=ll1; xx1+=dx1; yy1+=dy1; ll1+=dl1; }
         p.l0*=cos(p.ang-plr.a); // anti fish eye
         p.map=0xFFFFFFFF; p.x=p.x0; p.y=p.y0;
         for (;;)
             {
             // closest hit
             if (ll0<ll1) { p.tp1=tp0; p.x1=xx0; p.y1=yy0; p.l1=ll0; xx0+=dx0; yy0+=dy0; ll0+=dl0; }
              else        { p.tp1=tp1; p.x1=xx1; p.y1=yy1; p.l1=ll1; xx1+=dx1; yy1+=dy1; ll1+=dl1; }
             p.x=floor(0.5*(p.x0+p.x1)); // actaul cell position
             p.y=floor(0.5*(p.y0+p.y1));
             p.l1*=cos(p.ang-plr.a);     // anti fish eye
             // edge of map crossed?
             if ((p.x>=0)&&(p.x<map.xs)&&(p.y>=0)&&(p.y<map.ys)) p.map=map.pyx[p.y][p.x]; else break;
             // render
             draw_cell(p);               // scan line
             if (p.sy1<=0) break;        // scan line reached top of screen
             // prepare next cell position
             p.tp0=p.tp1; p.x0=p.x1; p.y0=p.y1; p.l0=p.l1;
             }
         // copy skiped scan lines
         for (mx=1;mx<scale_x;mx++)
          if (x+mx<scr.xs)
           for (y=0;y<scr.ys;y++)
            scr.pyx[y][x+mx]=scr.pyx[y][x];
         // render map ray
         if (x==sxs2)                   map2.bmp->Canvas->Pen->Color=0x000000FF;
         if ((x==0)||(x==sxs2+scale_x)) map2.bmp->Canvas->Pen->Color=0x00002020;
         map2.bmp->Canvas->MoveTo(plr.x*m,plr.y*m);
         map2.bmp->Canvas->LineTo(p.x1*m,p.y1*m);
         }
     map2.bmp->Canvas->Pen->Mode=pmCopy;
     map2.bmp->Canvas->Pen->Color=0x000000FF;
     map2.bmp->Canvas->Brush->Color=0x000000FF;
     c=focus*m;
     map2.bmp->Canvas->Ellipse(plr.x*m-c,plr.y*m-c,plr.x*m+c,plr.y*m+c);
     scr.bmp->Canvas->Draw(0,0,map2.bmp);
     // ... here HUD and info texts continues I skipped it to keep this simple
     }