德军总部式 3D 渲染背后的理论 [英] Theory behind Wolfenstein-style 3D rendering

查看:43
本文介绍了德军总部式 3D 渲染背后的理论的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在做一个关于 3D 渲染的项目,我正在尝试制作一个简单的程序,可以用 pygame 显示一个简单的 3D 房间(静态阴影,没有玩家移动,只有旋转)

I'm currently working on a project about 3D rendering, and I'm trying to make simplistic program that can display a simple 3D room (static shading, no player movement, only rotation) with pygame

到目前为止,我已经完成了这个理论:

So far I've worked through the theory:

  • 从每个节点"的 X 和 Z 坐标列表开始
  • 节点以形成闭环的顺序排列,因此一对节点将形成墙的任一侧
  • 墙的高度是在渲染时确定的,相对于与相机的距离
  • 墙壁是使用画家算法渲染的,因此更近的物体会被绘制在更远的物体之上
  • 用于着色假对比度",根据两个节点之间的梯度使墙壁变亮/变暗

虽然看起来很简单,但将 3D 坐标转换为屏幕上的 2D 点背后的过程证明我很难理解.

While it seems simple enough, the process behind translating the 3D coordinates into 2D points on the screen is proving the difficult for me to understand.

到目前为止,谷歌搜索这个主题只会产生这些等式:

Googling this topic has so far only yeilded these equations:

screenX = (worldX/worldZ)

screenY = (worldY/worldZ)

这对我来说似乎有缺陷,因为如果任何 Z 坐标为 0,您将得到除以零的误差.

Which seem flawed to me, as you would get a divide by zero error if any Z coordinate is 0.

所以如果有人能帮助解释这一点,我会非常感激.

So if anyone could help explain this, I'd be really greatful.

推荐答案

好吧

screenX = (worldX/worldZ)
screenY = (worldY/worldZ)

不是全部内容,只是 z 的透视划分,也不适用于 DOOM 或 Wolfenstein 技术.

is not the whole stuff that is just the perspective division by z and it is not meant for DOOM or Wolfenstein techniques.

好吧,在毁灭战士中只有一个视角(你可以向左/向右转,但不能向上/向下看,只有鸭子或跳跃,这是不一样的).所以我们需要知道我们的玩家位置和方向px,py,pz,pangle.z 仅在您还想实现 z 轴移动/寻找时才需要...

Well in Doom there is only single angle of viewing (you can turn left/right but cannot look up/down only duck or jump which is not the same). So we need to know our player position and direction px,py,pz,pangle. The z is needed only if you want to implement also z axis movement/looking...

如果您沿直线(红色)看,所有在 3D 中穿过该线的对象都将投影到播放器屏幕中的单个 x 坐标...

If you are looking in a straight line (Red) all the object that cross that line in the 3D are projected to single x coordinate in the player screen...

因此,如果我们正在看某个方向(红色),任何穿过/接触这条红线的对象/点都将被放置在屏幕的中心(在 x 轴上).剩下的内容将在左侧呈现,同样右侧的内容也将在右侧呈现...

So if we are looking at some direction (red) any object/point crossing/touching this red line will be place at the center of screen (in x axis). What is left from it will be rendered on the left and similarly whats on right will be rendered on the right too...

通过透视,我们需要定义我们有多大的视角...

With perspective we need to define how large viewing angle we got...

这限制了我们的视野,因此任何接触绿线的点都将被投影到视野的边缘(在 x 轴上).由此我们可以直接计算任意点(x,y,z)的屏幕x坐标sx:

This limits our view so any point touches the green line will be projected on the edge of view (in x axis). From this we can compute screen x coordinate sx of any point (x,y,z) directly:

// angle of point relative to player direction
sx = point_ang - pangle;
if (sx<-M_PI) sx+=2.0*M_PI;
if (sx>+M_PI) sx-=2.0*M_PI;
// scale to pixels
sx = screen_size_x/2 + sx*screen_size_x/FOVx

其中 screen_size_x 是我们视野区域的分辨率,点 ang 是 x,y,z 点相对于原点 px,py,pz.你可以这样计算:

where screen_size_x is resolution of our view area and point ang is angle of point x,y,z relative to origin px,py,pz. You can compute it like this:

point_ang = atan2(y-py,x-px)

但如果您真的进行了DOOM 光线投射,那么您已经获得了这个角度.

but if you truly do a DOOM ray-casting then you already got this angle.

现在我们需要计算屏幕y坐标sy,它取决于与玩家的距离和墙壁大小.我们可以利用三角形相似性.

Now we need to compute the screen y coordinate sy which is dependent on the distance from player and wall size. We can exploit triangle similarity.

所以:

sy = screen_size_y/2 (+/-) wall_height*focal_length/distance

其中焦距是具有 100% 高度的墙壁在 y 轴上正好覆盖整个屏幕的距离.如您所见,我们除以可能为零的距离.必须避免这种状态,因此您需要确保如果直接站在单元边界上,您的光线将在下一个单元处进行评估.我们还需要选择焦距,这样方墙将被投影为正方形.

Where focal length is the distance at which wall with 100% height will cover exactly the whole screen in y axis. As you can see we dividing by distance which might be zero. Such state must be avoided so you need to make sure your rays will be evaluated at the next cell if standing directly on cell boundary. Also we need to select the focal length so square wall will be projected as square.

这是我的 Doom 引擎的一段代码(放在一起):

Here a piece of code from mine Doom engine (putted all together):

double divide(double x,double y)
    {
    if ((y>=-1e-30)&&(y<=+1e-30)) return 0.0;
    return x/y;
    }
bool Doom3D::cell2screen(int &sx,int &sy,double x,double y,double z)
    {
    double a,l;
    // x,y relative to player
    x-=plrx;
    y-=plry;
    // convert z from [cell] to units
    z*=_Doom3D_cell_size;
    // angle -> sx
    a=atan2(y,x)-plra;
    if (a<-pi) a+=pi2;
    if (a>+pi) a-=pi2;
    sx=double(sxs2)*(1.0+(2.0*a/view_ang));
    // perpendicular distance -> sy
    l=sqrt((x*x)+(y*y))*cos(a);
    sy=sys2+divide((double(plrz+_Doom3D_cell_size)-z-z)*wall,l);
    // in front of player?
    return (fabs(a)<=0.5*pi);
    }

哪里:

_Doom3D_cell_size=100; // [units] cell cube size
view_ang=60.0*deg;     // FOVx
focus=0.25;            // [cells] view focal length (uncorrected)
wall=double(sxs)*(1.25+(0.288*a)+(2.04*a*a))*focus/double(_Doom3D_cell_size); // [px] projected wall size ratio size = height*wall/distance
sxs,sys = screen resolution
sxs2,sys2 = screen half resolution
pi=M_PI, pi2=2.0*M_PI

不要忘记使用垂直距离(像我一样乘以cos(a))否则会出现严重的鱼眼效应.有关更多信息,请参阅:

Do not forget to use perpendicular distances (multiplied by cos(a) as I did) otherwise serious fish-eye effect will occur. For more info see:

这篇关于德军总部式 3D 渲染背后的理论的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