软件光栅器六之透视纹理映射

来源:互联网 发布:java面试宝典2015版 编辑:程序博客网 时间:2024/06/06 14:07

我的软件渲染器博客系列的第五篇才讲到win32如何实现像素的绘制,第六篇就直接讲关于透视纹理映射的,这也可能是本系列的最后一篇了,其实整个3D渲染器我用2500多行代码实现,真心的,r如果非常详细的推导,起码能写20篇博客,我实在懒得写了,好了,在看本篇博客前我假设你已经有下面的基础了:

       (1)三角形的光栅化,如果不懂请参见<3D游戏编程大师技巧下册>的9.4小节

       (2)uv的插值,仿射纹理映射,如果不懂请参见<3D游戏编程大师技巧下册>的9.6小节



好吧,我继续分享两篇有关于透视纹理映射的技术博客:深入探索透视纹理映射(上)和深入探索透视纹理映射(下)


其实上面已经给出了能够完整实现一个透视纹理映射的资料,还有什么问题呢?

从深入探索透视纹理映射(上)和深入探索透视纹理映射(下)这两篇技术博客 我们看清了透视投影映射的完整推导过程,但衍生出了两三个问题,我一一说明并给出解决办法。



一,顶点在相机空间的Z值丢失问题。



二,怎么求出像素的Z缓存(像素在屏幕空间的Z值)。






给出我们光栅化的代码:


光栅化平顶三角形(透视纹理映射)的代码:

