Graphics Raycaster中高效的地板/天花板渲染
我在我的Raycaster引擎上工作了一段时间,我在运行速度较慢的机器上。 我遇到的最具挑战性的问题是高效的地板和天花板铸造 我的问题是: 我还可以使用什么其他方法? (我不确定Doom地板和天花板的渲染方式) 到目前为止,我尝试了两种典型的解决方案:Graphics Raycaster中高效的地板/天花板渲染,graphics,3d,rendering,raycasting,Graphics,3d,Rendering,Raycasting,我在我的Raycaster引擎上工作了一段时间,我在运行速度较慢的机器上。 我遇到的最具挑战性的问题是高效的地板和天花板铸造 我的问题是: 我还可以使用什么其他方法? (我不确定Doom地板和天花板的渲染方式) 到目前为止,我尝试了两种典型的解决方案: 如众所周知的洛德夫教程所述的垂直和水平铸造: 横向方法当然要快得多,但我还使用固定点变量对其进行了优化 不幸的是,即使是这种方法也是一个性能杀手——即使在速度更快的CPU上,fps也会大幅下降,而速度较慢的CPU则是一个瓶颈 我的其他想法:
- 如众所周知的洛德夫教程所述的垂直和水平铸造:
- 我想出了一个算法,将可见的地板/天花板贴图平铺转换为四边形,并将其拆分为两个三角形,然后像常规扫描线光栅化器一样对其进行光栅化。这要快得多——而且我可以按纹理id对瓷砖进行排序,使其更便于缓存。不幸的是,在这种情况下,我进入了“透视校正纹理贴图”,为了解决这个问题,我必须添加一些分割,这将降低性能。。但也可以进行一些优化
- < P>使用每2个射线(柱、行或两者)的水平铸造-我将填充平均纹理坐标的空白空间。
- 我还可以尝试将我的算法从1点与水平投射相结合——我可以根据ID对纹理进行排序,例如,我认为不会出现纹理扭曲
- 模式7
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
}