GAMES101-现代计算机图形学学习笔记(作业03)

Assignment 03

    • GAMES101-现代计算机图形学学习笔记(作业03)
  • 作业
    • 作业描述
    • 思路

原课程视频链接以及官网
b站视频链接: link.
课程官网链接: link.

作业

作业描述

在这次编程任务中,我们会进一步模拟现代图形技术。我们在代码中添加了 Object Loader(用于加载三维模型), Vertex Shader 与 Fragment Shader,并且支持 了纹理映射。
而在本次实验中,你需要完成的任务是:
1. 修改函数 rasterize_triangle(const Triangle& t) in rasterizer.cpp: 在此 处实现与作业 2 类似的插值算法,实现法向量、颜色、纹理颜色的插值。
2. 修改函数 get_projection_matrix() in main.cpp: 将你自己在之前的实验中 实现的投影矩阵填到此处,此时你可以运行./Rasterizer output.png normal 来观察法向量实现结果。
3. 修改函数 phong_fragment_shader() in main.cpp: 实现 Blinn-Phong 模型计算 Fragment Color.
4. 修改函数 texture_fragment_shader() in main.cpp: 在实现 Blinn-Phong 的基础上,将纹理颜色视为公式中的 kd,实现 Texture Shading Fragment Shader.
5. 修改函数 bump_fragment_shader() in main.cpp: 在实现 Blinn-Phong 的基础上,仔细阅读该函数中的注释,实现 Bump mapping.
6. 修改函数 displacement_fragment_shader() in main.cpp: 在实现 Bump mapping 的基础上,实现 displacement mapping.
7. 双线性纹理插值: 使用双线性插值进行纹理采样, 在 Texture 类中实现一个新方法 Vector3f getColorBilinear(float u, float v) 并 通过 fragment shader 调用它。为了使双线性插值的效果更加明显,你应该 考虑选择更小的纹理图。请同时提交纹理插值与双线性纹理插值的结果,并 进行比较。

思路

①插值

上文我们根据 c o m p u t e B a r y c e n t r i c 2 D ( i + 0.5 , j + 0.5 , t . v ) computeBarycentric2D(i + 0.5, j + 0.5, t.v) computeBarycentric2D(i+0.5,j+0.5,t.v) 函数,利用三角形重心的重心公式,计算出三角形内三个点对中心点的权重:
α = − ( x − x B ) ( y C − y B ) + ( y − y B ) ( x C − x B ) − ( x A − x B ) ( y C − y B ) + ( y A − y B ) ( x C − x B ) β = − ( x − x C ) ( y A − y C ) + ( y − y C ) ( x A − x C ) − ( x B − x C ) ( y A − y C ) + ( y B − y C ) ( x A − x C ) γ = 1 − α − β \begin{array}{l} \alpha=\frac{-\left(x-x_{B}\right)\left(y_{C}-y_{B}\right)+\left(y-y_{B}\right)\left(x_{C}-x_{B}\right)}{-\left(x_{A}-x_{B}\right)\left(y_{C}-y_{B}\right)+\left(y_{A}-y_{B}\right)\left(x_{C}-x_{B}\right)} \\ \beta=\frac{-\left(x-x_{C}\right)\left(y_{A}-y_{C}\right)+\left(y-y_{C}\right)\left(x_{A}-x_{C}\right)}{-\left(x_{B}-x_{C}\right)\left(y_{A}-y_{C}\right)+\left(y_{B}-y_{C}\right)\left(x_{A}-x_{C}\right)} \\ \gamma=1-\alpha-\beta \end{array} α=−(xA​−xB​)(yC​−yB​)+(yA​−yB​)(xC​−xB​)−(x−xB​)(yC​−yB​)+(y−yB​)(xC​−xB​)​β=−(xB​−xC​)(yA​−yC​)+(yB​−yC​)(xA​−xC​)−(x−xC​)(yA​−yC​)+(y−yC​)(xA​−xC​)​γ=1−α−β​
和他们对中心点的加权:
( x , y ) = α A + β B + γ C α + β + γ = 1 \begin{aligned} (x, y)=& \alpha A+\beta B+\gamma C \\ & \alpha+\beta+\gamma=1 \end{aligned} (x,y)=​αA+βB+γCα+β+γ=1​
同理,对法向,颜色,纹理坐标和shading坐标(片段坐标)的插值也可以通过上述公式进行,代码如下:

