math - 如何绘制一个物体并在倾斜正面投影中旋转它

标签 math 3d rotation pascal projection-matrix

如何绘制一个物体并在倾斜正面(二度)投影中正确旋转它?

投影图:

Oblique frontal projection

我已经编写了一个程序(Pascal with Graph unit)来执行此操作,但我认为它无法正确绘制对象。

program p7test;

uses PtcCrt, PtcGraph;

type
  TPixel = record
    x, y, z: real;
  end;
  TModel = record
    p: array [ 1..8 ] of TPixel;
  end;
  TCenter = record
    xc, zc: integer;
  end;

var
  Driver, Mode: integer;
  c: char;
  s: string;
  ns, rx, ry, rz, ra, m_l, m_w, m_h, m_l_d, m_w_d, m_h_d: integer;
  model_d, model: TModel;
  center: TCenter;

  procedure LineXYZ( sp_t, ep_t: TPixel; center_t: TCenter );
  var
    x1, y1, x2, y2: real;
  begin
    x1 := sp_t.x - sin( pi / 4 ) * sp_t.y / 2;
    y1 := sp_t.z - sin( pi / 4 ) * sp_t.y / 2;
    x2 := ep_t.x - sin( pi / 4 ) * ep_t.y / 2;
    y2 := ep_t.z - sin( pi / 4 ) * ep_t.y / 2;
    Line(
      round( center_t.xc - x1 ),
      round( center_t.zc - y1 ),
      round( center_t.xc - x2 ),
      round( center_t.zc - y2 )
    );
  end;

  procedure DrawModel( model_t: TModel; center_t: TCenter );
  var
    i: integer;
  begin
    LineXYZ( model_t.p[ 1 ], model_t.p[ 2 ], center_t );
    LineXYZ( model_t.p[ 2 ], model_t.p[ 3 ], center_t );
    LineXYZ( model_t.p[ 3 ], model_t.p[ 4 ], center_t );
    LineXYZ( model_t.p[ 4 ], model_t.p[ 1 ], center_t );
    LineXYZ( model_t.p[ 5 ], model_t.p[ 6 ], center_t );
    LineXYZ( model_t.p[ 6 ], model_t.p[ 7 ], center_t );
    LineXYZ( model_t.p[ 7 ], model_t.p[ 8 ], center_t );
    LineXYZ( model_t.p[ 8 ], model_t.p[ 5 ], center_t );
    LineXYZ( model_t.p[ 1 ], model_t.p[ 5 ], center_t );
    LineXYZ( model_t.p[ 2 ], model_t.p[ 6 ], center_t );
    LineXYZ( model_t.p[ 3 ], model_t.p[ 7 ], center_t );
    LineXYZ( model_t.p[ 4 ], model_t.p[ 8 ], center_t );
  end;

  function RotateZ( model_t: TModel; angle: real ): TModel;
  var
    x, y: real;
    i: integer;
  begin
    angle := angle * pi / 180;
    for i := 1 to 8 do
    begin
      x := model_t.p[ i ].x;
      y := model_t.p[ i ].y;
      model_t.p[ i ].x := x * cos( angle ) - y * sin( angle );
      model_t.p[ i ].y := y * cos( angle ) + x * sin( angle );
    end;
    RotateZ := model_t;
  end;

  function RotateY( model_t: TModel; angle: real ): TModel;
  var
    x, z: real;
    i: integer;
  begin
    angle := angle * pi / 180;
    for i := 1 to 8 do
    begin
      x := model_t.p[ i ].x;
      z := model_t.p[ i ].z;
      model_t.p[ i ].x := x * cos( angle ) - z * sin( angle );
      model_t.p[ i ].z := z * cos( angle ) + x * sin( angle );
    end;
    RotateY := model_t;
  end;

  function RotateX( model_t: TModel; angle: real ): TModel;
  var
    y, z: real;
    i: integer;
  begin
    angle := angle * pi / 180;
    for i := 1 to 8 do
    begin
      y := model_t.p[ i ].y;
      z := model_t.p[ i ].z;
      model_t.p[ i ].y := y * cos( angle ) - z * sin( angle );
      model_t.p[ i ].z := z * cos( angle ) + y * sin( angle );
    end;
    RotateX := model_t;
  end;

  function RotateXYZ( model_t: TModel; rx_t, ry_t, rz_t: integer ): TModel;
  begin
    model_t := RotateX( model_t, rx_t );
    model_t := RotateY( model_t, ry_t );
    model_t := RotateZ( model_t, rz_t );
    RotateXYZ := model_t;
  end;

