5

I'm trying to apply texture to my .md2 model. I used the Gouraud shading to color it (the standard algorithm with bottom/top flat triangle) and I have to use a similar code for texture co-ordinates U and V. But I don't really understand how to interpolate them. From what I've tried it seems that my code only interpolates down the edges, not between them. What did I miss? Thank you. enter image description here

Here the colour is formed by u as red, v as green and 255 as blue(just for the bottom flatted triangles): enter image description here

 void Rasteriser::TfillBottomFlatTriangle(Vertex vertex1, Vertex vertex2, Vertex vertex3, COLORREF c1, COLORREF c2, COLORREF c3, HDC hdc)
    {
        float slope1 = (vertex2.GetX() - vertex1.GetX()) / (vertex2.GetY() - vertex1.GetY());
        float slope2 = (vertex3.GetX() - vertex1.GetX()) / (vertex3.GetY() - vertex1.GetY());

    //U and V
    float slope1U = (vertex2.GetU() - vertex1.GetU()) / (vertex2.GetY() - vertex1.GetY());
    float slope2U = (vertex3.GetU() - vertex1.GetU()) / (vertex3.GetY() - vertex1.GetY());

    float slope1V = (vertex2.GetV() - vertex1.GetV()) / (vertex2.GetY() - vertex1.GetY());
    float slope2V = (vertex3.GetV() - vertex1.GetV()) / (vertex3.GetY() - vertex1.GetY());

    float x1 = vertex1.GetX();
    float x2 = vertex1.GetX() + 0.5f;

    //U and V
    float x1U = vertex1.GetU();
    float x2U = x1U;

    float x1V = vertex1.GetV();
    float x2V = x1V;


    if (slope2 < slope1)
    {
        float slopeTmp = slope1;
        slope1 = slope2;
        slope2 = slopeTmp;

        float slopeTmpU = slope1U;
        slope1U = slope2U;
        slope2U = slopeTmpU;

        float slopeTmpV = slope1V;
        slope1V = slope2V;
        slope2V = slopeTmpV;

    }

    for (float scanlineY = vertex1.GetY(); scanlineY <= vertex2.GetY(); scanlineY++)
    {
        /* loop over each pixel of horizontal line */

        for (float xPos = ceil(x1); xPos < x2; xPos++)
        {

                float t = (xPos - x1) / (x2 - x1);
                float u = (1 - t) * x1U + t * x2U;
                float v = (1 - t) * x1V + t * x2V;
                COLORREF colour = _model.GetTexture().GetTextureValue((int)u, (int)v);
                SetPixel(hdc, (int)xPos, (int)scanlineY, colour);

        }
        // get new x-coordinate of endpoints of horizontal line 
        x1 += slope1;
        x2 += slope2;
        x1U += slope1U;
        x2U += slope2U;
        x1V += slope1V;
        x2V += slope2V;
    }

}
Monica Hotea
  • 93
  • 1
  • 1
  • 5
  • With any kind of rendering issue, it's usually very helpful to post an image of what it looks like rather than just a textual description. Since you're talking about rendering a .md2 model, I assume these vertices are the projected vertices of a 3D scene? I assume you don't care about perspective correct interpolation for now? Are the input vertices sorted in some specific order? – Michael Kenzel Dec 15 '19 at 20:21
  • @MichaelKenzel l updated my post with a picture. First of all I just want to apply the texture and I'll see if I have time to think about the perspective correction. The vertices are in ascending order. – Monica Hotea Dec 15 '19 at 20:41
  • 1
    Try outputting your `uv` as color, rather than sampling. The resulting image will give you an idea of what's being generated. – 3Dave Dec 15 '19 at 21:23
  • This discussion on triangle interpolation and barycentric coordinates may be useful: https://codeplea.com/triangular-interpolation – 3Dave Dec 15 '19 at 21:24
  • @3Dave, thank you, but I don't want to color it, I need to apply texture to it, I've already read that article... – Monica Hotea Dec 16 '19 at 04:09
  • 1
    Yes, I understand that. The point is to view the UVs that are being generated *as color*. It's a diagnostic technique that has been in use for decades. – 3Dave Dec 16 '19 at 04:32
  • @3Dave The advantage of Monica's code is that you interpolate as you are rasterizing. Otherwise you'd have to find the barycentric coordinates for every pixel, and then perform the interpolation, instead of directly obtaining the interpolated value for every pixel you encounter. She goes over the grid pixel by pixel by sweeping across y and then x and for every pixel she gets an interpolated value of u and v (only there's some error in the code). – iliar Dec 16 '19 at 10:05
  • @iliar yes, I can see what. – 3Dave Dec 16 '19 at 14:15

1 Answers1

0

The problem is here:

    //U and V
    float x1U = vertex1.GetU();
    float x2U = vertex1.GetU() + 0.5f;

    float x1V = vertex1.GetV();
    float x2V = vertex1.GetV() + 0.5f;

0.5 is a lot for uv coordinates, so that tells me it's wrong.

I think the correct way would be something like:

    //U and V
    float x1U = vertex1.GetU();
    float x2U = x1U;

    float x1V = vertex1.GetV();
    float x2V = x1V;

Quick sanity test: for the initial y we have x1U = Vertex1.u and x2U = Vertex1.u. For the final y we have x1U = Vertex2.u and x2U = Vertex3.u am I right?

I think what was happening is that each triangle contained half of your texture, I think we can see it in the image.

EDIT: I implemented the same algorithm in python and got the expected result:

import numpy as np

p = np.array([[0,0],
              [-5,10],
              [10,10]],dtype=np.float)

uv = np.array([[0.1,0.6],
               [0.2,0.5],
               [0.3,0.4]])

slope = (p[[1,2],0] - p[0,0])/(p[[1,2],1] - p[0,1])

slope_uv = (uv[[1,2],:] - uv[0,:])/(p[[1,2],1] - p[0,1]).reshape(-1,1)

x1 = p[0,0]
x2 = p[0,0] + 0.5

uv1 = uv[0,:]
uv2 = np.copy(uv1)

result = []

for scanY in range(int(p[0,1]),int(p[1,1])):
    for x in range(int(np.ceil(x1)),int(x2)):
        t = (x-x1)/(x2-x1)
        u = (1-t) * uv1 + t * uv2
        result.append((x,scanY,u[0],u[1]))
    x1 += slope[0]
    x2 += slope[1]
    uv1 += slope_uv[0,:]
    uv2 += slope_uv[1,:]

#%%
R = np.array(result,dtype=float)
from matplotlib import pyplot as plt

plt.figure()
plt.subplot(2,1,1)
plt.scatter(R[:,0],R[:,1],c = R[:,2])
plt.title('u')
plt.colorbar()
plt.subplot(2,1,2)
plt.scatter(R[:,0],R[:,1],c = R[:,3])
plt.title('v')
plt.colorbar()

With the following result:

enter image description here

I guess that your problem is that the uv values for your vertices are incorrect.

Also, I can see in your code the following line:

                COLORREF colour = _model.GetTexture().GetTextureValue((int)u, (int)v);

Which makes very little sense, since u,v are typically between 0 and 1. I think may be you need to divide by the texture's size before you're calling the GetTextureValue or multiply by it or something.

Community
  • 1
  • 1
iliar
  • 932
  • 6
  • 11
  • I changed the values for x2U and x2V but the result it's the same... – Monica Hotea Dec 16 '19 at 04:10
  • @MonicaHotea as a debugging measure, please output the calculated uv as color. For example red=u and blue=v. Then add the resulting image to your question. – iliar Dec 16 '19 at 05:04
  • I added the photo with red=u, green = v and blue = 255. – Monica Hotea Dec 16 '19 at 10:14
  • @MonicaHotea Please look at what's after the 'EDIT:' – iliar Dec 19 '19 at 11:19
  • so you are saying that the algorithm is correct, thank you very much for your effort. I followed a tutorial to get the uv values so, honestly, I don't know if I got them right or not. – Monica Hotea Dec 20 '19 at 14:36
  • @MonicaHotea uv values are usually supplied by the 3d artist. They should be part of the mesh file you're importing. – iliar Dec 20 '19 at 20:58
  • Yes, I have a PCX file and when I took the uv coordinates from there I related them to a polygon. Every polygon is formed by 3 vertices so before I send the 3 vertices to be drawn I set in every vertex the uv values from the polygon. I don't know why they wouldn't be correct. – Monica Hotea Dec 20 '19 at 21:13
  • @MonicaHotea that doesn't seem right. I suggest you ask another question and link to that pcx file. Send me a message once you do. – iliar Dec 20 '19 at 21:17
  • Thank you but I don't know how to send you a message. I don't think it's possible on stackoverflow. And as I said, I took the steps for uv coordinates from a tutorial. – Monica Hotea Dec 20 '19 at 21:41
  • @MonicaHotea Yes, it's actually impossible to send a message, just add a comment here with a link to your new question if you want to. – iliar Dec 20 '19 at 22:38