void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos)
{// TODO: From your HW3, get the triangle rasterization code.// TODO: Inside your rasterization loop://    * v[i].w() is the vertex view space depth value z.//    * Z is interpolated view space depth for the current pixel//    * zp is depth between zNear and zFar, used for z-buffer// 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;// TODO: Interpolate the attributes:// auto interpolated_color// auto interpolated_normal// auto interpolated_texcoords// auto interpolated_shadingcoords// Use: fragment_shader_payload payload( interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);// Use: payload.view_pos = interpolated_shadingcoords;// Use: Instead of passing the triangle's color directly to the frame buffer, pass the color to the shaders first to get the final color;// Use: auto pixel_color = fragment_shader(payload);auto v = t.toVector4();// bounding boxfloat min_x = std::min(v[0][0], std::min(v[1][0], v[2][0]));float max_x = std::max(v[0][0], std::max(v[1][0], v[2][0]));float min_y = std::min(v[0][1], std::min(v[1][1], v[2][1]));float max_y = std::max(v[0][1], std::max(v[1][1], v[2][1]));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);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 interpolatedauto[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);// normalauto interpolated_normal = interpolate(alpha, beta, gamma, t.normal[0], t.normal[1], t.normal[2], 1).normalized();// textureauto interpolated_texcoords = interpolate(alpha, beta, gamma, t.tex_coords[0], t.tex_coords[1], t.tex_coords[2], 1);// shadingcoordsauto 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);}}}}
}

②投影矩阵
前文实现的投影矩阵有一点点问题,这里稍作修改:

Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();Eigen::Matrix4f M_persp2ortho(4, 4);Eigen::Matrix4f M_ortho_scale(4, 4);Eigen::Matrix4f M_ortho_trans(4, 4);float angle = eye_fov * MY_PI / 180.0; // half anglefloat height = zNear * tan(angle) * 2;float width = height * aspect_ratio;auto t = -zNear * tan(angle / 2);auto r = t * aspect_ratio;auto l = -r;auto b = -t;M_persp2ortho << zNear, 0, 0, 0,0, zNear, 0, 0,0, 0, zNear + zFar, -zNear * zFar,0, 0, 1, 0;// 之前我这里用的是 宽和高的长度,实际上r-l 和 t-b 应该是它们的两倍// 为了避免麻烦,我这里改成了坐标形式M_ortho_scale << 2 / (r - l), 0, 0, 0,0, 2 / (t - b), 0, 0,0, 0, 2 / (zNear - zFar), 0,0, 0, 0, 1;M_ortho_trans << 1, 0, 0, -(r + l) / 2,0, 1, 0, -(t + b) / 2,0, 0, 1, -(zNear + zFar) / 2,0, 0, 0, 1;Eigen::Matrix4f M_ortho = M_ortho_scale * M_ortho_trans;projection = M_ortho * M_persp2ortho * projection;return projection;
}

