0.简介
之前显示的图形中都是单一颜色的,这次我将纹理加上去,这样就可以显示更加丰富的元素了。
1.纹理
添加纹理功能需要对源程序多个地方修改,因为有了纹理就会增加纹理坐标的处理,所以在定义多边形的时候,就要在在原来顶点的基础上添加纹理坐标。然后根据光打在多边形上的位置,计算纹理坐标。
2.纹理坐标定位
为了能刚好的兼容各种i情况的纹理坐标,采用面积法来定位纹理坐标。
上图三角形就是纹理,P是纹理坐标中待计算的坐标,算法中利用A,B,C的坐标来表示P,所以就要计算A,B,C点对P点的作用分量,例如,三角形PAB,PCB,PCA,分别代表着三个顶点对P的作用大小,对应的面积越大,对P的位置影响就越大,利用这一原理,就能将P用A,B,C三个点组合起来表示,为什么不用向量来表示P呢?因为向量带有方向,而P被A,B,C组合是标量计算,所以带有方向计算就不是很方便。
至于计算面积,就用海伦公式,已知三角形三个变成计算面积的公式。设总面积是Area,那么A对于P的作用分量就是SPCB/Area,其中SPBC代表三角形PBC面积,以此类推计算出三个点的作用大小。
3.修改的代码
首先,三角形中输入的不再是三个点了,而是带了纹理坐标。因为总面积一直 不变,所以就计算一次。
Triangle(vec5 _A, vec5 _B, vec5 _C, Material * _m) :A(_A), B(_B), C(_C)
{
m = _m;
normal = normalize(cross(A.position-B.position, B.position-C.position));
lA = length(B.position-C.position);
lB = length(A.position-C.position);
lC = length(A.position-B.position);
float P = (lA + lB + lC) / 2;
area = sqrt(P*(P-lA)*(P-lB)*(P-lC));
}
设置了vec5类型来做到适用当前情况。
struct vec5
{
//物体坐标
vec3 position;
//纹理UV
vec2 textureUV;
vec5() {};
vec5(vec3 _pos) :position(_pos) { textureUV = vec2(0, 0); }
vec5(vec3 _pos, vec2 uv) :position(_pos), textureUV(uv) {}
vec5(float x, float y, float z)
{
position.x = x;
position.y = y;
position.z = z;
textureUV.x = 0;
textureUV.y = 0;
}
};
对于原来的材质类已经不够用了,所以在基础上拓展了新的材质类。
class HighMaterial : public Material
{
Mat texture;
public:
void setTexture(Mat _mat) { texture = _mat; }
HighMaterial();
vec3 getColor(vec2 uv);
HighMaterial(float _light, float _specular, float _diffuse, float _refract, float _transparent, vec3 _color)
{
light = _light;
specular = _specular;
transparent = _transparent;
diffuse = _diffuse;
refract = _refract;
color = _color;
}
~HighMaterial();
};
高级材质类中可以实现纹理映射,其中getColor就是根据计算出来的纹理坐标来返回对应点的颜色。
vec3 HighMaterial::getColor(vec2 uv)
{
if (texture.cols <= 0)
return color;
int j = abs((int(uv.x * texture.cols)) % texture.cols);
int i = abs((int(uv.y * texture.rows)) % texture.rows);
vec3 tcolor;
tcolor.z = texture.at<Vec3b>(i, j)[0];
tcolor.y = texture.at<Vec3b>(i, j)[1];
tcolor.x = texture.at<Vec3b>(i, j)[2];
return tcolor;
}
在物体颜色计算含税中,也适应了当前修改。
Ray Polygon::sample(Ray out, Ray reflect, Ray refract)
{
Ray res(out.direction, out.position, 0, vec3(0, 0, 0), out.polygon);
//out光线带的是对应物体的法向量值
float cosa = abs(dot(out.normal,-out.direction));//光线入射角和面法向量的cos值
//发光计算
res.color = m->getColor(out.end.textureUV) * m->light * std::fmaxf(cosa,0);
//反射颜色计算
if(reflect.polygon)
res.color += reflect.color * m->specular;
return res;
}
这样一来,既可以适用原来的材质类,也可以适用现在的高级材质,原来多边形类中的材质对象编程指针,这样一来就可以实现多态了。
class Polygon
{
public:
//位置
vec3 position;
//世界坐标矩阵
mat3 transforms;
//材质
Material * m;
virtual Ray intersect(Ray ray) { return Ray(vec3(0, 0, 0), vec3(0, 0, 0), 0, vec3(0, 0, 0), nullptr); }
virtual Ray sample(Ray out, Ray reflect, Ray refract);
virtual vec3 getNormal(vec3 _vector) { return vec3(0, 0, 0); }
Polygon();
~Polygon();
};
最后,本次主要添加的代码就是纹理坐标计算函数,实现在了三角形类中。
vec2 Triangle::getUVCoord(vec5 A, vec5 B, vec5 C, vec5 T)
{
//计算每个三角形需要的边长
float la, lb, lc;
la = length(T.position - wA.position);
lb = length(T.position - wB.position);
lc = length(T.position - wC.position);
float pa = (lA + lb + lc) / 2;
float pb = (lB + la + lc) / 2;
//计算三个三角形的面积
float area_a = sqrt(pa * (pa - lA) * (pa - lb) * (pa - lc));
float area_b = sqrt(pb * (pb - lB) * (pb - la) * (pb - lc));
float area_c = area - area_a - area_b;
return (area_a * A.textureUV + area_b * B.textureUV + area_c * C.textureUV) / area;
}
还好之前设计的时候耦合性没那么大,不然得改不少东西。
4.显示效果
我加载了一个木箱子的纹理。
然后显示出来如下。