491 lines
15 KiB
C++
491 lines
15 KiB
C++
|
#include <iostream>
|
||
|
#include <opencv2/opencv.hpp>
|
||
|
|
||
|
#include "global.hpp"
|
||
|
#include "rasterizer.hpp"
|
||
|
#include "Triangle.hpp"
|
||
|
#include "Shader.hpp"
|
||
|
#include "Texture.hpp"
|
||
|
#include "OBJ_Loader.h"
|
||
|
|
||
|
Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos)
|
||
|
{
|
||
|
Eigen::Matrix4f view = Eigen::Matrix4f::Identity();
|
||
|
|
||
|
Eigen::Matrix4f translate;
|
||
|
translate << 1,0,0,-eye_pos[0],
|
||
|
0,1,0,-eye_pos[1],
|
||
|
0,0,1,-eye_pos[2],
|
||
|
0,0,0,1;
|
||
|
|
||
|
view = translate*view;
|
||
|
|
||
|
return view;
|
||
|
}
|
||
|
|
||
|
Eigen::Matrix4f get_model_matrix(float angle)
|
||
|
{
|
||
|
Eigen::Matrix4f rotation;
|
||
|
angle = angle * MY_PI / 180.f;
|
||
|
rotation << cos(angle), 0, sin(angle), 0,
|
||
|
0, 1, 0, 0,
|
||
|
-sin(angle), 0, cos(angle), 0,
|
||
|
0, 0, 0, 1;
|
||
|
|
||
|
Eigen::Matrix4f scale;
|
||
|
scale << 2.5, 0, 0, 0,
|
||
|
0, 2.5, 0, 0,
|
||
|
0, 0, 2.5, 0,
|
||
|
0, 0, 0, 1;
|
||
|
|
||
|
Eigen::Matrix4f translate;
|
||
|
translate << 1, 0, 0, 0,
|
||
|
0, 1, 0, 0,
|
||
|
0, 0, 1, 0,
|
||
|
0, 0, 0, 1;
|
||
|
|
||
|
return translate * rotation * scale;
|
||
|
}
|
||
|
|
||
|
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
|
||
|
{
|
||
|
// TODO: Use the same projection matrix from the previous assignments
|
||
|
//init matrix
|
||
|
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
|
||
|
|
||
|
Eigen::Matrix4f persp_to_ortho = Eigen::Matrix4f::Identity();//透视->正交的“挤压”矩阵”
|
||
|
persp_to_ortho <<
|
||
|
zNear,0,0,0,
|
||
|
0,zNear,0,0,
|
||
|
0,0,zNear+zFar,-(zNear * zFar),
|
||
|
0,0,1,0;
|
||
|
|
||
|
float half_eye_fovY= eye_fov/2/180.0 * MY_PI;
|
||
|
|
||
|
//上下颠倒问题
|
||
|
float top = -zNear * tan(half_eye_fovY);
|
||
|
float bottom = -top;
|
||
|
float right = aspect_ratio * top;
|
||
|
float left = -right;
|
||
|
|
||
|
Eigen::Matrix4f ortho_translate = Eigen::Matrix4f::Identity();
|
||
|
ortho_translate <<
|
||
|
1,0,0,-(left+right)/2,
|
||
|
0,1,0,-(top+bottom)/2,
|
||
|
0,0,1,-(zNear-zFar)/2,
|
||
|
0,0,0,1;
|
||
|
|
||
|
Eigen::Matrix4f ortho_scale = Eigen::Matrix4f::Identity();
|
||
|
ortho_scale <<
|
||
|
2/(right-left),0,0,0,
|
||
|
0,2/(top-bottom),0,0,
|
||
|
0,0,2/(zNear-zFar),0,
|
||
|
0,0,0,1;
|
||
|
|
||
|
projection =ortho_scale * ortho_translate * persp_to_ortho * projection;
|
||
|
|
||
|
return projection;
|
||
|
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
Eigen::Vector3f vertex_shader(const vertex_shader_payload& payload)
|
||
|
{
|
||
|
return payload.position;
|
||
|
}
|
||
|
|
||
|
Eigen::Vector3f normal_fragment_shader(const fragment_shader_payload& payload)
|
||
|
{
|
||
|
Eigen::Vector3f return_color = (payload.normal.head<3>().normalized() + Eigen::Vector3f(1.0f, 1.0f, 1.0f)) / 2.f;
|
||
|
Eigen::Vector3f result;
|
||
|
result << return_color.x() * 255, return_color.y() * 255, return_color.z() * 255;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static Eigen::Vector3f reflect(const Eigen::Vector3f& vec, const Eigen::Vector3f& axis)
|
||
|
{
|
||
|
auto costheta = vec.dot(axis);
|
||
|
return (2 * costheta * axis - vec).normalized();
|
||
|
}
|
||
|
|
||
|
struct light
|
||
|
{
|
||
|
Eigen::Vector3f position;
|
||
|
Eigen::Vector3f intensity;
|
||
|
};
|
||
|
|
||
|
Eigen::Vector3f texture_fragment_shader(const fragment_shader_payload& payload)
|
||
|
{
|
||
|
Eigen::Vector3f return_color = {0, 0, 0};
|
||
|
if (payload.texture)
|
||
|
{
|
||
|
// TODO: Get the texture value at the texture coordinates of the current fragment
|
||
|
return_color = payload.texture->getColor(payload.tex_coords.x(),payload.tex_coords.y());
|
||
|
}
|
||
|
Eigen::Vector3f texture_color;
|
||
|
texture_color << return_color.x(), return_color.y(), return_color.z();
|
||
|
|
||
|
Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
|
||
|
Eigen::Vector3f kd = texture_color / 255.f;
|
||
|
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<light> lights = {l1, l2};
|
||
|
Eigen::Vector3f amb_light_intensity{10, 10, 10};
|
||
|
Eigen::Vector3f eye_pos{0, 0, 10};
|
||
|
|
||
|
float p = 150;
|
||
|
|
||
|
Eigen::Vector3f color = texture_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.
|
||
|
Eigen::Vector3f light_dir = (light.position-point).normalized();
|
||
|
Eigen::Vector3f view_dir = (eye_pos-point).normalized();
|
||
|
Eigen::Vector3f half_vector = (light_dir+view_dir).normalized();
|
||
|
// 距离衰减
|
||
|
float r2 = (light.position-point).dot(light.position-point);
|
||
|
//环境光
|
||
|
Eigen::Vector3f La=ka.cwiseProduct(amb_light_intensity);
|
||
|
//漫反射
|
||
|
Eigen::Vector3f Ld=kd.cwiseProduct(light.intensity/r2);
|
||
|
Ld *= std::max(0.0f,normal.normalized().dot(light_dir));
|
||
|
//高光
|
||
|
Eigen::Vector3f Ls=ks.cwiseProduct(light.intensity/r2);
|
||
|
Ls *=std::pow(std::max(0.0f,normal.normalized().dot(half_vector)),p);
|
||
|
|
||
|
result_color+=(La+Ls+Ld);
|
||
|
|
||
|
}
|
||
|
|
||
|
return result_color * 255.f;
|
||
|
}
|
||
|
|
||
|
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<light> 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.
|
||
|
Eigen::Vector3f light_dir = (light.position-point).normalized();
|
||
|
Eigen::Vector3f view_dir = (eye_pos-point).normalized();
|
||
|
Eigen::Vector3f half_vector = (light_dir+view_dir).normalized();
|
||
|
// 距离衰减
|
||
|
float r2 = (light.position-point).dot(light.position-point);
|
||
|
//环境光
|
||
|
Eigen::Vector3f La=ka.cwiseProduct(amb_light_intensity);
|
||
|
//漫反射
|
||
|
Eigen::Vector3f Ld=kd.cwiseProduct(light.intensity/r2);
|
||
|
Ld *= std::max(0.0f,normal.normalized().dot(light_dir));
|
||
|
//高光
|
||
|
Eigen::Vector3f Ls=ks.cwiseProduct(light.intensity/r2);
|
||
|
Ls *=std::pow(std::max(0.0f,normal.normalized().dot(half_vector)),p);
|
||
|
|
||
|
result_color+=(La+Ls+Ld);
|
||
|
}
|
||
|
|
||
|
return result_color * 255.f;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
Eigen::Vector3f displacement_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<light> 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;
|
||
|
|
||
|
float kh = 0.2, kn = 0.1;
|
||
|
|
||
|
// TODO: Implement displacement mapping here
|
||
|
float x = normal.x();
|
||
|
float y = normal.y();
|
||
|
float z = normal.z();
|
||
|
Eigen::Vector3f t = Eigen::Vector3f(x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z));
|
||
|
Eigen::Vector3f b = normal.cross(t);
|
||
|
Eigen::Matrix3f TBN;
|
||
|
TBN <<
|
||
|
t.x(),b.y(),normal.x(),
|
||
|
t.y(),b.x(),normal.y(),
|
||
|
t.z(),b.z(),normal.z();
|
||
|
|
||
|
float u = payload.tex_coords.x();
|
||
|
float v = payload.tex_coords.y();
|
||
|
float w = payload.texture->width;
|
||
|
float h = payload.texture->height;
|
||
|
|
||
|
float dU=kh * kn * (payload.texture->getColor(u+1.0f/w,v).norm()-payload.texture->getColor(u,v).norm());
|
||
|
float dV=kh * kn * (payload.texture->getColor(u,v+1.0f/h).norm()-payload.texture->getColor(u,v).norm());
|
||
|
|
||
|
Eigen::Vector3f ln = Eigen::Vector3f(-dU,-dV,1.0f);
|
||
|
point +=(kn*normal*payload.texture->getColor(u,v).norm());
|
||
|
normal=(TBN *ln).normalized();
|
||
|
// Let n = normal = (x, y, z)
|
||
|
// Vector t = (x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z))
|
||
|
// Vector b = n cross product t
|
||
|
// Matrix TBN = [t b n]
|
||
|
// dU = kh * kn * (h(u+1/w,v)-h(u,v))
|
||
|
// dV = kh * kn * (h(u,v+1/h)-h(u,v))
|
||
|
// Vector ln = (-dU, -dV, 1)
|
||
|
// Position p = p + kn * n * h(u,v)
|
||
|
// Normal n = normalize(TBN * ln)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
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.
|
||
|
Eigen::Vector3f light_dir = (light.position-point).normalized();
|
||
|
Eigen::Vector3f view_dir = (eye_pos-point).normalized();
|
||
|
Eigen::Vector3f half_vector = (light_dir+view_dir).normalized();
|
||
|
// 距离衰减
|
||
|
float r2 = (light.position-point).dot(light.position-point);
|
||
|
//环境光
|
||
|
Eigen::Vector3f La=ka.cwiseProduct(amb_light_intensity);
|
||
|
//漫反射
|
||
|
Eigen::Vector3f Ld=kd.cwiseProduct(light.intensity/r2);
|
||
|
Ld *= std::max(0.0f,normal.normalized().dot(light_dir));
|
||
|
//高光
|
||
|
Eigen::Vector3f Ls=ks.cwiseProduct(light.intensity/r2);
|
||
|
Ls *=std::pow(std::max(0.0f,normal.normalized().dot(half_vector)),p);
|
||
|
|
||
|
result_color+=(La+Ls+Ld);
|
||
|
|
||
|
}
|
||
|
|
||
|
return result_color * 255.f;
|
||
|
}
|
||
|
|
||
|
|
||
|
Eigen::Vector3f bump_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<light> 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;
|
||
|
|
||
|
|
||
|
float kh = 0.2, kn = 0.1;
|
||
|
|
||
|
// TODO: Implement bump mapping here
|
||
|
float x = normal.x();
|
||
|
float y = normal.y();
|
||
|
float z = normal.z();
|
||
|
Eigen::Vector3f t = Eigen::Vector3f(x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z));
|
||
|
Eigen::Vector3f b = normal.cross(t);
|
||
|
Eigen::Matrix3f TBN;
|
||
|
TBN <<
|
||
|
t.x(),b.y(),normal.x(),
|
||
|
t.y(),b.x(),normal.y(),
|
||
|
t.z(),b.z(),normal.z();
|
||
|
|
||
|
float u = payload.tex_coords.x();
|
||
|
float v = payload.tex_coords.y();
|
||
|
float w = payload.texture->width;
|
||
|
float h = payload.texture->height;
|
||
|
|
||
|
float dU=kh * kn * (payload.texture->getColor(u+1.0f/w,v).norm()-payload.texture->getColor(u,v).norm());
|
||
|
float dV=kh * kn * (payload.texture->getColor(u,v+1.0f/h).norm()-payload.texture->getColor(u,v).norm());
|
||
|
|
||
|
Eigen::Vector3f ln = Eigen::Vector3f(-dU,-dV,1.0f);
|
||
|
normal=TBN *ln;
|
||
|
|
||
|
// Let n = normal = (x, y, z)
|
||
|
// Vector t = (x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z))
|
||
|
// Vector b = n cross product t
|
||
|
// Matrix TBN = [t b n]
|
||
|
// dU = kh * kn * (h(u+1/w,v)-h(u,v))
|
||
|
// dV = kh * kn * (h(u,v+1/h)-h(u,v))
|
||
|
// Vector ln = (-dU, -dV, 1)
|
||
|
// Normal n = normalize(TBN * ln)
|
||
|
|
||
|
|
||
|
Eigen::Vector3f result_color = {0, 0, 0};
|
||
|
result_color = normal;
|
||
|
|
||
|
return result_color * 255.f;
|
||
|
}
|
||
|
|
||
|
int main(int argc, const char** argv)
|
||
|
{
|
||
|
std::vector<Triangle*> TriangleList;
|
||
|
|
||
|
float angle = 140.0;
|
||
|
bool command_line = false;
|
||
|
|
||
|
std::string filename = "output.png";
|
||
|
objl::Loader Loader;
|
||
|
std::string obj_path = "../models/spot/";
|
||
|
|
||
|
// Load .obj File
|
||
|
bool loadout = Loader.LoadFile("../models/spot/spot_triangulated_good.obj");
|
||
|
for(auto mesh:Loader.LoadedMeshes)
|
||
|
{
|
||
|
for(int i=0;i<mesh.Vertices.size();i+=3)
|
||
|
{
|
||
|
Triangle* t = new Triangle();
|
||
|
for(int j=0;j<3;j++)
|
||
|
{
|
||
|
t->setVertex(j,Vector4f(mesh.Vertices[i+j].Position.X,mesh.Vertices[i+j].Position.Y,mesh.Vertices[i+j].Position.Z,1.0));
|
||
|
t->setNormal(j,Vector3f(mesh.Vertices[i+j].Normal.X,mesh.Vertices[i+j].Normal.Y,mesh.Vertices[i+j].Normal.Z));
|
||
|
t->setTexCoord(j,Vector2f(mesh.Vertices[i+j].TextureCoordinate.X, mesh.Vertices[i+j].TextureCoordinate.Y));
|
||
|
}
|
||
|
TriangleList.push_back(t);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rst::rasterizer r(700, 700);
|
||
|
|
||
|
auto texture_path = "hmap.jpg";
|
||
|
r.set_texture(Texture(obj_path + texture_path));
|
||
|
|
||
|
std::function<Eigen::Vector3f(fragment_shader_payload)> active_shader = phong_fragment_shader;
|
||
|
|
||
|
if (argc >= 2)
|
||
|
{
|
||
|
command_line = true;
|
||
|
filename = std::string(argv[1]);
|
||
|
|
||
|
if (argc == 3 && std::string(argv[2]) == "texture")
|
||
|
{
|
||
|
std::cout << "Rasterizing using the texture shader\n";
|
||
|
active_shader = texture_fragment_shader;
|
||
|
texture_path = "spot_texture.png";
|
||
|
r.set_texture(Texture(obj_path + texture_path));
|
||
|
}
|
||
|
else if (argc == 3 && std::string(argv[2]) == "normal")
|
||
|
{
|
||
|
std::cout << "Rasterizing using the normal shader\n";
|
||
|
active_shader = normal_fragment_shader;
|
||
|
}
|
||
|
else if (argc == 3 && std::string(argv[2]) == "phong")
|
||
|
{
|
||
|
std::cout << "Rasterizing using the phong shader\n";
|
||
|
active_shader = phong_fragment_shader;
|
||
|
}
|
||
|
else if (argc == 3 && std::string(argv[2]) == "bump")
|
||
|
{
|
||
|
std::cout << "Rasterizing using the bump shader\n";
|
||
|
active_shader = bump_fragment_shader;
|
||
|
}
|
||
|
else if (argc == 3 && std::string(argv[2]) == "displacement")
|
||
|
{
|
||
|
std::cout << "Rasterizing using the bump shader\n";
|
||
|
active_shader = displacement_fragment_shader;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Eigen::Vector3f eye_pos = {0,0,10};
|
||
|
|
||
|
r.set_vertex_shader(vertex_shader);
|
||
|
r.set_fragment_shader(active_shader);
|
||
|
|
||
|
int key = 0;
|
||
|
int frame_count = 0;
|
||
|
|
||
|
if (command_line)
|
||
|
{
|
||
|
r.clear(rst::Buffers::Color | rst::Buffers::Depth);
|
||
|
r.set_model(get_model_matrix(angle));
|
||
|
r.set_view(get_view_matrix(eye_pos));
|
||
|
r.set_projection(get_projection_matrix(45.0, 1, 0.1, 50));
|
||
|
|
||
|
r.draw(TriangleList);
|
||
|
cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
|
||
|
image.convertTo(image, CV_8UC3, 1.0f);
|
||
|
cv::cvtColor(image, image, cv::COLOR_RGB2BGR);
|
||
|
|
||
|
cv::imwrite(filename, image);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
while(key != 27)
|
||
|
{
|
||
|
r.clear(rst::Buffers::Color | rst::Buffers::Depth);
|
||
|
|
||
|
r.set_model(get_model_matrix(angle));
|
||
|
r.set_view(get_view_matrix(eye_pos));
|
||
|
r.set_projection(get_projection_matrix(45.0, 1, 0.1, 50));
|
||
|
|
||
|
//r.draw(pos_id, ind_id, col_id, rst::Primitive::Triangle);
|
||
|
r.draw(TriangleList);
|
||
|
cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
|
||
|
image.convertTo(image, CV_8UC3, 1.0f);
|
||
|
cv::cvtColor(image, image, cv::COLOR_RGB2BGR);
|
||
|
|
||
|
cv::imshow("image", image);
|
||
|
cv::imwrite(filename, image);
|
||
|
key = cv::waitKey(10);
|
||
|
|
||
|
if (key == 'a' )
|
||
|
{
|
||
|
angle -= 0.1;
|
||
|
}
|
||
|
else if (key == 'd')
|
||
|
{
|
||
|
angle += 0.1;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return 0;
|
||
|
}
|