草庐IT

c++ - 透视正确的纹理贴图; z距离计算可能错误

coder 2024-02-04 原文

我正在制作一个软件光栅化程序,但遇到了一些麻烦:我似乎无法使透视正确的纹理映射正常工作。

我的算法是先按y对要绘制的坐标进行排序。这将返回最高,最低和中心点。然后,我使用delta遍历扫描线:

// ordering by y is put here

order[0] = &a_Triangle.p[v_order[0]];
order[1] = &a_Triangle.p[v_order[1]];
order[2] = &a_Triangle.p[v_order[2]];

float height1, height2, height3;

height1 = (float)((int)(order[2]->y + 1) - (int)(order[0]->y));
height2 = (float)((int)(order[1]->y + 1) - (int)(order[0]->y));
height3 = (float)((int)(order[2]->y + 1) - (int)(order[1]->y));

// x 

float x_start, x_end;
float x[3];
float x_delta[3];

x_delta[0] = (order[2]->x - order[0]->x) / height1;
x_delta[1] = (order[1]->x - order[0]->x) / height2;
x_delta[2] = (order[2]->x - order[1]->x) / height3;

x[0] = order[0]->x;
x[1] = order[0]->x;
x[2] = order[1]->x;

然后,我们从order[0]->y渲染为order[2]->y,将x_startx_end增加一个增量。渲染顶部时,增量为x_delta[0]x_delta[1]。渲染底部时,增量为x_delta[0]x_delta[2]。然后,我们在扫描线上的x_start和x_end之间进行线性插值。以相同的方式对UV坐标进行插值,并按y顺序从开始和结束开始,并在每个步骤中将其应用于增量。

除非我尝试进行透视正确的UV贴图,否则此方法工作正常。基本算法是为每个顶点获取UV/z1/z并在它们之间进行插值。对于每个像素,UV坐标变为UV_current * z_current。但是,这是结果:

反面的部分告诉您变化量在哪里。如您所见,两个三角形似乎都朝着地平线上的不同点移动。

这是我用来计算空间点上的Z的方法:
float GetZToPoint(Vec3 a_Point)
{
    Vec3 projected = m_Rotation * (a_Point - m_Position);

    // #define FOV_ANGLE 60.f
    // static const float FOCAL_LENGTH = 1 / tanf(_RadToDeg(FOV_ANGLE) / 2);
    // static const float DEPTH = HALFHEIGHT * FOCAL_LENGTH; 
    float zcamera = DEPTH / projected.z;

    return zcamera;
}

我说对了,这是z缓冲区问题吗?

最佳答案

ZBuffer与它无关。

ZBuffer仅在三角形重叠且您要确保正确绘制三角形时才有用(例如,在Z中正确排序)。 ZBuffer将为三角形的每个像素确定先前放置的像素是否更靠近相机,如果是,则不绘制三角形的像素。

由于您正在绘制2个不重叠的三角形,因此这不会成为问题。

我曾经(在手机上)定点制作过一个软件光栅化器,但是我的笔记本电脑上没有这些源。所以,让我今晚检查一下,我是如何做到的。本质上,您所拥有的还不错!这样的事情可能是由很小的错误引起的

调试的一般技巧是拥有一些测试三角形(左侧的斜度,右侧的斜度,90度角等),并与调试器一起逐步进行调试,并查看您的逻辑如何处理这些情况。

编辑:

我的光栅化器的伪代码(仅考虑U,V和Z ...如果还想进行gouraud,则还必须对R G和B进行所有操作,类似于对U,V和Z进行的操作:

这个想法是一个三角形可以分解为2个部分。顶部和底部。顶部从y [0]到y [1],底部从y [1]到y [2]。对于这两个集合,您都需要计算要进行插值的步长变量。下面的示例向您展示了如何进行顶部操作。如果需要,我也可以提供底部。

请注意,我已经为下面的“伪代码”片段的底部部分计算了所需的插值偏移量

  • 首先对coords(x,y,z,u,v)进行排序,以使coord [0] .y
  • 接下来检查是否有两组坐标相同(仅检查x和y)。如果是这样,请不要绘制
  • 异常(exception):三角形是否具有平顶?如果是这样,第一个斜率将是无限
  • exception2:三角形是否具有平坦的底部(是的三角形也可以具有这些平坦的底部; ^)),那么最后一个斜率也将是无限的
  • 计算2个斜率(左侧和右侧)
    leftDeltaX =(x [1]-x [0])/(y [1] -y [0])和rightDeltaX =(x [2]-x [0])/(y [2] -y [0] )
  • 三角形的第二部分的计算取决于:如果三角形的左侧现在真的在左侧(或需要交换)

  • 代码片段:
     if (leftDeltaX < rightDeltaX)
     {
          leftDeltaX2 = (x[2]-x[1]) / (y[2]-y[1])
          rightDeltaX2 = rightDeltaX
          leftDeltaU = (u[1]-u[0]) / (y[1]-y[0]) //for texture mapping
          leftDeltaU2 = (u[2]-u[1]) / (y[2]-y[1])
          leftDeltaV = (v[1]-v[0]) / (y[1]-y[0]) //for texture mapping
          leftDeltaV2 = (v[2]-v[1]) / (y[2]-y[1])
          leftDeltaZ = (z[1]-z[0]) / (y[1]-y[0]) //for texture mapping
          leftDeltaZ2 = (z[2]-z[1]) / (y[2]-y[1])
     }
     else
     {
          swap(leftDeltaX, rightDeltaX);
          leftDeltaX2 = leftDeltaX;
          rightDeltaX2 = (x[2]-x[1]) / (y[2]-y[1])
          leftDeltaU = (u[2]-u[0]) / (y[2]-y[0]) //for texture mapping
          leftDeltaU2 = leftDeltaU
          leftDeltaV = (v[2]-v[0]) / (y[2]-y[0]) //for texture mapping
          leftDeltaV2 = leftDeltaV
          leftDeltaZ = (z[2]-z[0]) / (y[2]-y[0]) //for texture mapping
          leftDeltaZ2 = leftDeltaZ
      }
    
  • 在x [0]上同时设置currentLeftX和currentRightX
  • 在leftDeltaU上设置currentLeftU,在leftDeltaV上设置currentLeftV,在leftDeltaZ上设置currentLeftZ
  • 第一个Y范围的计算起点和终点:startY = ceil(y [0]); endY = ceil(y [1])
  • 为子像素精度对y的小数部分进行x,u,v和z的前置步骤(我想这对于浮点数也是必需的)
    对于我的定点算法,需要使线条和纹理给人以比显示器的分辨率精细得多的步幅移动的错觉(
  • 计算x应该在y [1]处的位置:halfwayX =(x [2] -x [0])*(y [1] -y [0])/(y [2] -y [0])+ x [0]
    与U,V和z相同:HalfwayU =(u [2] -u [0])*(y [1] -y [0])/(y [2] -y [0])+ u [0 ]
  • 并使用halfwayX计算U,V和z的步进器:
    if(halfwayX-x [1] == 0){坡度U = 0,坡度V = 0,坡度Z = 0}否则{坡度U =(halfwayU-U [1])/(halfwayX-x [1])}//(与v和z相同)
  • 对Y顶部进行裁剪(因此,计算出三角形顶部不在屏幕上(或在裁剪矩形之外)的情况下,我们将开始绘制的位置)
  • 表示y = startY; y
    {
  • 是Y在屏幕底部下方吗?停止渲染!
  • 为第一条水平线计算startX和endX
    leftCurX = ceil(startx); leftCurY = ceil(endy);
  • 将要绘制的线裁剪到屏幕的左水平边框(或裁剪区域)
  • 准备一个指向目标缓冲区的指针(每次通过数组索引执行它都太慢了)
    unsigned int buf = destbuf +(ypitch)+ startX; (如果您正在执行24位或32位渲染,则为unsigned int)
    还要在这里准备您的ZBuffer指针(如果您正在使用的话)
  • for(x = startX; x
    {
  • 现在用于透视图纹理映射(不使用bilineair插值,您可以执行以下操作):

  • 代码片段:
             float tv = startV / startZ
             float tu = startU / startZ;
             tv %= texturePitch;  //make sure the texture coordinates stay on the texture if they are too wide/high
             tu %= texturePitch;  //I'm assuming square textures here. With fixed point you could have used &=
             unsigned int *textPtr = textureBuf+tu + (tv*texturePitch);   //in case of fixedpoints one could have shifted the tv. Now we have to multiply everytime. 
             int destColTm = *(textPtr);  //this is the color (if we only use texture mapping)  we'll be needing for the pixel
    
  • 虚拟线
  • 虚拟线
  • 虚拟行
  • 可选:检查zbuffer是否在此坐标上先前绘制的像素高于或低于我们的像素。
  • 绘制像素
  • startZ + =斜率Z; startU + = slopeU; startV + =斜率V;//更新所有插值器
  • } x循环的结尾
  • leftCurX + = leftDeltaX; rightCurX + = rightDeltaX; leftCurU + = rightDeltaU; leftCurV + = rightDeltaV; leftCurZ + = rightDeltaZ;//更新Y个插值器
  • } y循环的结尾

    //这是第一部分的结尾。现在,我们绘制了三角形的一半。从顶部到中间的Y坐标。
    //现在,我们基本上做了完全相同的事情,但现在是三角形的下半部分(使用另一组插值器)

  • 对“虚拟行”表示抱歉。.他们需要同步 Markdown 代码。 (花了我一段时间让一切看起来都按预期排序)

    让我知道这是否可以帮助您解决所面临的问题!

    关于c++ - 透视正确的纹理贴图; z距离计算可能错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2068134/

    有关c++ - 透视正确的纹理贴图; z距离计算可能错误的更多相关文章

    1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

      大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

    2. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

      我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

    3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    4. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

      我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

    5. ruby-on-rails - 使用一系列等级计算字母等级 - 2

      这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

    6. ruby-on-rails - 迷你测试错误 : "NameError: uninitialized constant" - 2

      我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test

    7. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

      我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

    8. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

      我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

    9. ruby-on-rails - 错误 : Error installing pg: ERROR: Failed to build gem native extension - 2

      我克隆了一个rails仓库,我现在正尝试捆绑安装背景:OSXElCapitanruby2.2.3p173(2015-08-18修订版51636)[x86_64-darwin15]rails-v在您的Gemfile中列出的或native可用的任何gem源中找不到gem'pg(>=0)ruby​​'。运行bundleinstall以安装缺少的gem。bundleinstallFetchinggemmetadatafromhttps://rubygems.org/............Fetchingversionmetadatafromhttps://rubygems.org/...Fe

    10. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

      在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

    随机推荐