//用扫描线法绘制平顶三角形,y2=y3//扫描线的ZU,ZV,Z各个扫描步长是不一样的,X和Y的扫描步长为1.0,而ZU//视口空间或者NDC空间的Z值与相机空间的Z值的倒数为线性关系,//支持漫反射光照和环境光void DrawTopTriangleInTexture(Vertex* p1, Vertex* p2, Vertex* p3){float A = SCREEN_FAR / (SCREEN_FAR - SCREEN_NEAR);float B = -(SCREEN_FAR* SCREEN_NEAR) / (SCREEN_FAR - SCREEN_NEAR);int x1 = p1->x;int y1 = p1->y;float u1 = p1->u;float v1 = p1->v;float z1 = p1->z;float nx1 = p1->nx;float ny1 = p1->ny;float nz1 = p1->nz;int  x2;int  y2;float u2;float v2;float z2;float nx2;float ny2;float nz2;int  x3;int  y3;float u3;float v3;float z3;float nx3;float ny3;float nz3;//调整P2的参数在左边,而P3的参数在右边if (p2->x > p3->x){x2 = p3->x;y2 = p3->y;z2 = p3->z;u2 = p3->u;v2 = p3->v;nx2 = p3->nx;ny2 = p3->ny;nz2 = p3->nz;x3 = p2->x;y3 = p2->y;z3 = p2->z;u3 = p2->u;v3 = p2->v;nx3 = p2->nx;ny3 = p2->ny;nz3 = p2->nz;}else{x2 = p2->x;y2 = p2->y;u2 = p2->u;v2 = p2->v;nx2 = p2->nx;ny2 = p2->ny;nz2 = p2->nz;z2 = p2->z;x3 = p3->x;y3 = p3->y;u3 = p3->u;v3 = p3->v;z3 = p3->z;nx3 = p3->nx;ny3 = p3->ny;nz3 = p3->nz;}//扫描线左右端点的值int  left_x;int  right_x;float left_zu;float right_zu;float left_zv;float right_zv;float left_z;float right_z;float left_znx;float right_znx;float left_zny;float right_zny;float left_znz;float right_znz;//每个变量在扫描线每步横跨的距离float ZVOneStep;float ZUOneStep;float ZOneStep;float ZNXOneStep;float ZNYOneStep;float ZNZOneStep;float ZU;float ZV;float Z;int x, y;float ZNX;float ZNY;float ZNZ;//求出各个顶点在相机空间的Z值的倒数float Z1_Camera_Rec = 1.0 / (B / (z1 - A));float Z2_Camera_Rec = 1.0 / (B / (z2 - A));float Z3_Camera_Rec = 1.0 / (B / (z3 - A));//循环里两点式分母不能为0if (y2 == y1 || y3 == y1){return;}//注意 y>=0&& y<=screen_h,我没进行视平面的裁剪for (y = y1; y >= 0 && y <= screen_h&&y >= y2; --y){//三角形左右边的点Xleft_x = (y - y1) *(x2 - x1) / (y2 - y1) + x1 ;right_x = (y - y1) *(x3 - x1) / (y3 - y1) + x1+2;left_zu = (float)(y - y1) *(Z2_Camera_Rec*u2 - Z1_Camera_Rec*u1) / (float)(y2 - y1) + Z1_Camera_Rec*u1;right_zu = (float)(y - y1) *(Z3_Camera_Rec*u3 - Z1_Camera_Rec*u1) / (float)(y3 - y1) + Z1_Camera_Rec*u1;//三角形左右边的点的(1/Z)V值left_zv = (float)(y - y1) *(Z2_Camera_Rec*v2 - Z1_Camera_Rec*v1) / (float)(y2 - y1) + Z1_Camera_Rec*v1;right_zv = (float)(y - y1) *(Z3_Camera_Rec*v3 - Z1_Camera_Rec*v1) / (float)(y3 - y1) + Z1_Camera_Rec*v1;//三角形左右边的点的(1/Z)值,left_z = (float)(y - y1) *(Z2_Camera_Rec - Z1_Camera_Rec) / (float)(y2 - y1) + Z1_Camera_Rec;right_z = (float)(y - y1) *(Z3_Camera_Rec - Z1_Camera_Rec) / (float)(y3 - y1) + Z1_Camera_Rec;//三角形左右边的点的(1/Z)*nx值,left_znx = (float)(y - y1) *(Z2_Camera_Rec*nx2 - Z1_Camera_Rec*nx1) / (float)(y2 - y1) + Z1_Camera_Rec*nx1;right_znx = (float)(y - y1) *(Z3_Camera_Rec*nx3 - Z1_Camera_Rec*nx1) / (float)(y3 - y1) + Z1_Camera_Rec*nx1;//三角形左右边的点的(1/Z)*ny值,left_zny = (float)(y - y1) *(Z2_Camera_Rec*ny2 - Z1_Camera_Rec*ny1) / (float)(y2 - y1) + Z1_Camera_Rec*ny1;right_zny = (float)(y - y1) *(Z3_Camera_Rec*ny3 - Z1_Camera_Rec*ny1) / (float)(y3 - y1) + Z1_Camera_Rec*ny1;//三角形左右边的点的(1/Z)*nz值,left_znz = (float)(y - y1) *(Z2_Camera_Rec*nz2 - Z1_Camera_Rec*nz1) / (float)(y2 - y1) + Z1_Camera_Rec*nz1;right_znz = (float)(y - y1) *(Z3_Camera_Rec*nz3 - Z1_Camera_Rec*nz1) / (float)(y3 - y1) + Z1_Camera_Rec*nz1;//求左右两边的扫描长度因此下面请注意float XLength = (right_x - left_x);if (XLength != 0.0f){//计算ZU,ZV,Z随每单位X距离变化而变化的长度ZUOneStep = (right_zu - left_zu) / XLength;ZVOneStep = (right_zv - left_zv) / XLength;ZOneStep = (right_z - left_z) / XLength;ZNXOneStep = (right_znx - left_znx) / XLength;ZNYOneStep = (right_zny - left_zny) / XLength;ZNZOneStep = (right_znz - left_znz) / XLength;//通过插值得到扫描线上每个像素的ZU,ZV,Z,ZNX,ZNY,ZNZ值//注意x>=0&&x<=screen_wfor (x = left_x + 1, ZU = left_zu, ZV = left_zv, Z = left_z, ZNX = left_znx, ZNY = left_zny, ZNZ = left_znz; x <= right_x&&x >= 0 && x <= screen_w; ++x, ZU += ZUOneStep, ZV += ZVOneStep, Z += ZOneStep, ZNX += ZNXOneStep, ZNY += ZNYOneStep, ZNZ += ZNZOneStep){//求出像素的Z缓冲, 0.0=<zBuffer<=1.0float zBuffer = Z*B + A;//Z缓存测试if (zBuffer >= DepthBuffer[x][y]){continue;}//相除求出像素的纹理坐标和向量float u = ZU / Z;float v = ZV / Z;float nx = ZNX / Z;float ny = ZNY / Z;float nz = ZNZ / Z;Vector NormalVec = BuildVector(nx, ny, nz);float texPosX = u*texture_w;float texPosY = v*texture_h;int texPosX_Int = int(texPosX);int texPosY_Int = int(texPosY);float texPosX_Frac = texPosX - texPosX_Int;float texPosY_Frac = texPosY - texPosY_Int;byte r0, g0, b0;byte r1, g1, b1;byte r2, g2, b2;byte r3, g3, b3;byte r, g, b;float LightR, LightG, LightB;Vector DiffuseColor;//防止访问纹理缓存超出界限if (texPosX_Int == texture_w || texPosY_Int == texture_h)continue;//纹理贴图的非边缘部分可进行双线性插值else if ((texPosX_Int + 1) < texture_w && (texPosY_Int + 1) < texture_h){//Po像素b0 = TextureBuffer[texPosY_Int * texture_w * 3 + (texPosX_Int + 1) * 3 - 1];g0 = TextureBuffer[texPosY_Int* texture_w * 3 + (texPosX_Int + 1) * 3 - 2];r0 = TextureBuffer[texPosY_Int* texture_w * 3 + (texPosX_Int + 1) * 3 - 3];//p1像素b1 = TextureBuffer[texPosY_Int * texture_w * 3 + (texPosX_Int + 2) * 3 - 1];g1 = TextureBuffer[texPosY_Int * texture_w * 3 + (texPosX_Int + 2) * 3 - 2];r1 = TextureBuffer[texPosY_Int * texture_w * 3 + (texPosX_Int + 2) * 3 - 3];//p3像素b2 = TextureBuffer[(texPosY_Int + 1)* texture_w * 3 + (texPosX_Int + 2) * 3 - 1];g2 = TextureBuffer[(texPosY_Int + 1)* texture_w * 3 + (texPosX_Int + 2) * 3 - 2];r2 = TextureBuffer[(texPosY_Int + 1)* texture_w * 3 + (texPosX_Int + 2) * 3 - 3];//p4像素b3 = TextureBuffer[(texPosY_Int + 1) * texture_w * 3 + (texPosX_Int + 1) * 3 - 1];g3 = TextureBuffer[(texPosY_Int + 1)* texture_w * 3 + (texPosX_Int + 1) * 3 - 2];r3 = TextureBuffer[(texPosY_Int + 1)* texture_w * 3 + (texPosX_Int + 1) * 3 - 3];b = b0*(1.0f - texPosX_Frac)*(1 - texPosY_Frac) + b1* texPosX_Frac*(1 - texPosY_Frac) + b2*texPosX_Frac*texPosY_Frac + b3*(1.0f - texPosX_Frac)*texPosY_Frac;g = g0*(1.0f - texPosX_Frac)*(1 - texPosY_Frac) + g1* texPosX_Frac*(1 - texPosY_Frac) + g2*texPosX_Frac*texPosY_Frac + g3*(1.0f - texPosX_Frac)*texPosY_Frac;r = r0*(1.0f - texPosX_Frac)*(1 - texPosY_Frac) + r1* texPosX_Frac*(1 - texPosY_Frac) + r2*texPosX_Frac*texPosY_Frac + r3*(1.0f - texPosX_Frac)*texPosY_Frac;}//纹理贴图的边缘部分可进行点插值else{b = TextureBuffer[texPosY_Int * texture_w * 3 + (texPosX_Int + 1) * 3 - 1];g = TextureBuffer[texPosY_Int* texture_w * 3 + (texPosX_Int + 1) * 3 - 2];r = TextureBuffer[texPosY_Int* texture_w * 3 + (texPosX_Int + 1) * 3 - 3];}//规格化法向量VectorNormalize(&NormalVec);//对单位法向量和漫反射光的反方向进行点乘float DiffuseFactor = VectorDotProduct(&NormalVec, &DiffuseDirRevse);//赋予环境光LightR = AmvientLight.x;LightG = AmvientLight.y;LightB = AmvientLight.z;//求出每个像素的漫反射光DiffuseColor.x = Clamp(DiffuseFactor*DiffuseLight.x, 0, 255.0f);DiffuseColor.y = Clamp(DiffuseFactor*DiffuseLight.y, 0, 255.0f);DiffuseColor.z = Clamp(DiffuseFactor*DiffuseLight.z, 0, 255.0f);//求出每个像素受到的总的光强(漫反射光强加上环境光强)LightR = Clamp(DiffuseColor.x + LightR, 0, 255.0f);LightG = Clamp(DiffuseColor.y + LightG, 0, 255.0f);LightB = Clamp(DiffuseColor.z + LightB, 0, 255.0f);//将光强因子缩小255LightR /= 255.0f;LightG /= 255.0f;LightB /= 255.0f;//最后用光强调节纹理颜色b = b*LightB;g = g*LightG;r = r*LightR;//更改背后缓存BackBuffer[int(y) *  screen_w * 3 + (int(x) + 1) * 3 - 1] = b;BackBuffer[int(y) *  screen_w * 3 + (int(x) + 1) * 3 - 2] = g;BackBuffer[int(y) *  screen_w * 3 + (int(x) + 1) * 3 - 3] = r;//更改深度缓存值颜色DepthBuffer[x][y] = zBuffer;}}}}




