今天做了一下午的作业3,踩过的坑真的太多了!!!
下面做个汇总 大家以后也少走些弯路。
1.框架中有错误找不到成员。 这个是C++语言版本的问题,需要更改成C++17
点击扩展设置
在这里更改成C++17
对于作业3中的第一步 光栅化。和作业2差不多,在这一起梳理一下。
光栅化
1.转换得到三角形的坐标 并且构建包围盒
auto v = t.toVector4();
float min_x = std::min(t.v[0].x(), std::min(t.v[1].x(), t.v[2].x()));
float max_x = std::max(t.v[0].x(), std::max(t.v[1].x(), t.v[2].x()));
float min_y = std::min(t.v[0].y(), std::min(t.v[1].y(), t.v[2].y()));
float max_y = std::max(t.v[0].y(), std::max(t.v[1].y(), t.v[2].y()));
值得注意的是 这里涉及到一个向上向下取整的问题。
int x_min = std::floor(min_x);
int x_max = std::ceil(max_x);
int y_min = std::floor(min_y);
int y_max = std::ceil(max_y);
接下来
1.遍历此 bounding box 内的所有像素。
2.使用像素中心的屏幕空间坐标(x + 0.5, y+0.5,t.v)来检查中心点是否在三角形内。(作业1)
3.并且对进行插值的系数计算,求出z
值,进行z-buffer
计算 (作业2)
4.对于需要绘制的像素点,利用上一步计算的插值系数,分别插值出颜色、法向量、UV坐标值、没有经过透视变换的真实3D坐标,初始化结构体fragment_shader_payload
for (int i = x_min; i <= x_max; i++)
{
for (int j = y_min; j <= y_max; j++)
{
if (insideTriangle(i + 0.5, j + 0.5, t.v))
{
//Depth interpolated
auto[alpha, beta, gamma] = computeBarycentric2D(i + 0.5, j + 0.5, t.v);
float Z = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
float zp = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
zp *= Z;
if (zp < depth_buf[get_index(i, j)])
{
// color
auto interpolated_color = interpolate(alpha, beta, gamma, t.color[0], t.color[1], t.color[2], 1);
// normal
auto interpolated_normal = interpolate(alpha, beta, gamma, t.normal[0], t.normal[1], t.normal[2], 1).normalized();
// texture
auto interpolated_texcoords = interpolate(alpha, beta, gamma, t.tex_coords[0], t.tex_coords[1], t.tex_coords[2], 1);
// shadingcoords
auto interpolated_shadingcoords = interpolate(alpha, beta, gamma, view_pos[0], view_pos[1], view_pos[2], 1);
// 用来传递插值结果的结构体
fragment_shader_payload payload(interpolated_color, interpolated_normal, interpolated_texcoords, texture ? &*texture : nullptr);
payload.view_pos = interpolated_shadingcoords;
auto pixel_color = fragment_shader(payload);
// 设置深度
depth_buf[get_index(i, j)] = zp;
// 设置颜色
set_pixel(Eigen::Vector2i(i, j), pixel_color);
}
}
}
}
}
光栅化至此完成。
2. 接下来编写 phong_fragment_shader,函数需要我们填写光照计算,而 phong 模型中光照又由环境光、漫反射和镜面反射三种构成。
漫反射
其他博主在这方面讲解的特别清楚了,我就不再赘述了 最好看下学习笔记再听一次课。
直接上代码
Eigen::Vector3f phong_fragment_shader(const fragment_shader_payload& payload)
{
Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
Eigen::Vector3f kd = payload.color;
Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);
auto l1 = light{{20, 20, 20}, {500, 500, 500}};
auto l2 = light{{-20, 20, 0}, {500, 500, 500}};
std::vector lights = {l1, l2};
Eigen::Vector3f amb_light_intensity{10, 10, 10};
Eigen::Vector3f eye_pos{0, 0, 10};
float p = 150;
Eigen::Vector3f color = payload.color;
Eigen::Vector3f point = payload.view_pos;
Eigen::Vector3f normal = payload.normal;
Eigen::Vector3f result_color = {0, 0, 0};
for (auto& light : lights)
{
// TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular*
// components are. Then, accumulate that result on the *result_color* object.
result_color += ka.cwiseProduct(amb_light_intensity);
Eigen::Vector3f light_pos = light.position - point;
Eigen::Vector3f now_eye = eye_pos - point;
float r2 = light_pos.dot(light_pos);
result_color += kd.cwiseProduct (light.intensity / r2) * std::max(0.0f, normal.normalized().dot(light_pos.normalized()));
result_color += ks.cwiseProduct (light.intensity / r2) * std:: pow(std::max(0.0f, now_eye.normalized().dot(reflect(light_pos, normal).normalized())), p);
}
return result_color * 255.f;