7

我正在制作一个软件光栅化器,但我遇到了一些障碍:我似乎无法让透视正确的纹理映射工作。

我的算法是首先对要绘制的坐标进行排序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然后我们从to渲染order[2]->y,将x_startandx_end增加一个增量。渲染顶部时,增量是x_delta[0]x_delta[1]。渲染底部时,增量是x_delta[0]x_delta[2]。然后我们在扫描线上的 x_start 和 x_end 之间进行线性插值。UV 坐标以相同的方式进行插值,按 y 排序,从 begin 和 end 开始,每一步都应用 delta。

这很好用,除非我尝试进行透视正确的 UV 映射。基本算法是对每个顶点取UV/z和并在它们之间进行插值。1/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;
}

我是对的,是 az 缓冲区问题吗?

4

3 回答 3

5

ZBuffer has nothing to do with it.

THe ZBuffer is only useful when triangles are overlapping and you want to make sure that they are drawn correctly (e.g. correctly ordered in the Z). The ZBuffer will, for every pixel of the triangle, determine if a previously placed pixel is nearer to the camera, and if so, not draw the pixel of your triangle.

Since you are drawing 2 triangles which don't overlap, this can not be the issue.

I've made a software rasterizer in fixed point once (for a mobile phone), but I don't have the sources on my laptop. So let me check tonight, how I did it. In essence what you've got is not bad! A thing like this could be caused by a very small error

General tips in debugging this is to have a few test triangles (slope left-side, slope right-side, 90 degree angles, etc etc) and step through it with the debugger and see how your logic deals with the cases.

EDIT:

peudocode of my rasterizer (only U, V and Z are taken into account... if you also want to do gouraud you also have to do everything for R G and B similar as to what you are doing for U and V and Z:

The idea is that a triangle can be broken down in 2 parts. The top part and the bottom part. The top is from y[0] to y[1] and the bottom part is from y[1] to y[2]. For both sets you need to calculate the step variables with which you are interpolating. The below example shows you how to do the top part. If needed I can supply the bottom part too.

Please note that I do already calculate the needed interpolation offsets for the bottom part in the below 'pseudocode' fragment

  • first order the coords(x,y,z,u,v) in the order so that coord[0].y < coord[1].y < coord[2].y
  • next check if any 2 sets of coordinates are identical (only check x and y). If so don't draw
  • exception: does the triangle have a flat top? if so, the first slope will be infinite
  • exception2: does the triangle have a flat bottom (yes triangles can have these too ;^) ) then the last slope too will be infinite
  • calculate 2 slopes (left side and right side)
    leftDeltaX = (x[1] - x[0]) / (y[1]-y[0]) and rightDeltaX = (x[2] - x[0]) / (y[2]-y[0])
  • the second part of the triangle is calculated dependent on: if the left side of the triangle is now really on the leftside (or needs swapping)

code fragment:

 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
  }
  • set the currentLeftX and currentRightX both on x[0]
  • set currentLeftU on leftDeltaU, currentLeftV on leftDeltaV and currentLeftZ on leftDeltaZ
  • calc start and endpoint for first Y range: startY = ceil(y[0]); endY = ceil(y[1])
  • prestep x,u,v and z for the fractional part of y for subpixel accuracy (I guess this is also needed for floats) For my fixedpoint algorithms this was needed to make the lines and textures give the illusion of moving in much finer steps then the resolution of the display)
  • calculate where x should be at y[1]: halfwayX = (x[2]-x[0]) * (y[1]-y[0]) / (y[2]-y[0]) + x[0] and same for U and V and z: halfwayU = (u[2]-u[0]) * (y[1]-y[0]) / (y[2]-y[0]) + u[0]
  • and using the halfwayX calculate the stepper for the U and V and z: if(halfwayX - x[1] == 0){ slopeU=0, slopeV=0, slopeZ=0 } else { slopeU = (halfwayU - U[1]) / (halfwayX - x[1])} //(and same for v and z)
  • do clipping for the Y top (so calculate where we are going to start to draw in case the top of the triangle is off screen (or off the clipping rectangle))
  • for y=startY; y < endY; y++) {
    • is Y past bottom of screen? stop rendering!
    • calc startX and endX for the first horizontal line leftCurX = ceil(startx); leftCurY = ceil(endy);
    • clip the line to be drawn to the left horizontal border of the screen (or clipping region)
    • prepare a pointer to the destination buffer (doing it through array indexes everytime is too slow) unsigned int buf = destbuf + (ypitch) + startX; (unsigned int in case you are doing 24bit or 32 bits rendering) also prepare your ZBuffer pointer here (if you are using this)
    • for(x=startX; x < endX; x++) {
      • now for perspective texture mapping (using no bilineair interpolation you do the following):

code fragment:

         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
  • dummy line
    • dummy line
      • dummy line
      • optional: check the zbuffer if the previously plotted pixel at this coordinate is higher or lower then ours.
      • plot the pixel
      • startZ += slopeZ; startU+=slopeU; startV += slopeV; //update all interpolators
    • } end of x loop
    • leftCurX+= leftDeltaX; rightCurX += rightDeltaX; leftCurU+= rightDeltaU; leftCurV += rightDeltaV; leftCurZ += rightDeltaZ; //update Y interpolators
  • } end of y loop

    //this is the end of the first part. We now have drawn half the triangle. from the top, to the middle Y coordinate. // we now basically do the exact same thing but now for the bottom half of the triangle (using the other set of interpolators)

sorry about the 'dummy lines'.. they were needed to get the markdown codes in sync. (took me a while to get everything sort off looking as intended)

let me know if this helps you solve the problem you are facing!

于 2010-01-18T11:10:21.237 回答
0

如果你是插值1/z,你需要乘以UV/zz而不是1/z。假设你有这个:

UV = UV_current * z_current

并且z_current正在插值1/z,您应该将其更改为:

UV = UV_current / z_current

然后你可能想重命名z_currentone_over_z_current.

于 2010-01-19T23:29:08.097 回答
0

我不知道我能不能帮你解决你的问题,但我当时读过的最好的软件渲染书籍之一是 Michael Abrash 的在线图形编程黑皮书

于 2010-01-14T23:32:29.187 回答