③blinn-phong模型:

  1. 求出光的方向和视角方向和衰减因子r
  2. 求环境光分量,这里直接用环境光颜色ka乘以强度即可,因为环境光默认是不会衰减的
  3. 求漫反射分量,根据公式求即可(kd为片段颜色):
    L d = k d ( I / r 2 ) max ⁡ ( 0 , n ⋅ l ) L_{d}=k_{d}\left(I / r^{2}\right) \max (0, \mathbf{n} \cdot \mathbf{l}) Ld​=kd​(I/r2)max(0,n⋅l)
  4. 求镜面反射分量,根据公式求即可:
    L s = k s ( I / r 2 ) max ⁡ ( 0 , cos ⁡ α ) p L s = k s ( I / r 2 ) max ⁡ ( 0 , n ⋅ h ) p \begin{aligned} &L_{s}=k_{s}\left(I / r^{2}\right) \max (0, \cos \alpha)^{p}\\ &L_{s}=k_{s}\left(I / r^{2}\right) \max (0, \mathbf{n} \cdot \mathbf{h})^{p} \end{aligned} ​Ls​=ks​(I/r2)max(0,cosα)pLs​=ks​(I/r2)max(0,n⋅h)p​
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){// 光的方向Eigen::Vector3f light_dir = light.position - point;// 视线方向Eigen::Vector3f view_dir = eye_pos - point;// 衰减因子float r = light_dir.dot(light_dir);// ambientEigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);// diffuseEigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r);Ld *= std::max(0.0f, normal.normalized().dot(light_dir.normalized()));// specularEigen::Vector3f h = (light_dir + view_dir).normalized();Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r);Ls *= std::pow(std::max(0.0f, normal.normalized().dot(h)), p);result_color += (La + Ld + Ls);}return result_color * 255.f;
}

结果:

④texture:
texture的实现只需要将纹理坐标对应的颜色传给kd即可,需要注意的是我这里的纹理坐标会出现负值,所以我对它进行了限定,即把u,v坐标限定在[0,1]范围内