begin
  Driver := D8bit;
  Mode := m800x600;
  InitGraph( Driver, Mode, '' );
  ra := 2;
  if ( GraphResult <> GrOk ) then WriteLn( '640x480x256''s not supported' ) else
  begin
    ClearDevice;
    center.xc := ( GetMaxX div 2 ) + 1;
    center.zc := ( GetMaxY div 2 ) + 1;
    m_l_d := 200; m_w_d := 200; m_h_d := 200;
    m_l := m_l_d; m_w := m_w_d; m_h := m_h_d;
    rx := -26; ry := 6; rz := 16;

    model_d.p[ 1 ].x := - m_l / 2; model_d.p[ 1 ].y := - m_w / 2; model_d.p[ 1 ].z := - m_h / 2;
    model_d.p[ 2 ].x := - m_l / 2; model_d.p[ 2 ].y :=   m_w / 2; model_d.p[ 2 ].z := - m_h / 2;
    model_d.p[ 3 ].x :=   m_l / 2; model_d.p[ 3 ].y :=   m_w / 2; model_d.p[ 3 ].z := - m_h / 2;
    model_d.p[ 4 ].x :=   m_l / 2; model_d.p[ 4 ].y := - m_w / 2; model_d.p[ 4 ].z := - m_h / 2;
    model_d.p[ 5 ].x := - m_l / 2; model_d.p[ 5 ].y := - m_w / 2; model_d.p[ 5 ].z :=   m_h / 2;
    model_d.p[ 6 ].x := - m_l / 2; model_d.p[ 6 ].y :=   m_w / 2; model_d.p[ 6 ].z :=   m_h / 2;
    model_d.p[ 7 ].x :=   m_l / 2; model_d.p[ 7 ].y :=   m_w / 2; model_d.p[ 7 ].z :=   m_h / 2;
    model_d.p[ 8 ].x :=   m_l / 2; model_d.p[ 8 ].y := - m_w / 2; model_d.p[ 8 ].z :=   m_h / 2;

    model := RotateXYZ( model_d, rx, ry, rz );
    SetColor( 2 ); DrawModel( model, center );
    SetColor( 12 );
    Str( rx, s ); OutTextXY( 2, 2, 'rx=' + s );
    Str( ry, s ); OutTextXY( 2, 12, 'ry=' + s );
    Str( rz, s ); OutTextXY( 2, 22, 'rz=' + s );

    repeat Delay( 100 ) until KeyPressed;
    if ns = 0 then ns := 1 else ns := 0;
    ReadKey;
    repeat
      c := ReadKey;
      case c of
        #113: begin rx := rx - ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
        #101: begin rx := rx + ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
        #119: begin ry := ry - ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
        #115: begin ry := ry + ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
        #97: begin rz := rz - ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
        #100: begin rz := rz + ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
        #117: begin
          rx := 0; ry := 0; rz := 0;
          model := RotateXYZ( model_d, rx, ry, rz );
        end;
      end;
      ClearDevice;

      SetColor( 2 ); DrawModel( model, center );
      SetColor( 12 );
      Str( rx, s ); OutTextXY( 2, 2, 'rx=' + s );
      Str( ry, s ); OutTextXY( 2, 12, 'ry=' + s );
      Str( rz, s ); OutTextXY( 2, 22, 'rz=' + s );

      if ns = 0 then
      begin
        SetActivePage(0);
        SetVisualPage(1)
      end
      else
      begin
        SetActivePage(1);
        SetVisualPage(0)
      end;
      if ns = 0 then ns := 1 else ns := 0;
    until c = #27;
    CloseGraph;
  end;
end.

你可以使用WASDQE R 旋转对象的键。

因此,正如您可能会在下面的动画中注意到的那样,当您查看它时会出现一些问题,它会稍微拉长:

Object rotation 1

它不应该像下面这样吗?:

Object rotation 2

我尝试将 LineXYZ() 代码行更改为:

x1 := sp_t.x - ( sp_t.y / 2 );
y1 := sp_t.z - ( sp_t.y / 2 );
x2 := ep_t.x - ( ep_t.y / 2 );
y2 := ep_t.z - ( ep_t.y / 2 );

,但它也可能不正确。


我是否使用 (Rotate*) 等函数正确旋转了 xyz 坐标? 顺便说一句,我认为主要问题是LineXYZ() 函数(y 坐标部分)。 如何在这种投影中绘制对象?

非常感谢,需要的。

最好的问候, V7

最佳答案

Isn't it should look like one below?

不是,下面的gif是90-90投影,而你的轴是135-90

你的代码是正确的,除了两点:

  1. 你的 X 轴方向错误
  2. 您使用sin 而不是cos,而角度是pi/4 它们是相同的,但如果您打算改变角度。 ..

您的代码:

x1 := sp_t.x - sin( pi / 4 ) * sp_t.y / 2;
y1 := sp_t.z - sin( pi / 4 ) * sp_t.y / 2;
x2 := ep_t.x - sin( pi / 4 ) * ep_t.y / 2;
y2 := ep_t.z - sin( pi / 4 ) * ep_t.y / 2;

应该是:

x1 := -sp_t.x - cos( pi / 4 ) * sp_t.y / 2;
y1 :=  sp_t.z - sin( pi / 4 ) * sp_t.y / 2;
x2 := -ep_t.x - cos( pi / 4 ) * ep_t.y / 2;
y2 :=  ep_t.z - sin( pi / 4 ) * ep_t.y / 2;

在休息时,您的渲染是正确的。

Live Demo


关于@lurker 笔记:
没有理由在这里修复透视错觉(这是人格化的东西 - 不可能为所有观众平等地补偿它)。

这张动图只是展示了这种错觉效果:
Illusion Demonstration

关于math - 如何绘制一个物体并在倾斜正面投影中旋转它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47141633/

相关文章:

math - 盒子到球体碰撞

c++ - 光线拾取方向计算不正确

python - 如何计算每个帖子的 xyz 位置和时间差的多边定位?

node.js - 了解Revit图元上传到Forge平台后的坐标系转换和引用

c++ - UE4 - 使用 CameraComponent 时观众不旋转(俯仰)

java - Apache commons 数学优化在每次迭代中获得最佳解决方案

css - 复合透视与 CSS 3d 转换

opengl - 3D 光线拾取在鼠标未锁定时使用鼠标坐标

旋转设备后 Android 对话框重新打开

three.js - 围绕给定轴和角度的枢轴点正确旋转对象