光栅化平底三角形(透视纹理映射)的代码:

//用扫描线法绘制平底三角形,y2=y3void DrawBottomTriangleInTexture(Vertex* p1, Vertex* p2, Vertex* p3){float A = SCREEN_FAR / (SCREEN_FAR - SCREEN_NEAR);float B = -(SCREEN_FAR* SCREEN_NEAR) / (SCREEN_FAR - SCREEN_NEAR);int x1 = p1->x;int y1 = p1->y;float u1 = p1->u;float v1 = p1->v;float z1 = p1->z;float nx1 = p1->nx;float ny1 = p1->ny;float nz1 = p1->nz;int  x2;int  y2;float u2;float v2;float z2;float nx2;float ny2;float nz2;int  x3;int  y3;float u3;float v3;float z3;float nx3;float ny3;float nz3; //调整P2的参数在左边,而P3的参数在右边if (p2->x > p3->x){x2 = p3->x;y2 = p3->y;z2 = p3->z;u2 = p3->u;v2 = p3->v;nx2 = p3->nx;ny2 = p3->ny;nz2 = p3->nz;x3 = p2->x;y3 = p2->y;z3 = p2->z;u3 = p2->u;v3 = p2->v;nx3 = p2->nx;ny3 = p2->ny;nz3 = p2->nz;}else{x2 = p2->x;y2 = p2->y;u2 = p2->u;v2 = p2->v;nx2 = p2->nx;ny2 = p2->ny;nz2 = p2->nz;z2 = p2->z;x3 = p3->x;y3 = p3->y;u3 = p3->u;v3 = p3->v;z3 = p3->z;nx3 = p3->nx;ny3 = p3->ny;nz3 = p3->nz;}//扫描线左右端点的值int  left_x;int  right_x;float left_zu;float right_zu;float left_zv;float right_zv;float left_z;float right_z;float left_znx;float right_znx;float left_zny;float right_zny;float left_znz;float right_znz;//每个变量在扫描线每步横跨的距离float ZVOneStep;float ZUOneStep;float ZOneStep;float ZNXOneStep;float ZNYOneStep;float ZNZOneStep;float ZU;float ZV;float Z;    int x, y;float ZNX;float ZNY;float ZNZ;//求出各个顶点在相机空间的Z值的倒数float Z1_Camera_Rec = 1.0 / (B / (z1 - A));float Z2_Camera_Rec = 1.0 / (B / (z2 - A));float Z3_Camera_Rec = 1.0 / (B / (z3 - A));//循环里两点式分母不能为0if (y2 == y1 || y3 == y1){return;}//判断循环条件不能缺少“=”//注意y>=0&&y<=screen_hfor (y = y1; y >= 0 && y <= screen_h&&y <= y2; ++y){//三角形左右边的点X,右边的X+1,来消除黑边,因为我发现是平顶或者平底三角形是右边缘绘制不到,所以加大右边的X值left_x = (y - y1) *(x2 - x1) / (y2 - y1) + x1;right_x = (y - y1) *(x3 - x1) / (y3 - y1) + x1 + 2;//三角形左右边的点的(1/Z)U值left_zu = (float)(y - y1) *(Z2_Camera_Rec*u2 - Z1_Camera_Rec*u1) / (float)(y2 - y1) + Z1_Camera_Rec*u1;right_zu = (float)(y - y1) *(Z3_Camera_Rec*u3 - Z1_Camera_Rec*u1) / (float)(y3 - y1) + Z1_Camera_Rec*u1;//三角形左右边的点的(1/Z)V值left_zv = (float)(y - y1) *(Z2_Camera_Rec*v2 - Z1_Camera_Rec*v1) / (float)(y2 - y1) + Z1_Camera_Rec*v1;right_zv = (float)(y - y1) *(Z3_Camera_Rec*v3 - Z1_Camera_Rec*v1) / (float)(y3 - y1) + Z1_Camera_Rec*v1;//三角形左右边的点的(1/Z)值,left_z = (float)(y - y1) *(Z2_Camera_Rec- Z1_Camera_Rec) / (float)(y2 - y1) + Z1_Camera_Rec;right_z = (float)(y - y1) *(Z3_Camera_Rec - Z1_Camera_Rec) / (float)(y3 - y1) + Z1_Camera_Rec;//三角形左右边的点的(1/Z)*nx值,left_znx = (float)(y - y1) *(Z2_Camera_Rec*nx2 - Z1_Camera_Rec*nx1) / (float)(y2 - y1) + Z1_Camera_Rec*nx1;right_znx = (float)(y - y1) *(Z3_Camera_Rec*nx3 - Z1_Camera_Rec*nx1) / (float)(y3 - y1) + Z1_Camera_Rec*nx1;//三角形左右边的点的(1/Z)*ny值,left_zny = (float)(y - y1) *(Z2_Camera_Rec*ny2 - Z1_Camera_Rec*ny1) / (float)(y2 - y1) + Z1_Camera_Rec*ny1;right_zny = (float)(y - y1) *(Z3_Camera_Rec*ny3 - Z1_Camera_Rec*ny1) / (float)(y3 - y1) + Z1_Camera_Rec*ny1;//三角形左右边的点的(1/Z)*nz值,left_znz = (float)(y - y1) *(Z2_Camera_Rec*nz2 - Z1_Camera_Rec*nz1) / (float)(y2 - y1) + Z1_Camera_Rec*nz1;right_znz = (float)(y - y1) *(Z3_Camera_Rec*nz3 - Z1_Camera_Rec*nz1) / (float)(y3 - y1) + Z1_Camera_Rec*nz1;//此时并不知道float XLength = right_x - left_x;if (XLength != 0.0f){//计算ZU,ZV,Z,ZNX,ZNY,ZNZ随每单位X距离变化而变化的长度ZUOneStep = (right_zu - left_zu) / XLength;ZVOneStep = (right_zv - left_zv) / XLength;ZOneStep = (right_z - left_z) / XLength;ZNXOneStep = (right_znx - left_znx) / XLength;ZNYOneStep = (right_zny - left_zny) / XLength;    ZNZOneStep = (right_znz - left_znz) / XLength;//通过插值得到扫描线上每个像素的ZU,ZV,Z,ZNX,ZNY,ZNZ值//注意x>=0&&x<=screen_wfor (x = left_x + 1, ZU = left_zu, ZV = left_zv, Z = left_z, ZNX = left_znx, ZNY = left_zny, ZNZ = left_znz; x <= right_x&&x >= 0 && x <= screen_w; ++x, ZU += ZUOneStep, ZV += ZVOneStep, Z += ZOneStep, ZNX += ZNXOneStep, ZNY += ZNYOneStep,ZNZ+=ZNZOneStep){//求出像素的Z缓冲, 0.0=<zBuffer<=1.0float zBuffer = Z*B + A;//Z缓存测试if (zBuffer >= DepthBuffer[x][y]){continue;}//相除求出像素的纹理坐标和向量float u = ZU / Z;float v = ZV / Z;float nx = ZNX / Z;float ny = ZNY/ Z;float nz = ZNZ / Z;Vector NormalVec = BuildVector(nx, ny, nz);float texPosX = u*texture_w;float texPosY = v*texture_h;int texPosX_Int = int(texPosX);int texPosY_Int = int(texPosY);float texPosX_Frac = texPosX - texPosX_Int;float texPosY_Frac = texPosY - texPosY_Int;byte r0, g0, b0;byte r1, g1, b1;byte r2, g2, b2;byte r3, g3, b3;byte r, g, b;float LightR, LightG, LightB;Vector DiffuseColor;//防止访问纹理缓存超出界限if (texPosX_Int == texture_w || texPosY_Int == texture_h)continue;//纹理贴图的非边缘部分可进行双线性插值else if ((texPosX_Int + 1) < texture_w && (texPosY_Int + 1) < texture_h){//Po像素b0 = TextureBuffer[texPosY_Int * texture_w * 3 + (texPosX_Int + 1) * 3 - 1];g0 = TextureBuffer[texPosY_Int* texture_w * 3 + (texPosX_Int + 1) * 3 - 2];r0 = TextureBuffer[texPosY_Int* texture_w * 3 + (texPosX_Int + 1) * 3 - 3];//p1像素b1 = TextureBuffer[texPosY_Int * texture_w * 3 + (texPosX_Int + 2) * 3 - 1];g1 = TextureBuffer[texPosY_Int * texture_w * 3 + (texPosX_Int + 2) * 3 - 2];r1 = TextureBuffer[texPosY_Int * texture_w * 3 + (texPosX_Int + 2) * 3 - 3];//p3像素b2 = TextureBuffer[(texPosY_Int + 1)* texture_w * 3 + (texPosX_Int + 2) * 3 - 1];g2 = TextureBuffer[(texPosY_Int + 1)* texture_w * 3 + (texPosX_Int + 2) * 3 - 2];r2 = TextureBuffer[(texPosY_Int + 1)* texture_w * 3 + (texPosX_Int + 2) * 3 - 3];//p4像素b3 = TextureBuffer[(texPosY_Int + 1) * texture_w * 3 + (texPosX_Int + 1) * 3 - 1];g3 = TextureBuffer[(texPosY_Int + 1)* texture_w * 3 + (texPosX_Int + 1) * 3 - 2];r3 = TextureBuffer[(texPosY_Int + 1)* texture_w * 3 + (texPosX_Int + 1) * 3 - 3];b = b0*(1.0f - texPosX_Frac)*(1 - texPosY_Frac) + b1* texPosX_Frac*(1 - texPosY_Frac) + b2*texPosX_Frac*texPosY_Frac + b3*(1.0f - texPosX_Frac)*texPosY_Frac;g = g0*(1.0f - texPosX_Frac)*(1 - texPosY_Frac) + g1* texPosX_Frac*(1 - texPosY_Frac) + g2*texPosX_Frac*texPosY_Frac + g3*(1.0f - texPosX_Frac)*texPosY_Frac;r = r0*(1.0f - texPosX_Frac)*(1 - texPosY_Frac) + r1* texPosX_Frac*(1 - texPosY_Frac) + r2*texPosX_Frac*texPosY_Frac + r3*(1.0f - texPosX_Frac)*texPosY_Frac;}//纹理贴图的边缘部分可进行点插值else{b = TextureBuffer[texPosY_Int * texture_w * 3 + (texPosX_Int + 1) * 3 - 1];g = TextureBuffer[texPosY_Int* texture_w * 3 + (texPosX_Int + 1) * 3 - 2];r = TextureBuffer[texPosY_Int* texture_w * 3 + (texPosX_Int + 1) * 3 - 3];}//规格化法向量VectorNormalize(&NormalVec);//对单位法向量和漫反射光的反方向进行点乘float DiffuseFactor = VectorDotProduct(&NormalVec, &DiffuseDirRevse);//赋予环境光LightR = AmvientLight.x;LightG = AmvientLight.y;LightB = AmvientLight.z;//求出每个像素的漫反射光DiffuseColor.x = Clamp(DiffuseFactor*DiffuseLight.x,0,255.0f);DiffuseColor.y = Clamp(DiffuseFactor*DiffuseLight.y, 0, 255.0f);DiffuseColor.z = Clamp(DiffuseFactor*DiffuseLight.z, 0, 255.0f);//求出每个像素受到的总的光强(漫反射光强加上环境光强)LightR = Clamp(DiffuseColor.x + LightR, 0, 255.0f);LightG = Clamp(DiffuseColor.y + LightG, 0, 255.0f);LightB = Clamp(DiffuseColor.z + LightB, 0, 255.0f);//将光强因子缩小255LightR /= 255.0f;LightG /= 255.0f;LightB /= 255.0f;//最后用光强调节纹理颜色b = b*LightB;g = g*LightG;r = r*LightR;//更改背后缓存颜色BackBuffer[int(y) *  screen_w * 3 + (int(x) + 1) * 3 - 1] = b;BackBuffer[int(y) *  screen_w * 3 + (int(x) + 1) * 3 - 2] = g;BackBuffer[int(y) *  screen_w * 3 + (int(x) + 1) * 3 - 3] = r;//更改深度缓存值DepthBuffer[x][y] = zBuffer;}}}}