// 纹理shader
Eigen::Vector3f texture_fragment_shader(const fragment_shader_payload& payload)
{Eigen::Vector3f return_color = { 0, 0, 0 };if (payload.texture){// 获取纹理坐标的颜色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 };Eigen::Vector3f ambient = ka * amb_light_intensity[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;Eigen::Vector3f view_dir = eye_pos - point;float r = light_dir.dot(light_dir);// ambientEigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);// diffuseEigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r);Ld *= std::max(0.0f, normal.normalized().dot(light_dir.normalized()));// specularEigen::Vector3f h = (light_dir + view_dir).normalized();Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r);Ls *= std::pow(std::max(0.0f, normal.normalized().dot(h)), p);result_color += (La + Ld + Ls);}return result_color * 255.f;
}
// 坐标限定
Eigen::Vector3f getColor(float u, float v){// 坐标限定if (u < 0) u = 0;if (u > 1) u = 1;if (v < 0) v = 0;if (v > 1) v = 1;auto u_img = u * width;auto v_img = (1 - v) * height;auto color = image_data.at<cv::Vec3b>(v_img, u_img);return Eigen::Vector3f(color[0], color[1], color[2]);}

结果:

⑤bump:
bump实现时需要注意获取纹理颜色时需要让u 加上 1.0f 而不是1,其次最后的颜色要用归一化的法向量

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// 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)float x = normal.x();float y = normal.y();float z = normal.z();Eigen::Vector3f t{ x * y / std::sqrt(x * x + z * z), std::sqrt(x * x + z * z), z*y / std::sqrt(x * x + z * z) };Eigen::Vector3f b = normal.cross(t);Eigen::Matrix3f TBN;TBN << t.x(), b.x(), normal.x(),t.y(), b.y(), 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{ -dU,-dV,1.0f };normal = TBN * ln;// 归一化Eigen::Vector3f result_color = normal.normalized();return result_color * 255.f;}

结果:

⑥displacement
bump只是将法向量和它的梯度作为模型的颜色可视化了出来,而displacement在此基础上考虑了光照因素

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// 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)float x = normal.x();float y = normal.y();float z = normal.z();Eigen::Vector3f t{ x*y / std::sqrt(x*x + z * z), std::sqrt(x*x + z * z), z*y / std::sqrt(x*x + z * z) };Eigen::Vector3f b = normal.cross(t);Eigen::Matrix3f TBN;TBN << t.x(), b.x(), normal.x(),t.y(), b.y(), 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{ -dU,-dV,1.0f };point += (kn * normal * payload.texture->getColor(u , v).norm());normal = TBN * ln;normal = normal.normalized();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;Eigen::Vector3f view_dir = eye_pos - point;float r = light_dir.dot(light_dir);// ambientEigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);// diffuseEigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r);Ld *= std::max(0.0f, normal.dot(light_dir.normalized()));// specularEigen::Vector3f h = (light_dir + view_dir).normalized();Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r);Ls *= std::pow(std::max(0.0f, normal.dot(h)), p);result_color += (La + Ld + Ls);}return result_color * 255.f;
}

结果:

⑦对纹理进行双线性插值
给定当前坐标(u_img,v_img),先把临近四个点取出来进行双边滤波,如下图所示,t为u方向,s为v方向,所以我们先把u_min,u_max,v_min,v_max取出来,用做表示四个方向上的点,然后先对u方向做两次插值,再对v方向做一次插值,即得到双边滤波结果:

这里还需要注意opencv坐标的高和uv坐标和高是相反的,即opencv图像坐标的y坐标是逐渐增大,而对应的uv坐标下的v坐标是逐渐减小,所以计算中心点的邻域点时,也要注意v_min实际上是在图像中中心点的上面,而v_max是在图像中中心点的下面

Eigen::Vector3f getColorBilinear(float u, float v){if (u < 0) u = 0;if (u > 1) u = 1;if (v < 0) v = 0;if (v > 1) v = 1;auto u_img = u * width;auto v_img = (1 - v) * height;float u_min = std::floor(u_img);float u_max = std::min((float)width, std::ceil(u_img));float v_min = std::floor(v_img);float v_max = std::min((float)height, std::ceil(v_img));auto Q11 = image_data.at<cv::Vec3b>(v_max, u_min);auto Q12 = image_data.at<cv::Vec3b>(v_max, u_max);auto Q21 = image_data.at<cv::Vec3b>(v_min, u_min);auto Q22 = image_data.at<cv::Vec3b>(v_min, u_max);float rs = (u_img - u_min) / (u_max - u_min);float rt = (v_img - v_max) / (v_min - v_max);auto cBot = (1 - rs) * Q11 + rs * Q12;auto cTop = (1 - rs) * Q21 + rs * Q22;auto P = (1 - rt) * cBot + rt * cTop;return Eigen::Vector3f(P[0], P[1], P[2]);}

下面是我对原纹理下采样到300×300的比较结果:
下采样图:

下采样图的双边滤波:

感觉最后这里的纹理双边滤波结果并不是特别好,如果有问题希望能够指出

GAMES101-现代计算机图形学学习笔记(作业03)相关推荐

  1. Games101计算机图形学学习笔记:线性代数-向量

    目录 一.标量与向量 1.标量 2.向量 1.向量的方向 2.向量的长度 3.向量的计算 1.向量加法 2.向量的减法 3.向量的乘法 1.点乘 1.在图形学中我们经常使用点乘来计算两个向量的夹角,比 ...

  2. 计算机图形学学习笔记——Whitted-Style Ray Tracing(GAMES101作业5讲解)

    计算机图形学学习笔记--Whitted-Style Ray Tracing GAMES101作业5讲解 遍历所有的像素生成光线 光线与平面求交 遍历所有的像素生成光线 关于作业五中如何遍历所有的像素, ...

  3. GAMES101-现代计算机图形学学习笔记(作业07)

    GAMES101-现代计算机图形学学习笔记(作业07) Assignment 07 GAMES101-现代计算机图形学学习笔记(作业07) 作业 作业描述 思路 结果 原课程视频链接以及官网 b站视频 ...

  4. GAMES101-现代计算机图形学学习笔记(作业02)

    GAMES101-现代计算机图形学学习笔记(作业02) Assignment 02 GAMES101-现代计算机图形学学习笔记(作业02) 作业 作业描述 需要补充的函数 思路 结果 原课程视频链接以 ...

  5. GAMES101-现代计算机图形学学习笔记(作业01)

    GAMES101-现代计算机图形学学习笔记(作业01) Assignment 01 GAMES101-现代计算机图形学学习笔记(作业01) 作业 作业描述 需要补充的函数 思路 结果 原课程视频链接以 ...

  6. 计算机图形学 学习笔记(五):多边形裁剪(Suther land-Hodgeman),文字裁剪

    接上文 计算机图形学 学习笔记(四):直线裁剪算法:Cohen-Suther land,中点分割法,Liang-Barsky 光栅图形学算法 3.4 多边形裁剪 之前上一篇文章中,我们介绍了直线段的裁 ...

  7. LearnOpenGL学习笔记—入门03:Hello Triangle

    LearnOpenGL学习笔记-入门03:Hello Triangle 0 前言 1 图形渲染管线 2 顶点输入 3 VAO,VBO 3.1 VAO建立 3.2 VBO建立 4 shader 5 绘制 ...

  8. 计算机图形学 学习笔记(七):二维图形变换:平移,比例,旋转,坐标变换等

    接上文 计算机图形学 学习笔记(六):消隐算法:Z-buffer,区间扫描线,Warnock,光栅图形学小结 在图形学中,有两大基本工具:向量分析,图形变换.本文将重点讲解向量和二维图形的变换. 5. ...

  9. 计算机图形学 学习笔记(八):三维图形变换:三维几何变换,投影变换(平行/ 透视 投影)

    接上文 计算机图形学 学习笔记(七):二维图形变换:平移,比例,旋转,坐标变换等 通过三维图形变换,可由简单图形得到复杂图形,三维图形变化则分为三维几何变换和投影变换. 6.1 三维图形几何变换 三维 ...

最新文章

  1. 总结和展望:情感分析研究的新视野
  2. 苹果上新了!新款iPad Pro发布:妙控键盘成最大惊喜
  3. 经典的Fisher-Yates Shuffle算法
  4. 启用Mac系统读写NFTS磁盘
  5. 论文:GeoGebra 在线数学应用函数演示
  6. python调用perl_文本处理,用perl 还是python?
  7. jwt重放攻击_JWT+ASP.NET MVC 时间戳防止重放攻击
  8. 专业计算机英语词汇翻译,计算机专业英语词汇与翻译复习题(已完成)
  9. 「一本通 4.5 例 1」树的统计(树链剖分)
  10. python微信聊天机器人_Python快速搭建会学习的微信聊天机器人
  11. webSphere介绍
  12. CCF中学生计算机程序设计入门篇练习2.4.1(NOI1001 温度转换) pascal
  13. 《洞察设计模式的底层逻辑》读后感
  14. 西湖论剑 2020 loader
  15. java itext图片大小_java – iText:降低图像质量(减少生成的PDF大小)
  16. 仿射密码 python实现
  17. cocosLua 之cocosStudio动画
  18. 洛谷 P5717 【深基3.习8】三角形分类
  19. 28人买可乐喝,3个可乐瓶盖可以换一瓶可乐
  20. C 阶梯式个人所得税计算

热门文章

  1. 阿里云性能测试 PTS 3 月新功能
  2. 字符识别系统仿真–Matlab GUI实现
  3. Redis基础笔记--自认为很详细(●ˇ∀ˇ●)
  4. 如何从初级运营成长为运营总监?
  5. 荔枝派Zero小白(六)
  6. ajax请求二进制流图片并渲染到html中img标签(转)
  7. C语言实现输入任意数量整数
  8. 京牌小客车转父母子女需要有哪些条件,如何办理?
  9. grom (二)关于gorm 线程安全性的考证
  10. 干涉测量技术的应用_百篇科普系列(71)—激光干涉的精密测量技术