三,关于斜三角形的处理。

我们光栅化斜三角形时,是把一个斜三角形分为一个平顶三角形和平顶三角形的,如下面图所示:





斜三角形S1S2S3分为平底三角形s1s2s4和平顶三角形s3s2s4,那么新引入的点s4的纹理坐标,Z坐标,法向量等等怎么求?其实也得按照透视纹理映射的方法来求:

代码实现:

//计算点s4的X,Y,Z,U,V,Z值  //(1/Zcamera)*U (1/Zcamera)*V  1/Zcamera ,(1/Zcamera)*nz,(1/Zcamera)*ny ,(1/Zcamera)*nz  与X和Y呈线性关系//先由Z缓存求出Z相机的值Vertex s4;s4.y = s2.y;s4.x = (s4.y - s1.y) *(s3.x - s1.x) / (s3.y - s1.y) + s1.x;//求出S1和S3的Z在相机空间的值float A = SCREEN_FAR / (SCREEN_FAR - SCREEN_NEAR);float B = -(SCREEN_FAR* SCREEN_NEAR) / (SCREEN_FAR - SCREEN_NEAR);//求出S1和S3的在相机空间的Z的倒数float ZCamera_S3_Rec = 1.0f / (B / (s3.z - A));float ZCamera_S1_Rec = 1.0f / (B / (s1.z - A));//求出S1和S3的 U/Zcamera, V/Camera, 1/Zcamera, nx/Zcamera,ny/Zcamera, nz/Zcamera, float ZU_S3 = s3.u * ZCamera_S3_Rec;float ZU_S1 = s1.u * ZCamera_S1_Rec;float ZV_S3 = s3.v * ZCamera_S3_Rec;float ZV_S1 = s1.v * ZCamera_S1_Rec;float ZNX_S3 = s3.nx * ZCamera_S3_Rec;float ZNX_S1 = s1.nx * ZCamera_S1_Rec;float ZNY_S3 = s3.ny * ZCamera_S3_Rec;float ZNY_S1 = s1.ny * ZCamera_S1_Rec;float ZNZ_S3 = s3.nz * ZCamera_S3_Rec;float ZNZ_S1 = s1.nz * ZCamera_S1_Rec;//对(1/Zcamera)*U (1/Zcamera)*V  1/Zcamera进行线性插值float Z_S4= (s4.y - s1.y) *(ZCamera_S3_Rec - ZCamera_S1_Rec) / (s3.y - s1.y) + ZCamera_S1_Rec;float ZU_S4 = (s4.y - s1.y) *(ZU_S3 - ZU_S1) / (s3.y - s1.y) + ZU_S1;float ZV_S4 = (s4.y - s1.y) *(ZV_S3 - ZV_S1) / (s3.y - s1.y) + ZV_S1;float ZNX_S4 = (s4.y - s1.y) *(ZNX_S3 - ZNX_S1) / (s3.y - s1.y) + ZNX_S1;float ZNY_S4 = (s4.y - s1.y) *(ZNY_S3 - ZNY_S1) / (s3.y - s1.y) + ZNY_S1;float ZNZ_S4 = (s4.y - s1.y) *(ZNZ_S3 - ZNZ_S1) / (s3.y - s1.y) + ZNZ_S1;//求s4的U,V值s4.u = ZU_S4 / Z_S4;s4.v = ZV_S4 / Z_S4;s4.nx= ZNX_S4/ Z_S4;s4.ny = ZNY_S4 / Z_S4;s4.nz = ZNZ_S4 / Z_S4;//s4的Z值可以从Z相机的倒数计算出来 s4.z = Z_S4*B + A;//s1s2s4为平底三角形DrawBottomTriangleInTexture(&s1, &s2, &s4);//s3s4s2为平顶三角形DrawTopTriangleInTexture(&s3, &s2, &s4);


四,光栅化得到像素的属性位于哪个空间?

通过深入探索透视纹理映射(上)和深入探索透视纹理映射(下)两篇技术博客,以及自己亲自实现一个软件光栅器,我明白了如果光栅化的三角形的顶点的属性位于哪个空间,那么相应的光栅化得到的像素的相应属性就是位于哪个空间,比如:如果我们构成三角形的顶点的法向量位于世界空间,则光栅化得到的像素的法向量则位于世界空间;
如果我们构成三角形的顶点的法向量位于局部空间,则光栅化得到的像素的法向量则位于局部空间;这就解释了D3D11的为什么只要光照方向和像素法向量只要是位于同一个空间计算,无论是局部空间还是世界空间,切线空间,甚至是相机空间,计算出的结果都是对的。


最终发出我们程序的截图:


按下“1”,为线框模式:






按下“2”,为Gouraud(也就是Per-Vertex-Shading)着色模式:






按下“3”,为phone(Per_Fragment_Shading)着色模式:

支持透视纹理映射
支持漫反射光和环境光
支持双采样滤波
支持Z缓存测试











下面放出我的整个项目源代码链接:
http://download.csdn.net/detail/qq_29523119/9698519






0 0
原创粉丝点击